publish_my_data 0.0.32 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. data/app/assets/images/publish_my_data/small-spinner.gif +0 -0
  2. data/app/assets/images/publish_my_data/sort-asc.gif +0 -0
  3. data/app/assets/images/publish_my_data/sort-desc.gif +0 -0
  4. data/app/assets/javascripts/bootstrap/affix.js +126 -0
  5. data/app/assets/javascripts/bootstrap/alert.js +98 -0
  6. data/app/assets/javascripts/bootstrap/button.js +109 -0
  7. data/app/assets/javascripts/bootstrap/carousel.js +217 -0
  8. data/app/assets/javascripts/bootstrap/collapse.js +179 -0
  9. data/app/assets/javascripts/bootstrap/dropdown.js +154 -0
  10. data/app/assets/javascripts/bootstrap/modal.js +246 -0
  11. data/app/assets/javascripts/bootstrap/popover.js +117 -0
  12. data/app/assets/javascripts/bootstrap/scrollspy.js +158 -0
  13. data/app/assets/javascripts/bootstrap/tab.js +135 -0
  14. data/app/assets/javascripts/bootstrap/tooltip.js +386 -0
  15. data/app/assets/javascripts/bootstrap/transition.js +56 -0
  16. data/app/assets/javascripts/publish_my_data/grid/10_data-loader.js +142 -0
  17. data/app/assets/javascripts/publish_my_data/grid/20_cube-grid.js +512 -0
  18. data/app/assets/javascripts/publish_my_data/grid/30_cube-dimension.js +77 -0
  19. data/app/assets/javascripts/publish_my_data/grid/40_cube-dimension-dropdown.js +87 -0
  20. data/app/assets/javascripts/publish_my_data/grid/45_cube-dimension-label.js +58 -0
  21. data/app/assets/javascripts/publish_my_data/grid/50_cube-dimensions-controls.js +336 -0
  22. data/app/assets/javascripts/publish_my_data/grid/60_dataset_cube_grid.js +187 -0
  23. data/app/assets/javascripts/publish_my_data/grid/70_sparql_results_grid.js +135 -0
  24. data/app/assets/javascripts/publish_my_data/select_text.js +27 -0
  25. data/app/assets/javascripts/publish_my_data.js +3 -0
  26. data/app/assets/javascripts/slick_grid/10_event-drag.js +402 -0
  27. data/app/assets/javascripts/slick_grid/20_slick-core.js +458 -0
  28. data/app/assets/javascripts/slick_grid/30_slick-grid.js +3295 -0
  29. data/app/assets/stylesheets/bootstrap/_alerts.scss +67 -0
  30. data/app/assets/stylesheets/bootstrap/_badges.scss +51 -0
  31. data/app/assets/stylesheets/bootstrap/_breadcrumbs.scss +23 -0
  32. data/app/assets/stylesheets/bootstrap/_button-groups.scss +248 -0
  33. data/app/assets/stylesheets/bootstrap/_buttons.scss +160 -0
  34. data/app/assets/stylesheets/bootstrap/_carousel.scss +209 -0
  35. data/app/assets/stylesheets/bootstrap/_close.scss +33 -0
  36. data/app/assets/stylesheets/bootstrap/_code.scss +56 -0
  37. data/app/assets/stylesheets/bootstrap/_component-animations.scss +29 -0
  38. data/app/assets/stylesheets/bootstrap/_dropdowns.scss +194 -0
  39. data/app/assets/stylesheets/bootstrap/_forms.scss +350 -0
  40. data/app/assets/stylesheets/bootstrap/_glyphicons.scss +232 -0
  41. data/app/assets/stylesheets/bootstrap/_grid.scss +346 -0
  42. data/app/assets/stylesheets/bootstrap/_input-groups.scss +127 -0
  43. data/app/assets/stylesheets/bootstrap/_jumbotron.scss +40 -0
  44. data/app/assets/stylesheets/bootstrap/_labels.scss +58 -0
  45. data/app/assets/stylesheets/bootstrap/_list-group.scss +88 -0
  46. data/app/assets/stylesheets/bootstrap/_media.scss +56 -0
  47. data/app/assets/stylesheets/bootstrap/_mixins.scss +728 -0
  48. data/app/assets/stylesheets/bootstrap/_modals.scss +145 -0
  49. data/app/assets/stylesheets/bootstrap/_navbar.scss +625 -0
  50. data/app/assets/stylesheets/bootstrap/_navs.scss +229 -0
  51. data/app/assets/stylesheets/bootstrap/_normalize.scss +396 -0
  52. data/app/assets/stylesheets/bootstrap/_pager.scss +55 -0
  53. data/app/assets/stylesheets/bootstrap/_pagination.scss +83 -0
  54. data/app/assets/stylesheets/bootstrap/_panels.scss +148 -0
  55. data/app/assets/stylesheets/bootstrap/_popovers.scss +133 -0
  56. data/app/assets/stylesheets/bootstrap/_print.scss +100 -0
  57. data/app/assets/stylesheets/bootstrap/_progress-bars.scss +95 -0
  58. data/app/assets/stylesheets/bootstrap/_responsive-utilities.scss +209 -0
  59. data/app/assets/stylesheets/bootstrap/_scaffolding.scss +130 -0
  60. data/app/assets/stylesheets/bootstrap/_tables.scss +236 -0
  61. data/app/assets/stylesheets/bootstrap/_theme.scss +232 -0
  62. data/app/assets/stylesheets/bootstrap/_thumbnails.scss +31 -0
  63. data/app/assets/stylesheets/bootstrap/_tooltip.scss +95 -0
  64. data/app/assets/stylesheets/bootstrap/_type.scss +238 -0
  65. data/app/assets/stylesheets/bootstrap/_utilities.scss +42 -0
  66. data/app/assets/stylesheets/bootstrap/_variables.scss +620 -0
  67. data/app/assets/stylesheets/bootstrap/_wells.scss +29 -0
  68. data/app/assets/stylesheets/bootstrap/bootstrap.scss +59 -0
  69. data/app/assets/stylesheets/publish_my_data/core.scss +244 -0
  70. data/app/assets/stylesheets/publish_my_data/data_grid.scss +103 -0
  71. data/app/assets/stylesheets/publish_my_data/variables.scss +1 -0
  72. data/app/assets/stylesheets/{publish_my_data.css → publish_my_data.scss} +13 -2
  73. data/app/assets/stylesheets/slick_grid/slick_grid.scss +155 -0
  74. data/app/assets/stylesheets/slick_grid/slick_grid_default_theme.scss +97 -0
  75. data/app/controllers/concerns/publish_my_data/data_cube.rb +42 -0
  76. data/app/controllers/publish_my_data/data_cube/dimensions_controller.rb +58 -0
  77. data/app/controllers/publish_my_data/data_cube/observations_controller.rb +54 -0
  78. data/app/controllers/publish_my_data/datasets_controller.rb +0 -11
  79. data/app/controllers/publish_my_data/example_resources_controller.rb +11 -0
  80. data/app/controllers/publish_my_data/home_controller.rb +4 -0
  81. data/app/controllers/publish_my_data/information_resources_controller.rb +0 -13
  82. data/app/helpers/publish_my_data/application_helper.rb +66 -0
  83. data/app/helpers/publish_my_data/crumb_helper.rb +61 -0
  84. data/app/helpers/publish_my_data/datasets_helper.rb +31 -0
  85. data/app/helpers/publish_my_data/resources_helper.rb +24 -0
  86. data/app/models/concerns/publish_my_data/cube_results.rb +51 -0
  87. data/app/models/concerns/publish_my_data/dataset_powers.rb +13 -0
  88. data/app/models/publish_my_data/data_cube/cube.rb +358 -0
  89. data/app/models/publish_my_data/data_cube/dimension.rb +58 -0
  90. data/app/models/publish_my_data/vocabulary.rb +0 -4
  91. data/app/views/layouts/publish_my_data/application.html.haml +18 -0
  92. data/app/views/layouts/publish_my_data/error.html.haml +21 -0
  93. data/app/views/publish_my_data/classes/show.html.haml +28 -0
  94. data/app/views/publish_my_data/concept_schemes/show.html.haml +79 -0
  95. data/app/views/publish_my_data/concepts/show.html.haml +28 -0
  96. data/app/views/publish_my_data/data_cube/_controls.html.haml +19 -0
  97. data/app/views/publish_my_data/data_cube/_grid.html.haml +37 -0
  98. data/app/views/publish_my_data/datasets/_example_resources.html.haml +47 -0
  99. data/app/views/publish_my_data/datasets/_types_table.html.erb +33 -0
  100. data/app/views/publish_my_data/datasets/index.html.haml +32 -0
  101. data/app/views/publish_my_data/datasets/show.html.haml +186 -0
  102. data/app/views/publish_my_data/errors/not_found.html.haml +18 -0
  103. data/app/views/publish_my_data/errors/response_too_large.html.haml +16 -0
  104. data/app/views/publish_my_data/errors/timeout.html.haml +16 -0
  105. data/app/views/publish_my_data/errors/uncaught.html.haml +16 -0
  106. data/app/views/publish_my_data/example_resources/index.js.erb +1 -0
  107. data/app/views/publish_my_data/home/accessibility.html.haml +1 -0
  108. data/app/views/publish_my_data/home/docs.html.haml +1 -0
  109. data/app/views/publish_my_data/home/home.html.haml +5 -0
  110. data/app/views/publish_my_data/home/privacy.html.haml +1 -0
  111. data/app/views/publish_my_data/ontologies/show.html.haml +100 -0
  112. data/app/views/publish_my_data/properties/show.html.haml +28 -0
  113. data/app/views/publish_my_data/resources/_resource_data.html.haml +16 -0
  114. data/app/views/publish_my_data/resources/_sparql_section.html.haml +22 -0
  115. data/app/views/publish_my_data/resources/index.html.haml +37 -0
  116. data/app/views/publish_my_data/resources/show.html.haml +36 -0
  117. data/app/views/publish_my_data/shared/_browser_warning.html.haml +4 -0
  118. data/app/views/publish_my_data/shared/_deprecation_notice.html.haml +8 -0
  119. data/app/views/publish_my_data/shared/_footer.html.haml +11 -0
  120. data/app/views/publish_my_data/shared/_google_analytics.html.haml +11 -0
  121. data/app/views/publish_my_data/shared/_logo.html.haml +1 -0
  122. data/app/views/publish_my_data/shared/_meta_title.html.haml +2 -0
  123. data/app/views/publish_my_data/sparql/_form.html.haml +28 -0
  124. data/app/views/publish_my_data/sparql/_formats_dropdown.html.haml +11 -0
  125. data/app/views/publish_my_data/sparql/endpoint.html.haml +125 -0
  126. data/app/views/publish_my_data/sparql/formats_dropdowns/_ask.html.haml +7 -0
  127. data/app/views/publish_my_data/sparql/formats_dropdowns/_construct.html.haml +7 -0
  128. data/app/views/publish_my_data/sparql/formats_dropdowns/_describe.html.haml +1 -0
  129. data/app/views/publish_my_data/sparql/formats_dropdowns/_select.html.haml +9 -0
  130. data/app/views/publish_my_data/themes/index.html.haml +30 -0
  131. data/app/views/publish_my_data/themes/show.html.haml +43 -0
  132. data/config/routes.rb +20 -5
  133. data/lib/publish_my_data/version.rb +1 -1
  134. data/lib/publish_my_data.rb +7 -12
  135. data/spec/controllers/publish_my_data/datasets_controller_spec.rb +0 -75
  136. data/spec/controllers/publish_my_data/information_resources_controller_spec.rb +2 -11
  137. data/spec/dummy/config/environments/development.rb +0 -4
  138. data/spec/dummy/config/environments/test.rb +0 -4
  139. data/spec/dummy/log/test.log +62962 -0
  140. data/spec/dummy/tmp/cache/sass/e509ccd4d793d2c162d70125e9f3656b6c28f357/(__TEMPLATE__)c +0 -0
  141. data/spec/features/running_a_sparql_query_spec.rb +1 -1
  142. data/spec/features/uri_dereferencing_spec.rb +1 -1
  143. metadata +200 -58
  144. data/app/controllers/concerns/publish_my_data/data_download.rb +0 -22
  145. data/app/controllers/publish_my_data/vocabularies_controller.rb +0 -18
  146. data/app/views/layouts/publish_my_data/application.html.erb +0 -13
  147. data/app/views/layouts/publish_my_data/error.html.erb +0 -13
  148. data/app/views/publish_my_data/classes/show.html.erb +0 -3
  149. data/app/views/publish_my_data/concept_schemes/_concepts.html.erb +0 -18
  150. data/app/views/publish_my_data/concept_schemes/show.html.erb +0 -11
  151. data/app/views/publish_my_data/concepts/show.html.erb +0 -3
  152. data/app/views/publish_my_data/datasets/index.html.erb +0 -20
  153. data/app/views/publish_my_data/datasets/show.html.erb +0 -18
  154. data/app/views/publish_my_data/errors/not_found.html.erb +0 -1
  155. data/app/views/publish_my_data/errors/response_too_large.html.erb +0 -1
  156. data/app/views/publish_my_data/errors/timeout.html.erb +0 -1
  157. data/app/views/publish_my_data/errors/uncaught.html.erb +0 -6
  158. data/app/views/publish_my_data/ontologies/show.html.erb +0 -17
  159. data/app/views/publish_my_data/properties/show.html.erb +0 -3
  160. data/app/views/publish_my_data/resources/_predicates_table.html.erb +0 -30
  161. data/app/views/publish_my_data/resources/_predicates_table_head.html.erb +0 -6
  162. data/app/views/publish_my_data/resources/_resource_formats.html.erb +0 -4
  163. data/app/views/publish_my_data/resources/_summaries.html.erb +0 -18
  164. data/app/views/publish_my_data/resources/_uri_and_label.html.erb +0 -3
  165. data/app/views/publish_my_data/resources/index.html.erb +0 -40
  166. data/app/views/publish_my_data/resources/show.html.erb +0 -3
  167. data/app/views/publish_my_data/sparql/_ask_formats.html.erb +0 -3
  168. data/app/views/publish_my_data/sparql/_construct_formats.html.erb +0 -3
  169. data/app/views/publish_my_data/sparql/_describe_formats.html.erb +0 -1
  170. data/app/views/publish_my_data/sparql/_error_message.html.erb +0 -3
  171. data/app/views/publish_my_data/sparql/_form.html.erb +0 -4
  172. data/app/views/publish_my_data/sparql/_formats.html.erb +0 -6
  173. data/app/views/publish_my_data/sparql/_pagination.html.erb +0 -6
  174. data/app/views/publish_my_data/sparql/_results.html.erb +0 -5
  175. data/app/views/publish_my_data/sparql/_results_data.html.erb +0 -4
  176. data/app/views/publish_my_data/sparql/_select_formats.html.erb +0 -3
  177. data/app/views/publish_my_data/sparql/endpoint.html.erb +0 -10
  178. data/app/views/publish_my_data/themes/index.html.erb +0 -13
  179. data/app/views/publish_my_data/themes/show.html.erb +0 -15
  180. data/config/initializers/20_s3_setup.rb +0 -4
  181. data/spec/controllers/publish_my_data/vocabularies_controller_spec.rb +0 -14
  182. data/spec/support/data_download.rb +0 -60
