smartkiosk-server 0.12.1 → 0.13

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