slickgrid 2.3.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +20 -0
  4. data/README.md +30 -0
  5. data/lib/slickgrid.rb +6 -0
  6. data/lib/slickgrid/version.rb +3 -0
  7. data/slickgrid.gemspec +20 -0
  8. data/vendor/assets/images/slickgrid/CheckboxN.png +0 -0
  9. data/vendor/assets/images/slickgrid/CheckboxY.png +0 -0
  10. data/vendor/assets/images/slickgrid/GrpCheckboxN.png +0 -0
  11. data/vendor/assets/images/slickgrid/GrpCheckboxY.png +0 -0
  12. data/vendor/assets/images/slickgrid/actions.gif +0 -0
  13. data/vendor/assets/images/slickgrid/ajax-loader-small.gif +0 -0
  14. data/vendor/assets/images/slickgrid/arrow-right.gif +0 -0
  15. data/vendor/assets/images/slickgrid/arrow_redo.png +0 -0
  16. data/vendor/assets/images/slickgrid/arrow_right_peppermint.png +0 -0
  17. data/vendor/assets/images/slickgrid/arrow_right_spearmint.png +0 -0
  18. data/vendor/assets/images/slickgrid/arrow_undo.png +0 -0
  19. data/vendor/assets/images/slickgrid/bullet_blue.png +0 -0
  20. data/vendor/assets/images/slickgrid/bullet_star.png +0 -0
  21. data/vendor/assets/images/slickgrid/bullet_toggle_minus.png +0 -0
  22. data/vendor/assets/images/slickgrid/bullet_toggle_plus.png +0 -0
  23. data/vendor/assets/images/slickgrid/calendar.gif +0 -0
  24. data/vendor/assets/images/slickgrid/collapse.gif +0 -0
  25. data/vendor/assets/images/slickgrid/comment_yellow.gif +0 -0
  26. data/vendor/assets/images/slickgrid/delete.png +0 -0
  27. data/vendor/assets/images/slickgrid/down.gif +0 -0
  28. data/vendor/assets/images/slickgrid/drag-handle.png +0 -0
  29. data/vendor/assets/images/slickgrid/editor-helper-bg.gif +0 -0
  30. data/vendor/assets/images/slickgrid/expand.gif +0 -0
  31. data/vendor/assets/images/slickgrid/header-bg.gif +0 -0
  32. data/vendor/assets/images/slickgrid/header-columns-bg.gif +0 -0
  33. data/vendor/assets/images/slickgrid/header-columns-over-bg.gif +0 -0
  34. data/vendor/assets/images/slickgrid/help.png +0 -0
  35. data/vendor/assets/images/slickgrid/info.gif +0 -0
  36. data/vendor/assets/images/slickgrid/listview.gif +0 -0
  37. data/vendor/assets/images/slickgrid/pencil.gif +0 -0
  38. data/vendor/assets/images/slickgrid/row-over-bg.gif +0 -0
  39. data/vendor/assets/images/slickgrid/sort-asc.gif +0 -0
  40. data/vendor/assets/images/slickgrid/sort-asc.png +0 -0
  41. data/vendor/assets/images/slickgrid/sort-desc.gif +0 -0
  42. data/vendor/assets/images/slickgrid/sort-desc.png +0 -0
  43. data/vendor/assets/images/slickgrid/stripes.png +0 -0
  44. data/vendor/assets/images/slickgrid/tag_red.png +0 -0
  45. data/vendor/assets/images/slickgrid/tick.png +0 -0
  46. data/vendor/assets/images/slickgrid/user_identity.gif +0 -0
  47. data/vendor/assets/images/slickgrid/user_identity_plus.gif +0 -0
  48. data/vendor/assets/javascripts/slickgrid.js +5 -0
  49. data/vendor/assets/javascripts/slickgrid/controls/columnpicker.js +221 -0
  50. data/vendor/assets/javascripts/slickgrid/controls/gridmenu.js +429 -0
  51. data/vendor/assets/javascripts/slickgrid/controls/pager.js +154 -0
  52. data/vendor/assets/javascripts/slickgrid/core.js +493 -0
  53. data/vendor/assets/javascripts/slickgrid/dataview.js +1220 -0
  54. data/vendor/assets/javascripts/slickgrid/editors.js +640 -0
  55. data/vendor/assets/javascripts/slickgrid/formatters.js +65 -0
  56. data/vendor/assets/javascripts/slickgrid/grid.js +3990 -0
  57. data/vendor/assets/javascripts/slickgrid/groupitemmetadataprovider.js +172 -0
  58. data/vendor/assets/javascripts/slickgrid/plugins/autotooltips.js +83 -0
  59. data/vendor/assets/javascripts/slickgrid/plugins/cellcopymanager.js +88 -0
  60. data/vendor/assets/javascripts/slickgrid/plugins/cellexternalcopymanager.js +452 -0
  61. data/vendor/assets/javascripts/slickgrid/plugins/cellrangedecorator.js +72 -0
  62. data/vendor/assets/javascripts/slickgrid/plugins/cellrangeselector.js +123 -0
  63. data/vendor/assets/javascripts/slickgrid/plugins/cellselectionmodel.js +168 -0
  64. data/vendor/assets/javascripts/slickgrid/plugins/checkboxselectcolumn.js +202 -0
  65. data/vendor/assets/javascripts/slickgrid/plugins/draggablegrouping.js +207 -0
  66. data/vendor/assets/javascripts/slickgrid/plugins/headerbuttons.js +177 -0
  67. data/vendor/assets/javascripts/slickgrid/plugins/headermenu.js +296 -0
  68. data/vendor/assets/javascripts/slickgrid/plugins/rowdetailview.js +455 -0
  69. data/vendor/assets/javascripts/slickgrid/plugins/rowmovemanager.js +138 -0
  70. data/vendor/assets/javascripts/slickgrid/plugins/rowselectionmodel.js +191 -0
  71. data/vendor/assets/javascripts/slickgrid/remotemodel.js +169 -0
  72. data/vendor/assets/stylesheets/slickgrid.scss +1 -0
  73. data/vendor/assets/stylesheets/slickgrid/controls/columnpicker.css +46 -0
  74. data/vendor/assets/stylesheets/slickgrid/controls/gridmenu.css +113 -0
  75. data/vendor/assets/stylesheets/slickgrid/controls/pager.css +41 -0
  76. data/vendor/assets/stylesheets/slickgrid/default-theme.css +132 -0
  77. data/vendor/assets/stylesheets/slickgrid/grid.css +189 -0
  78. data/vendor/assets/stylesheets/slickgrid/plugins/headerbuttons.css +39 -0
  79. data/vendor/assets/stylesheets/slickgrid/plugins/headermenu.css +59 -0
  80. data/vendor/assets/stylesheets/slickgrid/plugins/rowdetailview.css +39 -0
  81. metadata +165 -0