@@ -0,0 +1,3295 @@
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.append("<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 getRowTop(row) {
1298
+ return options.rowHeight * row - offset;
1299
+ }
1300
+
1301
+ function getRowFromPosition(y) {
1302
+ return Math.floor((y + offset) / options.rowHeight);
1303
+ }
1304
+
1305
+ function scrollTo(y) {
1306
+ y = Math.max(y, 0);
1307
+ y = Math.min(y, th - viewportH + (viewportHasHScroll ? scrollbarDimensions.height : 0));
1308
+
1309
+ var oldOffset = offset;
1310
+
1311
+ page = Math.min(n - 1, Math.floor(y / ph));
1312
+ offset = Math.round(page * cj);
1313
+ var newScrollTop = y - offset;
1314
+
1315
+ if (offset != oldOffset) {
1316
+ var range = getVisibleRange(newScrollTop);
1317
+ cleanupRows(range);
1318
+ updateRowPositions();
1319
+ }
1320
+
1321
+ if (prevScrollTop != newScrollTop) {
1322
+ vScrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1;
1323
+ $viewport[0].scrollTop = (lastRenderedScrollTop = scrollTop = prevScrollTop = newScrollTop);
1324
+
1325
+ trigger(self.onViewportChanged, {});
1326
+ }
1327
+ }
1328
+
1329
+ function defaultFormatter(row, cell, value, columnDef, dataContext) {
1330
+ if (value == null) {
1331
+ return "";
1332
+ } else {
1333
+ return value.toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
1334
+ }
1335
+ }
1336
+
1337
+ function getFormatter(row, column) {
1338
+ var rowMetadata = data.getItemMetadata && data.getItemMetadata(row);
1339
+
1340
+ // look up by id, then index
1341
+ var columnOverrides = rowMetadata &&
1342
+ rowMetadata.columns &&
1343
+ (rowMetadata.columns[column.id] || rowMetadata.columns[getColumnIndex(column.id)]);
1344
+
1345
+ return (columnOverrides && columnOverrides.formatter) ||
1346
+ (rowMetadata && rowMetadata.formatter) ||
1347
+ column.formatter ||
1348
+ (options.formatterFactory && options.formatterFactory.getFormatter(column)) ||
1349
+ options.defaultFormatter;
1350
+ }
1351
+
1352
+ function getEditor(row, cell) {
1353
+ var column = columns[cell];
1354
+ var rowMetadata = data.getItemMetadata && data.getItemMetadata(row);
1355
+ var columnMetadata = rowMetadata && rowMetadata.columns;
1356
+
1357
+ if (columnMetadata && columnMetadata[column.id] && columnMetadata[column.id].editor !== undefined) {
1358
+ return columnMetadata[column.id].editor;
1359
+ }
1360
+ if (columnMetadata && columnMetadata[cell] && columnMetadata[cell].editor !== undefined) {
1361
+ return columnMetadata[cell].editor;
1362
+ }
1363
+
1364
+ return column.editor || (options.editorFactory && options.editorFactory.getEditor(column));
1365
+ }
1366
+
1367
+ function getDataItemValueForColumn(item, columnDef) {
1368
+ if (options.dataItemColumnValueExtractor) {
1369
+ return options.dataItemColumnValueExtractor(item, columnDef);
1370
+ }
1371
+ return item[columnDef.field];
1372
+ }
1373
+
1374
+ function appendRowHtml(stringArray, row, range) {
1375
+ var d = getDataItem(row);
1376
+ var dataLoading = row < getDataLength() && !d;
1377
+ var rowCss = "slick-row" +
1378
+ (dataLoading ? " loading" : "") +
1379
+ (row === activeRow ? " active" : "") +
1380
+ (row % 2 == 1 ? " odd" : " even");
1381
+
1382
+ var metadata = data.getItemMetadata && data.getItemMetadata(row);
1383
+
1384
+ if (metadata && metadata.cssClasses) {
1385
+ rowCss += " " + metadata.cssClasses;
1386
+ }
1387
+
1388
+ stringArray.push("<div class='ui-widget-content " + rowCss + "' style='top:" + getRowTop(row) + "px'>");
1389
+
1390
+ var colspan, m;
1391
+ for (var i = 0, ii = columns.length; i < ii; i++) {
1392
+ m = columns[i];
1393
+ colspan = 1;
1394
+ if (metadata && metadata.columns) {
1395
+ var columnData = metadata.columns[m.id] || metadata.columns[i];
1396
+ colspan = (columnData && columnData.colspan) || 1;
1397
+ if (colspan === "*") {
1398
+ colspan = ii - i;
1399
+ }
1400
+ }
1401
+
1402
+ // Do not render cells outside of the viewport.
1403
+ if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) {
1404
+ if (columnPosLeft[i] > range.rightPx) {
1405
+ // All columns to the right are outside the range.
1406
+ break;
1407
+ }
1408
+
1409
+ appendCellHtml(stringArray, row, i, colspan);
1410
+ }
1411
+
1412
+ if (colspan > 1) {
1413
+ i += (colspan - 1);
1414
+ }
1415
+ }
1416
+
1417
+ stringArray.push("</div>");
1418
+ }
1419
+
1420
+ function appendCellHtml(stringArray, row, cell, colspan) {
1421
+ var m = columns[cell];
1422
+ var d = getDataItem(row);
1423
+ var cellCss = "slick-cell l" + cell + " r" + Math.min(columns.length - 1, cell + colspan - 1) +
1424
+ (m.cssClass ? " " + m.cssClass : "");
1425
+ if (row === activeRow && cell === activeCell) {
1426
+ cellCss += (" active");
1427
+ }
1428
+
1429
+ // TODO: merge them together in the setter
1430
+ for (var key in cellCssClasses) {
1431
+ if (cellCssClasses[key][row] && cellCssClasses[key][row][m.id]) {
1432
+ cellCss += (" " + cellCssClasses[key][row][m.id]);
1433
+ }
1434
+ }
1435
+
1436
+ stringArray.push("<div class='" + cellCss + "'>");
1437
+
1438
+ // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet)
1439
+ if (d) {
1440
+ var value = getDataItemValueForColumn(d, m);
1441
+ stringArray.push(getFormatter(row, m)(row, cell, value, m, d));
1442
+ }
1443
+
1444
+ stringArray.push("</div>");
1445
+
1446
+ rowsCache[row].cellRenderQueue.push(cell);
1447
+ rowsCache[row].cellColSpans[cell] = colspan;
1448
+ }
1449
+
1450
+
1451
+ function cleanupRows(rangeToKeep) {
1452
+ for (var i in rowsCache) {
1453
+ if (((i = parseInt(i, 10)) !== activeRow) && (i < rangeToKeep.top || i > rangeToKeep.bottom)) {
1454
+ removeRowFromCache(i);
1455
+ }
1456
+ }
1457
+ }
1458
+
1459
+ function invalidate() {
1460
+ updateRowCount();
1461
+ invalidateAllRows();
1462
+ render();
1463
+ }
1464
+
1465
+ function invalidateAllRows() {
1466
+ if (currentEditor) {
1467
+ makeActiveCellNormal();
1468
+ }
1469
+ for (var row in rowsCache) {
1470
+ removeRowFromCache(row);
1471
+ }
1472
+ }
1473
+
1474
+ function removeRowFromCache(row) {
1475
+ var cacheEntry = rowsCache[row];
1476
+ if (!cacheEntry) {
1477
+ return;
1478
+ }
1479
+ $canvas[0].removeChild(cacheEntry.rowNode);
1480
+ delete rowsCache[row];
1481
+ delete postProcessedRows[row];
1482
+ renderedRows--;
1483
+ counter_rows_removed++;
1484
+ }
1485
+
1486
+ function invalidateRows(rows) {
1487
+ var i, rl;
1488
+ if (!rows || !rows.length) {
1489
+ return;
1490
+ }
1491
+ vScrollDir = 0;
1492
+ for (i = 0, rl = rows.length; i < rl; i++) {
1493
+ if (currentEditor && activeRow === rows[i]) {
1494
+ makeActiveCellNormal();
1495
+ }
1496
+ if (rowsCache[rows[i]]) {
1497
+ removeRowFromCache(rows[i]);
1498
+ }
1499
+ }
1500
+ }
1501
+
1502
+ function invalidateRow(row) {
1503
+ invalidateRows([row]);
1504
+ }
1505
+
1506
+ function updateCell(row, cell) {
1507
+ var cellNode = getCellNode(row, cell);
1508
+ if (!cellNode) {
1509
+ return;
1510
+ }
1511
+
1512
+ var m = columns[cell], d = getDataItem(row);
1513
+ if (currentEditor && activeRow === row && activeCell === cell) {
1514
+ currentEditor.loadValue(d);
1515
+ } else {
1516
+ cellNode.innerHTML = d ? getFormatter(row, m)(row, cell, getDataItemValueForColumn(d, m), m, d) : "";
1517
+ invalidatePostProcessingResults(row);
1518
+ }
1519
+ }
1520
+
1521
+ function updateRow(row) {
1522
+ var cacheEntry = rowsCache[row];
1523
+ if (!cacheEntry) {
1524
+ return;
1525
+ }
1526
+
1527
+ ensureCellNodesInRowsCache(row);
1528
+
1529
+ for (var columnIdx in cacheEntry.cellNodesByColumnIdx) {
1530
+ if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(columnIdx)) {
1531
+ continue;
1532
+ }
1533
+
1534
+ columnIdx = columnIdx | 0;
1535
+ var m = columns[columnIdx],
1536
+ d = getDataItem(row),
1537
+ node = cacheEntry.cellNodesByColumnIdx[columnIdx];
1538
+
1539
+ if (row === activeRow && columnIdx === activeCell && currentEditor) {
1540
+ currentEditor.loadValue(d);
1541
+ } else if (d) {
1542
+ node.innerHTML = getFormatter(row, m)(row, columnIdx, getDataItemValueForColumn(d, m), m, d);
1543
+ } else {
1544
+ node.innerHTML = "";
1545
+ }
1546
+ }
1547
+
1548
+ invalidatePostProcessingResults(row);
1549
+ }
1550
+
1551
+ function getViewportHeight() {
1552
+ return parseFloat($.css($container[0], "height", true)) -
1553
+ parseFloat($.css($container[0], "paddingTop", true)) -
1554
+ parseFloat($.css($container[0], "paddingBottom", true)) -
1555
+ parseFloat($.css($headerScroller[0], "height")) - getVBoxDelta($headerScroller) -
1556
+ (options.showTopPanel ? options.topPanelHeight + getVBoxDelta($topPanelScroller) : 0) -
1557
+ (options.showHeaderRow ? options.headerRowHeight + getVBoxDelta($headerRowScroller) : 0);
1558
+ }
1559
+
1560
+ function resizeCanvas() {
1561
+ if (!initialized) { return; }
1562
+ if (options.autoHeight) {
1563
+ viewportH = options.rowHeight * (getDataLength() + (options.enableAddRow ? 1 : 0));
1564
+ } else {
1565
+ viewportH = getViewportHeight();
1566
+ }
1567
+
1568
+ numVisibleRows = Math.ceil(viewportH / options.rowHeight);
1569
+ viewportW = parseFloat($.css($container[0], "width", true));
1570
+ if (!options.autoHeight) {
1571
+ $viewport.height(viewportH);
1572
+ }
1573
+
1574
+ if (options.forceFitColumns) {
1575
+ autosizeColumns();
1576
+ }
1577
+
1578
+ updateRowCount();
1579
+ handleScroll();
1580
+ render();
1581
+ }
1582
+
1583
+ function updateRowCount() {
1584
+ if (!initialized) { return; }
1585
+ numberOfRows = getDataLength() +
1586
+ (options.enableAddRow ? 1 : 0) +
1587
+ (options.leaveSpaceForNewRows ? numVisibleRows - 1 : 0);
1588
+
1589
+ var oldViewportHasVScroll = viewportHasVScroll;
1590
+ // with autoHeight, we do not need to accommodate the vertical scroll bar
1591
+ viewportHasVScroll = !options.autoHeight && (numberOfRows * options.rowHeight > viewportH);
1592
+
1593
+ // remove the rows that are now outside of the data range
1594
+ // this helps avoid redundant calls to .removeRow() when the size of the data decreased by thousands of rows
1595
+ var l = options.enableAddRow ? getDataLength() : getDataLength() - 1;
1596
+ for (var i in rowsCache) {
1597
+ if (i >= l) {
1598
+ removeRowFromCache(i);
1599
+ }
1600
+ }
1601
+
1602
+ if (activeCellNode && activeRow > l) {
1603
+ resetActiveCell();
1604
+ }
1605
+
1606
+ var oldH = h;
1607
+ th = Math.max(options.rowHeight * numberOfRows, viewportH - scrollbarDimensions.height);
1608
+ if (th < maxSupportedCssHeight) {
1609
+ // just one page
1610
+ h = ph = th;
1611
+ n = 1;
1612
+ cj = 0;
1613
+ } else {
1614
+ // break into pages
1615
+ h = maxSupportedCssHeight;
1616
+ ph = h / 100;
1617
+ n = Math.floor(th / ph);
1618
+ cj = (th - h) / (n - 1);
1619
+ }
1620
+
1621
+ if (h !== oldH) {
1622
+ $canvas.css("height", h);
1623
+ scrollTop = $viewport[0].scrollTop;
1624
+ }
1625
+
1626
+ var oldScrollTopInRange = (scrollTop + offset <= th - viewportH);
1627
+
1628
+ if (th == 0 || scrollTop == 0) {
1629
+ page = offset = 0;
1630
+ } else if (oldScrollTopInRange) {
1631
+ // maintain virtual position
1632
+ scrollTo(scrollTop + offset);
1633
+ } else {
1634
+ // scroll to bottom
1635
+ scrollTo(th - viewportH);
1636
+ }
1637
+
1638
+ if (h != oldH && options.autoHeight) {
1639
+ resizeCanvas();
1640
+ }
1641
+
1642
+ if (options.forceFitColumns && oldViewportHasVScroll != viewportHasVScroll) {
1643
+ autosizeColumns();
1644
+ }
1645
+ updateCanvasWidth(false);
1646
+ }
1647
+
1648
+ function getVisibleRange(viewportTop, viewportLeft) {
1649
+ if (viewportTop == null) {
1650
+ viewportTop = scrollTop;
1651
+ }
1652
+ if (viewportLeft == null) {
1653
+ viewportLeft = scrollLeft;
1654
+ }
1655
+
1656
+ return {
1657
+ top: getRowFromPosition(viewportTop),
1658
+ bottom: getRowFromPosition(viewportTop + viewportH) + 1,
1659
+ leftPx: viewportLeft,
1660
+ rightPx: viewportLeft + viewportW
1661
+ };
1662
+ }
1663
+
1664
+ function getRenderedRange(viewportTop, viewportLeft) {
1665
+ var range = getVisibleRange(viewportTop, viewportLeft);
1666
+ var buffer = Math.round(viewportH / options.rowHeight);
1667
+ var minBuffer = 3;
1668
+
1669
+ if (vScrollDir == -1) {
1670
+ range.top -= buffer;
1671
+ range.bottom += minBuffer;
1672
+ } else if (vScrollDir == 1) {
1673
+ range.top -= minBuffer;
1674
+ range.bottom += buffer;
1675
+ } else {
1676
+ range.top -= minBuffer;
1677
+ range.bottom += minBuffer;
1678
+ }
1679
+
1680
+ range.top = Math.max(0, range.top);
1681
+ range.bottom = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, range.bottom);
1682
+
1683
+ range.leftPx -= viewportW;
1684
+ range.rightPx += viewportW;
1685
+
1686
+ range.leftPx = Math.max(0, range.leftPx);
1687
+ range.rightPx = Math.min(canvasWidth, range.rightPx);
1688
+
1689
+ return range;
1690
+ }
1691
+
1692
+ function ensureCellNodesInRowsCache(row) {
1693
+ var cacheEntry = rowsCache[row];
1694
+ if (cacheEntry) {
1695
+ if (cacheEntry.cellRenderQueue.length) {
1696
+ var lastChild = cacheEntry.rowNode.lastChild;
1697
+ while (cacheEntry.cellRenderQueue.length) {
1698
+ var columnIdx = cacheEntry.cellRenderQueue.pop();
1699
+ cacheEntry.cellNodesByColumnIdx[columnIdx] = lastChild;
1700
+ lastChild = lastChild.previousSibling;
1701
+ }
1702
+ }
1703
+ }
1704
+ }
1705
+
1706
+ function cleanUpCells(range, row) {
1707
+ var totalCellsRemoved = 0;
1708
+ var cacheEntry = rowsCache[row];
1709
+
1710
+ // Remove cells outside the range.
1711
+ var cellsToRemove = [];
1712
+ for (var i in cacheEntry.cellNodesByColumnIdx) {
1713
+ // I really hate it when people mess with Array.prototype.
1714
+ if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(i)) {
1715
+ continue;
1716
+ }
1717
+
1718
+ // This is a string, so it needs to be cast back to a number.
1719
+ i = i | 0;
1720
+
1721
+ var colspan = cacheEntry.cellColSpans[i];
1722
+ if (columnPosLeft[i] > range.rightPx ||
1723
+ columnPosRight[Math.min(columns.length - 1, i + colspan - 1)] < range.leftPx) {
1724
+ if (!(row == activeRow && i == activeCell)) {
1725
+ cellsToRemove.push(i);
1726
+ }
1727
+ }
1728
+ }
1729
+
1730
+ var cellToRemove;
1731
+ while ((cellToRemove = cellsToRemove.pop()) != null) {
1732
+ cacheEntry.rowNode.removeChild(cacheEntry.cellNodesByColumnIdx[cellToRemove]);
1733
+ delete cacheEntry.cellColSpans[cellToRemove];
1734
+ delete cacheEntry.cellNodesByColumnIdx[cellToRemove];
1735
+ if (postProcessedRows[row]) {
1736
+ delete postProcessedRows[row][cellToRemove];
1737
+ }
1738
+ totalCellsRemoved++;
1739
+ }
1740
+ }
1741
+
1742
+ function cleanUpAndRenderCells(range) {
1743
+ var cacheEntry;
1744
+ var stringArray = [];
1745
+ var processedRows = [];
1746
+ var cellsAdded;
1747
+ var totalCellsAdded = 0;
1748
+ var colspan;
1749
+
1750
+ for (var row = range.top; row <= range.bottom; row++) {
1751
+ cacheEntry = rowsCache[row];
1752
+ if (!cacheEntry) {
1753
+ continue;
1754
+ }
1755
+
1756
+ // cellRenderQueue populated in renderRows() needs to be cleared first
1757
+ ensureCellNodesInRowsCache(row);
1758
+
1759
+ cleanUpCells(range, row);
1760
+
1761
+ // Render missing cells.
1762
+ cellsAdded = 0;
1763
+
1764
+ var metadata = data.getItemMetadata && data.getItemMetadata(row);
1765
+ metadata = metadata && metadata.columns;
1766
+
1767
+ // TODO: shorten this loop (index? heuristics? binary search?)
1768
+ for (var i = 0, ii = columns.length; i < ii; i++) {
1769
+ // Cells to the right are outside the range.
1770
+ if (columnPosLeft[i] > range.rightPx) {
1771
+ break;
1772
+ }
1773
+
1774
+ // Already rendered.
1775
+ if ((colspan = cacheEntry.cellColSpans[i]) != null) {
1776
+ i += (colspan > 1 ? colspan - 1 : 0);
1777
+ continue;
1778
+ }
1779
+
1780
+ colspan = 1;
1781
+ if (metadata) {
1782
+ var columnData = metadata[columns[i].id] || metadata[i];
1783
+ colspan = (columnData && columnData.colspan) || 1;
1784
+ if (colspan === "*") {
1785
+ colspan = ii - i;
1786
+ }
1787
+ }
1788
+
1789
+ if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) {
1790
+ appendCellHtml(stringArray, row, i, colspan);
1791
+ cellsAdded++;
1792
+ }
1793
+
1794
+ i += (colspan > 1 ? colspan - 1 : 0);
1795
+ }
1796
+
1797
+ if (cellsAdded) {
1798
+ totalCellsAdded += cellsAdded;
1799
+ processedRows.push(row);
1800
+ }
1801
+ }
1802
+
1803
+ if (!stringArray.length) {
1804
+ return;
1805
+ }
1806
+
1807
+ var x = document.createElement("div");
1808
+ x.innerHTML = stringArray.join("");
1809
+
1810
+ var processedRow;
1811
+ var node;
1812
+ while ((processedRow = processedRows.pop()) != null) {
1813
+ cacheEntry = rowsCache[processedRow];
1814
+ var columnIdx;
1815
+ while ((columnIdx = cacheEntry.cellRenderQueue.pop()) != null) {
1816
+ node = x.lastChild;
1817
+ cacheEntry.rowNode.appendChild(node);
1818
+ cacheEntry.cellNodesByColumnIdx[columnIdx] = node;
1819
+ }
1820
+ }
1821
+ }
1822
+
1823
+ function renderRows(range) {
1824
+ var parentNode = $canvas[0],
1825
+ stringArray = [],
1826
+ rows = [],
1827
+ needToReselectCell = false;
1828
+
1829
+ for (var i = range.top; i <= range.bottom; i++) {
1830
+ if (rowsCache[i]) {
1831
+ continue;
1832
+ }
1833
+ renderedRows++;
1834
+ rows.push(i);
1835
+
1836
+ // Create an entry right away so that appendRowHtml() can
1837
+ // start populatating it.
1838
+ rowsCache[i] = {
1839
+ "rowNode": null,
1840
+
1841
+ // ColSpans of rendered cells (by column idx).
1842
+ // Can also be used for checking whether a cell has been rendered.
1843
+ "cellColSpans": [],
1844
+
1845
+ // Cell nodes (by column idx). Lazy-populated by ensureCellNodesInRowsCache().
1846
+ "cellNodesByColumnIdx": [],
1847
+
1848
+ // Column indices of cell nodes that have been rendered, but not yet indexed in
1849
+ // cellNodesByColumnIdx. These are in the same order as cell nodes added at the
1850
+ // end of the row.
1851
+ "cellRenderQueue": []
1852
+ };
1853
+
1854
+ appendRowHtml(stringArray, i, range);
1855
+ if (activeCellNode && activeRow === i) {
1856
+ needToReselectCell = true;
1857
+ }
1858
+ counter_rows_rendered++;
1859
+ }
1860
+
1861
+ if (!rows.length) { return; }
1862
+
1863
+ var x = document.createElement("div");
1864
+ x.innerHTML = stringArray.join("");
1865
+
1866
+ for (var i = 0, ii = rows.length; i < ii; i++) {
1867
+ rowsCache[rows[i]].rowNode = parentNode.appendChild(x.firstChild);
1868
+ }
1869
+
1870
+ if (needToReselectCell) {
1871
+ activeCellNode = getCellNode(activeRow, activeCell);
1872
+ }
1873
+ }
1874
+
1875
+ function startPostProcessing() {
1876
+ if (!options.enableAsyncPostRender) {
1877
+ return;
1878
+ }
1879
+ clearTimeout(h_postrender);
1880
+ h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay);
1881
+ }
1882
+
1883
+ function invalidatePostProcessingResults(row) {
1884
+ delete postProcessedRows[row];
1885
+ postProcessFromRow = Math.min(postProcessFromRow, row);
1886
+ postProcessToRow = Math.max(postProcessToRow, row);
1887
+ startPostProcessing();
1888
+ }
1889
+
1890
+ function updateRowPositions() {
1891
+ for (var row in rowsCache) {
1892
+ rowsCache[row].rowNode.style.top = getRowTop(row) + "px";
1893
+ }
1894
+ }
1895
+
1896
+ function render() {
1897
+ if (!initialized) { return; }
1898
+ var visible = getVisibleRange();
1899
+ var rendered = getRenderedRange();
1900
+
1901
+ // remove rows no longer in the viewport
1902
+ cleanupRows(rendered);
1903
+
1904
+ // add new rows & missing cells in existing rows
1905
+ if (lastRenderedScrollLeft != scrollLeft) {
1906
+ cleanUpAndRenderCells(rendered);
1907
+ }
1908
+
1909
+ // render missing rows
1910
+ renderRows(rendered);
1911
+
1912
+ postProcessFromRow = visible.top;
1913
+ postProcessToRow = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, visible.bottom);
1914
+ startPostProcessing();
1915
+
1916
+ lastRenderedScrollTop = scrollTop;
1917
+ lastRenderedScrollLeft = scrollLeft;
1918
+ h_render = null;
1919
+ }
1920
+
1921
+ function handleHeaderRowScroll() {
1922
+ var scrollLeft = $headerRowScroller[0].scrollLeft;
1923
+ if (scrollLeft != $viewport[0].scrollLeft) {
1924
+ $viewport[0].scrollLeft = scrollLeft;
1925
+ }
1926
+ }
1927
+
1928
+ function handleScroll() {
1929
+ scrollTop = $viewport[0].scrollTop;
1930
+ scrollLeft = $viewport[0].scrollLeft;
1931
+ var vScrollDist = Math.abs(scrollTop - prevScrollTop);
1932
+ var hScrollDist = Math.abs(scrollLeft - prevScrollLeft);
1933
+
1934
+ if (hScrollDist) {
1935
+ prevScrollLeft = scrollLeft;
1936
+ $headerScroller[0].scrollLeft = scrollLeft;
1937
+ $topPanelScroller[0].scrollLeft = scrollLeft;
1938
+ $headerRowScroller[0].scrollLeft = scrollLeft;
1939
+ }
1940
+
1941
+ if (vScrollDist) {
1942
+ vScrollDir = prevScrollTop < scrollTop ? 1 : -1;
1943
+ prevScrollTop = scrollTop;
1944
+
1945
+ // switch virtual pages if needed
1946
+ if (vScrollDist < viewportH) {
1947
+ scrollTo(scrollTop + offset);
1948
+ } else {
1949
+ var oldOffset = offset;
1950
+ if (h == viewportH) {
1951
+ page = 0;
1952
+ } else {
1953
+ page = Math.min(n - 1, Math.floor(scrollTop * ((th - viewportH) / (h - viewportH)) * (1 / ph)));
1954
+ }
1955
+ offset = Math.round(page * cj);
1956
+ if (oldOffset != offset) {
1957
+ invalidateAllRows();
1958
+ }
1959
+ }
1960
+ }
1961
+
1962
+ if (hScrollDist || vScrollDist) {
1963
+ if (h_render) {
1964
+ clearTimeout(h_render);
1965
+ }
1966
+
1967
+ if (Math.abs(lastRenderedScrollTop - scrollTop) > 20 ||
1968
+ Math.abs(lastRenderedScrollLeft - scrollLeft) > 20) {
1969
+ if (options.forceSyncScrolling || (
1970
+ Math.abs(lastRenderedScrollTop - scrollTop) < viewportH &&
1971
+ Math.abs(lastRenderedScrollLeft - scrollLeft) < viewportW)) {
1972
+ render();
1973
+ } else {
1974
+ h_render = setTimeout(render, 50);
1975
+ }
1976
+
1977
+ trigger(self.onViewportChanged, {});
1978
+ }
1979
+ }
1980
+
1981
+ trigger(self.onScroll, {scrollLeft: scrollLeft, scrollTop: scrollTop});
1982
+ }
1983
+
1984
+ function asyncPostProcessRows() {
1985
+ while (postProcessFromRow <= postProcessToRow) {
1986
+ var row = (vScrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--;
1987
+ var cacheEntry = rowsCache[row];
1988
+ if (!cacheEntry || row >= getDataLength()) {
1989
+ continue;
1990
+ }
1991
+
1992
+ if (!postProcessedRows[row]) {
1993
+ postProcessedRows[row] = {};
1994
+ }
1995
+
1996
+ ensureCellNodesInRowsCache(row);
1997
+ for (var columnIdx in cacheEntry.cellNodesByColumnIdx) {
1998
+ if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(columnIdx)) {
1999
+ continue;
2000
+ }
2001
+
2002
+ columnIdx = columnIdx | 0;
2003
+
2004
+ var m = columns[columnIdx];
2005
+ if (m.asyncPostRender && !postProcessedRows[row][columnIdx]) {
2006
+ var node = cacheEntry.cellNodesByColumnIdx[columnIdx];
2007
+ if (node) {
2008
+ m.asyncPostRender(node, row, getDataItem(row), m);
2009
+ }
2010
+ postProcessedRows[row][columnIdx] = true;
2011
+ }
2012
+ }
2013
+
2014
+ h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay);
2015
+ return;
2016
+ }
2017
+ }
2018
+
2019
+ function updateCellCssStylesOnRenderedRows(addedHash, removedHash) {
2020
+ var node, columnId, addedRowHash, removedRowHash;
2021
+ for (var row in rowsCache) {
2022
+ removedRowHash = removedHash && removedHash[row];
2023
+ addedRowHash = addedHash && addedHash[row];
2024
+
2025
+ if (removedRowHash) {
2026
+ for (columnId in removedRowHash) {
2027
+ if (!addedRowHash || removedRowHash[columnId] != addedRowHash[columnId]) {
2028
+ node = getCellNode(row, getColumnIndex(columnId));
2029
+ if (node) {
2030
+ $(node).removeClass(removedRowHash[columnId]);
2031
+ }
2032
+ }
2033
+ }
2034
+ }
2035
+
2036
+ if (addedRowHash) {
2037
+ for (columnId in addedRowHash) {
2038
+ if (!removedRowHash || removedRowHash[columnId] != addedRowHash[columnId]) {
2039
+ node = getCellNode(row, getColumnIndex(columnId));
2040
+ if (node) {
2041
+ $(node).addClass(addedRowHash[columnId]);
2042
+ }
2043
+ }
2044
+ }
2045
+ }
2046
+ }
2047
+ }
2048
+
2049
+ function addCellCssStyles(key, hash) {
2050
+ if (cellCssClasses[key]) {
2051
+ throw "addCellCssStyles: cell CSS hash with key '" + key + "' already exists.";
2052
+ }
2053
+
2054
+ cellCssClasses[key] = hash;
2055
+ updateCellCssStylesOnRenderedRows(hash, null);
2056
+
2057
+ trigger(self.onCellCssStylesChanged, { "key": key, "hash": hash });
2058
+ }
2059
+
2060
+ function removeCellCssStyles(key) {
2061
+ if (!cellCssClasses[key]) {
2062
+ return;
2063
+ }
2064
+
2065
+ updateCellCssStylesOnRenderedRows(null, cellCssClasses[key]);
2066
+ delete cellCssClasses[key];
2067
+
2068
+ trigger(self.onCellCssStylesChanged, { "key": key, "hash": null });
2069
+ }
2070
+
2071
+ function setCellCssStyles(key, hash) {
2072
+ var prevHash = cellCssClasses[key];
2073
+
2074
+ cellCssClasses[key] = hash;
2075
+ updateCellCssStylesOnRenderedRows(hash, prevHash);
2076
+
2077
+ trigger(self.onCellCssStylesChanged, { "key": key, "hash": hash });
2078
+ }
2079
+
2080
+ function getCellCssStyles(key) {
2081
+ return cellCssClasses[key];
2082
+ }
2083
+
2084
+ function flashCell(row, cell, speed) {
2085
+ speed = speed || 100;
2086
+ if (rowsCache[row]) {
2087
+ var $cell = $(getCellNode(row, cell));
2088
+
2089
+ function toggleCellClass(times) {
2090
+ if (!times) {
2091
+ return;
2092
+ }
2093
+ setTimeout(function () {
2094
+ $cell.queue(function () {
2095
+ $cell.toggleClass(options.cellFlashingCssClass).dequeue();
2096
+ toggleCellClass(times - 1);
2097
+ });
2098
+ },
2099
+ speed);
2100
+ }
2101
+
2102
+ toggleCellClass(4);
2103
+ }
2104
+ }
2105
+
2106
+ //////////////////////////////////////////////////////////////////////////////////////////////
2107
+ // Interactivity
2108
+
2109
+ function handleDragInit(e, dd) {
2110
+ var cell = getCellFromEvent(e);
2111
+ if (!cell || !cellExists(cell.row, cell.cell)) {
2112
+ return false;
2113
+ }
2114
+
2115
+ var retval = trigger(self.onDragInit, dd, e);
2116
+ if (e.isImmediatePropagationStopped()) {
2117
+ return retval;
2118
+ }
2119
+
2120
+ // if nobody claims to be handling drag'n'drop by stopping immediate propagation,
2121
+ // cancel out of it
2122
+ return false;
2123
+ }
2124
+
2125
+ function handleDragStart(e, dd) {
2126
+ var cell = getCellFromEvent(e);
2127
+ if (!cell || !cellExists(cell.row, cell.cell)) {
2128
+ return false;
2129
+ }
2130
+
2131
+ var retval = trigger(self.onDragStart, dd, e);
2132
+ if (e.isImmediatePropagationStopped()) {
2133
+ return retval;
2134
+ }
2135
+
2136
+ return false;
2137
+ }
2138
+
2139
+ function handleDrag(e, dd) {
2140
+ return trigger(self.onDrag, dd, e);
2141
+ }
2142
+
2143
+ function handleDragEnd(e, dd) {
2144
+ trigger(self.onDragEnd, dd, e);
2145
+ }
2146
+
2147
+ function handleKeyDown(e) {
2148
+ trigger(self.onKeyDown, {row: activeRow, cell: activeCell}, e);
2149
+ var handled = e.isImmediatePropagationStopped();
2150
+
2151
+ if (!handled) {
2152
+ if (!e.shiftKey && !e.altKey && !e.ctrlKey) {
2153
+ if (e.which == 27) {
2154
+ if (!getEditorLock().isActive()) {
2155
+ return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event)
2156
+ }
2157
+ cancelEditAndSetFocus();
2158
+ } else if (e.which == 37) {
2159
+ handled = navigateLeft();
2160
+ } else if (e.which == 39) {
2161
+ handled = navigateRight();
2162
+ } else if (e.which == 38) {
2163
+ handled = navigateUp();
2164
+ } else if (e.which == 40) {
2165
+ handled = navigateDown();
2166
+ } else if (e.which == 9) {
2167
+ handled = navigateNext();
2168
+ } else if (e.which == 13) {
2169
+ if (options.editable) {
2170
+ if (currentEditor) {
2171
+ // adding new row
2172
+ if (activeRow === getDataLength()) {
2173
+ navigateDown();
2174
+ } else {
2175
+ commitEditAndSetFocus();
2176
+ }
2177
+ } else {
2178
+ if (getEditorLock().commitCurrentEdit()) {
2179
+ makeActiveCellEditable();
2180
+ }
2181
+ }
2182
+ }
2183
+ handled = true;
2184
+ }
2185
+ } else if (e.which == 9 && e.shiftKey && !e.ctrlKey && !e.altKey) {
2186
+ handled = navigatePrev();
2187
+ }
2188
+ }
2189
+
2190
+ if (handled) {
2191
+ // the event has been handled so don't let parent element (bubbling/propagation) or browser (default) handle it
2192
+ e.stopPropagation();
2193
+ e.preventDefault();
2194
+ try {
2195
+ e.originalEvent.keyCode = 0; // prevent default behaviour for special keys in IE browsers (F3, F5, etc.)
2196
+ }
2197
+ // ignore exceptions - setting the original event's keycode throws access denied exception for "Ctrl"
2198
+ // (hitting control key only, nothing else), "Shift" (maybe others)
2199
+ catch (error) {
2200
+ }
2201
+ }
2202
+ }
2203
+
2204
+ function handleClick(e) {
2205
+ if (!currentEditor) {
2206
+ // if this click resulted in some cell child node getting focus,
2207
+ // don't steal it back - keyboard events will still bubble up
2208
+ if (e.target != document.activeElement) {
2209
+ setFocus();
2210
+ }
2211
+ }
2212
+
2213
+ var cell = getCellFromEvent(e);
2214
+ if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) {
2215
+ return;
2216
+ }
2217
+
2218
+ trigger(self.onClick, {row: cell.row, cell: cell.cell}, e);
2219
+ if (e.isImmediatePropagationStopped()) {
2220
+ return;
2221
+ }
2222
+
2223
+ if ((activeCell != cell.cell || activeRow != cell.row) && canCellBeActive(cell.row, cell.cell)) {
2224
+ if (!getEditorLock().isActive() || getEditorLock().commitCurrentEdit()) {
2225
+ scrollRowIntoView(cell.row, false);
2226
+ setActiveCellInternal(getCellNode(cell.row, cell.cell), (cell.row === getDataLength()) || options.autoEdit);
2227
+ }
2228
+ }
2229
+ }
2230
+
2231
+ function handleContextMenu(e) {
2232
+ var $cell = $(e.target).closest(".slick-cell", $canvas);
2233
+ if ($cell.length === 0) {
2234
+ return;
2235
+ }
2236
+
2237
+ // are we editing this cell?
2238
+ if (activeCellNode === $cell[0] && currentEditor !== null) {
2239
+ return;
2240
+ }
2241
+
2242
+ trigger(self.onContextMenu, {}, e);
2243
+ }
2244
+
2245
+ function handleDblClick(e) {
2246
+ var cell = getCellFromEvent(e);
2247
+ if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) {
2248
+ return;
2249
+ }
2250
+
2251
+ trigger(self.onDblClick, {row: cell.row, cell: cell.cell}, e);
2252
+ if (e.isImmediatePropagationStopped()) {
2253
+ return;
2254
+ }
2255
+
2256
+ if (options.editable) {
2257
+ gotoCell(cell.row, cell.cell, true);
2258
+ }
2259
+ }
2260
+
2261
+ function handleHeaderMouseEnter(e) {
2262
+ trigger(self.onHeaderMouseEnter, {
2263
+ "column": $(this).data("column")
2264
+ }, e);
2265
+ }
2266
+
2267
+ function handleHeaderMouseLeave(e) {
2268
+ trigger(self.onHeaderMouseLeave, {
2269
+ "column": $(this).data("column")
2270
+ }, e);
2271
+ }
2272
+
2273
+ function handleHeaderContextMenu(e) {
2274
+ var $header = $(e.target).closest(".slick-header-column", ".slick-header-columns");
2275
+ var column = $header && $header.data("column");
2276
+ trigger(self.onHeaderContextMenu, {column: column}, e);
2277
+ }
2278
+
2279
+ function handleHeaderClick(e) {
2280
+ var $header = $(e.target).closest(".slick-header-column", ".slick-header-columns");
2281
+ var column = $header && $header.data("column");
2282
+ if (column) {
2283
+ trigger(self.onHeaderClick, {column: column}, e);
2284
+ }
2285
+ }
2286
+
2287
+ function handleMouseEnter(e) {
2288
+ trigger(self.onMouseEnter, {}, e);
2289
+ }
2290
+
2291
+ function handleMouseLeave(e) {
2292
+ trigger(self.onMouseLeave, {}, e);
2293
+ }
2294
+
2295
+ function cellExists(row, cell) {
2296
+ return !(row < 0 || row >= getDataLength() || cell < 0 || cell >= columns.length);
2297
+ }
2298
+
2299
+ function getCellFromPoint(x, y) {
2300
+ var row = getRowFromPosition(y);
2301
+ var cell = 0;
2302
+
2303
+ var w = 0;
2304
+ for (var i = 0; i < columns.length && w < x; i++) {
2305
+ w += columns[i].width;
2306
+ cell++;
2307
+ }
2308
+
2309
+ if (cell < 0) {
2310
+ cell = 0;
2311
+ }
2312
+
2313
+ return {row: row, cell: cell - 1};
2314
+ }
2315
+
2316
+ function getCellFromNode(cellNode) {
2317
+ // read column number from .l<columnNumber> CSS class
2318
+ var cls = /l\d+/.exec(cellNode.className);
2319
+ if (!cls) {
2320
+ throw "getCellFromNode: cannot get cell - " + cellNode.className;
2321
+ }
2322
+ return parseInt(cls[0].substr(1, cls[0].length - 1), 10);
2323
+ }
2324
+
2325
+ function getRowFromNode(rowNode) {
2326
+ for (var row in rowsCache) {
2327
+ if (rowsCache[row].rowNode === rowNode) {
2328
+ return row | 0;
2329
+ }
2330
+ }
2331
+
2332
+ return null;
2333
+ }
2334
+
2335
+ function getCellFromEvent(e) {
2336
+ var $cell = $(e.target).closest(".slick-cell", $canvas);
2337
+ if (!$cell.length) {
2338
+ return null;
2339
+ }
2340
+
2341
+ var row = getRowFromNode($cell[0].parentNode);
2342
+ var cell = getCellFromNode($cell[0]);
2343
+
2344
+ if (row == null || cell == null) {
2345
+ return null;
2346
+ } else {
2347
+ return {
2348
+ "row": row,
2349
+ "cell": cell
2350
+ };
2351
+ }
2352
+ }
2353
+
2354
+ function getCellNodeBox(row, cell) {
2355
+ if (!cellExists(row, cell)) {
2356
+ return null;
2357
+ }
2358
+
2359
+ var y1 = getRowTop(row);
2360
+ var y2 = y1 + options.rowHeight - 1;
2361
+ var x1 = 0;
2362
+ for (var i = 0; i < cell; i++) {
2363
+ x1 += columns[i].width;
2364
+ }
2365
+ var x2 = x1 + columns[cell].width;
2366
+
2367
+ return {
2368
+ top: y1,
2369
+ left: x1,
2370
+ bottom: y2,
2371
+ right: x2
2372
+ };
2373
+ }
2374
+
2375
+ //////////////////////////////////////////////////////////////////////////////////////////////
2376
+ // Cell switching
2377
+
2378
+ function resetActiveCell() {
2379
+ setActiveCellInternal(null, false);
2380
+ }
2381
+
2382
+ function setFocus() {
2383
+ if (tabbingDirection == -1) {
2384
+ $focusSink[0].focus();
2385
+ } else {
2386
+ $focusSink2[0].focus();
2387
+ }
2388
+ }
2389
+
2390
+ function scrollCellIntoView(row, cell, doPaging) {
2391
+ scrollRowIntoView(row, doPaging);
2392
+
2393
+ var colspan = getColspan(row, cell);
2394
+ var left = columnPosLeft[cell],
2395
+ right = columnPosRight[cell + (colspan > 1 ? colspan - 1 : 0)],
2396
+ scrollRight = scrollLeft + viewportW;
2397
+
2398
+ if (left < scrollLeft) {
2399
+ $viewport.scrollLeft(left);
2400
+ handleScroll();
2401
+ render();
2402
+ } else if (right > scrollRight) {
2403
+ $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth));
2404
+ handleScroll();
2405
+ render();
2406
+ }
2407
+ }
2408
+
2409
+ function setActiveCellInternal(newCell, editMode) {
2410
+ if (activeCellNode !== null) {
2411
+ makeActiveCellNormal();
2412
+ $(activeCellNode).removeClass("active");
2413
+ if (rowsCache[activeRow]) {
2414
+ $(rowsCache[activeRow].rowNode).removeClass("active");
2415
+ }
2416
+ }
2417
+
2418
+ var activeCellChanged = (activeCellNode !== newCell);
2419
+ activeCellNode = newCell;
2420
+
2421
+ if (activeCellNode != null) {
2422
+ activeRow = getRowFromNode(activeCellNode.parentNode);
2423
+ activeCell = activePosX = getCellFromNode(activeCellNode);
2424
+
2425
+ $(activeCellNode).addClass("active");
2426
+ $(rowsCache[activeRow].rowNode).addClass("active");
2427
+
2428
+ if (options.editable && editMode && isCellPotentiallyEditable(activeRow, activeCell)) {
2429
+ clearTimeout(h_editorLoader);
2430
+
2431
+ if (options.asyncEditorLoading) {
2432
+ h_editorLoader = setTimeout(function () {
2433
+ makeActiveCellEditable();
2434
+ }, options.asyncEditorLoadDelay);
2435
+ } else {
2436
+ makeActiveCellEditable();
2437
+ }
2438
+ }
2439
+ } else {
2440
+ activeRow = activeCell = null;
2441
+ }
2442
+
2443
+ if (activeCellChanged) {
2444
+ trigger(self.onActiveCellChanged, getActiveCell());
2445
+ }
2446
+ }
2447
+
2448
+ function clearTextSelection() {
2449
+ if (document.selection && document.selection.empty) {
2450
+ document.selection.empty();
2451
+ } else if (window.getSelection) {
2452
+ var sel = window.getSelection();
2453
+ if (sel && sel.removeAllRanges) {
2454
+ sel.removeAllRanges();
2455
+ }
2456
+ }
2457
+ }
2458
+
2459
+ function isCellPotentiallyEditable(row, cell) {
2460
+ // is the data for this row loaded?
2461
+ if (row < getDataLength() && !getDataItem(row)) {
2462
+ return false;
2463
+ }
2464
+
2465
+ // are we in the Add New row? can we create new from this cell?
2466
+ if (columns[cell].cannotTriggerInsert && row >= getDataLength()) {
2467
+ return false;
2468
+ }
2469
+
2470
+ // does this cell have an editor?
2471
+ if (!getEditor(row, cell)) {
2472
+ return false;
2473
+ }
2474
+
2475
+ return true;
2476
+ }
2477
+
2478
+ function makeActiveCellNormal() {
2479
+ if (!currentEditor) {
2480
+ return;
2481
+ }
2482
+ trigger(self.onBeforeCellEditorDestroy, {editor: currentEditor});
2483
+ currentEditor.destroy();
2484
+ currentEditor = null;
2485
+
2486
+ if (activeCellNode) {
2487
+ var d = getDataItem(activeRow);
2488
+ $(activeCellNode).removeClass("editable invalid");
2489
+ if (d) {
2490
+ var column = columns[activeCell];
2491
+ var formatter = getFormatter(activeRow, column);
2492
+ activeCellNode.innerHTML = formatter(activeRow, activeCell, getDataItemValueForColumn(d, column), column, getDataItem(activeRow));
2493
+ invalidatePostProcessingResults(activeRow);
2494
+ }
2495
+ }
2496
+
2497
+ // if there previously was text selected on a page (such as selected text in the edit cell just removed),
2498
+ // IE can't set focus to anything else correctly
2499
+ if (navigator.userAgent.toLowerCase().match(/msie/)) {
2500
+ clearTextSelection();
2501
+ }
2502
+
2503
+ getEditorLock().deactivate(editController);
2504
+ }
2505
+
2506
+ function makeActiveCellEditable(editor) {
2507
+ if (!activeCellNode) {
2508
+ return;
2509
+ }
2510
+ if (!options.editable) {
2511
+ throw "Grid : makeActiveCellEditable : should never get called when options.editable is false";
2512
+ }
2513
+
2514
+ // cancel pending async call if there is one
2515
+ clearTimeout(h_editorLoader);
2516
+
2517
+ if (!isCellPotentiallyEditable(activeRow, activeCell)) {
2518
+ return;
2519
+ }
2520
+
2521
+ var columnDef = columns[activeCell];
2522
+ var item = getDataItem(activeRow);
2523
+
2524
+ if (trigger(self.onBeforeEditCell, {row: activeRow, cell: activeCell, item: item, column: columnDef}) === false) {
2525
+ setFocus();
2526
+ return;
2527
+ }
2528
+
2529
+ getEditorLock().activate(editController);
2530
+ $(activeCellNode).addClass("editable");
2531
+
2532
+ // don't clear the cell if a custom editor is passed through
2533
+ if (!editor) {
2534
+ activeCellNode.innerHTML = "";
2535
+ }
2536
+
2537
+ currentEditor = new (editor || getEditor(activeRow, activeCell))({
2538
+ grid: self,
2539
+ gridPosition: absBox($container[0]),
2540
+ position: absBox(activeCellNode),
2541
+ container: activeCellNode,
2542
+ column: columnDef,
2543
+ item: item || {},
2544
+ commitChanges: commitEditAndSetFocus,
2545
+ cancelChanges: cancelEditAndSetFocus
2546
+ });
2547
+
2548
+ if (item) {
2549
+ currentEditor.loadValue(item);
2550
+ }
2551
+
2552
+ serializedEditorValue = currentEditor.serializeValue();
2553
+
2554
+ if (currentEditor.position) {
2555
+ handleActiveCellPositionChange();
2556
+ }
2557
+ }
2558
+
2559
+ function commitEditAndSetFocus() {
2560
+ // if the commit fails, it would do so due to a validation error
2561
+ // if so, do not steal the focus from the editor
2562
+ if (getEditorLock().commitCurrentEdit()) {
2563
+ setFocus();
2564
+ if (options.autoEdit) {
2565
+ navigateDown();
2566
+ }
2567
+ }
2568
+ }
2569
+
2570
+ function cancelEditAndSetFocus() {
2571
+ if (getEditorLock().cancelCurrentEdit()) {
2572
+ setFocus();
2573
+ }
2574
+ }
2575
+
2576
+ function absBox(elem) {
2577
+ var box = {
2578
+ top: elem.offsetTop,
2579
+ left: elem.offsetLeft,
2580
+ bottom: 0,
2581
+ right: 0,
2582
+ width: $(elem).outerWidth(),
2583
+ height: $(elem).outerHeight(),
2584
+ visible: true};
2585
+ box.bottom = box.top + box.height;
2586
+ box.right = box.left + box.width;
2587
+
2588
+ // walk up the tree
2589
+ var offsetParent = elem.offsetParent;
2590
+ while ((elem = elem.parentNode) != document.body) {
2591
+ if (box.visible && elem.scrollHeight != elem.offsetHeight && $(elem).css("overflowY") != "visible") {
2592
+ box.visible = box.bottom > elem.scrollTop && box.top < elem.scrollTop + elem.clientHeight;
2593
+ }
2594
+
2595
+ if (box.visible && elem.scrollWidth != elem.offsetWidth && $(elem).css("overflowX") != "visible") {
2596
+ box.visible = box.right > elem.scrollLeft && box.left < elem.scrollLeft + elem.clientWidth;
2597
+ }
2598
+
2599
+ box.left -= elem.scrollLeft;
2600
+ box.top -= elem.scrollTop;
2601
+
2602
+ if (elem === offsetParent) {
2603
+ box.left += elem.offsetLeft;
2604
+ box.top += elem.offsetTop;
2605
+ offsetParent = elem.offsetParent;
2606
+ }
2607
+
2608
+ box.bottom = box.top + box.height;
2609
+ box.right = box.left + box.width;
2610
+ }
2611
+
2612
+ return box;
2613
+ }
2614
+
2615
+ function getActiveCellPosition() {
2616
+ return absBox(activeCellNode);
2617
+ }
2618
+
2619
+ function getGridPosition() {
2620
+ return absBox($container[0])
2621
+ }
2622
+
2623
+ function handleActiveCellPositionChange() {
2624
+ if (!activeCellNode) {
2625
+ return;
2626
+ }
2627
+
2628
+ trigger(self.onActiveCellPositionChanged, {});
2629
+
2630
+ if (currentEditor) {
2631
+ var cellBox = getActiveCellPosition();
2632
+ if (currentEditor.show && currentEditor.hide) {
2633
+ if (!cellBox.visible) {
2634
+ currentEditor.hide();
2635
+ } else {
2636
+ currentEditor.show();
2637
+ }
2638
+ }
2639
+
2640
+ if (currentEditor.position) {
2641
+ currentEditor.position(cellBox);
2642
+ }
2643
+ }
2644
+ }
2645
+
2646
+ function getCellEditor() {
2647
+ return currentEditor;
2648
+ }
2649
+
2650
+ function getActiveCell() {
2651
+ if (!activeCellNode) {
2652
+ return null;
2653
+ } else {
2654
+ return {row: activeRow, cell: activeCell};
2655
+ }
2656
+ }
2657
+
2658
+ function getActiveCellNode() {
2659
+ return activeCellNode;
2660
+ }
2661
+
2662
+ function scrollRowIntoView(row, doPaging) {
2663
+ var rowAtTop = row * options.rowHeight;
2664
+ var rowAtBottom = (row + 1) * options.rowHeight - viewportH + (viewportHasHScroll ? scrollbarDimensions.height : 0);
2665
+
2666
+ // need to page down?
2667
+ if ((row + 1) * options.rowHeight > scrollTop + viewportH + offset) {
2668
+ scrollTo(doPaging ? rowAtTop : rowAtBottom);
2669
+ render();
2670
+ }
2671
+ // or page up?
2672
+ else if (row * options.rowHeight < scrollTop + offset) {
2673
+ scrollTo(doPaging ? rowAtBottom : rowAtTop);
2674
+ render();
2675
+ }
2676
+ }
2677
+
2678
+ function scrollRowToTop(row) {
2679
+ scrollTo(row * options.rowHeight);
2680
+ render();
2681
+ }
2682
+
2683
+ function getColspan(row, cell) {
2684
+ var metadata = data.getItemMetadata && data.getItemMetadata(row);
2685
+ if (!metadata || !metadata.columns) {
2686
+ return 1;
2687
+ }
2688
+
2689
+ var columnData = metadata.columns[columns[cell].id] || metadata.columns[cell];
2690
+ var colspan = (columnData && columnData.colspan);
2691
+ if (colspan === "*") {
2692
+ colspan = columns.length - cell;
2693
+ } else {
2694
+ colspan = colspan || 1;
2695
+ }
2696
+
2697
+ return colspan;
2698
+ }
2699
+
2700
+ function findFirstFocusableCell(row) {
2701
+ var cell = 0;
2702
+ while (cell < columns.length) {
2703
+ if (canCellBeActive(row, cell)) {
2704
+ return cell;
2705
+ }
2706
+ cell += getColspan(row, cell);
2707
+ }
2708
+ return null;
2709
+ }
2710
+
2711
+ function findLastFocusableCell(row) {
2712
+ var cell = 0;
2713
+ var lastFocusableCell = null;
2714
+ while (cell < columns.length) {
2715
+ if (canCellBeActive(row, cell)) {
2716
+ lastFocusableCell = cell;
2717
+ }
2718
+ cell += getColspan(row, cell);
2719
+ }
2720
+ return lastFocusableCell;
2721
+ }
2722
+
2723
+ function gotoRight(row, cell, posX) {
2724
+ if (cell >= columns.length) {
2725
+ return null;
2726
+ }
2727
+
2728
+ do {
2729
+ cell += getColspan(row, cell);
2730
+ }
2731
+ while (cell < columns.length && !canCellBeActive(row, cell));
2732
+
2733
+ if (cell < columns.length) {
2734
+ return {
2735
+ "row": row,
2736
+ "cell": cell,
2737
+ "posX": cell
2738
+ };
2739
+ }
2740
+ return null;
2741
+ }
2742
+
2743
+ function gotoLeft(row, cell, posX) {
2744
+ if (cell <= 0) {
2745
+ return null;
2746
+ }
2747
+
2748
+ var firstFocusableCell = findFirstFocusableCell(row);
2749
+ if (firstFocusableCell === null || firstFocusableCell >= cell) {
2750
+ return null;
2751
+ }
2752
+
2753
+ var prev = {
2754
+ "row": row,
2755
+ "cell": firstFocusableCell,
2756
+ "posX": firstFocusableCell
2757
+ };
2758
+ var pos;
2759
+ while (true) {
2760
+ pos = gotoRight(prev.row, prev.cell, prev.posX);
2761
+ if (!pos) {
2762
+ return null;
2763
+ }
2764
+ if (pos.cell >= cell) {
2765
+ return prev;
2766
+ }
2767
+ prev = pos;
2768
+ }
2769
+ }
2770
+
2771
+ function gotoDown(row, cell, posX) {
2772
+ var prevCell;
2773
+ while (true) {
2774
+ if (++row >= getDataLength() + (options.enableAddRow ? 1 : 0)) {
2775
+ return null;
2776
+ }
2777
+
2778
+ prevCell = cell = 0;
2779
+ while (cell <= posX) {
2780
+ prevCell = cell;
2781
+ cell += getColspan(row, cell);
2782
+ }
2783
+
2784
+ if (canCellBeActive(row, prevCell)) {
2785
+ return {
2786
+ "row": row,
2787
+ "cell": prevCell,
2788
+ "posX": posX
2789
+ };
2790
+ }
2791
+ }
2792
+ }
2793
+
2794
+ function gotoUp(row, cell, posX) {
2795
+ var prevCell;
2796
+ while (true) {
2797
+ if (--row < 0) {
2798
+ return null;
2799
+ }
2800
+
2801
+ prevCell = cell = 0;
2802
+ while (cell <= posX) {
2803
+ prevCell = cell;
2804
+ cell += getColspan(row, cell);
2805
+ }
2806
+
2807
+ if (canCellBeActive(row, prevCell)) {
2808
+ return {
2809
+ "row": row,
2810
+ "cell": prevCell,
2811
+ "posX": posX
2812
+ };
2813
+ }
2814
+ }
2815
+ }
2816
+
2817
+ function gotoNext(row, cell, posX) {
2818
+ if (row == null && cell == null) {
2819
+ row = cell = posX = 0;
2820
+ if (canCellBeActive(row, cell)) {
2821
+ return {
2822
+ "row": row,
2823
+ "cell": cell,
2824
+ "posX": cell
2825
+ };
2826
+ }
2827
+ }
2828
+
2829
+ var pos = gotoRight(row, cell, posX);
2830
+ if (pos) {
2831
+ return pos;
2832
+ }
2833
+
2834
+ var firstFocusableCell = null;
2835
+ while (++row < getDataLength() + (options.enableAddRow ? 1 : 0)) {
2836
+ firstFocusableCell = findFirstFocusableCell(row);
2837
+ if (firstFocusableCell !== null) {
2838
+ return {
2839
+ "row": row,
2840
+ "cell": firstFocusableCell,
2841
+ "posX": firstFocusableCell
2842
+ };
2843
+ }
2844
+ }
2845
+ return null;
2846
+ }
2847
+
2848
+ function gotoPrev(row, cell, posX) {
2849
+ if (row == null && cell == null) {
2850
+ row = getDataLength() + (options.enableAddRow ? 1 : 0) - 1;
2851
+ cell = posX = columns.length - 1;
2852
+ if (canCellBeActive(row, cell)) {
2853
+ return {
2854
+ "row": row,
2855
+ "cell": cell,
2856
+ "posX": cell
2857
+ };
2858
+ }
2859
+ }
2860
+
2861
+ var pos;
2862
+ var lastSelectableCell;
2863
+ while (!pos) {
2864
+ pos = gotoLeft(row, cell, posX);
2865
+ if (pos) {
2866
+ break;
2867
+ }
2868
+ if (--row < 0) {
2869
+ return null;
2870
+ }
2871
+
2872
+ cell = 0;
2873
+ lastSelectableCell = findLastFocusableCell(row);
2874
+ if (lastSelectableCell !== null) {
2875
+ pos = {
2876
+ "row": row,
2877
+ "cell": lastSelectableCell,
2878
+ "posX": lastSelectableCell
2879
+ };
2880
+ }
2881
+ }
2882
+ return pos;
2883
+ }
2884
+
2885
+ function navigateRight() {
2886
+ return navigate("right");
2887
+ }
2888
+
2889
+ function navigateLeft() {
2890
+ return navigate("left");
2891
+ }
2892
+
2893
+ function navigateDown() {
2894
+ return navigate("down");
2895
+ }
2896
+
2897
+ function navigateUp() {
2898
+ return navigate("up");
2899
+ }
2900
+
2901
+ function navigateNext() {
2902
+ return navigate("next");
2903
+ }
2904
+
2905
+ function navigatePrev() {
2906
+ return navigate("prev");
2907
+ }
2908
+
2909
+ /**
2910
+ * @param {string} dir Navigation direction.
2911
+ * @return {boolean} Whether navigation resulted in a change of active cell.
2912
+ */
2913
+ function navigate(dir) {
2914
+ if (!options.enableCellNavigation) {
2915
+ return false;
2916
+ }
2917
+
2918
+ if (!activeCellNode && dir != "prev" && dir != "next") {
2919
+ return false;
2920
+ }
2921
+
2922
+ if (!getEditorLock().commitCurrentEdit()) {
2923
+ return true;
2924
+ }
2925
+ setFocus();
2926
+
2927
+ var tabbingDirections = {
2928
+ "up": -1,
2929
+ "down": 1,
2930
+ "left": -1,
2931
+ "right": 1,
2932
+ "prev": -1,
2933
+ "next": 1
2934
+ };
2935
+ tabbingDirection = tabbingDirections[dir];
2936
+
2937
+ var stepFunctions = {
2938
+ "up": gotoUp,
2939
+ "down": gotoDown,
2940
+ "left": gotoLeft,
2941
+ "right": gotoRight,
2942
+ "prev": gotoPrev,
2943
+ "next": gotoNext
2944
+ };
2945
+ var stepFn = stepFunctions[dir];
2946
+ var pos = stepFn(activeRow, activeCell, activePosX);
2947
+ if (pos) {
2948
+ var isAddNewRow = (pos.row == getDataLength());
2949
+ scrollCellIntoView(pos.row, pos.cell, !isAddNewRow);
2950
+ setActiveCellInternal(getCellNode(pos.row, pos.cell), isAddNewRow || options.autoEdit);
2951
+ activePosX = pos.posX;
2952
+ return true;
2953
+ } else {
2954
+ setActiveCellInternal(getCellNode(activeRow, activeCell), (activeRow == getDataLength()) || options.autoEdit);
2955
+ return false;
2956
+ }
2957
+ }
2958
+
2959
+ function getCellNode(row, cell) {
2960
+ if (rowsCache[row]) {
2961
+ ensureCellNodesInRowsCache(row);
2962
+ return rowsCache[row].cellNodesByColumnIdx[cell];
2963
+ }
2964
+ return null;
2965
+ }
2966
+
2967
+ function setActiveCell(row, cell) {
2968
+ if (!initialized) { return; }
2969
+ if (row > getDataLength() || row < 0 || cell >= columns.length || cell < 0) {
2970
+ return;
2971
+ }
2972
+
2973
+ if (!options.enableCellNavigation) {
2974
+ return;
2975
+ }
2976
+
2977
+ scrollCellIntoView(row, cell, false);
2978
+ setActiveCellInternal(getCellNode(row, cell), false);
2979
+ }
2980
+
2981
+ function canCellBeActive(row, cell) {
2982
+ if (!options.enableCellNavigation || row >= getDataLength() + (options.enableAddRow ? 1 : 0) ||
2983
+ row < 0 || cell >= columns.length || cell < 0) {
2984
+ return false;
2985
+ }
2986
+
2987
+ var rowMetadata = data.getItemMetadata && data.getItemMetadata(row);
2988
+ if (rowMetadata && typeof rowMetadata.focusable === "boolean") {
2989
+ return rowMetadata.focusable;
2990
+ }
2991
+
2992
+ var columnMetadata = rowMetadata && rowMetadata.columns;
2993
+ if (columnMetadata && columnMetadata[columns[cell].id] && typeof columnMetadata[columns[cell].id].focusable === "boolean") {
2994
+ return columnMetadata[columns[cell].id].focusable;
2995
+ }
2996
+ if (columnMetadata && columnMetadata[cell] && typeof columnMetadata[cell].focusable === "boolean") {
2997
+ return columnMetadata[cell].focusable;
2998
+ }
2999
+
3000
+ return columns[cell].focusable;
3001
+ }
3002
+
3003
+ function canCellBeSelected(row, cell) {
3004
+ if (row >= getDataLength() || row < 0 || cell >= columns.length || cell < 0) {
3005
+ return false;
3006
+ }
3007
+
3008
+ var rowMetadata = data.getItemMetadata && data.getItemMetadata(row);
3009
+ if (rowMetadata && typeof rowMetadata.selectable === "boolean") {
3010
+ return rowMetadata.selectable;
3011
+ }
3012
+
3013
+ var columnMetadata = rowMetadata && rowMetadata.columns && (rowMetadata.columns[columns[cell].id] || rowMetadata.columns[cell]);
3014
+ if (columnMetadata && typeof columnMetadata.selectable === "boolean") {
3015
+ return columnMetadata.selectable;
3016
+ }
3017
+
3018
+ return columns[cell].selectable;
3019
+ }
3020
+
3021
+ function gotoCell(row, cell, forceEdit) {
3022
+ if (!initialized) { return; }
3023
+ if (!canCellBeActive(row, cell)) {
3024
+ return;
3025
+ }
3026
+
3027
+ if (!getEditorLock().commitCurrentEdit()) {
3028
+ return;
3029
+ }
3030
+
3031
+ scrollCellIntoView(row, cell, false);
3032
+
3033
+ var newCell = getCellNode(row, cell);
3034
+
3035
+ // if selecting the 'add new' row, start editing right away
3036
+ setActiveCellInternal(newCell, forceEdit || (row === getDataLength()) || options.autoEdit);
3037
+
3038
+ // if no editor was created, set the focus back on the grid
3039
+ if (!currentEditor) {
3040
+ setFocus();
3041
+ }
3042
+ }
3043
+
3044
+
3045
+ //////////////////////////////////////////////////////////////////////////////////////////////
3046
+ // IEditor implementation for the editor lock
3047
+
3048
+ function commitCurrentEdit() {
3049
+ var item = getDataItem(activeRow);
3050
+ var column = columns[activeCell];
3051
+
3052
+ if (currentEditor) {
3053
+ if (currentEditor.isValueChanged()) {
3054
+ var validationResults = currentEditor.validate();
3055
+
3056
+ if (validationResults.valid) {
3057
+ if (activeRow < getDataLength()) {
3058
+ var editCommand = {
3059
+ row: activeRow,
3060
+ cell: activeCell,
3061
+ editor: currentEditor,
3062
+ serializedValue: currentEditor.serializeValue(),
3063
+ prevSerializedValue: serializedEditorValue,
3064
+ execute: function () {
3065
+ this.editor.applyValue(item, this.serializedValue);
3066
+ updateRow(this.row);
3067
+ },
3068
+ undo: function () {
3069
+ this.editor.applyValue(item, this.prevSerializedValue);
3070
+ updateRow(this.row);
3071
+ }
3072
+ };
3073
+
3074
+ if (options.editCommandHandler) {
3075
+ makeActiveCellNormal();
3076
+ options.editCommandHandler(item, column, editCommand);
3077
+ } else {
3078
+ editCommand.execute();
3079
+ makeActiveCellNormal();
3080
+ }
3081
+
3082
+ trigger(self.onCellChange, {
3083
+ row: activeRow,
3084
+ cell: activeCell,
3085
+ item: item
3086
+ });
3087
+ } else {
3088
+ var newItem = {};
3089
+ currentEditor.applyValue(newItem, currentEditor.serializeValue());
3090
+ makeActiveCellNormal();
3091
+ trigger(self.onAddNewRow, {item: newItem, column: column});
3092
+ }
3093
+
3094
+ // check whether the lock has been re-acquired by event handlers
3095
+ return !getEditorLock().isActive();
3096
+ } else {
3097
+ // TODO: remove and put in onValidationError handlers in examples
3098
+ $(activeCellNode).addClass("invalid");
3099
+ $(activeCellNode).stop(true, true).effect("highlight", {color: "red"}, 300);
3100
+
3101
+ trigger(self.onValidationError, {
3102
+ editor: currentEditor,
3103
+ cellNode: activeCellNode,
3104
+ validationResults: validationResults,
3105
+ row: activeRow,
3106
+ cell: activeCell,
3107
+ column: column
3108
+ });
3109
+
3110
+ currentEditor.focus();
3111
+ return false;
3112
+ }
3113
+ }
3114
+
3115
+ makeActiveCellNormal();
3116
+ }
3117
+ return true;
3118
+ }
3119
+
3120
+ function cancelCurrentEdit() {
3121
+ makeActiveCellNormal();
3122
+ return true;
3123
+ }
3124
+
3125
+ function rowsToRanges(rows) {
3126
+ var ranges = [];
3127
+ var lastCell = columns.length - 1;
3128
+ for (var i = 0; i < rows.length; i++) {
3129
+ ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
3130
+ }
3131
+ return ranges;
3132
+ }
3133
+
3134
+ function getSelectedRows() {
3135
+ if (!selectionModel) {
3136
+ throw "Selection model is not set";
3137
+ }
3138
+ return selectedRows;
3139
+ }
3140
+
3141
+ function setSelectedRows(rows) {
3142
+ if (!selectionModel) {
3143
+ throw "Selection model is not set";
3144
+ }
3145
+ selectionModel.setSelectedRanges(rowsToRanges(rows));
3146
+ }
3147
+
3148
+
3149
+ //////////////////////////////////////////////////////////////////////////////////////////////
3150
+ // Debug
3151
+
3152
+ this.debug = function () {
3153
+ var s = "";
3154
+
3155
+ s += ("\n" + "counter_rows_rendered: " + counter_rows_rendered);
3156
+ s += ("\n" + "counter_rows_removed: " + counter_rows_removed);
3157
+ s += ("\n" + "renderedRows: " + renderedRows);
3158
+ s += ("\n" + "numVisibleRows: " + numVisibleRows);
3159
+ s += ("\n" + "maxSupportedCssHeight: " + maxSupportedCssHeight);
3160
+ s += ("\n" + "n(umber of pages): " + n);
3161
+ s += ("\n" + "(current) page: " + page);
3162
+ s += ("\n" + "page height (ph): " + ph);
3163
+ s += ("\n" + "vScrollDir: " + vScrollDir);
3164
+
3165
+ alert(s);
3166
+ };
3167
+
3168
+ // a debug helper to be able to access private members
3169
+ this.eval = function (expr) {
3170
+ return eval(expr);
3171
+ };
3172
+
3173
+ //////////////////////////////////////////////////////////////////////////////////////////////
3174
+ // Public API
3175
+
3176
+ $.extend(this, {
3177
+ "slickGridVersion": "2.1",
3178
+
3179
+ // Events
3180
+ "onScroll": new Slick.Event(),
3181
+ "onSort": new Slick.Event(),
3182
+ "onHeaderMouseEnter": new Slick.Event(),
3183
+ "onHeaderMouseLeave": new Slick.Event(),
3184
+ "onHeaderContextMenu": new Slick.Event(),
3185
+ "onHeaderClick": new Slick.Event(),
3186
+ "onHeaderCellRendered": new Slick.Event(),
3187
+ "onBeforeHeaderCellDestroy": new Slick.Event(),
3188
+ "onHeaderRowCellRendered": new Slick.Event(),
3189
+ "onBeforeHeaderRowCellDestroy": new Slick.Event(),
3190
+ "onMouseEnter": new Slick.Event(),
3191
+ "onMouseLeave": new Slick.Event(),
3192
+ "onClick": new Slick.Event(),
3193
+ "onDblClick": new Slick.Event(),
3194
+ "onContextMenu": new Slick.Event(),
3195
+ "onKeyDown": new Slick.Event(),
3196
+ "onAddNewRow": new Slick.Event(),
3197
+ "onValidationError": new Slick.Event(),
3198
+ "onViewportChanged": new Slick.Event(),
3199
+ "onColumnsReordered": new Slick.Event(),
3200
+ "onColumnsResized": new Slick.Event(),
3201
+ "onCellChange": new Slick.Event(),
3202
+ "onBeforeEditCell": new Slick.Event(),
3203
+ "onBeforeCellEditorDestroy": new Slick.Event(),
3204
+ "onBeforeDestroy": new Slick.Event(),
3205
+ "onActiveCellChanged": new Slick.Event(),
3206
+ "onActiveCellPositionChanged": new Slick.Event(),
3207
+ "onDragInit": new Slick.Event(),
3208
+ "onDragStart": new Slick.Event(),
3209
+ "onDrag": new Slick.Event(),
3210
+ "onDragEnd": new Slick.Event(),
3211
+ "onSelectedRowsChanged": new Slick.Event(),
3212
+ "onCellCssStylesChanged": new Slick.Event(),
3213
+
3214
+ // Methods
3215
+ "registerPlugin": registerPlugin,
3216
+ "unregisterPlugin": unregisterPlugin,
3217
+ "getColumns": getColumns,
3218
+ "setColumns": setColumns,
3219
+ "getColumnIndex": getColumnIndex,
3220
+ "updateColumnHeader": updateColumnHeader,
3221
+ "setSortColumn": setSortColumn,
3222
+ "setSortColumns": setSortColumns,
3223
+ "getSortColumns": getSortColumns,
3224
+ "autosizeColumns": autosizeColumns,
3225
+ "getOptions": getOptions,
3226
+ "setOptions": setOptions,
3227
+ "getData": getData,
3228
+ "getDataLength": getDataLength,
3229
+ "getDataItem": getDataItem,
3230
+ "setData": setData,
3231
+ "getSelectionModel": getSelectionModel,
3232
+ "setSelectionModel": setSelectionModel,
3233
+ "getSelectedRows": getSelectedRows,
3234
+ "setSelectedRows": setSelectedRows,
3235
+
3236
+ "render": render,
3237
+ "invalidate": invalidate,
3238
+ "invalidateRow": invalidateRow,
3239
+ "invalidateRows": invalidateRows,
3240
+ "invalidateAllRows": invalidateAllRows,
3241
+ "updateCell": updateCell,
3242
+ "updateRow": updateRow,
3243
+ "getViewport": getVisibleRange,
3244
+ "getRenderedRange": getRenderedRange,
3245
+ "resizeCanvas": resizeCanvas,
3246
+ "updateRowCount": updateRowCount,
3247
+ "scrollRowIntoView": scrollRowIntoView,
3248
+ "scrollRowToTop": scrollRowToTop,
3249
+ "scrollCellIntoView": scrollCellIntoView,
3250
+ "getCanvasNode": getCanvasNode,
3251
+ "focus": setFocus,
3252
+
3253
+ "getCellFromPoint": getCellFromPoint,
3254
+ "getCellFromEvent": getCellFromEvent,
3255
+ "getActiveCell": getActiveCell,
3256
+ "setActiveCell": setActiveCell,
3257
+ "getActiveCellNode": getActiveCellNode,
3258
+ "getActiveCellPosition": getActiveCellPosition,
3259
+ "resetActiveCell": resetActiveCell,
3260
+ "editActiveCell": makeActiveCellEditable,
3261
+ "getCellEditor": getCellEditor,
3262
+ "getCellNode": getCellNode,
3263
+ "getCellNodeBox": getCellNodeBox,
3264
+ "canCellBeSelected": canCellBeSelected,
3265
+ "canCellBeActive": canCellBeActive,
3266
+ "navigatePrev": navigatePrev,
3267
+ "navigateNext": navigateNext,
3268
+ "navigateUp": navigateUp,
3269
+ "navigateDown": navigateDown,
3270
+ "navigateLeft": navigateLeft,
3271
+ "navigateRight": navigateRight,
3272
+ "gotoCell": gotoCell,
3273
+ "getTopPanel": getTopPanel,
3274
+ "setTopPanelVisibility": setTopPanelVisibility,
3275
+ "setHeaderRowVisibility": setHeaderRowVisibility,
3276
+ "getHeaderRow": getHeaderRow,
3277
+ "getHeaderRowColumn": getHeaderRowColumn,
3278
+ "getGridPosition": getGridPosition,
3279
+ "flashCell": flashCell,
3280
+ "addCellCssStyles": addCellCssStyles,
3281
+ "setCellCssStyles": setCellCssStyles,
3282
+ "removeCellCssStyles": removeCellCssStyles,
3283
+ "getCellCssStyles": getCellCssStyles,
3284
+
3285
+ "init": finishInitialization,
3286
+ "destroy": destroy,
3287
+
3288
+ // IEditor implementation
3289
+ "getEditorLock": getEditorLock,
3290
+ "getEditController": getEditController
3291
+ });
3292
+
3293
+ init();
3294
+ }
3295
+ }(jQuery));