slickgrid-requirejs-rails 1.0.0

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