@@ -0,0 +1,65 @@
1
+ /***
2
+ * Contains basic SlickGrid formatters.
3
+ *
4
+ * NOTE: These are merely examples. You will most likely need to implement something more
5
+ * robust/extensible/localizable/etc. for your use!
6
+ *
7
+ * @module Formatters
8
+ * @namespace Slick
9
+ */
10
+
11
+ (function ($) {
12
+ // register namespace
13
+ $.extend(true, window, {
14
+ "Slick": {
15
+ "Formatters": {
16
+ "PercentComplete": PercentCompleteFormatter,
17
+ "PercentCompleteBar": PercentCompleteBarFormatter,
18
+ "YesNo": YesNoFormatter,
19
+ "Checkmark": CheckmarkFormatter,
20
+ "Checkbox": CheckboxFormatter
21
+
22
+ }
23
+ }
24
+ });
25
+
26
+ function PercentCompleteFormatter(row, cell, value, columnDef, dataContext) {
27
+ if (value == null || value === "") {
28
+ return "-";
29
+ } else if (value < 50) {
30
+ return "<span style='color:red;font-weight:bold;'>" + value + "%</span>";
31
+ } else {
32
+ return "<span style='color:green'>" + value + "%</span>";
33
+ }
34
+ }
35
+
36
+ function PercentCompleteBarFormatter(row, cell, value, columnDef, dataContext) {
37
+ if (value == null || value === "") {
38
+ return "";
39
+ }
40
+
41
+ var color;
42
+
43
+ if (value < 30) {
44
+ color = "red";
45
+ } else if (value < 70) {
46
+ color = "silver";
47
+ } else {
48
+ color = "green";
49
+ }
50
+
51
+ return "<span class='percent-complete-bar' style='background:" + color + ";width:" + value + "%'></span>";
52
+ }
53
+
54
+ function YesNoFormatter(row, cell, value, columnDef, dataContext) {
55
+ return value ? "Yes" : "No";
56
+ }
57
+
58
+ function CheckboxFormatter(row, cell, value, columnDef, dataContext) {
59
+ return '<img class="slick-edit-preclick" src="../slickgrid/' + (value ? "CheckboxY" : "CheckboxN") + '.png">';
60
+ }
61
+
62
+ function CheckmarkFormatter(row, cell, value, columnDef, dataContext) {
63
+ return value ? "<img src='../slickgrid/tick.png'>" : "";
64
+ }
65
+ })(jQuery);
@@ -0,0 +1,3990 @@
1
+ /**
2
+ * @license
3
+ * (c) 2009-2016 Michael Leibman
4
+ * michael{dot}leibman{at}gmail{dot}com
5
+ * http://github.com/mleibman/slickgrid
6
+ *
7
+ * Distributed under MIT license.
8
+ * All rights reserved.
9
+ *
10
+ * SlickGrid v2.3
11
+ *
12
+ * NOTES:
13
+ * Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods.
14
+ * This increases the speed dramatically, but can only be done safely because there are no event handlers
15
+ * or data associated with any cell/row DOM nodes. Cell editors must make sure they implement .destroy()
16
+ * and do proper cleanup.
17
+ */
18
+
19
+ // make sure required JavaScript modules are loaded
20
+ if (typeof jQuery === "undefined") {
21
+ throw new Error("SlickGrid requires jquery module to be loaded");
22
+ }
23
+ if (!jQuery.fn.drag) {
24
+ throw new Error("SlickGrid requires jquery.event.drag module to be loaded");
25
+ }
26
+ if (typeof Slick === "undefined") {
27
+ throw new Error("slick.core.js not loaded");
28
+ }
29
+
30
+
31
+ (function ($) {
32
+ // Slick.Grid
33
+ $.extend(true, window, {
34
+ Slick: {
35
+ Grid: SlickGrid
36
+ }
37
+ });
38
+
39
+ // shared across all grids on the page
40
+ var scrollbarDimensions;
41
+ var maxSupportedCssHeight; // browser's breaking point
42
+
43
+ //////////////////////////////////////////////////////////////////////////////////////////////
44
+ // SlickGrid class implementation (available as Slick.Grid)
45
+
46
+ /**
47
+ * Creates a new instance of the grid.
48
+ * @class SlickGrid
49
+ * @constructor
50
+ * @param {Node} container Container node to create the grid in.
51
+ * @param {Array,Object} data An array of objects for databinding.
52
+ * @param {Array} columns An array of column definitions.
53
+ * @param {Object} options Grid options.
54
+ **/
55
+ function SlickGrid(container, data, columns, options) {
56
+ // settings
57
+ var defaults = {
58
+ explicitInitialization: false,
59
+ rowHeight: 25,
60
+ defaultColumnWidth: 80,
61
+ enableAddRow: false,
62
+ leaveSpaceForNewRows: false,
63
+ editable: false,
64
+ autoEdit: true,
65
+ enableCellNavigation: true,
66
+ enableColumnReorder: true,
67
+ asyncEditorLoading: false,
68
+ asyncEditorLoadDelay: 100,
69
+ forceFitColumns: false,
70
+ enableAsyncPostRender: false,
71
+ asyncPostRenderDelay: 50,
72
+ enableAsyncPostRenderCleanup: false,
73
+ asyncPostRenderCleanupDelay: 40,
74
+ autoHeight: false,
75
+ editorLock: Slick.GlobalEditorLock,
76
+ showHeaderRow: false,
77
+ headerRowHeight: 25,
78
+ createFooterRow: false,
79
+ showFooterRow: false,
80
+ footerRowHeight: 25,
81
+ createPreHeaderPanel: false,
82
+ showPreHeaderPanel: false,
83
+ preHeaderPanelHeight: 25,
84
+ showTopPanel: false,
85
+ topPanelHeight: 25,
86
+ formatterFactory: null,
87
+ editorFactory: null,
88
+ cellFlashingCssClass: "flashing",
89
+ selectedCellCssClass: "selected",
90
+ multiSelect: true,
91
+ enableTextSelectionOnCells: false,
92
+ dataItemColumnValueExtractor: null,
93
+ fullWidthRows: false,
94
+ multiColumnSort: false,
95
+ numberedMultiColumnSort: false,
96
+ tristateMultiColumnSort: false,
97
+ defaultFormatter: defaultFormatter,
98
+ forceSyncScrolling: false,
99
+ addNewRowCssClass: "new-row",
100
+ preserveCopiedSelectionOnPaste: false,
101
+ showCellSelection: true,
102
+ viewportClass: null,
103
+ minRowBuffer: 3,
104
+ emulatePagingWhenScrolling: true
105
+ };
106
+
107
+ var columnDefaults = {
108
+ name: "",
109
+ resizable: true,
110
+ sortable: false,
111
+ minWidth: 30,
112
+ rerenderOnResize: false,
113
+ headerCssClass: null,
114
+ defaultSortAsc: true,
115
+ focusable: true,
116
+ selectable: true
117
+ };
118
+
119
+ // scroller
120
+ var th; // virtual height
121
+ var h; // real scrollable height
122
+ var ph; // page height
123
+ var n; // number of pages
124
+ var cj; // "jumpiness" coefficient
125
+
126
+ var page = 0; // current page
127
+ var offset = 0; // current page offset
128
+ var vScrollDir = 1;
129
+
130
+ // private
131
+ var initialized = false;
132
+ var $container;
133
+ var uid = "slickgrid_" + Math.round(1000000 * Math.random());
134
+ var self = this;
135
+ var $focusSink, $focusSink2;
136
+ var $headerScroller;
137
+ var $headers;
138
+ var $headerRow, $headerRowScroller, $headerRowSpacer;
139
+ var $footerRow, $footerRowScroller, $footerRowSpacer;
140
+ var $preHeaderPanel, $preHeaderPanelScroller, $preHeaderPanelSpacer;
141
+ var $topPanelScroller;
142
+ var $topPanel;
143
+ var $viewport;
144
+ var $canvas;
145
+ var $style;
146
+ var $boundAncestors;
147
+ var stylesheet, columnCssRulesL, columnCssRulesR;
148
+ var viewportH, viewportW;
149
+ var canvasWidth;
150
+ var viewportHasHScroll, viewportHasVScroll;
151
+ var headerColumnWidthDiff = 0, headerColumnHeightDiff = 0, // border+padding
152
+ cellWidthDiff = 0, cellHeightDiff = 0, jQueryNewWidthBehaviour = false;
153
+ var absoluteColumnMinWidth;
154
+ var sortIndicatorCssClass = "slick-sort-indicator";
155
+
156
+ var tabbingDirection = 1;
157
+ var activePosX;
158
+ var activeRow, activeCell;
159
+ var activeCellNode = null;
160
+ var currentEditor = null;
161
+ var serializedEditorValue;
162
+ var editController;
163
+
164
+ var rowsCache = {};
165
+ var renderedRows = 0;
166
+ var numVisibleRows;
167
+ var prevScrollTop = 0;
168
+ var scrollTop = 0;
169
+ var lastRenderedScrollTop = 0;
170
+ var lastRenderedScrollLeft = 0;
171
+ var prevScrollLeft = 0;
172
+ var scrollLeft = 0;
173
+
174
+ var selectionModel;
175
+ var selectedRows = [];
176
+
177
+ var plugins = [];
178
+ var cellCssClasses = {};
179
+
180
+ var columnsById = {};
181
+ var sortColumns = [];
182
+ var columnPosLeft = [];
183
+ var columnPosRight = [];
184
+
185
+ var pagingActive = false;
186
+ var pagingIsLastPage = false;
187
+
188
+ // async call handles
189
+ var h_editorLoader = null;
190
+ var h_render = null;
191
+ var h_postrender = null;
192
+ var h_postrenderCleanup = null;
193
+ var postProcessedRows = {};
194
+ var postProcessToRow = null;
195
+ var postProcessFromRow = null;
196
+ var postProcessedCleanupQueue = [];
197
+ var postProcessgroupId = 0;
198
+
199
+ // perf counters
200
+ var counter_rows_rendered = 0;
201
+ var counter_rows_removed = 0;
202
+
203
+ // These two variables work around a bug with inertial scrolling in Webkit/Blink on Mac.
204
+ // See http://crbug.com/312427.
205
+ var rowNodeFromLastMouseWheelEvent; // this node must not be deleted while inertial scrolling
206
+ var zombieRowNodeFromLastMouseWheelEvent; // node that was hidden instead of getting deleted
207
+ var zombieRowCacheFromLastMouseWheelEvent; // row cache for above node
208
+ var zombieRowPostProcessedFromLastMouseWheelEvent; // post processing references for above node
209
+
210
+ // store css attributes if display:none is active in container or parent
211
+ var cssShow = { position: 'absolute', visibility: 'hidden', display: 'block' };
212
+ var $hiddenParents;
213
+ var oldProps = [];
214
+ var columnResizeDragging = false;
215
+
216
+ //////////////////////////////////////////////////////////////////////////////////////////////
217
+ // Initialization
218
+
219
+ function init() {
220
+ if (container instanceof jQuery) {
221
+ $container = container;
222
+ } else {
223
+ $container = $(container);
224
+ }
225
+ if ($container.length < 1) {
226
+ throw new Error("SlickGrid requires a valid container, " + container + " does not exist in the DOM.");
227
+ }
228
+
229
+ cacheCssForHiddenInit();
230
+
231
+ // calculate these only once and share between grid instances
232
+ maxSupportedCssHeight = maxSupportedCssHeight || getMaxSupportedCssHeight();
233
+
234
+ options = $.extend({}, defaults, options);
235
+ validateAndEnforceOptions();
236
+ columnDefaults.width = options.defaultColumnWidth;
237
+
238
+ columnsById = {};
239
+ for (var i = 0; i < columns.length; i++) {
240
+ var m = columns[i] = $.extend({}, columnDefaults, columns[i]);
241
+ columnsById[m.id] = i;
242
+ if (m.minWidth && m.width < m.minWidth) {
243
+ m.width = m.minWidth;
244
+ }
245
+ if (m.maxWidth && m.width > m.maxWidth) {
246
+ m.width = m.maxWidth;
247
+ }
248
+ }
249
+
250
+ // validate loaded JavaScript modules against requested options
251
+ if (options.enableColumnReorder && !$.fn.sortable) {
252
+ throw new Error("SlickGrid's 'enableColumnReorder = true' option requires jquery-ui.sortable module to be loaded");
253
+ }
254
+
255
+ editController = {
256
+ "commitCurrentEdit": commitCurrentEdit,
257
+ "cancelCurrentEdit": cancelCurrentEdit
258
+ };
259
+
260
+ $container
261
+ .empty()
262
+ .css("overflow", "hidden")
263
+ .css("outline", 0)
264
+ .addClass(uid)
265
+ .addClass("ui-widget");
266
+
267
+ // set up a positioning container if needed
268
+ if (!/relative|absolute|fixed/.test($container.css("position"))) {
269
+ $container.css("position", "relative");
270
+ }
271
+
272
+ $focusSink = $("<div tabIndex='0' hideFocus style='position:fixed;width:0;height:0;top:0;left:0;outline:0;'></div>").appendTo($container);
273
+
274
+ if (options.createPreHeaderPanel) {
275
+ $preHeaderPanelScroller = $("<div class='slick-preheader-panel ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container);
276
+ $preHeaderPanel = $("<div />").appendTo($preHeaderPanelScroller);
277
+ $preHeaderPanelSpacer = $("<div style='display:block;height:1px;position:absolute;top:0;left:0;'></div>")
278
+ .appendTo($preHeaderPanelScroller);
279
+
280
+ if (!options.showPreHeaderPanel) {
281
+ $preHeaderPanelScroller.hide();
282
+ }
283
+ }
284
+
285
+ $headerScroller = $("<div class='slick-header ui-state-default' />").appendTo($container);
286
+ $headers = $("<div class='slick-header-columns' style='left:-1000px' />").appendTo($headerScroller);
287
+
288
+ $headerRowScroller = $("<div class='slick-headerrow ui-state-default' />").appendTo($container);
289
+ $headerRow = $("<div class='slick-headerrow-columns' />").appendTo($headerRowScroller);
290
+ $headerRowSpacer = $("<div style='display:block;height:1px;position:absolute;top:0;left:0;'></div>")
291
+ .appendTo($headerRowScroller);
292
+
293
+ $topPanelScroller = $("<div class='slick-top-panel-scroller ui-state-default' />").appendTo($container);
294
+ $topPanel = $("<div class='slick-top-panel' style='width:10000px' />").appendTo($topPanelScroller);
295
+
296
+ if (!options.showTopPanel) {
297
+ $topPanelScroller.hide();
298
+ }
299
+
300
+ if (!options.showHeaderRow) {
301
+ $headerRowScroller.hide();
302
+ }
303
+
304
+ $viewport = $("<div class='slick-viewport' style='width:100%;overflow:auto;outline:0;position:relative;;'>").appendTo($container);
305
+ $viewport.css("overflow-y", options.autoHeight ? "hidden" : "auto");
306
+ if (options.viewportClass) $viewport.toggleClass(options.viewportClass, true);
307
+
308
+ $canvas = $("<div class='grid-canvas' />").appendTo($viewport);
309
+
310
+ scrollbarDimensions = scrollbarDimensions || measureScrollbar();
311
+
312
+ if ($preHeaderPanelSpacer) $preHeaderPanelSpacer.css("width", getCanvasWidth() + scrollbarDimensions.width + "px");
313
+ $headers.width(getHeadersWidth());
314
+ $headerRowSpacer.css("width", getCanvasWidth() + scrollbarDimensions.width + "px");
315
+
316
+
317
+
318
+ if (options.createFooterRow) {
319
+ $footerRowScroller = $("<div class='slick-footerrow ui-state-default' />").appendTo($container);
320
+ $footerRow = $("<div class='slick-footerrow-columns' />").appendTo($footerRowScroller);
321
+ $footerRowSpacer = $("<div style='display:block;height:1px;position:absolute;top:0;left:0;'></div>")
322
+ .css("width", getCanvasWidth() + scrollbarDimensions.width + "px")
323
+ .appendTo($footerRowScroller);
324
+
325
+ if (!options.showFooterRow) {
326
+ $footerRowScroller.hide();
327
+ }
328
+ }
329
+
330
+ if (options.numberedMultiColumnSort) { sortIndicatorCssClass = "slick-sort-indicator-numbered"; }
331
+
332
+ $focusSink2 = $focusSink.clone().appendTo($container);
333
+
334
+ if (!options.explicitInitialization) {
335
+ finishInitialization();
336
+ }
337
+ }
338
+
339
+ function finishInitialization() {
340
+ if (!initialized) {
341
+ initialized = true;
342
+
343
+ viewportW = parseFloat($.css($container[0], "width", true));
344
+
345
+ // header columns and cells may have different padding/border skewing width calculations (box-sizing, hello?)
346
+ // calculate the diff so we can set consistent sizes
347
+ measureCellPaddingAndBorder();
348
+
349
+ // for usability reasons, all text selection in SlickGrid is disabled
350
+ // with the exception of input and textarea elements (selection must
351
+ // be enabled there so that editors work as expected); note that
352
+ // selection in grid cells (grid body) is already unavailable in
353
+ // all browsers except IE
354
+ disableSelection($headers); // disable all text selection in header (including input and textarea)
355
+
356
+ if (!options.enableTextSelectionOnCells) {
357
+ // disable text selection in grid cells except in input and textarea elements
358
+ // (this is IE-specific, because selectstart event will only fire in IE)
359
+ $viewport.on("selectstart.ui", function (event) {
360
+ return $(event.target).is("input,textarea");
361
+ });
362
+ }
363
+
364
+ updateColumnCaches();
365
+ createColumnHeaders();
366
+ setupColumnSort();
367
+ createCssRules();
368
+ resizeCanvas();
369
+ bindAncestorScrollEvents();
370
+
371
+ $container
372
+ .on("resize.slickgrid", resizeCanvas);
373
+ $viewport
374
+ //.on("click", handleClick)
375
+ .on("scroll", handleScroll);
376
+ $headerScroller
377
+ //.on("scroll", handleHeaderScroll)
378
+ .on("contextmenu", handleHeaderContextMenu)
379
+ .on("click", handleHeaderClick)
380
+ .on("mouseenter", ".slick-header-column", handleHeaderMouseEnter)
381
+ .on("mouseleave", ".slick-header-column", handleHeaderMouseLeave);
382
+ $headerRowScroller
383
+ .on("scroll", handleHeaderRowScroll);
384
+
385
+ if (options.createFooterRow) {
386
+ $footerRowScroller
387
+ .on("scroll", handleFooterRowScroll);
388
+ }
389
+
390
+ if (options.createPreHeaderPanel) {
391
+ $preHeaderPanelScroller
392
+ .on("scroll", handlePreHeaderPanelScroll);
393
+ }
394
+
395
+ $focusSink.add($focusSink2)
396
+ .on("keydown", handleKeyDown);
397
+ $canvas
398
+ .on("keydown", handleKeyDown)
399
+ .on("click", handleClick)
400
+ .on("dblclick", handleDblClick)
401
+ .on("contextmenu", handleContextMenu)
402
+ .on("draginit", handleDragInit)
403
+ .on("dragstart", {distance: 3}, handleDragStart)
404
+ .on("drag", handleDrag)
405
+ .on("dragend", handleDragEnd)
406
+ .on("mouseenter", ".slick-cell", handleMouseEnter)
407
+ .on("mouseleave", ".slick-cell", handleMouseLeave);
408
+
409
+ // Work around http://crbug.com/312427.
410
+ if (navigator.userAgent.toLowerCase().match(/webkit/) &&
411
+ navigator.userAgent.toLowerCase().match(/macintosh/)) {
412
+ $canvas.on("mousewheel", handleMouseWheel);
413
+ }
414
+ restoreCssFromHiddenInit();
415
+ }
416
+ }
417
+
418
+ function cacheCssForHiddenInit() {
419
+ // handle display:none on container or container parents
420
+ $hiddenParents = $container.parents().addBack().not(':visible');
421
+ $hiddenParents.each(function() {
422
+ var old = {};
423
+ for ( var name in cssShow ) {
424
+ old[ name ] = this.style[ name ];
425
+ this.style[ name ] = cssShow[ name ];
426
+ }
427
+ oldProps.push(old);
428
+ });
429
+ }
430
+
431
+ function restoreCssFromHiddenInit() {
432
+ // finish handle display:none on container or container parents
433
+ // - put values back the way they were
434
+ $hiddenParents.each(function(i) {
435
+ var old = oldProps[i];
436
+ for ( var name in cssShow ) {
437
+ this.style[ name ] = old[ name ];
438
+ }
439
+ });
440
+ }
441
+
442
+ function registerPlugin(plugin) {
443
+ plugins.unshift(plugin);
444
+ plugin.init(self);
445
+ }
446
+
447
+ function unregisterPlugin(plugin) {
448
+ for (var i = plugins.length; i >= 0; i--) {
449
+ if (plugins[i] === plugin) {
450
+ if (plugins[i].destroy) {
451
+ plugins[i].destroy();
452
+ }
453
+ plugins.splice(i, 1);
454
+ break;
455
+ }
456
+ }
457
+ }
458
+
459
+ function setSelectionModel(model) {
460
+ if (selectionModel) {
461
+ selectionModel.onSelectedRangesChanged.unsubscribe(handleSelectedRangesChanged);
462
+ if (selectionModel.destroy) {
463
+ selectionModel.destroy();
464
+ }
465
+ }
466
+
467
+ selectionModel = model;
468
+ if (selectionModel) {
469
+ selectionModel.init(self);
470
+ selectionModel.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged);
471
+ }
472
+ }
473
+
474
+ function getSelectionModel() {
475
+ return selectionModel;
476
+ }
477
+
478
+ function getCanvasNode() {
479
+ return $canvas[0];
480
+ }
481
+
482
+ function measureScrollbar() {
483
+ var $outerdiv = $('<div class="' + $viewport.className + '" style="position:absolute; top:-10000px; left:-10000px; overflow:auto; width:100px; height:100px;"></div>').appendTo($viewport);
484
+ var $innerdiv = $('<div style="width:200px; height:200px; overflow:auto;"></div>').appendTo($outerdiv);
485
+ var dim = {
486
+ width: $outerdiv[0].offsetWidth - $outerdiv[0].clientWidth,
487
+ height: $outerdiv[0].offsetHeight - $outerdiv[0].clientHeight
488
+ };
489
+ $innerdiv.remove();
490
+ $outerdiv.remove();
491
+ return dim;
492
+ }
493
+
494
+ function getColumnTotalWidth(includeScrollbar) {
495
+ var totalWidth = 0;
496
+ for (var i = 0, ii = columns.length; i < ii; i++) {
497
+ var width = columns[i].width;
498
+ totalWidth += width;
499
+ }
500
+ if (includeScrollbar) {
501
+ totalWidth += scrollbarDimensions.width;
502
+ }
503
+ return totalWidth;
504
+ }
505
+
506
+ function getHeadersWidth() {
507
+ var headersWidth = getColumnTotalWidth(!options.autoHeight);
508
+ return Math.max(headersWidth, viewportW) + 1000;
509
+ }
510
+
511
+ function getCanvasWidth() {
512
+ var availableWidth = viewportHasVScroll ? viewportW - scrollbarDimensions.width : viewportW;
513
+ var rowWidth = 0;
514
+ var i = columns.length;
515
+ while (i--) {
516
+ rowWidth += columns[i].width;
517
+ }
518
+ return options.fullWidthRows ? Math.max(rowWidth, availableWidth) : rowWidth;
519
+ }
520
+
521
+ function updateCanvasWidth(forceColumnWidthsUpdate) {
522
+ var oldCanvasWidth = canvasWidth;
523
+ canvasWidth = getCanvasWidth();
524
+
525
+ if (canvasWidth != oldCanvasWidth) {
526
+ $canvas.width(canvasWidth);
527
+ $headerRow.width(canvasWidth);
528
+ if (options.createFooterRow) { $footerRow.width(canvasWidth); }
529
+ if (options.createPreHeaderPanel) { $preHeaderPanel.width(canvasWidth); }
530
+ $headers.width(getHeadersWidth());
531
+ viewportHasHScroll = (canvasWidth > viewportW - scrollbarDimensions.width);
532
+ }
533
+
534
+ var w=canvasWidth + (viewportHasVScroll ? scrollbarDimensions.width : 0);
535
+ $headerRowSpacer.width(w);
536
+ if (options.createFooterRow) { $footerRowSpacer.width(w); }
537
+ if (options.createPreHeaderPanel) { $preHeaderPanelSpacer.width(w); }
538
+
539
+ if (canvasWidth != oldCanvasWidth || forceColumnWidthsUpdate) {
540
+ applyColumnWidths();
541
+ }
542
+ }
543
+
544
+ function disableSelection($target) {
545
+ if ($target && $target.jquery) {
546
+ $target
547
+ .attr("unselectable", "on")
548
+ .css("MozUserSelect", "none")
549
+ .on("selectstart.ui", function () {
550
+ return false;
551
+ }); // from jquery:ui.core.js 1.7.2
552
+ }
553
+ }
554
+
555
+ function getMaxSupportedCssHeight() {
556
+ var supportedHeight = 1000000;
557
+ // FF reports the height back but still renders blank after ~6M px
558
+ var testUpTo = navigator.userAgent.toLowerCase().match(/firefox/) ? 6000000 : 1000000000;
559
+ var div = $("<div style='display:none' />").appendTo(document.body);
560
+
561
+ while (true) {
562
+ var test = supportedHeight * 2;
563
+ div.css("height", test);
564
+ if (test > testUpTo || div.height() !== test) {
565
+ break;
566
+ } else {
567
+ supportedHeight = test;
568
+ }
569
+ }
570
+
571
+ div.remove();
572
+ return supportedHeight;
573
+ }
574
+
575
+ function getUID() {
576
+ return uid;
577
+ }
578
+
579
+ function getHeaderColumnWidthDiff() {
580
+ return headerColumnWidthDiff;
581
+ }
582
+
583
+ function getScrollbarDimensions() {
584
+ return scrollbarDimensions;
585
+ }
586
+
587
+ // TODO: this is static. need to handle page mutation.
588
+ function bindAncestorScrollEvents() {
589
+ var elem = $canvas[0];
590
+ while ((elem = elem.parentNode) != document.body && elem != null) {
591
+ // bind to scroll containers only
592
+ if (elem == $viewport[0] || elem.scrollWidth != elem.clientWidth || elem.scrollHeight != elem.clientHeight) {
593
+ var $elem = $(elem);
594
+ if (!$boundAncestors) {
595
+ $boundAncestors = $elem;
596
+ } else {
597
+ $boundAncestors = $boundAncestors.add($elem);
598
+ }
599
+ $elem.on("scroll." + uid, handleActiveCellPositionChange);
600
+ }
601
+ }
602
+ }
603
+
604
+ function unbindAncestorScrollEvents() {
605
+ if (!$boundAncestors) {
606
+ return;
607
+ }
608
+ $boundAncestors.off("scroll." + uid);
609
+ $boundAncestors = null;
610
+ }
611
+
612
+ function updateColumnHeader(columnId, title, toolTip) {
613
+ if (!initialized) { return; }
614
+ var idx = getColumnIndex(columnId);
615
+ if (idx == null) {
616
+ return;
617
+ }
618
+
619
+ var columnDef = columns[idx];
620
+ var $header = $headers.children().eq(idx);
621
+ if ($header) {
622
+ if (title !== undefined) {
623
+ columns[idx].name = title;
624
+ }
625
+ if (toolTip !== undefined) {
626
+ columns[idx].toolTip = toolTip;
627
+ }
628
+
629
+ trigger(self.onBeforeHeaderCellDestroy, {
630
+ "node": $header[0],
631
+ "column": columnDef,
632
+ "grid": self
633
+ });
634
+
635
+ $header
636
+ .attr("title", toolTip || "")
637
+ .children().eq(0).html(title);
638
+
639
+ trigger(self.onHeaderCellRendered, {
640
+ "node": $header[0],
641
+ "column": columnDef,
642
+ "grid": self
643
+ });
644
+ }
645
+ }
646
+
647
+ function getHeader() {
648
+ return $header[0];
649
+ }
650
+
651
+ function getHeaderColumn(columnIdOrIdx) {
652
+ var idx = (typeof columnIdOrIdx === "number" ? columnIdOrIdx : getColumnIndex[columnIdOrIdx]);
653
+ var $rtn = $header.children().eq(idx);
654
+ return $rtn && $rtn[0];
655
+ }
656
+
657
+ function getHeaderRow() {
658
+ return $headerRow[0];
659
+ }
660
+
661
+ function getFooterRow() {
662
+ return $footerRow[0];
663
+ }
664
+
665
+ function getPreHeaderPanel() {
666
+ return $preHeaderPanel[0];
667
+ }
668
+
669
+ function getHeaderRowColumn(columnIdOrIdx) {
670
+ var idx = (typeof columnIdOrIdx === "number" ? columnIdOrIdx : getColumnIndex[columnIdOrIdx]);
671
+ var $rtn = $headerRow.children().eq(idx);
672
+ return $rtn && $rtn[0];
673
+ }
674
+
675
+ function getFooterRowColumn(columnIdOrIdx) {
676
+ var idx = (typeof columnIdOrIdx === "number" ? columnIdOrIdx : getColumnIndex[columnIdOrIdx]);
677
+ var $rtn = $footerRow.children().eq(idx);
678
+ return $rtn && $rtn[0];
679
+ }
680
+
681
+ function createColumnHeaders() {
682
+ function onMouseEnter() {
683
+ $(this).addClass("ui-state-hover");
684
+ }
685
+
686
+ function onMouseLeave() {
687
+ $(this).removeClass("ui-state-hover");
688
+ }
689
+
690
+ $headers.find(".slick-header-column")
691
+ .each(function() {
692
+ var columnDef = $(this).data("column");
693
+ if (columnDef) {
694
+ trigger(self.onBeforeHeaderCellDestroy, {
695
+ "node": this,
696
+ "column": columnDef,
697
+ "grid": self
698
+ });
699
+ }
700
+ });
701
+ $headers.empty();
702
+ $headers.width(getHeadersWidth());
703
+
704
+ $headerRow.find(".slick-headerrow-column")
705
+ .each(function() {
706
+ var columnDef = $(this).data("column");
707
+ if (columnDef) {
708
+ trigger(self.onBeforeHeaderRowCellDestroy, {
709
+ "node": this,
710
+ "column": columnDef,
711
+ "grid": self
712
+ });
713
+ }
714
+ });
715
+ $headerRow.empty();
716
+
717
+ if (options.createFooterRow) {
718
+ $footerRow.find(".slick-footerrow-column")
719
+ .each(function() {
720
+ var columnDef = $(this).data("column");
721
+ if (columnDef) {
722
+ trigger(self.onBeforeFooterRowCellDestroy, {
723
+ "node": this,
724
+ "column": columnDef
725
+ });
726
+ }
727
+ });
728
+ $footerRow.empty();
729
+ }
730
+
731
+ for (var i = 0; i < columns.length; i++) {
732
+ var m = columns[i];
733
+
734
+ var header = $("<div class='ui-state-default slick-header-column' />")
735
+ .html("<span class='slick-column-name'>" + m.name + "</span>")
736
+ .width(m.width - headerColumnWidthDiff)
737
+ .attr("id", "" + uid + m.id)
738
+ .attr("title", m.toolTip || "")
739
+ .data("column", m)
740
+ .addClass(m.headerCssClass || "")
741
+ .appendTo($headers);
742
+
743
+ if (options.enableColumnReorder || m.sortable) {
744
+ header
745
+ .on('mouseenter', onMouseEnter)
746
+ .on('mouseleave', onMouseLeave);
747
+ }
748
+
749
+ if (m.sortable) {
750
+ header.addClass("slick-header-sortable");
751
+ header.append("<span class='" + sortIndicatorCssClass + "' />");
752
+ }
753
+
754
+ trigger(self.onHeaderCellRendered, {
755
+ "node": header[0],
756
+ "column": m,
757
+ "grid": self
758
+ });
759
+
760
+ if (options.showHeaderRow) {
761
+ var headerRowCell = $("<div class='ui-state-default slick-headerrow-column l" + i + " r" + i + "'></div>")
762
+ .data("column", m)
763
+ .appendTo($headerRow);
764
+
765
+ trigger(self.onHeaderRowCellRendered, {
766
+ "node": headerRowCell[0],
767
+ "column": m,
768
+ "grid": self
769
+ });
770
+ }
771
+ if (options.createFooterRow && options.showFooterRow) {
772
+ var footerRowCell = $("<div class='ui-state-default slick-footerrow-column l" + i + " r" + i + "'></div>")
773
+ .data("column", m)
774
+ .appendTo($footerRow);
775
+
776
+ trigger(self.onFooterRowCellRendered, {
777
+ "node": footerRowCell[0],
778
+ "column": m
779
+ });
780
+ }
781
+ }
782
+
783
+ setSortColumns(sortColumns);
784
+ setupColumnResize();
785
+ if (options.enableColumnReorder) {
786
+ if (typeof options.enableColumnReorder == 'function') {
787
+ options.enableColumnReorder(self, $headers, headerColumnWidthDiff, setColumns, setupColumnResize, columns, getColumnIndex, uid, trigger);
788
+ } else {
789
+ setupColumnReorder();
790
+ }
791
+ }
792
+ }
793
+
794
+ function setupColumnSort() {
795
+ $headers.click(function (e) {
796
+ if (columnResizeDragging) return;
797
+ // temporary workaround for a bug in jQuery 1.7.1 (http://bugs.jquery.com/ticket/11328)
798
+ e.metaKey = e.metaKey || e.ctrlKey;
799
+
800
+ if ($(e.target).hasClass("slick-resizable-handle")) {
801
+ return;
802
+ }
803
+
804
+ var $col = $(e.target).closest(".slick-header-column");
805
+ if (!$col.length) {
806
+ return;
807
+ }
808
+
809
+ var column = $col.data("column");
810
+ if (column.sortable) {
811
+ if (!getEditorLock().commitCurrentEdit()) {
812
+ return;
813
+ }
814
+
815
+ var sortColumn = null;
816
+ var i = 0;
817
+ for (; i < sortColumns.length; i++) {
818
+ if (sortColumns[i].columnId == column.id) {
819
+ sortColumn = sortColumns[i];
820
+ sortColumn.sortAsc = !sortColumn.sortAsc;
821
+ break;
822
+ }
823
+ }
824
+ var hadSortCol = !!sortColumn;
825
+
826
+ if (options.tristateMultiColumnSort) {
827
+ if (!sortColumn) {
828
+ sortColumn = { columnId: column.id, sortAsc: column.defaultSortAsc };
829
+ }
830
+ if (hadSortCol && sortColumn.sortAsc) {
831
+ // three state: remove sort rather than go back to ASC
832
+ sortColumns.splice(i, 1);
833
+ sortColumn = null;
834
+ }
835
+ if (!options.multiColumnSort) { sortColumns = []; }
836
+ if (sortColumn && (!hadSortCol || !options.multiColumnSort)) {
837
+ sortColumns.push(sortColumn);
838
+ }
839
+ } else {
840
+ // legacy behaviour
841
+ if (e.metaKey && options.multiColumnSort) {
842
+ if (sortColumn) {
843
+ sortColumns.splice(i, 1);
844
+ }
845
+ }
846
+ else {
847
+ if ((!e.shiftKey && !e.metaKey) || !options.multiColumnSort) {
848
+ sortColumns = [];
849
+ }
850
+
851
+ if (!sortColumn) {
852
+ sortColumn = { columnId: column.id, sortAsc: column.defaultSortAsc };
853
+ sortColumns.push(sortColumn);
854
+ } else if (sortColumns.length == 0) {
855
+ sortColumns.push(sortColumn);
856
+ }
857
+ }
858
+ }
859
+
860
+ setSortColumns(sortColumns);
861
+
862
+ if (!options.multiColumnSort) {
863
+ trigger(self.onSort, {
864
+ multiColumnSort: false,
865
+ sortCol: (sortColumns.length > 0 ? column : null),
866
+ sortAsc: (sortColumns.length > 0 ? sortColumns[0].sortAsc : true),
867
+ grid: self}, e);
868
+ } else {
869
+ trigger(self.onSort, {
870
+ multiColumnSort: true,
871
+ sortCols: $.map(sortColumns, function(col) {
872
+ return {sortCol: columns[getColumnIndex(col.columnId)], sortAsc: col.sortAsc };
873
+ }),
874
+ grid: self}, e);
875
+ }
876
+ }
877
+ });
878
+ }
879
+
880
+ function setupColumnReorder() {
881
+ $headers.filter(":ui-sortable").sortable("destroy");
882
+ $headers.sortable({
883
+ containment: "parent",
884
+ distance: 3,
885
+ axis: "x",
886
+ cursor: "default",
887
+ tolerance: "intersection",
888
+ helper: "clone",
889
+ placeholder: "slick-sortable-placeholder ui-state-default slick-header-column",
890
+ start: function (e, ui) {
891
+ ui.placeholder.width(ui.helper.outerWidth() - headerColumnWidthDiff);
892
+ $(ui.helper).addClass("slick-header-column-active");
893
+ },
894
+ beforeStop: function (e, ui) {
895
+ $(ui.helper).removeClass("slick-header-column-active");
896
+ },
897
+ stop: function (e) {
898
+ if (!getEditorLock().commitCurrentEdit()) {
899
+ $(this).sortable("cancel");
900
+ return;
901
+ }
902
+
903
+ var reorderedIds = $headers.sortable("toArray");
904
+ var reorderedColumns = [];
905
+ for (var i = 0; i < reorderedIds.length; i++) {
906
+ reorderedColumns.push(columns[getColumnIndex(reorderedIds[i].replace(uid, ""))]);
907
+ }
908
+ setColumns(reorderedColumns);
909
+
910
+ trigger(self.onColumnsReordered, {grid: self});
911
+ e.stopPropagation();
912
+ setupColumnResize();
913
+ }
914
+ });
915
+ }
916
+
917
+ function setupColumnResize() {
918
+ var $col, j, c, pageX, columnElements, minPageX, maxPageX, firstResizable, lastResizable;
919
+ columnElements = $headers.children();
920
+ columnElements.find(".slick-resizable-handle").remove();
921
+ columnElements.each(function (i, e) {
922
+ if (i >= columns.length) { return; }
923
+ if (columns[i].resizable) {
924
+ if (firstResizable === undefined) {
925
+ firstResizable = i;
926
+ }
927
+ lastResizable = i;
928
+ }
929
+ });
930
+ if (firstResizable === undefined) {
931
+ return;
932
+ }
933
+ columnElements.each(function (i, e) {
934
+ if (i >= columns.length) { return; }
935
+ if (i < firstResizable || (options.forceFitColumns && i >= lastResizable)) {
936
+ return;
937
+ }
938
+ $col = $(e);
939
+ $("<div class='slick-resizable-handle' />")
940
+ .appendTo(e)
941
+ .on("dragstart", function (e, dd) {
942
+ if (!getEditorLock().commitCurrentEdit()) {
943
+ return false;
944
+ }
945
+ pageX = e.pageX;
946
+ $(this).parent().addClass("slick-header-column-active");
947
+ var shrinkLeewayOnRight = null, stretchLeewayOnRight = null;
948
+ // lock each column's width option to current width
949
+ columnElements.each(function (i, e) {
950
+ if (i >= columns.length) { return; }
951
+ columns[i].previousWidth = $(e).outerWidth();
952
+ });
953
+ if (options.forceFitColumns) {
954
+ shrinkLeewayOnRight = 0;
955
+ stretchLeewayOnRight = 0;
956
+ // colums on right affect maxPageX/minPageX
957
+ for (j = i + 1; j < columns.length; j++) {
958
+ c = columns[j];
959
+ if (c.resizable) {
960
+ if (stretchLeewayOnRight !== null) {
961
+ if (c.maxWidth) {
962
+ stretchLeewayOnRight += c.maxWidth - c.previousWidth;
963
+ } else {
964
+ stretchLeewayOnRight = null;
965
+ }
966
+ }
967
+ shrinkLeewayOnRight += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth);
968
+ }
969
+ }
970
+ }
971
+ var shrinkLeewayOnLeft = 0, stretchLeewayOnLeft = 0;
972
+ for (j = 0; j <= i; j++) {
973
+ // columns on left only affect minPageX
974
+ c = columns[j];
975
+ if (c.resizable) {
976
+ if (stretchLeewayOnLeft !== null) {
977
+ if (c.maxWidth) {
978
+ stretchLeewayOnLeft += c.maxWidth - c.previousWidth;
979
+ } else {
980
+ stretchLeewayOnLeft = null;
981
+ }
982
+ }
983
+ shrinkLeewayOnLeft += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth);
984
+ }
985
+ }
986
+ if (shrinkLeewayOnRight === null) {
987
+ shrinkLeewayOnRight = 100000;
988
+ }
989
+ if (shrinkLeewayOnLeft === null) {
990
+ shrinkLeewayOnLeft = 100000;
991
+ }
992
+ if (stretchLeewayOnRight === null) {
993
+ stretchLeewayOnRight = 100000;
994
+ }
995
+ if (stretchLeewayOnLeft === null) {
996
+ stretchLeewayOnLeft = 100000;
997
+ }
998
+ maxPageX = pageX + Math.min(shrinkLeewayOnRight, stretchLeewayOnLeft);
999
+ minPageX = pageX - Math.min(shrinkLeewayOnLeft, stretchLeewayOnRight);
1000
+ })
1001
+ .on("drag", function (e, dd) {
1002
+ columnResizeDragging = true;
1003
+ var actualMinWidth, d = Math.min(maxPageX, Math.max(minPageX, e.pageX)) - pageX, x;
1004
+ if (d < 0) { // shrink column
1005
+ x = d;
1006
+ for (j = i; j >= 0; j--) {
1007
+ c = columns[j];
1008
+ if (c.resizable) {
1009
+ actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth);
1010
+ if (x && c.previousWidth + x < actualMinWidth) {
1011
+ x += c.previousWidth - actualMinWidth;
1012
+ c.width = actualMinWidth;
1013
+ } else {
1014
+ c.width = c.previousWidth + x;
1015
+ x = 0;
1016
+ }
1017
+ }
1018
+ }
1019
+
1020
+ if (options.forceFitColumns) {
1021
+ x = -d;
1022
+ for (j = i + 1; j < columns.length; j++) {
1023
+ c = columns[j];
1024
+ if (c.resizable) {
1025
+ if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) {
1026
+ x -= c.maxWidth - c.previousWidth;
1027
+ c.width = c.maxWidth;
1028
+ } else {
1029
+ c.width = c.previousWidth + x;
1030
+ x = 0;
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+ } else { // stretch column
1036
+ x = d;
1037
+ for (j = i; j >= 0; j--) {
1038
+ c = columns[j];
1039
+ if (c.resizable) {
1040
+ if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) {
1041
+ x -= c.maxWidth - c.previousWidth;
1042
+ c.width = c.maxWidth;
1043
+ } else {
1044
+ c.width = c.previousWidth + x;
1045
+ x = 0;
1046
+ }
1047
+ }
1048
+ }
1049
+
1050
+ if (options.forceFitColumns) {
1051
+ x = -d;
1052
+ for (j = i + 1; j < columns.length; j++) {
1053
+ c = columns[j];
1054
+ if (c.resizable) {
1055
+ actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth);
1056
+ if (x && c.previousWidth + x < actualMinWidth) {
1057
+ x += c.previousWidth - actualMinWidth;
1058
+ c.width = actualMinWidth;
1059
+ } else {
1060
+ c.width = c.previousWidth + x;
1061
+ x = 0;
1062
+ }
1063
+ }
1064
+ }
1065
+ }
1066
+ }
1067
+ applyColumnHeaderWidths();
1068
+ if (options.syncColumnCellResize) {
1069
+ applyColumnWidths();
1070
+ }
1071
+ })
1072
+ .on("dragend", function (e, dd) {
1073
+ var newWidth;
1074
+ $(this).parent().removeClass("slick-header-column-active");
1075
+ for (j = 0; j < columns.length; j++) {
1076
+ c = columns[j];
1077
+ newWidth = $(columnElements[j]).outerWidth();
1078
+
1079
+ if (c.previousWidth !== newWidth && c.rerenderOnResize) {
1080
+ invalidateAllRows();
1081
+ }
1082
+ }
1083
+ updateCanvasWidth(true);
1084
+ render();
1085
+ trigger(self.onColumnsResized, {grid: self});
1086
+ setTimeout(function () { columnResizeDragging = false; }, 300);
1087
+ });
1088
+ });
1089
+ }
1090
+
1091
+ function getVBoxDelta($el) {
1092
+ var p = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"];
1093
+ var delta = 0;
1094
+ $.each(p, function (n, val) {
1095
+ delta += parseFloat($el.css(val)) || 0;
1096
+ });
1097
+ return delta;
1098
+ }
1099
+
1100
+ function measureCellPaddingAndBorder() {
1101
+ var el;
1102
+ var h = ["borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight"];
1103
+ var v = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"];
1104
+
1105
+ // jquery prior to version 1.8 handles .width setter/getter as a direct css write/read
1106
+ // jquery 1.8 changed .width to read the true inner element width if box-sizing is set to border-box, and introduced a setter for .outerWidth
1107
+ // so for equivalent functionality, prior to 1.8 use .width, and after use .outerWidth
1108
+ var verArray = $.fn.jquery.split('.');
1109
+ jQueryNewWidthBehaviour = (verArray[0]==1 && verArray[1]>=8) || verArray[0] >=2;
1110
+
1111
+ el = $("<div class='ui-state-default slick-header-column' style='visibility:hidden'>-</div>").appendTo($headers);
1112
+ headerColumnWidthDiff = headerColumnHeightDiff = 0;
1113
+ if (el.css("box-sizing") != "border-box" && el.css("-moz-box-sizing") != "border-box" && el.css("-webkit-box-sizing") != "border-box") {
1114
+ $.each(h, function (n, val) {
1115
+ headerColumnWidthDiff += parseFloat(el.css(val)) || 0;
1116
+ });
1117
+ $.each(v, function (n, val) {
1118
+ headerColumnHeightDiff += parseFloat(el.css(val)) || 0;
1119
+ });
1120
+ }
1121
+ el.remove();
1122
+
1123
+ var r = $("<div class='slick-row' />").appendTo($canvas);
1124
+ el = $("<div class='slick-cell' id='' style='visibility:hidden'>-</div>").appendTo(r);
1125
+ cellWidthDiff = cellHeightDiff = 0;
1126
+ if (el.css("box-sizing") != "border-box" && el.css("-moz-box-sizing") != "border-box" && el.css("-webkit-box-sizing") != "border-box") {
1127
+ $.each(h, function (n, val) {
1128
+ cellWidthDiff += parseFloat(el.css(val)) || 0;
1129
+ });
1130
+ $.each(v, function (n, val) {
1131
+ cellHeightDiff += parseFloat(el.css(val)) || 0;
1132
+ });
1133
+ }
1134
+ r.remove();
1135
+
1136
+ absoluteColumnMinWidth = Math.max(headerColumnWidthDiff, cellWidthDiff);
1137
+ }
1138
+
1139
+ function createCssRules() {
1140
+ $style = $("<style type='text/css' rel='stylesheet' />").appendTo($("head"));
1141
+ var rowHeight = (options.rowHeight - cellHeightDiff);
1142
+ var rules = [
1143
+ "." + uid + " .slick-header-column { left: 1000px; }",
1144
+ "." + uid + " .slick-top-panel { height:" + options.topPanelHeight + "px; }",
1145
+ "." + uid + " .slick-preheader-panel { height:" + options.preHeaderPanelHeight + "px; }",
1146
+ "." + uid + " .slick-headerrow-columns { height:" + options.headerRowHeight + "px; }",
1147
+ "." + uid + " .slick-footerrow-columns { height:" + options.footerRowHeight + "px; }",
1148
+ "." + uid + " .slick-cell { height:" + rowHeight + "px; }",
1149
+ "." + uid + " .slick-row { height:" + options.rowHeight + "px; }"
1150
+ ];
1151
+
1152
+ for (var i = 0; i < columns.length; i++) {
1153
+ rules.push("." + uid + " .l" + i + " { }");
1154
+ rules.push("." + uid + " .r" + i + " { }");
1155
+ }
1156
+
1157
+ if ($style[0].styleSheet) { // IE
1158
+ $style[0].styleSheet.cssText = rules.join(" ");
1159
+ } else {
1160
+ $style[0].appendChild(document.createTextNode(rules.join(" ")));
1161
+ }
1162
+ }
1163
+
1164
+ function getColumnCssRules(idx) {
1165
+ var i;
1166
+ if (!stylesheet) {
1167
+ var sheets = document.styleSheets;
1168
+ for (i = 0; i < sheets.length; i++) {
1169
+ if ((sheets[i].ownerNode || sheets[i].owningElement) == $style[0]) {
1170
+ stylesheet = sheets[i];
1171
+ break;
1172
+ }
1173
+ }
1174
+
1175
+ if (!stylesheet) {
1176
+ throw new Error("Cannot find stylesheet.");
1177
+ }
1178
+
1179
+ // find and cache column CSS rules
1180
+ columnCssRulesL = [];
1181
+ columnCssRulesR = [];
1182
+ var cssRules = (stylesheet.cssRules || stylesheet.rules);
1183
+ var matches, columnIdx;
1184
+ for (i = 0; i < cssRules.length; i++) {
1185
+ var selector = cssRules[i].selectorText;
1186
+ if (matches = /\.l\d+/.exec(selector)) {
1187
+ columnIdx = parseInt(matches[0].substr(2, matches[0].length - 2), 10);
1188
+ columnCssRulesL[columnIdx] = cssRules[i];
1189
+ } else if (matches = /\.r\d+/.exec(selector)) {
1190
+ columnIdx = parseInt(matches[0].substr(2, matches[0].length - 2), 10);
1191
+ columnCssRulesR[columnIdx] = cssRules[i];
1192
+ }
1193
+ }
1194
+ }
1195
+
1196
+ return {
1197
+ "left": columnCssRulesL[idx],
1198
+ "right": columnCssRulesR[idx]
1199
+ };
1200
+ }
1201
+
1202
+ function removeCssRules() {
1203
+ $style.remove();
1204
+ stylesheet = null;
1205
+ }
1206
+
1207
+ function destroy() {
1208
+ getEditorLock().cancelCurrentEdit();
1209
+
1210
+ trigger(self.onBeforeDestroy, {grid: self});
1211
+
1212
+ var i = plugins.length;
1213
+ while(i--) {
1214
+ unregisterPlugin(plugins[i]);
1215
+ }
1216
+
1217
+ if (options.enableColumnReorder) {
1218
+ $headers.filter(":ui-sortable").sortable("destroy");
1219
+ }
1220
+
1221
+ unbindAncestorScrollEvents();
1222
+ $container.off(".slickgrid");
1223
+ removeCssRules();
1224
+
1225
+ $canvas.off("draginit dragstart dragend drag");
1226
+ $container.empty().removeClass(uid);
1227
+ }
1228
+
1229
+
1230
+ //////////////////////////////////////////////////////////////////////////////////////////////
1231
+ // General
1232
+
1233
+ function trigger(evt, args, e) {
1234
+ e = e || new Slick.EventData();
1235
+ args = args || {};
1236
+ args.grid = self;
1237
+ return evt.notify(args, e, self);
1238
+ }
1239
+
1240
+ function getEditorLock() {
1241
+ return options.editorLock;
1242
+ }
1243
+
1244
+ function getEditController() {
1245
+ return editController;
1246
+ }
1247
+
1248
+ function getColumnIndex(id) {
1249
+ return columnsById[id];
1250
+ }
1251
+
1252
+ function autosizeColumns() {
1253
+ var i, c,
1254
+ widths = [],
1255
+ shrinkLeeway = 0,
1256
+ total = 0,
1257
+ prevTotal,
1258
+ availWidth = viewportHasVScroll ? viewportW - scrollbarDimensions.width : viewportW;
1259
+
1260
+ for (i = 0; i < columns.length; i++) {
1261
+ c = columns[i];
1262
+ widths.push(c.width);
1263
+ total += c.width;
1264
+ if (c.resizable) {
1265
+ shrinkLeeway += c.width - Math.max(c.minWidth, absoluteColumnMinWidth);
1266
+ }
1267
+ }
1268
+
1269
+ // shrink
1270
+ prevTotal = total;
1271
+ while (total > availWidth && shrinkLeeway) {
1272
+ var shrinkProportion = (total - availWidth) / shrinkLeeway;
1273
+ for (i = 0; i < columns.length && total > availWidth; i++) {
1274
+ c = columns[i];
1275
+ var width = widths[i];
1276
+ if (!c.resizable || width <= c.minWidth || width <= absoluteColumnMinWidth) {
1277
+ continue;
1278
+ }
1279
+ var absMinWidth = Math.max(c.minWidth, absoluteColumnMinWidth);
1280
+ var shrinkSize = Math.floor(shrinkProportion * (width - absMinWidth)) || 1;
1281
+ shrinkSize = Math.min(shrinkSize, width - absMinWidth);
1282
+ total -= shrinkSize;
1283
+ shrinkLeeway -= shrinkSize;
1284
+ widths[i] -= shrinkSize;
1285
+ }
1286
+ if (prevTotal <= total) { // avoid infinite loop
1287
+ break;
1288
+ }
1289
+ prevTotal = total;
1290
+ }
1291
+
1292
+ // grow
1293
+ prevTotal = total;
1294
+ while (total < availWidth) {
1295
+ var growProportion = availWidth / total;
1296
+ for (i = 0; i < columns.length && total < availWidth; i++) {
1297
+ c = columns[i];
1298
+ var currentWidth = widths[i];
1299
+ var growSize;
1300
+
1301
+ if (!c.resizable || c.maxWidth <= currentWidth) {
1302
+ growSize = 0;
1303
+ } else {
1304
+ growSize = Math.min(Math.floor(growProportion * currentWidth) - currentWidth, (c.maxWidth - currentWidth) || 1000000) || 1;
1305
+ }
1306
+ total += growSize;
1307
+ widths[i] += (total <= availWidth ? growSize : 0);
1308
+ }
1309
+ if (prevTotal >= total) { // avoid infinite loop
1310
+ break;
1311
+ }
1312
+ prevTotal = total;
1313
+ }
1314
+
1315
+ var reRender = false;
1316
+ for (i = 0; i < columns.length; i++) {
1317
+ if (columns[i].rerenderOnResize && columns[i].width != widths[i]) {
1318
+ reRender = true;
1319
+ }
1320
+ columns[i].width = widths[i];
1321
+ }
1322
+
1323
+ applyColumnHeaderWidths();
1324
+ updateCanvasWidth(true);
1325
+ if (reRender) {
1326
+ invalidateAllRows();
1327
+ render();
1328
+ }
1329
+ }
1330
+
1331
+ function applyColumnHeaderWidths() {
1332
+ if (!initialized) { return; }
1333
+ var h;
1334
+
1335
+ for (var i = 0, headers = $headers.children(), ii = columns.length; i < ii; i++) {
1336
+ h = $(headers[i]);
1337
+ if (jQueryNewWidthBehaviour) {
1338
+ if (h.outerWidth() !== columns[i].width) {
1339
+ h.outerWidth(columns[i].width);
1340
+ }
1341
+ } else {
1342
+ if (h.width() !== columns[i].width - headerColumnWidthDiff) {
1343
+ h.width(columns[i].width - headerColumnWidthDiff);
1344
+ }
1345
+ }
1346
+ }
1347
+
1348
+ updateColumnCaches();
1349
+ }
1350
+
1351
+ function applyColumnWidths() {
1352
+ var x = 0, w, rule;
1353
+ for (var i = 0; i < columns.length; i++) {
1354
+ w = columns[i].width;
1355
+
1356
+ rule = getColumnCssRules(i);
1357
+ rule.left.style.left = x + "px";
1358
+ rule.right.style.right = (canvasWidth - x - w) + "px";
1359
+
1360
+ x += columns[i].width;
1361
+ }
1362
+ }
1363
+
1364
+ function setSortColumn(columnId, ascending) {
1365
+ setSortColumns([{ columnId: columnId, sortAsc: ascending}]);
1366
+ }
1367
+
1368
+ function setSortColumns(cols) {
1369
+ sortColumns = cols;
1370
+ var numberCols = options.numberedMultiColumnSort && sortColumns.length > 1;
1371
+ var headerColumnEls = $headers.children();
1372
+ var sortIndicatorEl = headerColumnEls
1373
+ .removeClass("slick-header-column-sorted")
1374
+ .find("." + sortIndicatorCssClass)
1375
+ .removeClass("slick-sort-indicator-asc slick-sort-indicator-desc");
1376
+ sortIndicatorEl.text('');
1377
+
1378
+ $.each(sortColumns, function(i, col) {
1379
+ if (col.sortAsc == null) {
1380
+ col.sortAsc = true;
1381
+ }
1382
+ var columnIndex = getColumnIndex(col.columnId);
1383
+ if (columnIndex != null) {
1384
+ sortIndicatorEl = headerColumnEls.eq(columnIndex)
1385
+ .addClass("slick-header-column-sorted")
1386
+ .find("." + sortIndicatorCssClass)
1387
+ .addClass(col.sortAsc ? "slick-sort-indicator-asc" : "slick-sort-indicator-desc");
1388
+ if (numberCols) { sortIndicatorEl.text(i+1); }
1389
+ }
1390
+ });
1391
+ }
1392
+
1393
+ function getSortColumns() {
1394
+ return sortColumns;
1395
+ }
1396
+
1397
+ function handleSelectedRangesChanged(e, ranges) {
1398
+ selectedRows = [];
1399
+ var hash = {};
1400
+ for (var i = 0; i < ranges.length; i++) {
1401
+ for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
1402
+ if (!hash[j]) { // prevent duplicates
1403
+ selectedRows.push(j);
1404
+ hash[j] = {};
1405
+ }
1406
+ for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) {
1407
+ if (canCellBeSelected(j, k)) {
1408
+ hash[j][columns[k].id] = options.selectedCellCssClass;
1409
+ }
1410
+ }
1411
+ }
1412
+ }
1413
+
1414
+ setCellCssStyles(options.selectedCellCssClass, hash);
1415
+
1416
+ trigger(self.onSelectedRowsChanged, {rows: getSelectedRows(), grid: self}, e);
1417
+ }
1418
+
1419
+ function getColumns() {
1420
+ return columns;
1421
+ }
1422
+
1423
+ function updateColumnCaches() {
1424
+ // Pre-calculate cell boundaries.
1425
+ columnPosLeft = [];
1426
+ columnPosRight = [];
1427
+ var x = 0;
1428
+ for (var i = 0, ii = columns.length; i < ii; i++) {
1429
+ columnPosLeft[i] = x;
1430
+ columnPosRight[i] = x + columns[i].width;
1431
+ x += columns[i].width;
1432
+ }
1433
+ }
1434
+
1435
+ function setColumns(columnDefinitions) {
1436
+ columns = columnDefinitions;
1437
+
1438
+ columnsById = {};
1439
+ for (var i = 0; i < columns.length; i++) {
1440
+ var m = columns[i] = $.extend({}, columnDefaults, columns[i]);
1441
+ columnsById[m.id] = i;
1442
+ if (m.minWidth && m.width < m.minWidth) {
1443
+ m.width = m.minWidth;
1444
+ }
1445
+ if (m.maxWidth && m.width > m.maxWidth) {
1446
+ m.width = m.maxWidth;
1447
+ }
1448
+ }
1449
+
1450
+ updateColumnCaches();
1451
+
1452
+ if (initialized) {
1453
+ invalidateAllRows();
1454
+ createColumnHeaders();
1455
+ removeCssRules();
1456
+ createCssRules();
1457
+ resizeCanvas();
1458
+ applyColumnWidths();
1459
+ handleScroll();
1460
+ }
1461
+ }
1462
+
1463
+ function getOptions() {
1464
+ return options;
1465
+ }
1466
+
1467
+ function setOptions(args, suppressRender) {
1468
+ if (!getEditorLock().commitCurrentEdit()) {
1469
+ return;
1470
+ }
1471
+
1472
+ makeActiveCellNormal();
1473
+
1474
+ if (options.enableAddRow !== args.enableAddRow) {
1475
+ invalidateRow(getDataLength());
1476
+ }
1477
+
1478
+ options = $.extend(options, args);
1479
+ validateAndEnforceOptions();
1480
+
1481
+ $viewport.css("overflow-y", options.autoHeight ? "hidden" : "auto");
1482
+ if (!suppressRender) { render(); }
1483
+ }
1484
+
1485
+ function validateAndEnforceOptions() {
1486
+ if (options.autoHeight) {
1487
+ options.leaveSpaceForNewRows = false;
1488
+ }
1489
+ }
1490
+
1491
+ function setData(newData, scrollToTop) {
1492
+ data = newData;
1493
+ invalidateAllRows();
1494
+ updateRowCount();
1495
+ if (scrollToTop) {
1496
+ scrollTo(0);
1497
+ }
1498
+ }
1499
+
1500
+ function getData() {
1501
+ return data;
1502
+ }
1503
+
1504
+ function getDataLength() {
1505
+ if (data.getLength) {
1506
+ return data.getLength();
1507
+ } else {
1508
+ return data.length;
1509
+ }
1510
+ }
1511
+
1512
+ function getDataLengthIncludingAddNew() {
1513
+ return getDataLength() + (!options.enableAddRow ? 0
1514
+ : (!pagingActive || pagingIsLastPage ? 1 : 0)
1515
+ );
1516
+ }
1517
+
1518
+ function getDataItem(i) {
1519
+ if (data.getItem) {
1520
+ return data.getItem(i);
1521
+ } else {
1522
+ return data[i];
1523
+ }
1524
+ }
1525
+
1526
+ function getTopPanel() {
1527
+ return $topPanel[0];
1528
+ }
1529
+
1530
+ function setTopPanelVisibility(visible) {
1531
+ if (options.showTopPanel != visible) {
1532
+ options.showTopPanel = visible;
1533
+ if (visible) {
1534
+ $topPanelScroller.slideDown("fast", resizeCanvas);
1535
+ } else {
1536
+ $topPanelScroller.slideUp("fast", resizeCanvas);
1537
+ }
1538
+ }
1539
+ }
1540
+
1541
+ function setHeaderRowVisibility(visible) {
1542
+ if (options.showHeaderRow != visible) {
1543
+ options.showHeaderRow = visible;
1544
+ if (visible) {
1545
+ $headerRowScroller.slideDown("fast", resizeCanvas);
1546
+ } else {
1547
+ $headerRowScroller.slideUp("fast", resizeCanvas);
1548
+ }
1549
+ }
1550
+ }
1551
+
1552
+ function setFooterRowVisibility(visible) {
1553
+ if (options.showFooterRow != visible) {
1554
+ options.showFooterRow = visible;
1555
+ if (visible) {
1556
+ $footerRowScroller.slideDown("fast", resizeCanvas);
1557
+ } else {
1558
+ $footerRowScroller.slideUp("fast", resizeCanvas);
1559
+ }
1560
+ }
1561
+ }
1562
+
1563
+ function setPreHeaderPanelVisibility(visible) {
1564
+ if (options.showPreHeaderPanel != visible) {
1565
+ options.showPreHeaderPanel = visible;
1566
+ if (visible) {
1567
+ $preHeaderPanelScroller.slideDown("fast", resizeCanvas);
1568
+ } else {
1569
+ $preHeaderPanelScroller.slideUp("fast", resizeCanvas);
1570
+ }
1571
+ }
1572
+ }
1573
+
1574
+ function getContainerNode() {
1575
+ return $container.get(0);
1576
+ }
1577
+
1578
+ //////////////////////////////////////////////////////////////////////////////////////////////
1579
+ // Rendering / Scrolling
1580
+
1581
+ function getRowTop(row) {
1582
+ return options.rowHeight * row - offset;
1583
+ }
1584
+
1585
+ function getRowFromPosition(y) {
1586
+ return Math.floor((y + offset) / options.rowHeight);
1587
+ }
1588
+
1589
+ function scrollTo(y) {
1590
+ y = Math.max(y, 0);
1591
+ y = Math.min(y, th - viewportH + (viewportHasHScroll ? scrollbarDimensions.height : 0));
1592
+
1593
+ var oldOffset = offset;
1594
+
1595
+ page = Math.min(n - 1, Math.floor(y / ph));
1596
+ offset = Math.round(page * cj);
1597
+ var newScrollTop = y - offset;
1598
+
1599
+ if (offset != oldOffset) {
1600
+ var range = getVisibleRange(newScrollTop);
1601
+ cleanupRows(range);
1602
+ updateRowPositions();
1603
+ }
1604
+
1605
+ if (prevScrollTop != newScrollTop) {
1606
+ vScrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1;
1607
+ $viewport[0].scrollTop = (lastRenderedScrollTop = scrollTop = prevScrollTop = newScrollTop);
1608
+
1609
+ trigger(self.onViewportChanged, {grid: self});
1610
+ }
1611
+ }
1612
+
1613
+ function defaultFormatter(row, cell, value, columnDef, dataContext, grid) {
1614
+ if (value == null) {
1615
+ return "";
1616
+ } else {
1617
+ return (value + "").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
1618
+ }
1619
+ }
1620
+
1621
+ function getFormatter(row, column) {
1622
+ var rowMetadata = data.getItemMetadata && data.getItemMetadata(row);
1623
+
1624
+ // look up by id, then index
1625
+ var columnOverrides = rowMetadata &&
1626
+ rowMetadata.columns &&
1627
+ (rowMetadata.columns[column.id] || rowMetadata.columns[getColumnIndex(column.id)]);
1628
+
1629
+ return (columnOverrides && columnOverrides.formatter) ||
1630
+ (rowMetadata && rowMetadata.formatter) ||
1631
+ column.formatter ||
1632
+ (options.formatterFactory && options.formatterFactory.getFormatter(column)) ||
1633
+ options.defaultFormatter;
1634
+ }
1635
+
1636
+ function getEditor(row, cell) {
1637
+ var column = columns[cell];
1638
+ var rowMetadata = data.getItemMetadata && data.getItemMetadata(row);
1639
+ var columnMetadata = rowMetadata && rowMetadata.columns;
1640
+
1641
+ if (columnMetadata && columnMetadata[column.id] && columnMetadata[column.id].editor !== undefined) {
1642
+ return columnMetadata[column.id].editor;
1643
+ }
1644
+ if (columnMetadata && columnMetadata[cell] && columnMetadata[cell].editor !== undefined) {
1645
+ return columnMetadata[cell].editor;
1646
+ }
1647
+
1648
+ return column.editor || (options.editorFactory && options.editorFactory.getEditor(column));
1649
+ }
1650
+
1651
+ function getDataItemValueForColumn(item, columnDef) {
1652
+ if (options.dataItemColumnValueExtractor) {
1653
+ return options.dataItemColumnValueExtractor(item, columnDef);
1654
+ }
1655
+ return item[columnDef.field];
1656
+ }
1657
+
1658
+ function appendRowHtml(stringArray, row, range, dataLength) {
1659
+ var d = getDataItem(row);
1660
+ var dataLoading = row < dataLength && !d;
1661
+ var rowCss = "slick-row" +
1662
+ (dataLoading ? " loading" : "") +
1663
+ (row === activeRow ? " active" : "") +
1664
+ (row % 2 == 1 ? " odd" : " even");
1665
+
1666
+ if (!d) {
1667
+ rowCss += " " + options.addNewRowCssClass;
1668
+ }
1669
+
1670
+ var metadata = data.getItemMetadata && data.getItemMetadata(row);
1671
+
1672
+ if (metadata && metadata.cssClasses) {
1673
+ rowCss += " " + metadata.cssClasses;
1674
+ }
1675
+
1676
+ stringArray.push("<div class='ui-widget-content " + rowCss + "' style='top:" + getRowTop(row) + "px'>");
1677
+
1678
+ var colspan, m;
1679
+ for (var i = 0, ii = columns.length; i < ii; i++) {
1680
+ m = columns[i];
1681
+ colspan = 1;
1682
+ if (metadata && metadata.columns) {
1683
+ var columnData = metadata.columns[m.id] || metadata.columns[i];
1684
+ colspan = (columnData && columnData.colspan) || 1;
1685
+ if (colspan === "*") {
1686
+ colspan = ii - i;
1687
+ }
1688
+ }
1689
+
1690
+ // Do not render cells outside of the viewport.
1691
+ if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) {
1692
+ if (columnPosLeft[i] > range.rightPx) {
1693
+ // All columns to the right are outside the range.
1694
+ break;
1695
+ }
1696
+
1697
+ appendCellHtml(stringArray, row, i, colspan, d);
1698
+ }
1699
+
1700
+ if (colspan > 1) {
1701
+ i += (colspan - 1);
1702
+ }
1703
+ }
1704
+
1705
+ stringArray.push("</div>");
1706
+ }
1707
+
1708
+ function appendCellHtml(stringArray, row, cell, colspan, item) {
1709
+ // stringArray: stringBuilder containing the HTML parts
1710
+ // row, cell: row and column index
1711
+ // colspan: HTML colspan
1712
+ // item: grid data for row
1713
+
1714
+ var m = columns[cell];
1715
+ var cellCss = "slick-cell l" + cell + " r" + Math.min(columns.length - 1, cell + colspan - 1) +
1716
+ (m.cssClass ? " " + m.cssClass : "");
1717
+ if (row === activeRow && cell === activeCell) {
1718
+ cellCss += (" active");
1719
+ }
1720
+
1721
+ // TODO: merge them together in the setter
1722
+ for (var key in cellCssClasses) {
1723
+ if (cellCssClasses[key][row] && cellCssClasses[key][row][m.id]) {
1724
+ cellCss += (" " + cellCssClasses[key][row][m.id]);
1725
+ }
1726
+ }
1727
+
1728
+ var value = null, formatterResult = '';
1729
+ if (item) {
1730
+ value = getDataItemValueForColumn(item, m);
1731
+ formatterResult = getFormatter(row, m)(row, cell, value, m, item, self);
1732
+ if (formatterResult === null || formatterResult === undefined) { formatterResult = ''; }
1733
+ }
1734
+
1735
+ // get addl css class names from object type formatter return and from string type return of onBeforeAppendCell
1736
+ var addlCssClasses = trigger(self.onBeforeAppendCell, { row: row, cell: cell, grid: self, value: value, dataContext: item }) || '';
1737
+ addlCssClasses += (formatterResult.addClasses ? (addlCssClasses ? ' ' : '') + formatterResult.addClasses : '');
1738
+
1739
+ stringArray.push("<div class='" + cellCss + (addlCssClasses ? ' ' + addlCssClasses : '') + "'>");
1740
+
1741
+ // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet)
1742
+ if (item) {
1743
+ stringArray.push(Object.prototype.toString.call(formatterResult) !== '[object Object]' ? formatterResult : formatterResult.text);
1744
+ }
1745
+
1746
+ stringArray.push("</div>");
1747
+
1748
+ rowsCache[row].cellRenderQueue.push(cell);
1749
+ rowsCache[row].cellColSpans[cell] = colspan;
1750
+ }
1751
+
1752
+
1753
+ function cleanupRows(rangeToKeep) {
1754
+ for (var i in rowsCache) {
1755
+ if (((i = parseInt(i, 10)) !== activeRow) && (i < rangeToKeep.top || i > rangeToKeep.bottom)) {
1756
+ removeRowFromCache(i);
1757
+ }
1758
+ }
1759
+ if (options.enableAsyncPostRenderCleanup) { startPostProcessingCleanup(); }
1760
+ }
1761
+
1762
+ function invalidate() {
1763
+ updateRowCount();
1764
+ invalidateAllRows();
1765
+ render();
1766
+ }
1767
+
1768
+ function invalidateAllRows() {
1769
+ if (currentEditor) {
1770
+ makeActiveCellNormal();
1771
+ }
1772
+ for (var row in rowsCache) {
1773
+ removeRowFromCache(row);
1774
+ }
1775
+ if (options.enableAsyncPostRenderCleanup) { startPostProcessingCleanup(); }
1776
+ }
1777
+
1778
+ function queuePostProcessedRowForCleanup(cacheEntry, postProcessedRow, rowIdx) {
1779
+ postProcessgroupId++;
1780
+
1781
+ // store and detach node for later async cleanup
1782
+ for (var columnIdx in postProcessedRow) {
1783
+ if (postProcessedRow.hasOwnProperty(columnIdx)) {
1784
+ postProcessedCleanupQueue.push({
1785
+ actionType: 'C',
1786
+ groupId: postProcessgroupId,
1787
+ node: cacheEntry.cellNodesByColumnIdx[ columnIdx | 0],
1788
+ columnIdx: columnIdx | 0,
1789
+ rowIdx: rowIdx
1790
+ });
1791
+ }
1792
+ }
1793
+ postProcessedCleanupQueue.push({
1794
+ actionType: 'R',
1795
+ groupId: postProcessgroupId,
1796
+ node: cacheEntry.rowNode
1797
+ });
1798
+ $(cacheEntry.rowNode).detach();
1799
+ }
1800
+
1801
+ function queuePostProcessedCellForCleanup(cellnode, columnIdx, rowIdx) {
1802
+ postProcessedCleanupQueue.push({
1803
+ actionType: 'C',
1804
+ groupId: postProcessgroupId,
1805
+ node: cellnode,
1806
+ columnIdx: columnIdx,
1807
+ rowIdx: rowIdx
1808
+ });
1809
+ $(cellnode).detach();
1810
+ }
1811
+
1812
+ function removeRowFromCache(row) {
1813
+ var cacheEntry = rowsCache[row];
1814
+ if (!cacheEntry) {
1815
+ return;
1816
+ }
1817
+
1818
+ if (cacheEntry.rowNode) {
1819
+ if (rowNodeFromLastMouseWheelEvent === cacheEntry.rowNode) {
1820
+ cacheEntry.rowNode.style.display = 'none';
1821
+ zombieRowNodeFromLastMouseWheelEvent = rowNodeFromLastMouseWheelEvent;
1822
+ zombieRowCacheFromLastMouseWheelEvent = cacheEntry;
1823
+ zombieRowPostProcessedFromLastMouseWheelEvent = postProcessedRows[row];
1824
+ // ignore post processing cleanup in this case - it will be dealt with later
1825
+ } else {
1826
+ if (options.enableAsyncPostRenderCleanup && postProcessedRows[row]) {
1827
+ queuePostProcessedRowForCleanup(cacheEntry, postProcessedRows[row], row);
1828
+ } else {
1829
+ $canvas[0].removeChild(cacheEntry.rowNode);
1830
+ }
1831
+ }
1832
+ }
1833
+
1834
+ delete rowsCache[row];
1835
+ delete postProcessedRows[row];
1836
+ renderedRows--;
1837
+ counter_rows_removed++;
1838
+ }
1839
+
1840
+ function invalidateRows(rows) {
1841
+ var i, rl;
1842
+ if (!rows || !rows.length) {
1843
+ return;
1844
+ }
1845
+ vScrollDir = 0;
1846
+ rl = rows.length;
1847
+ for (i = 0; i < rl; i++) {
1848
+ if (currentEditor && activeRow === rows[i]) {
1849
+ makeActiveCellNormal();
1850
+ }
1851
+ if (rowsCache[rows[i]]) {
1852
+ removeRowFromCache(rows[i]);
1853
+ }
1854
+ }
1855
+ if (options.enableAsyncPostRenderCleanup) { startPostProcessingCleanup(); }
1856
+
1857
+ }
1858
+
1859
+ function invalidateRow(row) {
1860
+ invalidateRows([row]);
1861
+ }
1862
+
1863
+ function applyFormatResultToCellNode(formatterResult, cellNode, suppressRemove) {
1864
+ if (Object.prototype.toString.call(formatterResult) !== '[object Object]') {
1865
+ cellNode.innerHTML = formatterResult;
1866
+ return;
1867
+ }
1868
+ cellNode.innerHTML = formatterResult.text;
1869
+ if (formatterResult.removeClasses && !suppressRemove) {
1870
+ $(cellNode).removeClass(formatterResult.removeClasses);
1871
+ }
1872
+ if (formatterResult.addClasses) {
1873
+ $(cellNode).addClass(formatterResult.addClasses);
1874
+ }
1875
+ }
1876
+
1877
+ function updateCell(row, cell) {
1878
+ var cellNode = getCellNode(row, cell);
1879
+ if (!cellNode) {
1880
+ return;
1881
+ }
1882
+
1883
+ var m = columns[cell], d = getDataItem(row);
1884
+ if (currentEditor && activeRow === row && activeCell === cell) {
1885
+ currentEditor.loadValue(d);
1886
+ } else {
1887
+ var formatterResult = d ? getFormatter(row, m)(row, cell, getDataItemValueForColumn(d, m), m, d, self) : "";
1888
+ applyFormatResultToCellNode(formatterResult, cellNode);
1889
+ invalidatePostProcessingResults(row);
1890
+ }
1891
+ }
1892
+
1893
+ function updateRow(row) {
1894
+ var cacheEntry = rowsCache[row];
1895
+ if (!cacheEntry) {
1896
+ return;
1897
+ }
1898
+
1899
+ ensureCellNodesInRowsCache(row);
1900
+
1901
+ var formatterResult, d = getDataItem(row);
1902
+
1903
+ for (var columnIdx in cacheEntry.cellNodesByColumnIdx) {
1904
+ if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(columnIdx)) {
1905
+ continue;
1906
+ }
1907
+
1908
+ columnIdx = columnIdx | 0;
1909
+ var m = columns[columnIdx],
1910
+ node = cacheEntry.cellNodesByColumnIdx[columnIdx];
1911
+
1912
+ if (row === activeRow && columnIdx === activeCell && currentEditor) {
1913
+ currentEditor.loadValue(d);
1914
+ } else if (d) {
1915
+ formatterResult = getFormatter(row, m)(row, columnIdx, getDataItemValueForColumn(d, m), m, d, self);
1916
+ applyFormatResultToCellNode(formatterResult, node);
1917
+ } else {
1918
+ node.innerHTML = "";
1919
+ }
1920
+ }
1921
+
1922
+ invalidatePostProcessingResults(row);
1923
+ }
1924
+
1925
+ function getViewportHeight() {
1926
+ return parseFloat($.css($container[0], "height", true)) -
1927
+ parseFloat($.css($container[0], "paddingTop", true)) -
1928
+ parseFloat($.css($container[0], "paddingBottom", true)) -
1929
+ parseFloat($.css($headerScroller[0], "height")) - getVBoxDelta($headerScroller) -
1930
+ (options.showTopPanel ? options.topPanelHeight + getVBoxDelta($topPanelScroller) : 0) -
1931
+ (options.showHeaderRow ? options.headerRowHeight + getVBoxDelta($headerRowScroller) : 0) -
1932
+ (options.createFooterRow && options.showFooterRow ? options.footerRowHeight + getVBoxDelta($footerRowScroller) : 0) -
1933
+ (options.createPreHeaderPanel && options.showPreHeaderPanel ? options.preHeaderPanelHeight + getVBoxDelta($preHeaderPanelScroller) : 0);
1934
+ }
1935
+
1936
+ function resizeCanvas() {
1937
+ if (!initialized) { return; }
1938
+ if (options.autoHeight) {
1939
+ viewportH = options.rowHeight * getDataLengthIncludingAddNew();
1940
+ } else {
1941
+ viewportH = getViewportHeight();
1942
+ }
1943
+
1944
+ numVisibleRows = Math.ceil(viewportH / options.rowHeight);
1945
+ viewportW = parseFloat($.css($container[0], "width", true));
1946
+ if (!options.autoHeight) {
1947
+ $viewport.height(viewportH);
1948
+ }
1949
+
1950
+ if (!scrollbarDimensions || !scrollbarDimensions.width) {
1951
+ scrollbarDimensions = measureScrollbar();
1952
+ }
1953
+
1954
+ if (options.forceFitColumns) {
1955
+ autosizeColumns();
1956
+ }
1957
+
1958
+ updateRowCount();
1959
+ handleScroll();
1960
+ // Since the width has changed, force the render() to reevaluate virtually rendered cells.
1961
+ lastRenderedScrollLeft = -1;
1962
+ render();
1963
+ }
1964
+
1965
+ function updatePagingStatusFromView( pagingInfo ) {
1966
+ pagingActive = (pagingInfo.pageSize !== 0);
1967
+ pagingIsLastPage = (pagingInfo.pageNum == pagingInfo.totalPages - 1);
1968
+ }
1969
+
1970
+ function updateRowCount() {
1971
+ if (!initialized) { return; }
1972
+
1973
+ var dataLength = getDataLength();
1974
+ var dataLengthIncludingAddNew = getDataLengthIncludingAddNew();
1975
+ var numberOfRows = dataLengthIncludingAddNew +
1976
+ (options.leaveSpaceForNewRows ? numVisibleRows - 1 : 0);
1977
+
1978
+ var oldViewportHasVScroll = viewportHasVScroll;
1979
+ // with autoHeight, we do not need to accommodate the vertical scroll bar
1980
+ viewportHasVScroll = !options.autoHeight && (numberOfRows * options.rowHeight > viewportH);
1981
+ viewportHasHScroll = (canvasWidth > viewportW - scrollbarDimensions.width);
1982
+
1983
+ makeActiveCellNormal();
1984
+
1985
+ // remove the rows that are now outside of the data range
1986
+ // this helps avoid redundant calls to .removeRow() when the size of the data decreased by thousands of rows
1987
+ var r1 = dataLength - 1;
1988
+ for (var i in rowsCache) {
1989
+ if (i > r1) {
1990
+ removeRowFromCache(i);
1991
+ }
1992
+ }
1993
+ if (options.enableAsyncPostRenderCleanup) { startPostProcessingCleanup(); }
1994
+
1995
+ if (activeCellNode && activeRow > r1) {
1996
+ resetActiveCell();
1997
+ }
1998
+
1999
+ var oldH = h;
2000
+ th = Math.max(options.rowHeight * numberOfRows, viewportH - scrollbarDimensions.height);
2001
+ if (th < maxSupportedCssHeight) {
2002
+ // just one page
2003
+ h = ph = th;
2004
+ n = 1;
2005
+ cj = 0;
2006
+ } else {
2007
+ // break into pages
2008
+ h = maxSupportedCssHeight;
2009
+ ph = h / 100;
2010
+ n = Math.floor(th / ph);
2011
+ cj = (th - h) / (n - 1);
2012
+ }
2013
+
2014
+ if (h !== oldH) {
2015
+ $canvas.css("height", h);
2016
+ scrollTop = $viewport[0].scrollTop;
2017
+ }
2018
+
2019
+ var oldScrollTopInRange = (scrollTop + offset <= th - viewportH);
2020
+
2021
+ if (th == 0 || scrollTop == 0) {
2022
+ page = offset = 0;
2023
+ } else if (oldScrollTopInRange) {
2024
+ // maintain virtual position
2025
+ scrollTo(scrollTop + offset);
2026
+ } else {
2027
+ // scroll to bottom
2028
+ scrollTo(th - viewportH);
2029
+ }
2030
+
2031
+ if (h != oldH && options.autoHeight) {
2032
+ resizeCanvas();
2033
+ }
2034
+
2035
+ if (options.forceFitColumns && oldViewportHasVScroll != viewportHasVScroll) {
2036
+ autosizeColumns();
2037
+ }
2038
+ updateCanvasWidth(false);
2039
+ }
2040
+
2041
+ function getVisibleRange(viewportTop, viewportLeft) {
2042
+ if (viewportTop == null) {
2043
+ viewportTop = scrollTop;
2044
+ }
2045
+ if (viewportLeft == null) {
2046
+ viewportLeft = scrollLeft;
2047
+ }
2048
+
2049
+ return {
2050
+ top: getRowFromPosition(viewportTop),
2051
+ bottom: getRowFromPosition(viewportTop + viewportH) + 1,
2052
+ leftPx: viewportLeft,
2053
+ rightPx: viewportLeft + viewportW
2054
+ };
2055
+ }
2056
+
2057
+ function getRenderedRange(viewportTop, viewportLeft) {
2058
+ var range = getVisibleRange(viewportTop, viewportLeft);
2059
+ var buffer = Math.round(viewportH / options.rowHeight);
2060
+ var minBuffer = options.minRowBuffer;
2061
+
2062
+ if (vScrollDir == -1) {
2063
+ range.top -= buffer;
2064
+ range.bottom += minBuffer;
2065
+ } else if (vScrollDir == 1) {
2066
+ range.top -= minBuffer;
2067
+ range.bottom += buffer;
2068
+ } else {
2069
+ range.top -= minBuffer;
2070
+ range.bottom += minBuffer;
2071
+ }
2072
+
2073
+ range.top = Math.max(0, range.top);
2074
+ range.bottom = Math.min(getDataLengthIncludingAddNew() - 1, range.bottom);
2075
+
2076
+ range.leftPx -= viewportW;
2077
+ range.rightPx += viewportW;
2078
+
2079
+ range.leftPx = Math.max(0, range.leftPx);
2080
+ range.rightPx = Math.min(canvasWidth, range.rightPx);
2081
+
2082
+ return range;
2083
+ }
2084
+
2085
+ function ensureCellNodesInRowsCache(row) {
2086
+ var cacheEntry = rowsCache[row];
2087
+ if (cacheEntry) {
2088
+ if (cacheEntry.cellRenderQueue.length) {
2089
+ var lastChild = cacheEntry.rowNode.lastChild;
2090
+ while (cacheEntry.cellRenderQueue.length) {
2091
+ var columnIdx = cacheEntry.cellRenderQueue.pop();
2092
+ cacheEntry.cellNodesByColumnIdx[columnIdx] = lastChild;
2093
+ lastChild = lastChild.previousSibling;
2094
+ }
2095
+ }
2096
+ }
2097
+ }
2098
+
2099
+ function cleanUpCells(range, row) {
2100
+ var totalCellsRemoved = 0;
2101
+ var cacheEntry = rowsCache[row];
2102
+
2103
+ // Remove cells outside the range.
2104
+ var cellsToRemove = [];
2105
+ for (var i in cacheEntry.cellNodesByColumnIdx) {
2106
+ // I really hate it when people mess with Array.prototype.
2107
+ if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(i)) {
2108
+ continue;
2109
+ }
2110
+
2111
+ // This is a string, so it needs to be cast back to a number.
2112
+ i = i | 0;
2113
+
2114
+ var colspan = cacheEntry.cellColSpans[i];
2115
+ if (columnPosLeft[i] > range.rightPx ||
2116
+ columnPosRight[Math.min(columns.length - 1, i + colspan - 1)] < range.leftPx) {
2117
+ if (!(row == activeRow && i == activeCell)) {
2118
+ cellsToRemove.push(i);
2119
+ }
2120
+ }
2121
+ }
2122
+
2123
+ var cellToRemove, node;
2124
+ postProcessgroupId++;
2125
+ while ((cellToRemove = cellsToRemove.pop()) != null) {
2126
+ node = cacheEntry.cellNodesByColumnIdx[cellToRemove];
2127
+ if (options.enableAsyncPostRenderCleanup && postProcessedRows[row] && postProcessedRows[row][cellToRemove]) {
2128
+ queuePostProcessedCellForCleanup(node, cellToRemove, row);
2129
+ } else {
2130
+ cacheEntry.rowNode.removeChild(node);
2131
+ }
2132
+
2133
+ delete cacheEntry.cellColSpans[cellToRemove];
2134
+ delete cacheEntry.cellNodesByColumnIdx[cellToRemove];
2135
+ if (postProcessedRows[row]) {
2136
+ delete postProcessedRows[row][cellToRemove];
2137
+ }
2138
+ totalCellsRemoved++;
2139
+ }
2140
+ }
2141
+
2142
+ function cleanUpAndRenderCells(range) {
2143
+ var cacheEntry;
2144
+ var stringArray = [];
2145
+ var processedRows = [];
2146
+ var cellsAdded;
2147
+ var totalCellsAdded = 0;
2148
+ var colspan;
2149
+
2150
+ for (var row = range.top, btm = range.bottom; row <= btm; row++) {
2151
+ cacheEntry = rowsCache[row];
2152
+ if (!cacheEntry) {
2153
+ continue;
2154
+ }
2155
+
2156
+ // cellRenderQueue populated in renderRows() needs to be cleared first
2157
+ ensureCellNodesInRowsCache(row);
2158
+
2159
+ cleanUpCells(range, row);
2160
+
2161
+ // Render missing cells.
2162
+ cellsAdded = 0;
2163
+
2164
+ var metadata = data.getItemMetadata && data.getItemMetadata(row);
2165
+ metadata = metadata && metadata.columns;
2166
+
2167
+ var d = getDataItem(row);
2168
+
2169
+ // TODO: shorten this loop (index? heuristics? binary search?)
2170
+ for (var i = 0, ii = columns.length; i < ii; i++) {
2171
+ // Cells to the right are outside the range.
2172
+ if (columnPosLeft[i] > range.rightPx) {
2173
+ break;
2174
+ }
2175
+
2176
+ // Already rendered.
2177
+ if ((colspan = cacheEntry.cellColSpans[i]) != null) {
2178
+ i += (colspan > 1 ? colspan - 1 : 0);
2179
+ continue;
2180
+ }
2181
+
2182
+ colspan = 1;
2183
+ if (metadata) {
2184
+ var columnData = metadata[columns[i].id] || metadata[i];
2185
+ colspan = (columnData && columnData.colspan) || 1;
2186
+ if (colspan === "*") {
2187
+ colspan = ii - i;
2188
+ }
2189
+ }
2190
+
2191
+ if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) {
2192
+ appendCellHtml(stringArray, row, i, colspan, d);
2193
+ cellsAdded++;
2194
+ }
2195
+
2196
+ i += (colspan > 1 ? colspan - 1 : 0);
2197
+ }
2198
+
2199
+ if (cellsAdded) {
2200
+ totalCellsAdded += cellsAdded;
2201
+ processedRows.push(row);
2202
+ }
2203
+ }
2204
+
2205
+ if (!stringArray.length) {
2206
+ return;
2207
+ }
2208
+
2209
+ var x = document.createElement("div");
2210
+ x.innerHTML = stringArray.join("");
2211
+
2212
+ var processedRow;
2213
+ var node;
2214
+ while ((processedRow = processedRows.pop()) != null) {
2215
+ cacheEntry = rowsCache[processedRow];
2216
+ var columnIdx;
2217
+ while ((columnIdx = cacheEntry.cellRenderQueue.pop()) != null) {
2218
+ node = x.lastChild;
2219
+ cacheEntry.rowNode.appendChild(node);
2220
+ cacheEntry.cellNodesByColumnIdx[columnIdx] = node;
2221
+ }
2222
+ }
2223
+ }
2224
+
2225
+ function renderRows(range) {
2226
+ var parentNode = $canvas[0],
2227
+ stringArray = [],
2228
+ rows = [],
2229
+ needToReselectCell = false,
2230
+ dataLength = getDataLength();
2231
+
2232
+ for (var i = range.top, ii = range.bottom; i <= ii; i++) {
2233
+ if (rowsCache[i]) {
2234
+ continue;
2235
+ }
2236
+ renderedRows++;
2237
+ rows.push(i);
2238
+
2239
+ // Create an entry right away so that appendRowHtml() can
2240
+ // start populatating it.
2241
+ rowsCache[i] = {
2242
+ "rowNode": null,
2243
+
2244
+ // ColSpans of rendered cells (by column idx).
2245
+ // Can also be used for checking whether a cell has been rendered.
2246
+ "cellColSpans": [],
2247
+
2248
+ // Cell nodes (by column idx). Lazy-populated by ensureCellNodesInRowsCache().
2249
+ "cellNodesByColumnIdx": [],
2250
+
2251
+ // Column indices of cell nodes that have been rendered, but not yet indexed in
2252
+ // cellNodesByColumnIdx. These are in the same order as cell nodes added at the
2253
+ // end of the row.
2254
+ "cellRenderQueue": []
2255
+ };
2256
+
2257
+ appendRowHtml(stringArray, i, range, dataLength);
2258
+ if (activeCellNode && activeRow === i) {
2259
+ needToReselectCell = true;
2260
+ }
2261
+ counter_rows_rendered++;
2262
+ }
2263
+
2264
+ if (!rows.length) { return; }
2265
+
2266
+ var x = document.createElement("div");
2267
+ x.innerHTML = stringArray.join("");
2268
+
2269
+ for (var i = 0, ii = rows.length; i < ii; i++) {
2270
+ rowsCache[rows[i]].rowNode = parentNode.appendChild(x.firstChild);
2271
+ }
2272
+
2273
+ if (needToReselectCell) {
2274
+ activeCellNode = getCellNode(activeRow, activeCell);
2275
+ }
2276
+ }
2277
+
2278
+ function startPostProcessing() {
2279
+ if (!options.enableAsyncPostRender) {
2280
+ return;
2281
+ }
2282
+ clearTimeout(h_postrender);
2283
+ h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay);
2284
+ }
2285
+
2286
+ function startPostProcessingCleanup() {
2287
+ if (!options.enableAsyncPostRenderCleanup) {
2288
+ return;
2289
+ }
2290
+ clearTimeout(h_postrenderCleanup);
2291
+ h_postrenderCleanup = setTimeout(asyncPostProcessCleanupRows, options.asyncPostRenderCleanupDelay);
2292
+ }
2293
+
2294
+ function invalidatePostProcessingResults(row) {
2295
+ // change status of columns to be re-rendered
2296
+ for (var columnIdx in postProcessedRows[row]) {
2297
+ if (postProcessedRows[row].hasOwnProperty(columnIdx)) {
2298
+ postProcessedRows[row][columnIdx] = 'C';
2299
+ }
2300
+ }
2301
+ postProcessFromRow = Math.min(postProcessFromRow, row);
2302
+ postProcessToRow = Math.max(postProcessToRow, row);
2303
+ startPostProcessing();
2304
+ }
2305
+
2306
+ function updateRowPositions() {
2307
+ for (var row in rowsCache) {
2308
+ rowsCache[row].rowNode.style.top = getRowTop(row) + "px";
2309
+ }
2310
+ }
2311
+
2312
+ function render() {
2313
+ if (!initialized) { return; }
2314
+ var visible = getVisibleRange();
2315
+ var rendered = getRenderedRange();
2316
+
2317
+ // remove rows no longer in the viewport
2318
+ cleanupRows(rendered);
2319
+
2320
+ // add new rows & missing cells in existing rows
2321
+ if (lastRenderedScrollLeft != scrollLeft) {
2322
+ cleanUpAndRenderCells(rendered);
2323
+ }
2324
+
2325
+ // render missing rows
2326
+ renderRows(rendered);
2327
+
2328
+ postProcessFromRow = visible.top;
2329
+ postProcessToRow = Math.min(getDataLengthIncludingAddNew() - 1, visible.bottom);
2330
+ startPostProcessing();
2331
+
2332
+ lastRenderedScrollTop = scrollTop;
2333
+ lastRenderedScrollLeft = scrollLeft;
2334
+ h_render = null;
2335
+ }
2336
+
2337
+ function handleHeaderScroll() {
2338
+ handleElementScroll($headerScroller[0]);
2339
+ }
2340
+
2341
+ function handleHeaderRowScroll() {
2342
+ handleElementScroll($headerRowScroller[0]);
2343
+ }
2344
+
2345
+ function handleFooterRowScroll() {
2346
+ handleElementScroll($footerRowScroller[0]);
2347
+ }
2348
+
2349
+ function handlePreHeaderPanelScroll() {
2350
+ handleElementScroll($preHeaderPanelScroller[0]);
2351
+ }
2352
+
2353
+ function handleElementScroll(element) {
2354
+ var scrollLeft = element.scrollLeft;
2355
+ if (scrollLeft != $viewport[0].scrollLeft) {
2356
+ $viewport[0].scrollLeft = scrollLeft;
2357
+ }
2358
+ }
2359
+
2360
+ function handleScroll() {
2361
+ scrollTop = $viewport[0].scrollTop;
2362
+ scrollLeft = $viewport[0].scrollLeft;
2363
+ var vScrollDist = Math.abs(scrollTop - prevScrollTop);
2364
+ var hScrollDist = Math.abs(scrollLeft - prevScrollLeft);
2365
+
2366
+ if (hScrollDist) {
2367
+ prevScrollLeft = scrollLeft;
2368
+ $headerScroller[0].scrollLeft = scrollLeft;
2369
+ $topPanelScroller[0].scrollLeft = scrollLeft;
2370
+ $headerRowScroller[0].scrollLeft = scrollLeft;
2371
+ if (options.createFooterRow) {
2372
+ $footerRowScroller[0].scrollLeft = scrollLeft;
2373
+ }
2374
+ if (options.createPreHeaderPanel) {
2375
+ $preHeaderPanelScroller[0].scrollLeft = scrollLeft;
2376
+ }
2377
+ }
2378
+
2379
+ if (vScrollDist) {
2380
+ vScrollDir = prevScrollTop < scrollTop ? 1 : -1;
2381
+ prevScrollTop = scrollTop;
2382
+
2383
+ // switch virtual pages if needed
2384
+ if (vScrollDist < viewportH) {
2385
+ scrollTo(scrollTop + offset);
2386
+ } else {
2387
+ var oldOffset = offset;
2388
+ if (h == viewportH) {
2389
+ page = 0;
2390
+ } else {
2391
+ page = Math.min(n - 1, Math.floor(scrollTop * ((th - viewportH) / (h - viewportH)) * (1 / ph)));
2392
+ }
2393
+ offset = Math.round(page * cj);
2394
+ if (oldOffset != offset) {
2395
+ invalidateAllRows();
2396
+ }
2397
+ }
2398
+ }
2399
+
2400
+ if (hScrollDist || vScrollDist) {
2401
+ if (h_render) {
2402
+ clearTimeout(h_render);
2403
+ }
2404
+
2405
+ if (Math.abs(lastRenderedScrollTop - scrollTop) > 20 ||
2406
+ Math.abs(lastRenderedScrollLeft - scrollLeft) > 20) {
2407
+ if (options.forceSyncScrolling || (
2408
+ Math.abs(lastRenderedScrollTop - scrollTop) < viewportH &&
2409
+ Math.abs(lastRenderedScrollLeft - scrollLeft) < viewportW)) {
2410
+ render();
2411
+ } else {
2412
+ h_render = setTimeout(render, 50);
2413
+ }
2414
+
2415
+ trigger(self.onViewportChanged, {grid: self});
2416
+ }
2417
+ }
2418
+
2419
+ trigger(self.onScroll, {scrollLeft: scrollLeft, scrollTop: scrollTop, grid: self});
2420
+ }
2421
+
2422
+ function asyncPostProcessRows() {
2423
+ var dataLength = getDataLength();
2424
+ while (postProcessFromRow <= postProcessToRow) {
2425
+ var row = (vScrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--;
2426
+ var cacheEntry = rowsCache[row];
2427
+ if (!cacheEntry || row >= dataLength) {
2428
+ continue;
2429
+ }
2430
+
2431
+ if (!postProcessedRows[row]) {
2432
+ postProcessedRows[row] = {};
2433
+ }
2434
+
2435
+ ensureCellNodesInRowsCache(row);
2436
+ for (var columnIdx in cacheEntry.cellNodesByColumnIdx) {
2437
+ if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(columnIdx)) {
2438
+ continue;
2439
+ }
2440
+
2441
+ columnIdx = columnIdx | 0;
2442
+
2443
+ var m = columns[columnIdx];
2444
+ var processedStatus = postProcessedRows[row][columnIdx]; // C=cleanup and re-render, R=rendered
2445
+ if (m.asyncPostRender && processedStatus !== 'R') {
2446
+ var node = cacheEntry.cellNodesByColumnIdx[columnIdx];
2447
+ if (node) {
2448
+ m.asyncPostRender(node, row, getDataItem(row), m, (processedStatus === 'C'));
2449
+ }
2450
+ postProcessedRows[row][columnIdx] = 'R';
2451
+ }
2452
+ }
2453
+
2454
+ h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay);
2455
+ return;
2456
+ }
2457
+ }
2458
+
2459
+ function asyncPostProcessCleanupRows() {
2460
+ if (postProcessedCleanupQueue.length > 0) {
2461
+ var groupId = postProcessedCleanupQueue[0].groupId;
2462
+
2463
+ // loop through all queue members with this groupID
2464
+ while (postProcessedCleanupQueue.length > 0 && postProcessedCleanupQueue[0].groupId == groupId) {
2465
+ var entry = postProcessedCleanupQueue.shift();
2466
+ if (entry.actionType == 'R') {
2467
+ $(entry.node).remove();
2468
+ }
2469
+ if (entry.actionType == 'C') {
2470
+ var column = columns[entry.columnIdx];
2471
+ if (column.asyncPostRenderCleanup && entry.node) {
2472
+ // cleanup must also remove element
2473
+ column.asyncPostRenderCleanup(entry.node, entry.rowIdx, column);
2474
+ }
2475
+ }
2476
+ }
2477
+
2478
+ // call this function again after the specified delay
2479
+ h_postrenderCleanup = setTimeout(asyncPostProcessCleanupRows, options.asyncPostRenderCleanupDelay);
2480
+ }
2481
+ }
2482
+
2483
+ function updateCellCssStylesOnRenderedRows(addedHash, removedHash) {
2484
+ var node, columnId, addedRowHash, removedRowHash;
2485
+ for (var row in rowsCache) {
2486
+ removedRowHash = removedHash && removedHash[row];
2487
+ addedRowHash = addedHash && addedHash[row];
2488
+
2489
+ if (removedRowHash) {
2490
+ for (columnId in removedRowHash) {
2491
+ if (!addedRowHash || removedRowHash[columnId] != addedRowHash[columnId]) {
2492
+ node = getCellNode(row, getColumnIndex(columnId));
2493
+ if (node) {
2494
+ $(node).removeClass(removedRowHash[columnId]);
2495
+ }
2496
+ }
2497
+ }
2498
+ }
2499
+
2500
+ if (addedRowHash) {
2501
+ for (columnId in addedRowHash) {
2502
+ if (!removedRowHash || removedRowHash[columnId] != addedRowHash[columnId]) {
2503
+ node = getCellNode(row, getColumnIndex(columnId));
2504
+ if (node) {
2505
+ $(node).addClass(addedRowHash[columnId]);
2506
+ }
2507
+ }
2508
+ }
2509
+ }
2510
+ }
2511
+ }
2512
+
2513
+ function addCellCssStyles(key, hash) {
2514
+ if (cellCssClasses[key]) {
2515
+ throw new Error("addCellCssStyles: cell CSS hash with key '" + key + "' already exists.");
2516
+ }
2517
+
2518
+ cellCssClasses[key] = hash;
2519
+ updateCellCssStylesOnRenderedRows(hash, null);
2520
+
2521
+ trigger(self.onCellCssStylesChanged, { "key": key, "hash": hash, "grid": self });
2522
+ }
2523
+
2524
+ function removeCellCssStyles(key) {
2525
+ if (!cellCssClasses[key]) {
2526
+ return;
2527
+ }
2528
+
2529
+ updateCellCssStylesOnRenderedRows(null, cellCssClasses[key]);
2530
+ delete cellCssClasses[key];
2531
+
2532
+ trigger(self.onCellCssStylesChanged, { "key": key, "hash": null, "grid": self });
2533
+ }
2534
+
2535
+ function setCellCssStyles(key, hash) {
2536
+ var prevHash = cellCssClasses[key];
2537
+
2538
+ cellCssClasses[key] = hash;
2539
+ updateCellCssStylesOnRenderedRows(hash, prevHash);
2540
+
2541
+ trigger(self.onCellCssStylesChanged, { "key": key, "hash": hash, "grid": self });
2542
+ }
2543
+
2544
+ function getCellCssStyles(key) {
2545
+ return cellCssClasses[key];
2546
+ }
2547
+
2548
+ function flashCell(row, cell, speed) {
2549
+ speed = speed || 100;
2550
+ if (rowsCache[row]) {
2551
+ var $cell = $(getCellNode(row, cell));
2552
+
2553
+ function toggleCellClass(times) {
2554
+ if (!times) {
2555
+ return;
2556
+ }
2557
+ setTimeout(function () {
2558
+ $cell.queue(function () {
2559
+ $cell.toggleClass(options.cellFlashingCssClass).dequeue();
2560
+ toggleCellClass(times - 1);
2561
+ });
2562
+ },
2563
+ speed);
2564
+ }
2565
+
2566
+ toggleCellClass(4);
2567
+ }
2568
+ }
2569
+
2570
+ //////////////////////////////////////////////////////////////////////////////////////////////
2571
+ // Interactivity
2572
+
2573
+ function handleMouseWheel(e) {
2574
+ var rowNode = $(e.target).closest(".slick-row")[0];
2575
+ if (rowNode != rowNodeFromLastMouseWheelEvent) {
2576
+ if (zombieRowNodeFromLastMouseWheelEvent && zombieRowNodeFromLastMouseWheelEvent != rowNode) {
2577
+ if (options.enableAsyncPostRenderCleanup && zombieRowPostProcessedFromLastMouseWheelEvent) {
2578
+ queuePostProcessedRowForCleanup(zombieRowCacheFromLastMouseWheelEvent,
2579
+ zombieRowPostProcessedFromLastMouseWheelEvent);
2580
+ } else {
2581
+ $canvas[0].removeChild(zombieRowNodeFromLastMouseWheelEvent);
2582
+ }
2583
+ zombieRowNodeFromLastMouseWheelEvent = null;
2584
+ zombieRowCacheFromLastMouseWheelEvent = null;
2585
+ zombieRowPostProcessedFromLastMouseWheelEvent = null;
2586
+
2587
+ if (options.enableAsyncPostRenderCleanup) { startPostProcessingCleanup(); }
2588
+ }
2589
+ rowNodeFromLastMouseWheelEvent = rowNode;
2590
+ }
2591
+ }
2592
+
2593
+ function handleDragInit(e, dd) {
2594
+ var cell = getCellFromEvent(e);
2595
+ if (!cell || !cellExists(cell.row, cell.cell)) {
2596
+ return false;
2597
+ }
2598
+
2599
+ var retval = trigger(self.onDragInit, dd, e);
2600
+ if (e.isImmediatePropagationStopped()) {
2601
+ return retval;
2602
+ }
2603
+
2604
+ // if nobody claims to be handling drag'n'drop by stopping immediate propagation,
2605
+ // cancel out of it
2606
+ return false;
2607
+ }
2608
+
2609
+ function handleDragStart(e, dd) {
2610
+ var cell = getCellFromEvent(e);
2611
+ if (!cell || !cellExists(cell.row, cell.cell)) {
2612
+ return false;
2613
+ }
2614
+
2615
+ var retval = trigger(self.onDragStart, dd, e);
2616
+ if (e.isImmediatePropagationStopped()) {
2617
+ return retval;
2618
+ }
2619
+
2620
+ return false;
2621
+ }
2622
+
2623
+ function handleDrag(e, dd) {
2624
+ return trigger(self.onDrag, dd, e);
2625
+ }
2626
+
2627
+ function handleDragEnd(e, dd) {
2628
+ trigger(self.onDragEnd, dd, e);
2629
+ }
2630
+
2631
+ function handleKeyDown(e) {
2632
+ trigger(self.onKeyDown, {row: activeRow, cell: activeCell, grid: self}, e);
2633
+ var handled = e.isImmediatePropagationStopped();
2634
+ var keyCode = Slick.keyCode;
2635
+
2636
+ if (!handled) {
2637
+ if (!e.shiftKey && !e.altKey) {
2638
+ if (options.editable && currentEditor && currentEditor.keyCaptureList) {
2639
+ if (currentEditor.keyCaptureList.indexOf(e.which) > -1) {
2640
+ return;
2641
+ }
2642
+ }
2643
+ if (e.which == keyCode.HOME) {
2644
+ handled = (e.ctrlKey) ? navigateTop() : navigateRowStart();
2645
+ } else if (e.which == keyCode.END) {
2646
+ handled = (e.ctrlKey) ? navigateBottom() : navigateRowEnd();
2647
+ }
2648
+ }
2649
+ }
2650
+ if (!handled) {
2651
+ if (!e.shiftKey && !e.altKey && !e.ctrlKey) {
2652
+ // editor may specify an array of keys to bubble
2653
+ if (options.editable && currentEditor && currentEditor.keyCaptureList) {
2654
+ if (currentEditor.keyCaptureList.indexOf( e.which ) > -1) {
2655
+ return;
2656
+ }
2657
+ }
2658
+ if (e.which == keyCode.ESCAPE) {
2659
+ if (!getEditorLock().isActive()) {
2660
+ return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event)
2661
+ }
2662
+ cancelEditAndSetFocus();
2663
+ } else if (e.which == keyCode.PAGE_DOWN) {
2664
+ navigatePageDown();
2665
+ handled = true;
2666
+ } else if (e.which == keyCode.PAGE_UP) {
2667
+ navigatePageUp();
2668
+ handled = true;
2669
+ } else if (e.which == keyCode.LEFT) {
2670
+ handled = navigateLeft();
2671
+ } else if (e.which == keyCode.RIGHT) {
2672
+ handled = navigateRight();
2673
+ } else if (e.which == keyCode.UP) {
2674
+ handled = navigateUp();
2675
+ } else if (e.which == keyCode.DOWN) {
2676
+ handled = navigateDown();
2677
+ } else if (e.which == keyCode.TAB) {
2678
+ handled = navigateNext();
2679
+ } else if (e.which == keyCode.ENTER) {
2680
+ if (options.editable) {
2681
+ if (currentEditor) {
2682
+ // adding new row
2683
+ if (activeRow === getDataLength()) {
2684
+ navigateDown();
2685
+ } else {
2686
+ commitEditAndSetFocus();
2687
+ }
2688
+ } else {
2689
+ if (getEditorLock().commitCurrentEdit()) {
2690
+ makeActiveCellEditable();
2691
+ }
2692
+ }
2693
+ }
2694
+ handled = true;
2695
+ }
2696
+ } else if (e.which == keyCode.TAB && e.shiftKey && !e.ctrlKey && !e.altKey) {
2697
+ handled = navigatePrev();
2698
+ }
2699
+ }
2700
+
2701
+ if (handled) {
2702
+ // the event has been handled so don't let parent element (bubbling/propagation) or browser (default) handle it
2703
+ e.stopPropagation();
2704
+ e.preventDefault();
2705
+ try {
2706
+ e.originalEvent.keyCode = 0; // prevent default behaviour for special keys in IE browsers (F3, F5, etc.)
2707
+ }
2708
+ // ignore exceptions - setting the original event's keycode throws access denied exception for "Ctrl"
2709
+ // (hitting control key only, nothing else), "Shift" (maybe others)
2710
+ catch (error) {
2711
+ }
2712
+ }
2713
+ }
2714
+
2715
+ function handleClick(e) {
2716
+ if (!currentEditor) {
2717
+ // if this click resulted in some cell child node getting focus,
2718
+ // don't steal it back - keyboard events will still bubble up
2719
+ // IE9+ seems to default DIVs to tabIndex=0 instead of -1, so check for cell clicks directly.
2720
+ if (e.target != document.activeElement || $(e.target).hasClass("slick-cell")) {
2721
+ setFocus();
2722
+ }
2723
+ }
2724
+
2725
+ var cell = getCellFromEvent(e);
2726
+ if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) {
2727
+ return;
2728
+ }
2729
+
2730
+ trigger(self.onClick, {row: cell.row, cell: cell.cell, grid: self}, e);
2731
+ if (e.isImmediatePropagationStopped()) {
2732
+ return;
2733
+ }
2734
+
2735
+ // this optimisation causes trouble - MLeibman #329
2736
+ //if ((activeCell != cell.cell || activeRow != cell.row) && canCellBeActive(cell.row, cell.cell)) {
2737
+ if (canCellBeActive(cell.row, cell.cell)) {
2738
+ if (!getEditorLock().isActive() || getEditorLock().commitCurrentEdit()) {
2739
+ scrollRowIntoView(cell.row, false);
2740
+
2741
+ var preClickModeOn = (e.target && e.target.className === Slick.preClickClassName);
2742
+ setActiveCellInternal(getCellNode(cell.row, cell.cell), null, preClickModeOn);
2743
+ }
2744
+ }
2745
+ }
2746
+
2747
+ function handleContextMenu(e) {
2748
+ var $cell = $(e.target).closest(".slick-cell", $canvas);
2749
+ if ($cell.length === 0) {
2750
+ return;
2751
+ }
2752
+
2753
+ // are we editing this cell?
2754
+ if (activeCellNode === $cell[0] && currentEditor !== null) {
2755
+ return;
2756
+ }
2757
+
2758
+ trigger(self.onContextMenu, {grid: self}, e);
2759
+ }
2760
+
2761
+ function handleDblClick(e) {
2762
+ var cell = getCellFromEvent(e);
2763
+ if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) {
2764
+ return;
2765
+ }
2766
+
2767
+ trigger(self.onDblClick, {row: cell.row, cell: cell.cell, grid: self}, e);
2768
+ if (e.isImmediatePropagationStopped()) {
2769
+ return;
2770
+ }
2771
+
2772
+ if (options.editable) {
2773
+ gotoCell(cell.row, cell.cell, true);
2774
+ }
2775
+ }
2776
+
2777
+ function handleHeaderMouseEnter(e) {
2778
+ trigger(self.onHeaderMouseEnter, {
2779
+ "column": $(this).data("column"),
2780
+ "grid": self
2781
+ }, e);
2782
+ }
2783
+
2784
+ function handleHeaderMouseLeave(e) {
2785
+ trigger(self.onHeaderMouseLeave, {
2786
+ "column": $(this).data("column"),
2787
+ "grid": self
2788
+ }, e);
2789
+ }
2790
+
2791
+ function handleHeaderContextMenu(e) {
2792
+ var $header = $(e.target).closest(".slick-header-column", ".slick-header-columns");
2793
+ var column = $header && $header.data("column");
2794
+ trigger(self.onHeaderContextMenu, {column: column, grid: self}, e);
2795
+ }
2796
+
2797
+ function handleHeaderClick(e) {
2798
+ if (columnResizeDragging) return;
2799
+ var $header = $(e.target).closest(".slick-header-column", ".slick-header-columns");
2800
+ var column = $header && $header.data("column");
2801
+ if (column) {
2802
+ trigger(self.onHeaderClick, {column: column, grid: self}, e);
2803
+ }
2804
+ }
2805
+
2806
+ function handleMouseEnter(e) {
2807
+ trigger(self.onMouseEnter, {grid: self}, e);
2808
+ }
2809
+
2810
+ function handleMouseLeave(e) {
2811
+ trigger(self.onMouseLeave, {grid: self}, e);
2812
+ }
2813
+
2814
+ function cellExists(row, cell) {
2815
+ return !(row < 0 || row >= getDataLength() || cell < 0 || cell >= columns.length);
2816
+ }
2817
+
2818
+ function getCellFromPoint(x, y) {
2819
+ var row = getRowFromPosition(y);
2820
+ var cell = 0;
2821
+
2822
+ var w = 0;
2823
+ for (var i = 0; i < columns.length && w < x; i++) {
2824
+ w += columns[i].width;
2825
+ cell++;
2826
+ }
2827
+
2828
+ if (cell < 0) {
2829
+ cell = 0;
2830
+ }
2831
+
2832
+ return {row: row, cell: cell - 1};
2833
+ }
2834
+
2835
+ function getCellFromNode(cellNode) {
2836
+ // read column number from .l<columnNumber> CSS class
2837
+ var cls = /l\d+/.exec(cellNode.className);
2838
+ if (!cls) {
2839
+ throw new Error("getCellFromNode: cannot get cell - " + cellNode.className);
2840
+ }
2841
+ return parseInt(cls[0].substr(1, cls[0].length - 1), 10);
2842
+ }
2843
+
2844
+ function getRowFromNode(rowNode) {
2845
+ for (var row in rowsCache) {
2846
+ if (rowsCache[row].rowNode === rowNode) {
2847
+ return row | 0;
2848
+ }
2849
+ }
2850
+
2851
+ return null;
2852
+ }
2853
+
2854
+ function getCellFromEvent(e) {
2855
+ var $cell = $(e.target).closest(".slick-cell", $canvas);
2856
+ if (!$cell.length) {
2857
+ return null;
2858
+ }
2859
+
2860
+ var row = getRowFromNode($cell[0].parentNode);
2861
+ var cell = getCellFromNode($cell[0]);
2862
+
2863
+ if (row == null || cell == null) {
2864
+ return null;
2865
+ } else {
2866
+ return {
2867
+ "row": row,
2868
+ "cell": cell
2869
+ };
2870
+ }
2871
+ }
2872
+
2873
+ function getCellNodeBox(row, cell) {
2874
+ if (!cellExists(row, cell)) {
2875
+ return null;
2876
+ }
2877
+
2878
+ var y1 = getRowTop(row);
2879
+ var y2 = y1 + options.rowHeight - 1;
2880
+ var x1 = 0;
2881
+ for (var i = 0; i < cell; i++) {
2882
+ x1 += columns[i].width;
2883
+ }
2884
+ var x2 = x1 + columns[cell].width;
2885
+
2886
+ return {
2887
+ top: y1,
2888
+ left: x1,
2889
+ bottom: y2,
2890
+ right: x2
2891
+ };
2892
+ }
2893
+
2894
+ //////////////////////////////////////////////////////////////////////////////////////////////
2895
+ // Cell switching
2896
+
2897
+ function resetActiveCell() {
2898
+ setActiveCellInternal(null, false);
2899
+ }
2900
+
2901
+ function setFocus() {
2902
+ if (tabbingDirection == -1) {
2903
+ $focusSink[0].focus();
2904
+ } else {
2905
+ $focusSink2[0].focus();
2906
+ }
2907
+ }
2908
+
2909
+ function scrollCellIntoView(row, cell, doPaging) {
2910
+ scrollRowIntoView(row, doPaging);
2911
+
2912
+ var colspan = getColspan(row, cell);
2913
+ internalScrollColumnIntoView(columnPosLeft[cell], columnPosRight[cell + (colspan > 1 ? colspan - 1 : 0)]);
2914
+ }
2915
+
2916
+ function internalScrollColumnIntoView(left, right) {
2917
+ var scrollRight = scrollLeft + viewportW;
2918
+
2919
+ if (left < scrollLeft) {
2920
+ $viewport.scrollLeft(left);
2921
+ handleScroll();
2922
+ render();
2923
+ } else if (right > scrollRight) {
2924
+ $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth));
2925
+ handleScroll();
2926
+ render();
2927
+ }
2928
+ }
2929
+
2930
+ function scrollColumnIntoView(cell) {
2931
+ internalScrollColumnIntoView(columnPosLeft[cell], columnPosRight[cell]);
2932
+ }
2933
+
2934
+ function setActiveCellInternal(newCell, opt_editMode, preClickModeOn, suppressActiveCellChangedEvent) {
2935
+ if (activeCellNode !== null) {
2936
+ makeActiveCellNormal();
2937
+ $(activeCellNode).removeClass("active");
2938
+ if (rowsCache[activeRow]) {
2939
+ $(rowsCache[activeRow].rowNode).removeClass("active");
2940
+ }
2941
+ }
2942
+
2943
+ var activeCellChanged = (activeCellNode !== newCell);
2944
+ activeCellNode = newCell;
2945
+
2946
+ if (activeCellNode != null) {
2947
+ activeRow = getRowFromNode(activeCellNode.parentNode);
2948
+ activeCell = activePosX = getCellFromNode(activeCellNode);
2949
+
2950
+ if (opt_editMode == null) {
2951
+ opt_editMode = (activeRow == getDataLength()) || options.autoEdit;
2952
+ }
2953
+
2954
+ if (options.showCellSelection) {
2955
+ $(activeCellNode).addClass("active");
2956
+ $(rowsCache[activeRow].rowNode).addClass("active");
2957
+ }
2958
+
2959
+ if (options.editable && opt_editMode && isCellPotentiallyEditable(activeRow, activeCell)) {
2960
+ clearTimeout(h_editorLoader);
2961
+
2962
+ if (options.asyncEditorLoading) {
2963
+ h_editorLoader = setTimeout(function () {
2964
+ makeActiveCellEditable(undefined, preClickModeOn);
2965
+ }, options.asyncEditorLoadDelay);
2966
+ } else {
2967
+ makeActiveCellEditable(undefined, preClickModeOn);
2968
+ }
2969
+ }
2970
+ } else {
2971
+ activeRow = activeCell = null;
2972
+ }
2973
+
2974
+ // this optimisation causes trouble - MLeibman #329
2975
+ //if (activeCellChanged) {
2976
+ if (!suppressActiveCellChangedEvent) { trigger(self.onActiveCellChanged, getActiveCell()); }
2977
+ //}
2978
+ }
2979
+
2980
+ function clearTextSelection() {
2981
+ if (document.selection && document.selection.empty) {
2982
+ try {
2983
+ //IE fails here if selected element is not in dom
2984
+ document.selection.empty();
2985
+ } catch (e) { }
2986
+ } else if (window.getSelection) {
2987
+ var sel = window.getSelection();
2988
+ if (sel && sel.removeAllRanges) {
2989
+ sel.removeAllRanges();
2990
+ }
2991
+ }
2992
+ }
2993
+
2994
+ function isCellPotentiallyEditable(row, cell) {
2995
+ var dataLength = getDataLength();
2996
+ // is the data for this row loaded?
2997
+ if (row < dataLength && !getDataItem(row)) {
2998
+ return false;
2999
+ }
3000
+
3001
+ // are we in the Add New row? can we create new from this cell?
3002
+ if (columns[cell].cannotTriggerInsert && row >= dataLength) {
3003
+ return false;
3004
+ }
3005
+
3006
+ // does this cell have an editor?
3007
+ if (!getEditor(row, cell)) {
3008
+ return false;
3009
+ }
3010
+
3011
+ return true;
3012
+ }
3013
+
3014
+ function makeActiveCellNormal() {
3015
+ if (!currentEditor) {
3016
+ return;
3017
+ }
3018
+ trigger(self.onBeforeCellEditorDestroy, {editor: currentEditor, grid: self});
3019
+ currentEditor.destroy();
3020
+ currentEditor = null;
3021
+
3022
+ if (activeCellNode) {
3023
+ var d = getDataItem(activeRow);
3024
+ $(activeCellNode).removeClass("editable invalid");
3025
+ if (d) {
3026
+ var column = columns[activeCell];
3027
+ var formatter = getFormatter(activeRow, column);
3028
+ var formatterResult = formatter(activeRow, activeCell, getDataItemValueForColumn(d, column), column, d, self);
3029
+ applyFormatResultToCellNode(formatterResult, activeCellNode);
3030
+ invalidatePostProcessingResults(activeRow);
3031
+ }
3032
+ }
3033
+
3034
+ // if there previously was text selected on a page (such as selected text in the edit cell just removed),
3035
+ // IE can't set focus to anything else correctly
3036
+ if (navigator.userAgent.toLowerCase().match(/msie/)) {
3037
+ clearTextSelection();
3038
+ }
3039
+
3040
+ getEditorLock().deactivate(editController);
3041
+ }
3042
+
3043
+ function makeActiveCellEditable(editor, preClickModeOn) {
3044
+ if (!activeCellNode) {
3045
+ return;
3046
+ }
3047
+ if (!options.editable) {
3048
+ throw new Error("Grid : makeActiveCellEditable : should never get called when options.editable is false");
3049
+ }
3050
+
3051
+ // cancel pending async call if there is one
3052
+ clearTimeout(h_editorLoader);
3053
+
3054
+ if (!isCellPotentiallyEditable(activeRow, activeCell)) {
3055
+ return;
3056
+ }
3057
+
3058
+ var columnDef = columns[activeCell];
3059
+ var item = getDataItem(activeRow);
3060
+
3061
+ if (trigger(self.onBeforeEditCell, {row: activeRow, cell: activeCell, item: item, column: columnDef, grid: self}) === false) {
3062
+ setFocus();
3063
+ return;
3064
+ }
3065
+
3066
+ getEditorLock().activate(editController);
3067
+ $(activeCellNode).addClass("editable");
3068
+
3069
+ var useEditor = editor || getEditor(activeRow, activeCell);
3070
+
3071
+ // don't clear the cell if a custom editor is passed through
3072
+ if (!editor && !useEditor.suppressClearOnEdit) {
3073
+ activeCellNode.innerHTML = "";
3074
+ }
3075
+
3076
+ currentEditor = new useEditor({
3077
+ grid: self,
3078
+ gridPosition: absBox($container[0]),
3079
+ position: absBox(activeCellNode),
3080
+ container: activeCellNode,
3081
+ column: columnDef,
3082
+ item: item || {},
3083
+ commitChanges: commitEditAndSetFocus,
3084
+ cancelChanges: cancelEditAndSetFocus
3085
+ });
3086
+
3087
+ if (item) {
3088
+ currentEditor.loadValue(item);
3089
+ if (preClickModeOn && currentEditor.preClick) {
3090
+ currentEditor.preClick();
3091
+ }
3092
+ }
3093
+
3094
+ serializedEditorValue = currentEditor.serializeValue();
3095
+
3096
+ if (currentEditor.position) {
3097
+ handleActiveCellPositionChange();
3098
+ }
3099
+ }
3100
+
3101
+ function commitEditAndSetFocus() {
3102
+ // if the commit fails, it would do so due to a validation error
3103
+ // if so, do not steal the focus from the editor
3104
+ if (getEditorLock().commitCurrentEdit()) {
3105
+ setFocus();
3106
+ if (options.autoEdit) {
3107
+ navigateDown();
3108
+ }
3109
+ }
3110
+ }
3111
+
3112
+ function cancelEditAndSetFocus() {
3113
+ if (getEditorLock().cancelCurrentEdit()) {
3114
+ setFocus();
3115
+ }
3116
+ }
3117
+
3118
+ function absBox(elem) {
3119
+ var box = {
3120
+ top: elem.offsetTop,
3121
+ left: elem.offsetLeft,
3122
+ bottom: 0,
3123
+ right: 0,
3124
+ width: $(elem).outerWidth(),
3125
+ height: $(elem).outerHeight(),
3126
+ visible: true};
3127
+ box.bottom = box.top + box.height;
3128
+ box.right = box.left + box.width;
3129
+
3130
+ // walk up the tree
3131
+ var offsetParent = elem.offsetParent;
3132
+ while ((elem = elem.parentNode) != document.body) {
3133
+ if (elem == null) break;
3134
+
3135
+ if (box.visible && elem.scrollHeight != elem.offsetHeight && $(elem).css("overflowY") != "visible") {
3136
+ box.visible = box.bottom > elem.scrollTop && box.top < elem.scrollTop + elem.clientHeight;
3137
+ }
3138
+
3139
+ if (box.visible && elem.scrollWidth != elem.offsetWidth && $(elem).css("overflowX") != "visible") {
3140
+ box.visible = box.right > elem.scrollLeft && box.left < elem.scrollLeft + elem.clientWidth;
3141
+ }
3142
+
3143
+ box.left -= elem.scrollLeft;
3144
+ box.top -= elem.scrollTop;
3145
+
3146
+ if (elem === offsetParent) {
3147
+ box.left += elem.offsetLeft;
3148
+ box.top += elem.offsetTop;
3149
+ offsetParent = elem.offsetParent;
3150
+ }
3151
+
3152
+ box.bottom = box.top + box.height;
3153
+ box.right = box.left + box.width;
3154
+ }
3155
+
3156
+ return box;
3157
+ }
3158
+
3159
+ function getActiveCellPosition() {
3160
+ return absBox(activeCellNode);
3161
+ }
3162
+
3163
+ function getGridPosition() {
3164
+ return absBox($container[0])
3165
+ }
3166
+
3167
+ function handleActiveCellPositionChange() {
3168
+ if (!activeCellNode) {
3169
+ return;
3170
+ }
3171
+
3172
+ trigger(self.onActiveCellPositionChanged, {grid: self});
3173
+
3174
+ if (currentEditor) {
3175
+ var cellBox = getActiveCellPosition();
3176
+ if (currentEditor.show && currentEditor.hide) {
3177
+ if (!cellBox.visible) {
3178
+ currentEditor.hide();
3179
+ } else {
3180
+ currentEditor.show();
3181
+ }
3182
+ }
3183
+
3184
+ if (currentEditor.position) {
3185
+ currentEditor.position(cellBox);
3186
+ }
3187
+ }
3188
+ }
3189
+
3190
+ function getCellEditor() {
3191
+ return currentEditor;
3192
+ }
3193
+
3194
+ function getActiveCell() {
3195
+ if (!activeCellNode) {
3196
+ return null;
3197
+ } else {
3198
+ return {row: activeRow, cell: activeCell, grid: self};
3199
+ }
3200
+ }
3201
+
3202
+ function getActiveCellNode() {
3203
+ return activeCellNode;
3204
+ }
3205
+
3206
+ function scrollRowIntoView(row, doPaging) {
3207
+ var rowAtTop = row * options.rowHeight;
3208
+ var rowAtBottom = (row + 1) * options.rowHeight - viewportH + (viewportHasHScroll ? scrollbarDimensions.height : 0);
3209
+
3210
+ // need to page down?
3211
+ if ((row + 1) * options.rowHeight > scrollTop + viewportH + offset) {
3212
+ scrollTo(doPaging ? rowAtTop : rowAtBottom);
3213
+ render();
3214
+ }
3215
+ // or page up?
3216
+ else if (row * options.rowHeight < scrollTop + offset) {
3217
+ scrollTo(doPaging ? rowAtBottom : rowAtTop);
3218
+ render();
3219
+ }
3220
+ }
3221
+
3222
+ function scrollRowToTop(row) {
3223
+ scrollTo(row * options.rowHeight);
3224
+ render();
3225
+ }
3226
+
3227
+ function scrollPage(dir) {
3228
+ var deltaRows = dir * numVisibleRows;
3229
+ scrollTo((getRowFromPosition(scrollTop) + deltaRows) * options.rowHeight);
3230
+ render();
3231
+
3232
+ if (options.enableCellNavigation && activeRow != null) {
3233
+ var row = activeRow + deltaRows;
3234
+ var dataLengthIncludingAddNew = getDataLengthIncludingAddNew();
3235
+ if (row >= dataLengthIncludingAddNew) {
3236
+ row = dataLengthIncludingAddNew - 1;
3237
+ }
3238
+ if (row < 0) {
3239
+ row = 0;
3240
+ }
3241
+
3242
+ var cell = 0, prevCell = null;
3243
+ var prevActivePosX = activePosX;
3244
+ while (cell <= activePosX) {
3245
+ if (canCellBeActive(row, cell)) {
3246
+ prevCell = cell;
3247
+ }
3248
+ cell += getColspan(row, cell);
3249
+ }
3250
+
3251
+ if (prevCell !== null) {
3252
+ setActiveCellInternal(getCellNode(row, prevCell));
3253
+ activePosX = prevActivePosX;
3254
+ } else {
3255
+ resetActiveCell();
3256
+ }
3257
+ }
3258
+ }
3259
+
3260
+ function navigatePageDown() {
3261
+ scrollPage(1);
3262
+ }
3263
+
3264
+ function navigatePageUp() {
3265
+ scrollPage(-1);
3266
+ }
3267
+
3268
+ function navigateTop() {
3269
+ navigateToRow(0);
3270
+ }
3271
+
3272
+ function navigateBottom() {
3273
+ navigateToRow(getDataLength()-1);
3274
+ }
3275
+
3276
+ function navigateToRow(row) {
3277
+ var num_rows = getDataLength();
3278
+ if (!num_rows) return true;
3279
+
3280
+ if (row < 0) row = 0;
3281
+ else if (row >= num_rows) row = num_rows - 1;
3282
+
3283
+ scrollCellIntoView(row, 0, true);
3284
+ if (options.enableCellNavigation && activeRow != null) {
3285
+ var cell = 0, prevCell = null;
3286
+ var prevActivePosX = activePosX;
3287
+ while (cell <= activePosX) {
3288
+ if (canCellBeActive(row, cell)) {
3289
+ prevCell = cell;
3290
+ }
3291
+ cell += getColspan(row, cell);
3292
+ }
3293
+
3294
+ if (prevCell !== null) {
3295
+ setActiveCellInternal(getCellNode(row, prevCell));
3296
+ activePosX = prevActivePosX;
3297
+ } else {
3298
+ resetActiveCell();
3299
+ }
3300
+ }
3301
+ return true;
3302
+ }
3303
+
3304
+ function getColspan(row, cell) {
3305
+ var metadata = data.getItemMetadata && data.getItemMetadata(row);
3306
+ if (!metadata || !metadata.columns) {
3307
+ return 1;
3308
+ }
3309
+
3310
+ var columnData = metadata.columns[columns[cell].id] || metadata.columns[cell];
3311
+ var colspan = (columnData && columnData.colspan);
3312
+ if (colspan === "*") {
3313
+ colspan = columns.length - cell;
3314
+ } else {
3315
+ colspan = colspan || 1;
3316
+ }
3317
+
3318
+ return colspan;
3319
+ }
3320
+
3321
+ function findFirstFocusableCell(row) {
3322
+ var cell = 0;
3323
+ while (cell < columns.length) {
3324
+ if (canCellBeActive(row, cell)) {
3325
+ return cell;
3326
+ }
3327
+ cell += getColspan(row, cell);
3328
+ }
3329
+ return null;
3330
+ }
3331
+
3332
+ function findLastFocusableCell(row) {
3333
+ var cell = 0;
3334
+ var lastFocusableCell = null;
3335
+ while (cell < columns.length) {
3336
+ if (canCellBeActive(row, cell)) {
3337
+ lastFocusableCell = cell;
3338
+ }
3339
+ cell += getColspan(row, cell);
3340
+ }
3341
+ return lastFocusableCell;
3342
+ }
3343
+
3344
+ function gotoRight(row, cell, posX) {
3345
+ if (cell >= columns.length) {
3346
+ return null;
3347
+ }
3348
+
3349
+ do {
3350
+ cell += getColspan(row, cell);
3351
+ }
3352
+ while (cell < columns.length && !canCellBeActive(row, cell));
3353
+
3354
+ if (cell < columns.length) {
3355
+ return {
3356
+ "row": row,
3357
+ "cell": cell,
3358
+ "posX": cell
3359
+ };
3360
+ }
3361
+ return null;
3362
+ }
3363
+
3364
+ function gotoLeft(row, cell, posX) {
3365
+ if (cell <= 0) {
3366
+ return null;
3367
+ }
3368
+
3369
+ var firstFocusableCell = findFirstFocusableCell(row);
3370
+ if (firstFocusableCell === null || firstFocusableCell >= cell) {
3371
+ return null;
3372
+ }
3373
+
3374
+ var prev = {
3375
+ "row": row,
3376
+ "cell": firstFocusableCell,
3377
+ "posX": firstFocusableCell
3378
+ };
3379
+ var pos;
3380
+ while (true) {
3381
+ pos = gotoRight(prev.row, prev.cell, prev.posX);
3382
+ if (!pos) {
3383
+ return null;
3384
+ }
3385
+ if (pos.cell >= cell) {
3386
+ return prev;
3387
+ }
3388
+ prev = pos;
3389
+ }
3390
+ }
3391
+
3392
+ function gotoDown(row, cell, posX) {
3393
+ var prevCell;
3394
+ var dataLengthIncludingAddNew = getDataLengthIncludingAddNew();
3395
+ while (true) {
3396
+ if (++row >= dataLengthIncludingAddNew) {
3397
+ return null;
3398
+ }
3399
+
3400
+ prevCell = cell = 0;
3401
+ while (cell <= posX) {
3402
+ prevCell = cell;
3403
+ cell += getColspan(row, cell);
3404
+ }
3405
+
3406
+ if (canCellBeActive(row, prevCell)) {
3407
+ return {
3408
+ "row": row,
3409
+ "cell": prevCell,
3410
+ "posX": posX
3411
+ };
3412
+ }
3413
+ }
3414
+ }
3415
+
3416
+ function gotoUp(row, cell, posX) {
3417
+ var prevCell;
3418
+ while (true) {
3419
+ if (--row < 0) {
3420
+ return null;
3421
+ }
3422
+
3423
+ prevCell = cell = 0;
3424
+ while (cell <= posX) {
3425
+ prevCell = cell;
3426
+ cell += getColspan(row, cell);
3427
+ }
3428
+
3429
+ if (canCellBeActive(row, prevCell)) {
3430
+ return {
3431
+ "row": row,
3432
+ "cell": prevCell,
3433
+ "posX": posX
3434
+ };
3435
+ }
3436
+ }
3437
+ }
3438
+
3439
+ function gotoNext(row, cell, posX) {
3440
+ if (row == null && cell == null) {
3441
+ row = cell = posX = 0;
3442
+ if (canCellBeActive(row, cell)) {
3443
+ return {
3444
+ "row": row,
3445
+ "cell": cell,
3446
+ "posX": cell
3447
+ };
3448
+ }
3449
+ }
3450
+
3451
+ var pos = gotoRight(row, cell, posX);
3452
+ if (pos) {
3453
+ return pos;
3454
+ }
3455
+
3456
+ var firstFocusableCell = null;
3457
+ var dataLengthIncludingAddNew = getDataLengthIncludingAddNew();
3458
+
3459
+ // if at last row, cycle through columns rather than get stuck in the last one
3460
+ if (row === dataLengthIncludingAddNew - 1) { row--; }
3461
+
3462
+ while (++row < dataLengthIncludingAddNew) {
3463
+ firstFocusableCell = findFirstFocusableCell(row);
3464
+ if (firstFocusableCell !== null) {
3465
+ return {
3466
+ "row": row,
3467
+ "cell": firstFocusableCell,
3468
+ "posX": firstFocusableCell
3469
+ };
3470
+ }
3471
+ }
3472
+ return null;
3473
+ }
3474
+
3475
+ function gotoPrev(row, cell, posX) {
3476
+ if (row == null && cell == null) {
3477
+ row = getDataLengthIncludingAddNew() - 1;
3478
+ cell = posX = columns.length - 1;
3479
+ if (canCellBeActive(row, cell)) {
3480
+ return {
3481
+ "row": row,
3482
+ "cell": cell,
3483
+ "posX": cell
3484
+ };
3485
+ }
3486
+ }
3487
+
3488
+ var pos;
3489
+ var lastSelectableCell;
3490
+ while (!pos) {
3491
+ pos = gotoLeft(row, cell, posX);
3492
+ if (pos) {
3493
+ break;
3494
+ }
3495
+ if (--row < 0) {
3496
+ return null;
3497
+ }
3498
+
3499
+ cell = 0;
3500
+ lastSelectableCell = findLastFocusableCell(row);
3501
+ if (lastSelectableCell !== null) {
3502
+ pos = {
3503
+ "row": row,
3504
+ "cell": lastSelectableCell,
3505
+ "posX": lastSelectableCell
3506
+ };
3507
+ }
3508
+ }
3509
+ return pos;
3510
+ }
3511
+
3512
+ function gotoRowStart(row, cell, posX) {
3513
+ var newCell = findFirstFocusableCell(row);
3514
+ if (newCell === null) return null;
3515
+
3516
+ return {
3517
+ "row": row,
3518
+ "cell": newCell,
3519
+ "posX": posX
3520
+ };
3521
+ }
3522
+
3523
+ function gotoRowEnd(row, cell, posX) {
3524
+ var newCell = findLastFocusableCell(row);
3525
+ if (newCell === null) return null;
3526
+
3527
+ return {
3528
+ "row": row,
3529
+ "cell": newCell,
3530
+ "posX": posX
3531
+ };
3532
+ }
3533
+
3534
+ function navigateRight() {
3535
+ return navigate("right");
3536
+ }
3537
+
3538
+ function navigateLeft() {
3539
+ return navigate("left");
3540
+ }
3541
+
3542
+ function navigateDown() {
3543
+ return navigate("down");
3544
+ }
3545
+
3546
+ function navigateUp() {
3547
+ return navigate("up");
3548
+ }
3549
+
3550
+ function navigateNext() {
3551
+ return navigate("next");
3552
+ }
3553
+
3554
+ function navigatePrev() {
3555
+ return navigate("prev");
3556
+ }
3557
+
3558
+ function navigateRowStart() {
3559
+ return navigate("home");
3560
+ }
3561
+
3562
+ function navigateRowEnd() {
3563
+ return navigate("end");
3564
+ }
3565
+
3566
+ /**
3567
+ * @param {string} dir Navigation direction.
3568
+ * @return {boolean} Whether navigation resulted in a change of active cell.
3569
+ */
3570
+ function navigate(dir) {
3571
+ if (!options.enableCellNavigation) {
3572
+ return false;
3573
+ }
3574
+
3575
+ if (!activeCellNode && dir != "prev" && dir != "next") {
3576
+ return false;
3577
+ }
3578
+
3579
+ if (!getEditorLock().commitCurrentEdit()) {
3580
+ return true;
3581
+ }
3582
+ setFocus();
3583
+
3584
+ var tabbingDirections = {
3585
+ "up": -1,
3586
+ "down": 1,
3587
+ "left": -1,
3588
+ "right": 1,
3589
+ "prev": -1,
3590
+ "next": 1,
3591
+ "home": -1,
3592
+ "end": 1
3593
+ };
3594
+ tabbingDirection = tabbingDirections[dir];
3595
+
3596
+ var stepFunctions = {
3597
+ "up": gotoUp,
3598
+ "down": gotoDown,
3599
+ "left": gotoLeft,
3600
+ "right": gotoRight,
3601
+ "prev": gotoPrev,
3602
+ "next": gotoNext,
3603
+ "home": gotoRowStart,
3604
+ "end": gotoRowEnd
3605
+ };
3606
+ var stepFn = stepFunctions[dir];
3607
+ var pos = stepFn(activeRow, activeCell, activePosX);
3608
+ if (pos) {
3609
+ var isAddNewRow = (pos.row == getDataLength());
3610
+ scrollCellIntoView(pos.row, pos.cell, !isAddNewRow && options.emulatePagingWhenScrolling);
3611
+ setActiveCellInternal(getCellNode(pos.row, pos.cell));
3612
+ activePosX = pos.posX;
3613
+ return true;
3614
+ } else {
3615
+ setActiveCellInternal(getCellNode(activeRow, activeCell));
3616
+ return false;
3617
+ }
3618
+ }
3619
+
3620
+ function getCellNode(row, cell) {
3621
+ if (rowsCache[row]) {
3622
+ ensureCellNodesInRowsCache(row);
3623
+ return rowsCache[row].cellNodesByColumnIdx[cell];
3624
+ }
3625
+ return null;
3626
+ }
3627
+
3628
+ function setActiveCell(row, cell, opt_editMode, preClickModeOn, suppressActiveCellChangedEvent) {
3629
+ if (!initialized) { return; }
3630
+ if (row > getDataLength() || row < 0 || cell >= columns.length || cell < 0) {
3631
+ return;
3632
+ }
3633
+
3634
+ if (!options.enableCellNavigation) {
3635
+ return;
3636
+ }
3637
+
3638
+ scrollCellIntoView(row, cell, false);
3639
+ setActiveCellInternal(getCellNode(row, cell), opt_editMode, preClickModeOn, suppressActiveCellChangedEvent);
3640
+ }
3641
+
3642
+ function canCellBeActive(row, cell) {
3643
+ if (!options.enableCellNavigation || row >= getDataLengthIncludingAddNew() ||
3644
+ row < 0 || cell >= columns.length || cell < 0) {
3645
+ return false;
3646
+ }
3647
+
3648
+ var rowMetadata = data.getItemMetadata && data.getItemMetadata(row);
3649
+ if (rowMetadata && typeof rowMetadata.focusable !== "undefined") {
3650
+ return !!rowMetadata.focusable;
3651
+ }
3652
+
3653
+ var columnMetadata = rowMetadata && rowMetadata.columns;
3654
+ if (columnMetadata && columnMetadata[columns[cell].id] && typeof columnMetadata[columns[cell].id].focusable !== "undefined") {
3655
+ return !!columnMetadata[columns[cell].id].focusable;
3656
+ }
3657
+ if (columnMetadata && columnMetadata[cell] && typeof columnMetadata[cell].focusable !== "undefined") {
3658
+ return !!columnMetadata[cell].focusable;
3659
+ }
3660
+
3661
+ return !!columns[cell].focusable;
3662
+ }
3663
+
3664
+ function canCellBeSelected(row, cell) {
3665
+ if (row >= getDataLength() || row < 0 || cell >= columns.length || cell < 0) {
3666
+ return false;
3667
+ }
3668
+
3669
+ var rowMetadata = data.getItemMetadata && data.getItemMetadata(row);
3670
+ if (rowMetadata && typeof rowMetadata.selectable !== "undefined") {
3671
+ return !!rowMetadata.selectable;
3672
+ }
3673
+
3674
+ var columnMetadata = rowMetadata && rowMetadata.columns && (rowMetadata.columns[columns[cell].id] || rowMetadata.columns[cell]);
3675
+ if (columnMetadata && typeof columnMetadata.selectable !== "undefined") {
3676
+ return !!columnMetadata.selectable;
3677
+ }
3678
+
3679
+ return !!columns[cell].selectable;
3680
+ }
3681
+
3682
+ function gotoCell(row, cell, forceEdit) {
3683
+ if (!initialized) { return; }
3684
+ if (!canCellBeActive(row, cell)) {
3685
+ return;
3686
+ }
3687
+
3688
+ if (!getEditorLock().commitCurrentEdit()) {
3689
+ return;
3690
+ }
3691
+
3692
+ scrollCellIntoView(row, cell, false);
3693
+
3694
+ var newCell = getCellNode(row, cell);
3695
+
3696
+ // if selecting the 'add new' row, start editing right away
3697
+ setActiveCellInternal(newCell, forceEdit || (row === getDataLength()) || options.autoEdit);
3698
+
3699
+ // if no editor was created, set the focus back on the grid
3700
+ if (!currentEditor) {
3701
+ setFocus();
3702
+ }
3703
+ }
3704
+
3705
+
3706
+ //////////////////////////////////////////////////////////////////////////////////////////////
3707
+ // IEditor implementation for the editor lock
3708
+
3709
+ function commitCurrentEdit() {
3710
+ var item = getDataItem(activeRow);
3711
+ var column = columns[activeCell];
3712
+
3713
+ if (currentEditor) {
3714
+ if (currentEditor.isValueChanged()) {
3715
+ var validationResults = currentEditor.validate();
3716
+
3717
+ if (validationResults.valid) {
3718
+ if (activeRow < getDataLength()) {
3719
+ var editCommand = {
3720
+ row: activeRow,
3721
+ cell: activeCell,
3722
+ editor: currentEditor,
3723
+ serializedValue: currentEditor.serializeValue(),
3724
+ prevSerializedValue: serializedEditorValue,
3725
+ execute: function () {
3726
+ this.editor.applyValue(item, this.serializedValue);
3727
+ updateRow(this.row);
3728
+ trigger(self.onCellChange, {
3729
+ row: this.row,
3730
+ cell: this.cell,
3731
+ item: item,
3732
+ grid: self
3733
+ });
3734
+ },
3735
+ undo: function () {
3736
+ this.editor.applyValue(item, this.prevSerializedValue);
3737
+ updateRow(this.row);
3738
+ trigger(self.onCellChange, {
3739
+ row: this.row,
3740
+ cell: this.cell,
3741
+ item: item,
3742
+ grid: self
3743
+ });
3744
+ }
3745
+ };
3746
+
3747
+ if (options.editCommandHandler) {
3748
+ makeActiveCellNormal();
3749
+ options.editCommandHandler(item, column, editCommand);
3750
+ } else {
3751
+ editCommand.execute();
3752
+ makeActiveCellNormal();
3753
+ }
3754
+
3755
+ } else {
3756
+ var newItem = {};
3757
+ currentEditor.applyValue(newItem, currentEditor.serializeValue());
3758
+ makeActiveCellNormal();
3759
+ trigger(self.onAddNewRow, {item: newItem, column: column, grid: self});
3760
+ }
3761
+
3762
+ // check whether the lock has been re-acquired by event handlers
3763
+ return !getEditorLock().isActive();
3764
+ } else {
3765
+ // Re-add the CSS class to trigger transitions, if any.
3766
+ $(activeCellNode).removeClass("invalid");
3767
+ $(activeCellNode).width(); // force layout
3768
+ $(activeCellNode).addClass("invalid");
3769
+
3770
+ trigger(self.onValidationError, {
3771
+ editor: currentEditor,
3772
+ cellNode: activeCellNode,
3773
+ validationResults: validationResults,
3774
+ row: activeRow,
3775
+ cell: activeCell,
3776
+ column: column,
3777
+ grid: self
3778
+ });
3779
+
3780
+ currentEditor.focus();
3781
+ return false;
3782
+ }
3783
+ }
3784
+
3785
+ makeActiveCellNormal();
3786
+ }
3787
+ return true;
3788
+ }
3789
+
3790
+ function cancelCurrentEdit() {
3791
+ makeActiveCellNormal();
3792
+ return true;
3793
+ }
3794
+
3795
+ function rowsToRanges(rows) {
3796
+ var ranges = [];
3797
+ var lastCell = columns.length - 1;
3798
+ for (var i = 0; i < rows.length; i++) {
3799
+ ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
3800
+ }
3801
+ return ranges;
3802
+ }
3803
+
3804
+ function getSelectedRows() {
3805
+ if (!selectionModel) {
3806
+ throw new Error("Selection model is not set");
3807
+ }
3808
+ return selectedRows;
3809
+ }
3810
+
3811
+ function setSelectedRows(rows) {
3812
+ if (!selectionModel) {
3813
+ throw new Error("Selection model is not set");
3814
+ }
3815
+ selectionModel.setSelectedRanges(rowsToRanges(rows));
3816
+ }
3817
+
3818
+
3819
+ //////////////////////////////////////////////////////////////////////////////////////////////
3820
+ // Debug
3821
+
3822
+ this.debug = function () {
3823
+ var s = "";
3824
+
3825
+ s += ("\n" + "counter_rows_rendered: " + counter_rows_rendered);
3826
+ s += ("\n" + "counter_rows_removed: " + counter_rows_removed);
3827
+ s += ("\n" + "renderedRows: " + renderedRows);
3828
+ s += ("\n" + "numVisibleRows: " + numVisibleRows);
3829
+ s += ("\n" + "maxSupportedCssHeight: " + maxSupportedCssHeight);
3830
+ s += ("\n" + "n(umber of pages): " + n);
3831
+ s += ("\n" + "(current) page: " + page);
3832
+ s += ("\n" + "page height (ph): " + ph);
3833
+ s += ("\n" + "vScrollDir: " + vScrollDir);
3834
+
3835
+ alert(s);
3836
+ };
3837
+
3838
+ // a debug helper to be able to access private members
3839
+ this.eval = function (expr) {
3840
+ return eval(expr);
3841
+ };
3842
+
3843
+ //////////////////////////////////////////////////////////////////////////////////////////////
3844
+ // Public API
3845
+
3846
+ $.extend(this, {
3847
+ "slickGridVersion": "2.3.16",
3848
+
3849
+ // Events
3850
+ "onScroll": new Slick.Event(),
3851
+ "onSort": new Slick.Event(),
3852
+ "onHeaderMouseEnter": new Slick.Event(),
3853
+ "onHeaderMouseLeave": new Slick.Event(),
3854
+ "onHeaderContextMenu": new Slick.Event(),
3855
+ "onHeaderClick": new Slick.Event(),
3856
+ "onHeaderCellRendered": new Slick.Event(),
3857
+ "onBeforeHeaderCellDestroy": new Slick.Event(),
3858
+ "onHeaderRowCellRendered": new Slick.Event(),
3859
+ "onFooterRowCellRendered": new Slick.Event(),
3860
+ "onBeforeHeaderRowCellDestroy": new Slick.Event(),
3861
+ "onBeforeFooterRowCellDestroy": new Slick.Event(),
3862
+ "onMouseEnter": new Slick.Event(),
3863
+ "onMouseLeave": new Slick.Event(),
3864
+ "onClick": new Slick.Event(),
3865
+ "onDblClick": new Slick.Event(),
3866
+ "onContextMenu": new Slick.Event(),
3867
+ "onKeyDown": new Slick.Event(),
3868
+ "onAddNewRow": new Slick.Event(),
3869
+ "onBeforeAppendCell": new Slick.Event(),
3870
+ "onValidationError": new Slick.Event(),
3871
+ "onViewportChanged": new Slick.Event(),
3872
+ "onColumnsReordered": new Slick.Event(),
3873
+ "onColumnsResized": new Slick.Event(),
3874
+ "onCellChange": new Slick.Event(),
3875
+ "onBeforeEditCell": new Slick.Event(),
3876
+ "onBeforeCellEditorDestroy": new Slick.Event(),
3877
+ "onBeforeDestroy": new Slick.Event(),
3878
+ "onActiveCellChanged": new Slick.Event(),
3879
+ "onActiveCellPositionChanged": new Slick.Event(),
3880
+ "onDragInit": new Slick.Event(),
3881
+ "onDragStart": new Slick.Event(),
3882
+ "onDrag": new Slick.Event(),
3883
+ "onDragEnd": new Slick.Event(),
3884
+ "onSelectedRowsChanged": new Slick.Event(),
3885
+ "onCellCssStylesChanged": new Slick.Event(),
3886
+
3887
+ // Methods
3888
+ "registerPlugin": registerPlugin,
3889
+ "unregisterPlugin": unregisterPlugin,
3890
+ "getColumns": getColumns,
3891
+ "setColumns": setColumns,
3892
+ "getColumnIndex": getColumnIndex,
3893
+ "updateColumnHeader": updateColumnHeader,
3894
+ "setSortColumn": setSortColumn,
3895
+ "setSortColumns": setSortColumns,
3896
+ "getSortColumns": getSortColumns,
3897
+ "autosizeColumns": autosizeColumns,
3898
+ "getOptions": getOptions,
3899
+ "setOptions": setOptions,
3900
+ "getData": getData,
3901
+ "getDataLength": getDataLength,
3902
+ "getDataItem": getDataItem,
3903
+ "setData": setData,
3904
+ "getSelectionModel": getSelectionModel,
3905
+ "setSelectionModel": setSelectionModel,
3906
+ "getSelectedRows": getSelectedRows,
3907
+ "setSelectedRows": setSelectedRows,
3908
+ "getContainerNode": getContainerNode,
3909
+ "updatePagingStatusFromView": updatePagingStatusFromView,
3910
+
3911
+ "render": render,
3912
+ "invalidate": invalidate,
3913
+ "invalidateRow": invalidateRow,
3914
+ "invalidateRows": invalidateRows,
3915
+ "invalidateAllRows": invalidateAllRows,
3916
+ "updateCell": updateCell,
3917
+ "updateRow": updateRow,
3918
+ "getViewport": getVisibleRange,
3919
+ "getRenderedRange": getRenderedRange,
3920
+ "resizeCanvas": resizeCanvas,
3921
+ "updateRowCount": updateRowCount,
3922
+ "scrollRowIntoView": scrollRowIntoView,
3923
+ "scrollRowToTop": scrollRowToTop,
3924
+ "scrollCellIntoView": scrollCellIntoView,
3925
+ "scrollColumnIntoView": scrollColumnIntoView,
3926
+ "getCanvasNode": getCanvasNode,
3927
+ "getUID": getUID,
3928
+ "getHeaderColumnWidthDiff": getHeaderColumnWidthDiff,
3929
+ "getScrollbarDimensions": getScrollbarDimensions,
3930
+ "getHeadersWidth": getHeadersWidth,
3931
+ "getCanvasWidth": getCanvasWidth,
3932
+ "focus": setFocus,
3933
+ "scrollTo": scrollTo,
3934
+
3935
+ "getCellFromPoint": getCellFromPoint,
3936
+ "getCellFromEvent": getCellFromEvent,
3937
+ "getActiveCell": getActiveCell,
3938
+ "setActiveCell": setActiveCell,
3939
+ "getActiveCellNode": getActiveCellNode,
3940
+ "getActiveCellPosition": getActiveCellPosition,
3941
+ "resetActiveCell": resetActiveCell,
3942
+ "editActiveCell": makeActiveCellEditable,
3943
+ "getCellEditor": getCellEditor,
3944
+ "getCellNode": getCellNode,
3945
+ "getCellNodeBox": getCellNodeBox,
3946
+ "canCellBeSelected": canCellBeSelected,
3947
+ "canCellBeActive": canCellBeActive,
3948
+ "navigatePrev": navigatePrev,
3949
+ "navigateNext": navigateNext,
3950
+ "navigateUp": navigateUp,
3951
+ "navigateDown": navigateDown,
3952
+ "navigateLeft": navigateLeft,
3953
+ "navigateRight": navigateRight,
3954
+ "navigatePageUp": navigatePageUp,
3955
+ "navigatePageDown": navigatePageDown,
3956
+ "navigateTop": navigateTop,
3957
+ "navigateBottom": navigateBottom,
3958
+ "navigateRowStart": navigateRowStart,
3959
+ "navigateRowEnd": navigateRowEnd,
3960
+ "gotoCell": gotoCell,
3961
+ "getTopPanel": getTopPanel,
3962
+ "setTopPanelVisibility": setTopPanelVisibility,
3963
+ "getPreHeaderPanel": getPreHeaderPanel,
3964
+ "setPreHeaderPanelVisibility": setPreHeaderPanelVisibility,
3965
+ "getHeader": getHeader,
3966
+ "getHeaderColumn": getHeaderColumn,
3967
+ "setHeaderRowVisibility": setHeaderRowVisibility,
3968
+ "getHeaderRow": getHeaderRow,
3969
+ "getHeaderRowColumn": getHeaderRowColumn,
3970
+ "setFooterRowVisibility": setFooterRowVisibility,
3971
+ "getFooterRow": getFooterRow,
3972
+ "getFooterRowColumn": getFooterRowColumn,
3973
+ "getGridPosition": getGridPosition,
3974
+ "flashCell": flashCell,
3975
+ "addCellCssStyles": addCellCssStyles,
3976
+ "setCellCssStyles": setCellCssStyles,
3977
+ "removeCellCssStyles": removeCellCssStyles,
3978
+ "getCellCssStyles": getCellCssStyles,
3979
+
3980
+ "init": finishInitialization,
3981
+ "destroy": destroy,
3982
+
3983
+ // IEditor implementation
3984
+ "getEditorLock": getEditorLock,
3985
+ "getEditController": getEditController
3986
+ });
3987
+
3988
+ init();
3989
+ }
3990
+ }(jQuery));