You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

274 lines
9.1 KiB

  1. (function ($) {
  2. // register namespace
  3. $.extend(true, window, {
  4. "Slick": {
  5. "CellExternalCopyManager": CellExternalCopyManager
  6. }
  7. });
  8. function CellExternalCopyManager(options) {
  9. /*
  10. This manager enables users to copy/paste data from/to an external Spreadsheet application
  11. Since it is not possible to access directly the clipboard in javascript, the plugin uses
  12. a trick to do it's job. After detecting the keystroke, we dynamically create a textarea
  13. where the browser copies/pastes the serialized data.
  14. options:
  15. copiedCellStyle : sets the css className used for copied cells. default : "copied"
  16. copiedCellStyleLayerKey : sets the layer key for setting css values of copied cells. default : "copy-manager"
  17. dataItemColumnValueExtractor : option to specify a custom column value extractor function
  18. dataItemColumnValueSetter : option to specify a custom column value setter function
  19. */
  20. var _grid;
  21. var _self = this;
  22. var _copiedRanges;
  23. var _options = options || {};
  24. var _copiedCellStyleLayerKey = _options.copiedCellStyleLayerKey || "copy-manager";
  25. var _copiedCellStyle = _options.copiedCellStyle || "copied";
  26. var _clearCopyTI = 0;
  27. var keyCodes = {
  28. 'C':67,
  29. 'V':86
  30. }
  31. function init(grid) {
  32. _grid = grid;
  33. _grid.onKeyDown.subscribe(handleKeyDown);
  34. // we need a cell selection model
  35. var cellSelectionModel = grid.getSelectionModel();
  36. if (!cellSelectionModel){
  37. throw new Error("Selection model is mandatory for this plugin. Please set a selection model on the grid before adding this plugin: grid.setSelectionModel(new Slick.CellSelectionModel())");
  38. }
  39. // we give focus on the grid when a selection is done on it.
  40. // without this, if the user selects a range of cell without giving focus on a particular cell, the grid doesn't get the focus and key stroke handles (ctrl+c) don't work
  41. cellSelectionModel.onSelectedRangesChanged.subscribe(function(e, args){
  42. _grid.focus();
  43. });
  44. }
  45. function destroy() {
  46. _grid.onKeyDown.unsubscribe(handleKeyDown);
  47. }
  48. function getDataItemValueForColumn(item, columnDef) {
  49. if (_options.dataItemColumnValueExtractor) {
  50. return _options.dataItemColumnValueExtractor(item, columnDef);
  51. }
  52. // if a custom getter is not defined, we call serializeValue of the editor to serialize
  53. var editorArgs = {
  54. 'container':$(document), // a dummy container
  55. 'column':columnDef
  56. };
  57. var editor = new columnDef.editor(editorArgs);
  58. var retVal = '';
  59. editor.loadValue(item);
  60. retVal = editor.serializeValue();
  61. editor.destroy();
  62. return retVal;
  63. }
  64. function setDataItemValueForColumn(item, columnDef, value) {
  65. if (_options.dataItemColumnValueSetter) {
  66. return _options.dataItemColumnValueSetter(item, columnDef, value);
  67. }
  68. // if a custom setter is not defined, we call applyValue of the editor to unserialize
  69. var editorArgs = {
  70. 'container':$(document), // a dummy container
  71. 'column':columnDef
  72. };
  73. var editor = new columnDef.editor(editorArgs);
  74. editor.loadValue(item);
  75. editor.applyValue(item, value);
  76. editor.destroy();
  77. }
  78. function _createTextBox(innerText){
  79. var ta = document.createElement('textarea');
  80. ta.style.position = 'absolute';
  81. ta.style.left = '-1000px';
  82. ta.style.top = '-1000px';
  83. ta.value = innerText;
  84. document.body.appendChild(ta);
  85. ta.focus();
  86. return ta;
  87. }
  88. function _decodeTabularData(_grid, ta){
  89. var columns = _grid.getColumns();
  90. var clipText = ta.value;
  91. var clipRows = clipText.split(/[\n\f\r]/);
  92. var clippedRange = [];
  93. document.body.removeChild(ta);
  94. for (var i=0; i<clipRows.length; i++) {
  95. if (clipRows[i]!="")
  96. clippedRange[i] = clipRows[i].split("\t");
  97. }
  98. var selectedCell = _grid.getActiveCell();
  99. var ranges = _grid.getSelectionModel().getSelectedRanges();
  100. var selectedRange = ranges && ranges.length ? ranges[0] : null; // pick only one selection
  101. var activeRow = null;
  102. var activeCell = null;
  103. if (selectedRange){
  104. activeRow = selectedRange.fromRow;
  105. activeCell = selectedRange.fromCell;
  106. } else if (selectedCell){
  107. activeRow = selectedCell.row;
  108. activeCell = selectedCell.cell;
  109. } else {
  110. // we don't know where to paste
  111. return;
  112. }
  113. var oneCellToMultiple = false;
  114. var destH = clippedRange.length;
  115. var destW = clippedRange.length ? clippedRange[0].length : 0;
  116. if (clippedRange.length == 1 && clippedRange[0].length == 1 && selectedRange){
  117. oneCellToMultiple = true;
  118. destH = selectedRange.toRow - selectedRange.fromRow +1;
  119. destW = selectedRange.toCell - selectedRange.fromCell +1;
  120. }
  121. var desty = activeRow;
  122. var destx = activeCell;
  123. var h = 0;
  124. var w = 0;
  125. for (var y = 0; y < destH; y++){
  126. h++;
  127. w=0;
  128. for (var x = 0; x < destW; x++){
  129. w++;
  130. var desty = activeRow + y;
  131. var destx = activeCell + x;
  132. if (desty < data.length && destx < grid.getColumns().length ) {
  133. var nd = _grid.getCellNode(desty, destx);
  134. var dt = _grid.getDataItem(desty);
  135. if (oneCellToMultiple)
  136. setDataItemValueForColumn(dt, columns[destx], clippedRange[0][0]);
  137. else
  138. setDataItemValueForColumn(dt, columns[destx], clippedRange[y][x]);
  139. _grid.updateCell(desty, destx);
  140. }
  141. }
  142. }
  143. var bRange = {
  144. 'fromCell': activeCell,
  145. 'fromRow': activeRow,
  146. 'toCell': activeCell+w-1,
  147. 'toRow': activeRow+h-1
  148. }
  149. markCopySelection([bRange]);
  150. _grid.getSelectionModel().setSelectedRanges([bRange]);
  151. _self.onPasteCells.notify({ranges: [bRange]});
  152. }
  153. function handleKeyDown(e, args) {
  154. var ranges;
  155. if (!_grid.getEditorLock().isActive()) {
  156. if (e.which == $.ui.keyCode.ESCAPE) {
  157. if (_copiedRanges) {
  158. e.preventDefault();
  159. clearCopySelection();
  160. _self.onCopyCancelled.notify({ranges: _copiedRanges});
  161. _copiedRanges = null;
  162. }
  163. }
  164. if (e.which == keyCodes.C && (e.ctrlKey || e.metaKey)) { // CTRL + C
  165. ranges = _grid.getSelectionModel().getSelectedRanges();
  166. if (ranges.length != 0) {
  167. _copiedRanges = ranges;
  168. markCopySelection(ranges);
  169. _self.onCopyCells.notify({ranges: ranges});
  170. var columns = _grid.getColumns();
  171. var clipTextArr = [];
  172. for (var rg = 0; rg < ranges.length; rg++){
  173. var range = ranges[rg];
  174. var clipTextRows = [];
  175. for (var i=range.fromRow; i< range.toRow+1 ; i++){
  176. var clipTextCells = [];
  177. var dt = _grid.getDataItem(i);
  178. for (var j=range.fromCell; j< range.toCell+1 ; j++){
  179. clipTextCells.push(getDataItemValueForColumn(dt, columns[j]));
  180. }
  181. clipTextRows.push(clipTextCells.join("\t"));
  182. }
  183. clipTextArr.push(clipTextRows.join("\r\n"));
  184. }
  185. var clipText = clipTextArr.join('');
  186. var ta = _createTextBox(clipText);
  187. $(ta).select();
  188. setTimeout(function(){
  189. document.body.removeChild(ta);
  190. }, 100);
  191. return false;
  192. }
  193. }
  194. if (e.which == keyCodes.V && (e.ctrlKey || e.metaKey)) { // CTRL + V
  195. var ta = _createTextBox('');
  196. setTimeout(function(){
  197. _decodeTabularData(_grid, ta);
  198. }, 100);
  199. return false;
  200. }
  201. }
  202. }
  203. function markCopySelection(ranges) {
  204. clearCopySelection();
  205. var columns = _grid.getColumns();
  206. var hash = {};
  207. for (var i = 0; i < ranges.length; i++) {
  208. for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
  209. hash[j] = {};
  210. for (var k = ranges[i].fromCell; k <= ranges[i].toCell && k<columns.length; k++) {
  211. hash[j][columns[k].id] = _copiedCellStyle;
  212. }
  213. }
  214. }
  215. _grid.setCellCssStyles(_copiedCellStyleLayerKey, hash);
  216. clearTimeout(_clearCopyTI);
  217. _clearCopyTI = setTimeout(function(){
  218. _self.clearCopySelection();
  219. }, 2000);
  220. }
  221. function clearCopySelection() {
  222. _grid.removeCellCssStyles(_copiedCellStyleLayerKey);
  223. }
  224. $.extend(this, {
  225. "init": init,
  226. "destroy": destroy,
  227. "clearCopySelection": clearCopySelection,
  228. "handleKeyDown":handleKeyDown,
  229. "onCopyCells": new Slick.Event(),
  230. "onCopyCancelled": new Slick.Event(),
  231. "onPasteCells": new Slick.Event()
  232. });
  233. }
  234. })(jQuery);