808cc0798a7d0634ffb58d07d2a400a9a2871c34da62c0c215b1341b06f7edab4e3a7216198cd04ca33c350c24282ca3060909f76d305a27dee1de5878e743 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import ZeroClipboard from 'zeroclipboard';
  2. import BasePlugin from './../_base';
  3. import {removeClass} from './../../helpers/dom/element';
  4. import {arrayEach} from './../../helpers/array';
  5. import EventManager from './../../eventManager';
  6. import {registerPlugin} from './../../plugins';
  7. import {SEPARATOR} from './../contextMenu/predefinedItems';
  8. /**
  9. * @description
  10. * This plugin adds a copy/paste functionality to the context menu. Due to browser restrictions, it uses ZeroClipboard to allow
  11. * copying data with a click.
  12. *
  13. * @plugin ContextMenuCopyPaste
  14. * @dependencies ContextMenu
  15. */
  16. class ContextMenuCopyPaste extends BasePlugin {
  17. /**
  18. * @param {Object} hotInstance
  19. */
  20. constructor(hotInstance) {
  21. super(hotInstance);
  22. /**
  23. * Instance of {@link EventManager}.
  24. *
  25. * @type {EventManager}
  26. */
  27. this.eventManager = new EventManager(this);
  28. /**
  29. * Path to swf file which is necessary for ZeroClipboard.
  30. *
  31. * @type {String}
  32. */
  33. this.swfPath = null;
  34. /**
  35. * outsideClickDeselectsCache setting cache.
  36. *
  37. * @type {Boolean}
  38. */
  39. this.outsideClickDeselectsCache = null;
  40. }
  41. /**
  42. * Check if the plugin is enabled in the handsontable settings.
  43. *
  44. * @returns {Boolean}
  45. */
  46. isEnabled() {
  47. return this.hot.getSettings().contextMenuCopyPaste;
  48. }
  49. /**
  50. * Enable plugin for this Handsontable instance.
  51. */
  52. enablePlugin() {
  53. if (this.enabled) {
  54. return;
  55. }
  56. if (typeof this.hot.getSettings().contextMenuCopyPaste === 'object') {
  57. this.swfPath = this.hot.getSettings().contextMenuCopyPaste.swfPath;
  58. }
  59. if (typeof ZeroClipboard === 'undefined') {
  60. console.error('To be able to use the Copy/Paste feature from the context menu, you need to manually include ZeroClipboard.js file to your website.');
  61. }
  62. try {
  63. /* eslint-disable no-new */
  64. new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
  65. } catch (exception) {
  66. if (typeof navigator.mimeTypes['application/x-shockwave-flash'] == 'undefined') {
  67. console.error('To be able to use the Copy/Paste feature from the context menu, your browser needs to have Flash Plugin installed.');
  68. }
  69. }
  70. if (this.swfPath) {
  71. ZeroClipboard.config({
  72. swfPath: this.swfPath
  73. });
  74. }
  75. this.hot.addHook('afterContextMenuShow', () => this.onAfterContextMenuShow());
  76. this.hot.addHook('afterContextMenuDefaultOptions', (options) => this.onAfterContextMenuDefaultOptions(options));
  77. this.registerEvents();
  78. super.enablePlugin();
  79. }
  80. /**
  81. * Disable plugin for this Handsontable instance.
  82. */
  83. disablePlugin() {
  84. super.disablePlugin();
  85. }
  86. /**
  87. * @private
  88. */
  89. registerEvents() {
  90. this.eventManager.addEventListener(document, 'mouseenter', () => this.removeCurrentClass());
  91. this.eventManager.addEventListener(document, 'mouseleave', () => this.removeZeroClipboardClass());
  92. }
  93. /**
  94. * Get a value to copy.
  95. *
  96. * @returns {String}
  97. */
  98. getCopyValue() {
  99. this.hot.copyPaste.setCopyableText();
  100. this.hot.copyPaste.copyPasteInstance.triggerCopy();
  101. return this.hot.copyPaste.copyPasteInstance.elTextarea.value;
  102. }
  103. /**
  104. * Add Copy and Paste functionality to the context menu.
  105. *
  106. * @private
  107. * @param {Object} defaultOptions
  108. */
  109. onAfterContextMenuDefaultOptions(defaultOptions) {
  110. defaultOptions.items.unshift({
  111. key: 'copy',
  112. name: 'Copy',
  113. disabled() {
  114. return this.selection.selectedHeader.corner;
  115. },
  116. }, {
  117. key: 'paste',
  118. name: 'Paste',
  119. callback() {
  120. this.copyPaste.triggerPaste();
  121. },
  122. disabled() {
  123. return this.selection.selectedHeader.corner;
  124. },
  125. },
  126. {name: SEPARATOR}
  127. );
  128. }
  129. /**
  130. * After context menu show listener.
  131. *
  132. * @private
  133. */
  134. onAfterContextMenuShow() {
  135. const contextMenu = this.hot.getPlugin('contextMenu');
  136. const data = contextMenu.menu.hotMenu.getSourceData();
  137. // find position of 'copy' option.
  138. arrayEach(data, (item, index) => {
  139. if (item.key === 'copy') {
  140. let zeroClipboardInstance = new ZeroClipboard(contextMenu.menu.hotMenu.getCell(index, 0));
  141. zeroClipboardInstance.off();
  142. zeroClipboardInstance.on('copy', (event) => {
  143. let clipboard = event.clipboardData;
  144. clipboard.setData('text/plain', this.getCopyValue());
  145. this.hot.getSettings().outsideClickDeselects = this.outsideClickDeselectsCache;
  146. });
  147. return false;
  148. }
  149. });
  150. }
  151. /**
  152. * @private
  153. */
  154. removeCurrentClass() {
  155. const contextMenu = this.hot.getPlugin('contextMenu');
  156. if (!contextMenu.enabled) {
  157. return;
  158. }
  159. if (contextMenu.menu.isOpened()) {
  160. let element = contextMenu.menu.hotMenu.rootElement.querySelector('td.current');
  161. if (element) {
  162. removeClass(element, 'current');
  163. }
  164. }
  165. this.outsideClickDeselectsCache = this.hot.getSettings().outsideClickDeselects;
  166. this.hot.getSettings().outsideClickDeselects = false;
  167. }
  168. /**
  169. * @private
  170. */
  171. removeZeroClipboardClass() {
  172. const contextMenu = this.hot.getPlugin('contextMenu');
  173. if (!contextMenu.enabled) {
  174. return;
  175. }
  176. if (contextMenu.menu.isOpened()) {
  177. let element = contextMenu.menu.hotMenu.rootElement.querySelector('td.zeroclipboard-is-hover');
  178. if (element) {
  179. removeClass(element, 'zeroclipboard-is-hover');
  180. }
  181. }
  182. this.hot.getSettings().outsideClickDeselects = this.outsideClickDeselectsCache;
  183. }
  184. }
  185. registerPlugin('contextMenuCopyPaste', ContextMenuCopyPaste);
  186. export default ContextMenuCopyPaste;