slickgrid-bootstrap-rails 0.0.1.10

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