slickgrid-requirejs-rails 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,121 @@
1
+ // Universal module definition
2
+ (function (root, factory) {
3
+ if (typeof define === 'function' && define.amd) {
4
+ // AMD. Register as an anonymous module.
5
+ define(factory);
6
+ } else {
7
+ // Browser globals
8
+ root.Slick.Data = root.Slick.Data || {};
9
+ root.Slick.Data.Aggregators = factory();
10
+ }
11
+ }(this, function () {
12
+
13
+ function AvgAggregator(field) {
14
+ this.field_ = field;
15
+
16
+ this.init = function () {
17
+ this.count_ = 0;
18
+ this.nonNullCount_ = 0;
19
+ this.sum_ = 0;
20
+ };
21
+
22
+ this.accumulate = function (item) {
23
+ var val = item[this.field_];
24
+ this.count_ ++;
25
+ if (val != null && val != "" && val != NaN) {
26
+ this.nonNullCount_ ++;
27
+ this.sum_ += parseFloat(val);
28
+ }
29
+ };
30
+
31
+ this.storeResult = function (groupTotals) {
32
+ if (! groupTotals.avg) {
33
+ groupTotals.avg = {};
34
+ }
35
+ if (this.nonNullCount_ != 0) {
36
+ groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_;
37
+ }
38
+ };
39
+ }
40
+
41
+ function MinAggregator(field) {
42
+ this.field_ = field;
43
+
44
+ this.init = function () {
45
+ this.min_ = null;
46
+ };
47
+
48
+ this.accumulate = function (item) {
49
+ var val = item[this.field_];
50
+ if (val != null && val != "" && val != NaN) {
51
+ if (this.min_ == null || val < this.min_) {
52
+ this.min_ = val;
53
+ }
54
+ }
55
+ };
56
+
57
+ this.storeResult = function (groupTotals) {
58
+ if (! groupTotals.min) {
59
+ groupTotals.min = {};
60
+ }
61
+ groupTotals.min[this.field_] = this.min_;
62
+ }
63
+ }
64
+
65
+ function MaxAggregator(field) {
66
+ this.field_ = field;
67
+
68
+ this.init = function () {
69
+ this.max_ = null;
70
+ };
71
+
72
+ this.accumulate = function (item) {
73
+ var val = item[this.field_];
74
+ if (val != null && val != "" && val != NaN) {
75
+ if (this.max_ == null || val > this.max_) {
76
+ this.max_ = val;
77
+ }
78
+ }
79
+ };
80
+
81
+ this.storeResult = function (groupTotals) {
82
+ if (! groupTotals.max) {
83
+ groupTotals.max = {};
84
+ }
85
+ groupTotals.max[this.field_] = this.max_;
86
+ }
87
+ }
88
+
89
+ function SumAggregator(field) {
90
+ this.field_ = field;
91
+
92
+ this.init = function () {
93
+ this.sum_ = null;
94
+ };
95
+
96
+ this.accumulate = function (item) {
97
+ var val = item[this.field_];
98
+ if (val != null && val != "" && val != NaN) {
99
+ this.sum_ += parseFloat(val);
100
+ }
101
+ };
102
+
103
+ this.storeResult = function (groupTotals) {
104
+ if (! groupTotals.sum) {
105
+ groupTotals.sum = {};
106
+ }
107
+ groupTotals.sum[this.field_] = this.sum_;
108
+ }
109
+ }
110
+
111
+ // TODO: add more built-in aggregators
112
+ // TODO: merge common aggregators in one to prevent needles iterating
113
+
114
+ return {
115
+ Avg: AvgAggregator,
116
+ Min: MinAggregator,
117
+ Max: MaxAggregator,
118
+ Sum: SumAggregator
119
+ };
120
+
121
+ }));
@@ -0,0 +1,1061 @@
1
+ // Universal module definition
2
+ (function (root, factory) {
3
+ if (typeof define === 'function' && define.amd) {
4
+ // AMD. Register as an anonymous module.
5
+ define(['jquery', './slick.core', './slick.groupitemmetadataprovider'], factory);
6
+ } else {
7
+ // Browser globals
8
+ root.Slick.Data = root.Slick.Data || {};
9
+ root.Slick.Data.DataView = factory(root.jQuery, root.Slick, root.Slick.Data.GroupItemMetadataProvider);
10
+ }
11
+ }(this, function ($, Slick, GroupItemMetadataProvider) {
12
+
13
+
14
+ /***
15
+ * A sample Model implementation.
16
+ * Provides a filtered view of the underlying data.
17
+ *
18
+ * Relies on the data item having an "id" property uniquely identifying it.
19
+ */
20
+ function DataView(options) {
21
+ var self = this;
22
+
23
+ var defaults = {
24
+ groupItemMetadataProvider: null,
25
+ inlineFilters: false
26
+ };
27
+
28
+
29
+ // private
30
+ var idProperty = "id"; // property holding a unique row id
31
+ var items = []; // data by index
32
+ var rows = []; // data by row
33
+ var idxById = {}; // indexes by id
34
+ var rowsById = null; // rows by id; lazy-calculated
35
+ var filter = null; // filter function
36
+ var updated = null; // updated item ids
37
+ var suspend = false; // suspends the recalculation
38
+ var sortAsc = true;
39
+ var fastSortField;
40
+ var sortComparer;
41
+ var refreshHints = {};
42
+ var prevRefreshHints = {};
43
+ var filterArgs;
44
+ var filteredItems = [];
45
+ var compiledFilter;
46
+ var compiledFilterWithCaching;
47
+ var filterCache = [];
48
+
49
+ // grouping
50
+ var groupingInfoDefaults = {
51
+ getter: null,
52
+ formatter: null,
53
+ comparer: function(a, b) { return a.value - b.value; },
54
+ predefinedValues: [],
55
+ aggregators: [],
56
+ aggregateEmpty: false,
57
+ aggregateCollapsed: false,
58
+ aggregateChildGroups: false,
59
+ collapsed: false,
60
+ displayTotalsRow: true
61
+ };
62
+ var groupingInfos = [];
63
+ var groups = [];
64
+ var toggledGroupsByLevel = [];
65
+ var groupingDelimiter = ':|:';
66
+
67
+ var pagesize = 0;
68
+ var pagenum = 0;
69
+ var totalRows = 0;
70
+
71
+ // events
72
+ var onRowCountChanged = new Slick.Event();
73
+ var onRowsChanged = new Slick.Event();
74
+ var onPagingInfoChanged = new Slick.Event();
75
+
76
+ options = $.extend(true, {}, defaults, options);
77
+
78
+
79
+ function beginUpdate() {
80
+ suspend = true;
81
+ }
82
+
83
+ function endUpdate() {
84
+ suspend = false;
85
+ refresh();
86
+ }
87
+
88
+ function setRefreshHints(hints) {
89
+ refreshHints = hints;
90
+ }
91
+
92
+ function setFilterArgs(args) {
93
+ filterArgs = args;
94
+ }
95
+
96
+ function updateIdxById(startingIndex) {
97
+ startingIndex = startingIndex || 0;
98
+ var id;
99
+ for (var i = startingIndex, l = items.length; i < l; i++) {
100
+ id = items[i][idProperty];
101
+ if (id === undefined) {
102
+ throw "Each data element must implement a unique 'id' property";
103
+ }
104
+ idxById[id] = i;
105
+ }
106
+ }
107
+
108
+ function ensureIdUniqueness() {
109
+ var id;
110
+ for (var i = 0, l = items.length; i < l; i++) {
111
+ id = items[i][idProperty];
112
+ if (id === undefined || idxById[id] !== i) {
113
+ throw "Each data element must implement a unique 'id' property";
114
+ }
115
+ }
116
+ }
117
+
118
+ function getItems() {
119
+ return items;
120
+ }
121
+
122
+ function setItems(data, objectIdProperty) {
123
+ if (objectIdProperty !== undefined) {
124
+ idProperty = objectIdProperty;
125
+ }
126
+ items = filteredItems = data;
127
+ idxById = {};
128
+ updateIdxById();
129
+ ensureIdUniqueness();
130
+ refresh();
131
+ }
132
+
133
+ function setPagingOptions(args) {
134
+ if (args.pageSize != undefined) {
135
+ pagesize = args.pageSize;
136
+ pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0;
137
+ }
138
+
139
+ if (args.pageNum != undefined) {
140
+ pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1));
141
+ }
142
+
143
+ onPagingInfoChanged.notify(getPagingInfo(), null, self);
144
+
145
+ refresh();
146
+ }
147
+
148
+ function getPagingInfo() {
149
+ var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1;
150
+ return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages};
151
+ }
152
+
153
+ function sort(comparer, ascending) {
154
+ sortAsc = ascending;
155
+ sortComparer = comparer;
156
+ fastSortField = null;
157
+ if (ascending === false) {
158
+ items.reverse();
159
+ }
160
+ items.sort(comparer);
161
+ if (ascending === false) {
162
+ items.reverse();
163
+ }
164
+ idxById = {};
165
+ updateIdxById();
166
+ refresh();
167
+ }
168
+
169
+ /***
170
+ * Provides a workaround for the extremely slow sorting in IE.
171
+ * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString
172
+ * to return the value of that field and then doing a native Array.sort().
173
+ */
174
+ function fastSort(field, ascending) {
175
+ sortAsc = ascending;
176
+ fastSortField = field;
177
+ sortComparer = null;
178
+ var oldToString = Object.prototype.toString;
179
+ Object.prototype.toString = (typeof field == "function") ? field : function () {
180
+ return this[field]
181
+ };
182
+ // an extra reversal for descending sort keeps the sort stable
183
+ // (assuming a stable native sort implementation, which isn't true in some cases)
184
+ if (ascending === false) {
185
+ items.reverse();
186
+ }
187
+ items.sort();
188
+ Object.prototype.toString = oldToString;
189
+ if (ascending === false) {
190
+ items.reverse();
191
+ }
192
+ idxById = {};
193
+ updateIdxById();
194
+ refresh();
195
+ }
196
+
197
+ function reSort() {
198
+ if (sortComparer) {
199
+ sort(sortComparer, sortAsc);
200
+ } else if (fastSortField) {
201
+ fastSort(fastSortField, sortAsc);
202
+ }
203
+ }
204
+
205
+ function setFilter(filterFn) {
206
+ filter = filterFn;
207
+ if (options.inlineFilters) {
208
+ compiledFilter = compileFilter();
209
+ compiledFilterWithCaching = compileFilterWithCaching();
210
+ }
211
+ refresh();
212
+ }
213
+
214
+ function getGrouping() {
215
+ return groupingInfos;
216
+ }
217
+
218
+ function setGrouping(groupingInfo) {
219
+ if (!options.groupItemMetadataProvider) {
220
+ options.groupItemMetadataProvider = new GroupItemMetadataProvider();
221
+ }
222
+
223
+ groups = [];
224
+ toggledGroupsByLevel = [];
225
+ groupingInfo = groupingInfo || [];
226
+ groupingInfos = (groupingInfo instanceof Array) ? groupingInfo : [groupingInfo];
227
+
228
+ for (var i = 0; i < groupingInfos.length; i++) {
229
+ var gi = groupingInfos[i] = $.extend(true, {}, groupingInfoDefaults, groupingInfos[i]);
230
+ gi.getterIsAFn = typeof gi.getter === "function";
231
+
232
+ // pre-compile accumulator loops
233
+ gi.compiledAccumulators = [];
234
+ var idx = gi.aggregators.length;
235
+ while (idx--) {
236
+ gi.compiledAccumulators[idx] = compileAccumulatorLoop(gi.aggregators[idx]);
237
+ }
238
+
239
+ toggledGroupsByLevel[i] = {};
240
+ }
241
+
242
+ refresh();
243
+ }
244
+
245
+ /**
246
+ * @deprecated Please use {@link setGrouping}.
247
+ */
248
+ function groupBy(valueGetter, valueFormatter, sortComparer) {
249
+ if (valueGetter == null) {
250
+ setGrouping([]);
251
+ return;
252
+ }
253
+
254
+ setGrouping({
255
+ getter: valueGetter,
256
+ formatter: valueFormatter,
257
+ comparer: sortComparer
258
+ });
259
+ }
260
+
261
+ /**
262
+ * @deprecated Please use {@link setGrouping}.
263
+ */
264
+ function setAggregators(groupAggregators, includeCollapsed) {
265
+ if (!groupingInfos.length) {
266
+ throw new Error("At least one grouping must be specified before calling setAggregators().");
267
+ }
268
+
269
+ groupingInfos[0].aggregators = groupAggregators;
270
+ groupingInfos[0].aggregateCollapsed = includeCollapsed;
271
+
272
+ setGrouping(groupingInfos);
273
+ }
274
+
275
+ function getItemByIdx(i) {
276
+ return items[i];
277
+ }
278
+
279
+ function getIdxById(id) {
280
+ return idxById[id];
281
+ }
282
+
283
+ function ensureRowsByIdCache() {
284
+ if (!rowsById) {
285
+ rowsById = {};
286
+ for (var i = 0, l = rows.length; i < l; i++) {
287
+ rowsById[rows[i][idProperty]] = i;
288
+ }
289
+ }
290
+ }
291
+
292
+ function getRowById(id) {
293
+ ensureRowsByIdCache();
294
+ return rowsById[id];
295
+ }
296
+
297
+ function getItemById(id) {
298
+ return items[idxById[id]];
299
+ }
300
+
301
+ function mapIdsToRows(idArray) {
302
+ var rows = [];
303
+ ensureRowsByIdCache();
304
+ for (var i = 0; i < idArray.length; i++) {
305
+ var row = rowsById[idArray[i]];
306
+ if (row != null) {
307
+ rows[rows.length] = row;
308
+ }
309
+ }
310
+ return rows;
311
+ }
312
+
313
+ function mapRowsToIds(rowArray) {
314
+ var ids = [];
315
+ for (var i = 0; i < rowArray.length; i++) {
316
+ if (rowArray[i] < rows.length) {
317
+ ids[ids.length] = rows[rowArray[i]][idProperty];
318
+ }
319
+ }
320
+ return ids;
321
+ }
322
+
323
+ function updateItem(id, item) {
324
+ if (idxById[id] === undefined || id !== item[idProperty]) {
325
+ throw "Invalid or non-matching id";
326
+ }
327
+ items[idxById[id]] = item;
328
+ if (!updated) {
329
+ updated = {};
330
+ }
331
+ updated[id] = true;
332
+ refresh();
333
+ }
334
+
335
+ function insertItem(insertBefore, item) {
336
+ items.splice(insertBefore, 0, item);
337
+ updateIdxById(insertBefore);
338
+ refresh();
339
+ }
340
+
341
+ function addItem(item) {
342
+ items.push(item);
343
+ updateIdxById(items.length - 1);
344
+ refresh();
345
+ }
346
+
347
+ function deleteItem(id) {
348
+ var idx = idxById[id];
349
+ if (idx === undefined) {
350
+ throw "Invalid id";
351
+ }
352
+ delete idxById[id];
353
+ items.splice(idx, 1);
354
+ updateIdxById(idx);
355
+ refresh();
356
+ }
357
+
358
+ function getLength() {
359
+ return rows.length;
360
+ }
361
+
362
+ function getItem(i) {
363
+ return rows[i];
364
+ }
365
+
366
+ function getItemMetadata(i) {
367
+ var item = rows[i];
368
+ if (item === undefined) {
369
+ return null;
370
+ }
371
+
372
+ // overrides for grouping rows
373
+ if (item.__group) {
374
+ return options.groupItemMetadataProvider.getGroupRowMetadata(item);
375
+ }
376
+
377
+ // overrides for totals rows
378
+ if (item.__groupTotals) {
379
+ return options.groupItemMetadataProvider.getTotalsRowMetadata(item);
380
+ }
381
+
382
+ return null;
383
+ }
384
+
385
+ function expandCollapseAllGroups(level, collapse) {
386
+ if (level == null) {
387
+ for (var i = 0; i < groupingInfos.length; i++) {
388
+ toggledGroupsByLevel[i] = {};
389
+ groupingInfos[i].collapsed = collapse;
390
+ }
391
+ } else {
392
+ toggledGroupsByLevel[level] = {};
393
+ groupingInfos[level].collapsed = collapse;
394
+ }
395
+ refresh();
396
+ }
397
+
398
+ /**
399
+ * @param level {Number} Optional level to collapse. If not specified, applies to all levels.
400
+ */
401
+ function collapseAllGroups(level) {
402
+ expandCollapseAllGroups(level, true);
403
+ }
404
+
405
+ /**
406
+ * @param level {Number} Optional level to expand. If not specified, applies to all levels.
407
+ */
408
+ function expandAllGroups(level) {
409
+ expandCollapseAllGroups(level, false);
410
+ }
411
+
412
+ function expandCollapseGroup(level, groupingKey, collapse) {
413
+ toggledGroupsByLevel[level][groupingKey] = groupingInfos[level].collapsed ^ collapse;
414
+ refresh();
415
+ }
416
+
417
+ /**
418
+ * @param varArgs Either a Slick.Group's "groupingKey" property, or a
419
+ * variable argument list of grouping values denoting a unique path to the row. For
420
+ * example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of
421
+ * the 'high' setGrouping.
422
+ */
423
+ function collapseGroup(varArgs) {
424
+ var args = Array.prototype.slice.call(arguments);
425
+ var arg0 = args[0];
426
+ if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
427
+ expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, true);
428
+ } else {
429
+ expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), true);
430
+ }
431
+ }
432
+
433
+ /**
434
+ * @param varArgs Either a Slick.Group's "groupingKey" property, or a
435
+ * variable argument list of grouping values denoting a unique path to the row. For
436
+ * example, calling expandGroup('high', '10%') will expand the '10%' subgroup of
437
+ * the 'high' setGrouping.
438
+ */
439
+ function expandGroup(varArgs) {
440
+ var args = Array.prototype.slice.call(arguments);
441
+ var arg0 = args[0];
442
+ if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
443
+ expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, false);
444
+ } else {
445
+ expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), false);
446
+ }
447
+ }
448
+
449
+ function getGroups() {
450
+ return groups;
451
+ }
452
+
453
+ function extractGroups(rows, parentGroup) {
454
+ var group;
455
+ var val;
456
+ var groups = [];
457
+ var groupsByVal = [];
458
+ var r;
459
+ var level = parentGroup ? parentGroup.level + 1 : 0;
460
+ var gi = groupingInfos[level];
461
+
462
+ for (var i = 0, l = gi.predefinedValues.length; i < l; i++) {
463
+ val = gi.predefinedValues[i];
464
+ group = groupsByVal[val];
465
+ if (!group) {
466
+ group = new Slick.Group();
467
+ group.value = val;
468
+ group.level = level;
469
+ group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
470
+ groups[groups.length] = group;
471
+ groupsByVal[val] = group;
472
+ }
473
+ }
474
+
475
+ for (var i = 0, l = rows.length; i < l; i++) {
476
+ r = rows[i];
477
+ val = gi.getterIsAFn ? gi.getter(r) : r[gi.getter];
478
+ group = groupsByVal[val];
479
+ if (!group) {
480
+ group = new Slick.Group();
481
+ group.value = val;
482
+ group.level = level;
483
+ group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
484
+ groups[groups.length] = group;
485
+ groupsByVal[val] = group;
486
+ }
487
+
488
+ group.rows[group.count++] = r;
489
+ }
490
+
491
+ if (level < groupingInfos.length - 1) {
492
+ for (var i = 0; i < groups.length; i++) {
493
+ group = groups[i];
494
+ group.groups = extractGroups(group.rows, group);
495
+ }
496
+ }
497
+
498
+ groups.sort(groupingInfos[level].comparer);
499
+
500
+ return groups;
501
+ }
502
+
503
+ // TODO: lazy totals calculation
504
+ function calculateGroupTotals(group) {
505
+ // TODO: try moving iterating over groups into compiled accumulator
506
+ var gi = groupingInfos[group.level];
507
+ var isLeafLevel = (group.level == groupingInfos.length);
508
+ var totals = new Slick.GroupTotals();
509
+ var agg, idx = gi.aggregators.length;
510
+ while (idx--) {
511
+ agg = gi.aggregators[idx];
512
+ agg.init();
513
+ gi.compiledAccumulators[idx].call(agg,
514
+ (!isLeafLevel && gi.aggregateChildGroups) ? group.groups : group.rows);
515
+ agg.storeResult(totals);
516
+ }
517
+ totals.group = group;
518
+ group.totals = totals;
519
+ }
520
+
521
+ function calculateTotals(groups, level) {
522
+ level = level || 0;
523
+ var gi = groupingInfos[level];
524
+ var idx = groups.length, g;
525
+ while (idx--) {
526
+ g = groups[idx];
527
+
528
+ if (g.collapsed && !gi.aggregateCollapsed) {
529
+ continue;
530
+ }
531
+
532
+ // Do a depth-first aggregation so that parent setGrouping aggregators can access subgroup totals.
533
+ if (g.groups) {
534
+ calculateTotals(g.groups, level + 1);
535
+ }
536
+
537
+ if (gi.aggregators.length && (
538
+ gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.length))) {
539
+ calculateGroupTotals(g);
540
+ }
541
+ }
542
+ }
543
+
544
+ function finalizeGroups(groups, level) {
545
+ level = level || 0;
546
+ var gi = groupingInfos[level];
547
+ var groupCollapsed = gi.collapsed;
548
+ var toggledGroups = toggledGroupsByLevel[level];
549
+ var idx = groups.length, g;
550
+ while (idx--) {
551
+ g = groups[idx];
552
+ g.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey];
553
+ g.title = gi.formatter ? gi.formatter(g) : g.value;
554
+
555
+ if (g.groups) {
556
+ finalizeGroups(g.groups, level + 1);
557
+ // Let the non-leaf setGrouping rows get garbage-collected.
558
+ // They may have been used by aggregates that go over all of the descendants,
559
+ // but at this point they are no longer needed.
560
+ g.rows = [];
561
+ }
562
+ }
563
+ }
564
+
565
+ function flattenGroupedRows(groups, level) {
566
+ level = level || 0;
567
+ var gi = groupingInfos[level];
568
+ var groupedRows = [], rows, gl = 0, g;
569
+ for (var i = 0, l = groups.length; i < l; i++) {
570
+ g = groups[i];
571
+ groupedRows[gl++] = g;
572
+
573
+ if (!g.collapsed) {
574
+ rows = g.groups ? flattenGroupedRows(g.groups, level + 1) : g.rows;
575
+ for (var j = 0, jj = rows.length; j < jj; j++) {
576
+ groupedRows[gl++] = rows[j];
577
+ }
578
+ }
579
+
580
+ if (g.totals && gi.displayTotalsRow && (!g.collapsed || gi.aggregateCollapsed)) {
581
+ groupedRows[gl++] = g.totals;
582
+ }
583
+ }
584
+ return groupedRows;
585
+ }
586
+
587
+ function getFunctionInfo(fn) {
588
+ var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/;
589
+ var matches = fn.toString().match(fnRegex);
590
+ return {
591
+ params: matches[1].split(","),
592
+ body: matches[2]
593
+ };
594
+ }
595
+
596
+ function compileAccumulatorLoop(aggregator) {
597
+ var accumulatorInfo = getFunctionInfo(aggregator.accumulate);
598
+ var fn = new Function(
599
+ "_items",
600
+ "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" +
601
+ accumulatorInfo.params[0] + " = _items[_i]; " +
602
+ accumulatorInfo.body +
603
+ "}"
604
+ );
605
+ fn.displayName = fn.name = "compiledAccumulatorLoop";
606
+ return fn;
607
+ }
608
+
609
+ function compileFilter() {
610
+ var filterInfo = getFunctionInfo(filter);
611
+
612
+ var filterBody = filterInfo.body
613
+ .replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1")
614
+ .replace(/return true\s*([;}]|$)/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }$1")
615
+ .replace(/return ([^;}]+?)\s*([;}]|$)/gi,
616
+ "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }$2");
617
+
618
+ // This preserves the function template code after JS compression,
619
+ // so that replace() commands still work as expected.
620
+ var tpl = [
621
+ //"function(_items, _args) { ",
622
+ "var _retval = [], _idx = 0; ",
623
+ "var $item$, $args$ = _args; ",
624
+ "_coreloop: ",
625
+ "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
626
+ "$item$ = _items[_i]; ",
627
+ "$filter$; ",
628
+ "} ",
629
+ "return _retval; "
630
+ //"}"
631
+ ].join("");
632
+ tpl = tpl.replace(/\$filter\$/gi, filterBody);
633
+ tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
634
+ tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
635
+
636
+ var fn = new Function("_items,_args", tpl);
637
+ fn.displayName = fn.name = "compiledFilter";
638
+ return fn;
639
+ }
640
+
641
+ function compileFilterWithCaching() {
642
+ var filterInfo = getFunctionInfo(filter);
643
+
644
+ var filterBody = filterInfo.body
645
+ .replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1")
646
+ .replace(/return true\s*([;}]|$)/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }$1")
647
+ .replace(/return ([^;}]+?)\s*([;}]|$)/gi,
648
+ "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }$2");
649
+
650
+ // This preserves the function template code after JS compression,
651
+ // so that replace() commands still work as expected.
652
+ var tpl = [
653
+ //"function(_items, _args, _cache) { ",
654
+ "var _retval = [], _idx = 0; ",
655
+ "var $item$, $args$ = _args; ",
656
+ "_coreloop: ",
657
+ "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
658
+ "$item$ = _items[_i]; ",
659
+ "if (_cache[_i]) { ",
660
+ "_retval[_idx++] = $item$; ",
661
+ "continue _coreloop; ",
662
+ "} ",
663
+ "$filter$; ",
664
+ "} ",
665
+ "return _retval; "
666
+ //"}"
667
+ ].join("");
668
+ tpl = tpl.replace(/\$filter\$/gi, filterBody);
669
+ tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
670
+ tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
671
+
672
+ var fn = new Function("_items,_args,_cache", tpl);
673
+ fn.displayName = fn.name = "compiledFilterWithCaching";
674
+ return fn;
675
+ }
676
+
677
+ function uncompiledFilter(items, args) {
678
+ var retval = [], idx = 0;
679
+
680
+ for (var i = 0, ii = items.length; i < ii; i++) {
681
+ if (filter(items[i], args)) {
682
+ retval[idx++] = items[i];
683
+ }
684
+ }
685
+
686
+ return retval;
687
+ }
688
+
689
+ function uncompiledFilterWithCaching(items, args, cache) {
690
+ var retval = [], idx = 0, item;
691
+
692
+ for (var i = 0, ii = items.length; i < ii; i++) {
693
+ item = items[i];
694
+ if (cache[i]) {
695
+ retval[idx++] = item;
696
+ } else if (filter(item, args)) {
697
+ retval[idx++] = item;
698
+ cache[i] = true;
699
+ }
700
+ }
701
+
702
+ return retval;
703
+ }
704
+
705
+ function getFilteredAndPagedItems(items) {
706
+ if (filter) {
707
+ var batchFilter = options.inlineFilters ? compiledFilter : uncompiledFilter;
708
+ var batchFilterWithCaching = options.inlineFilters ? compiledFilterWithCaching : uncompiledFilterWithCaching;
709
+
710
+ if (refreshHints.isFilterNarrowing) {
711
+ filteredItems = batchFilter(filteredItems, filterArgs);
712
+ } else if (refreshHints.isFilterExpanding) {
713
+ filteredItems = batchFilterWithCaching(items, filterArgs, filterCache);
714
+ } else if (!refreshHints.isFilterUnchanged) {
715
+ filteredItems = batchFilter(items, filterArgs);
716
+ }
717
+ } else {
718
+ // special case: if not filtering and not paging, the resulting
719
+ // rows collection needs to be a copy so that changes due to sort
720
+ // can be caught
721
+ filteredItems = pagesize ? items : items.concat();
722
+ }
723
+
724
+ // get the current page
725
+ var paged;
726
+ if (pagesize) {
727
+ if (filteredItems.length < pagenum * pagesize) {
728
+ pagenum = Math.floor(filteredItems.length / pagesize);
729
+ }
730
+ paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize);
731
+ } else {
732
+ paged = filteredItems;
733
+ }
734
+
735
+ return {totalRows: filteredItems.length, rows: paged};
736
+ }
737
+
738
+ function getRowDiffs(rows, newRows) {
739
+ var item, r, eitherIsNonData, diff = [];
740
+ var from = 0, to = newRows.length;
741
+
742
+ if (refreshHints && refreshHints.ignoreDiffsBefore) {
743
+ from = Math.max(0,
744
+ Math.min(newRows.length, refreshHints.ignoreDiffsBefore));
745
+ }
746
+
747
+ if (refreshHints && refreshHints.ignoreDiffsAfter) {
748
+ to = Math.min(newRows.length,
749
+ Math.max(0, refreshHints.ignoreDiffsAfter));
750
+ }
751
+
752
+ for (var i = from, rl = rows.length; i < to; i++) {
753
+ if (i >= rl) {
754
+ diff[diff.length] = i;
755
+ } else {
756
+ item = newRows[i];
757
+ r = rows[i];
758
+
759
+ if ((groupingInfos.length && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) &&
760
+ item.__group !== r.__group ||
761
+ item.__group && !item.equals(r))
762
+ || (eitherIsNonData &&
763
+ // no good way to compare totals since they are arbitrary DTOs
764
+ // deep object comparison is pretty expensive
765
+ // always considering them 'dirty' seems easier for the time being
766
+ (item.__groupTotals || r.__groupTotals))
767
+ || item[idProperty] != r[idProperty]
768
+ || (updated && updated[item[idProperty]])
769
+ ) {
770
+ diff[diff.length] = i;
771
+ }
772
+ }
773
+ }
774
+ return diff;
775
+ }
776
+
777
+ function recalc(_items) {
778
+ rowsById = null;
779
+
780
+ if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing ||
781
+ refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) {
782
+ filterCache = [];
783
+ }
784
+
785
+ var filteredItems = getFilteredAndPagedItems(_items);
786
+ totalRows = filteredItems.totalRows;
787
+ var newRows = filteredItems.rows;
788
+
789
+ groups = [];
790
+ if (groupingInfos.length) {
791
+ groups = extractGroups(newRows);
792
+ if (groups.length) {
793
+ calculateTotals(groups);
794
+ finalizeGroups(groups);
795
+ newRows = flattenGroupedRows(groups);
796
+ }
797
+ }
798
+
799
+ var diff = getRowDiffs(rows, newRows);
800
+
801
+ rows = newRows;
802
+
803
+ return diff;
804
+ }
805
+
806
+ function refresh() {
807
+ if (suspend) {
808
+ return;
809
+ }
810
+
811
+ var countBefore = rows.length;
812
+ var totalRowsBefore = totalRows;
813
+
814
+ var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit
815
+
816
+ // if the current page is no longer valid, go to last page and recalc
817
+ // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized
818
+ if (pagesize && totalRows < pagenum * pagesize) {
819
+ pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1);
820
+ diff = recalc(items, filter);
821
+ }
822
+
823
+ updated = null;
824
+ prevRefreshHints = refreshHints;
825
+ refreshHints = {};
826
+
827
+ if (totalRowsBefore != totalRows) {
828
+ onPagingInfoChanged.notify(getPagingInfo(), null, self);
829
+ }
830
+ if (countBefore != rows.length) {
831
+ onRowCountChanged.notify({previous: countBefore, current: rows.length}, null, self);
832
+ }
833
+ if (diff.length > 0) {
834
+ onRowsChanged.notify({rows: diff}, null, self);
835
+ }
836
+ }
837
+
838
+ function syncGridSelection(grid, preserveHidden) {
839
+ var self = this;
840
+ var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());;
841
+ var inHandler;
842
+
843
+ function update() {
844
+ if (selectedRowIds.length > 0) {
845
+ inHandler = true;
846
+ var selectedRows = self.mapIdsToRows(selectedRowIds);
847
+ if (!preserveHidden) {
848
+ selectedRowIds = self.mapRowsToIds(selectedRows);
849
+ }
850
+ grid.setSelectedRows(selectedRows);
851
+ inHandler = false;
852
+ }
853
+ }
854
+
855
+ grid.onSelectedRowsChanged.subscribe(function(e, args) {
856
+ if (inHandler) { return; }
857
+ selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
858
+ });
859
+
860
+ this.onRowsChanged.subscribe(update);
861
+
862
+ this.onRowCountChanged.subscribe(update);
863
+ }
864
+
865
+ function syncGridCellCssStyles(grid, key) {
866
+ var hashById;
867
+ var inHandler;
868
+
869
+ // since this method can be called after the cell styles have been set,
870
+ // get the existing ones right away
871
+ storeCellCssStyles(grid.getCellCssStyles(key));
872
+
873
+ function storeCellCssStyles(hash) {
874
+ hashById = {};
875
+ for (var row in hash) {
876
+ var id = rows[row][idProperty];
877
+ hashById[id] = hash[row];
878
+ }
879
+ }
880
+
881
+ function update() {
882
+ if (hashById) {
883
+ inHandler = true;
884
+ ensureRowsByIdCache();
885
+ var newHash = {};
886
+ for (var id in hashById) {
887
+ var row = rowsById[id];
888
+ if (row != undefined) {
889
+ newHash[row] = hashById[id];
890
+ }
891
+ }
892
+ grid.setCellCssStyles(key, newHash);
893
+ inHandler = false;
894
+ }
895
+ }
896
+
897
+ grid.onCellCssStylesChanged.subscribe(function(e, args) {
898
+ if (inHandler) { return; }
899
+ if (key != args.key) { return; }
900
+ if (args.hash) {
901
+ storeCellCssStyles(args.hash);
902
+ }
903
+ });
904
+
905
+ this.onRowsChanged.subscribe(update);
906
+
907
+ this.onRowCountChanged.subscribe(update);
908
+ }
909
+
910
+ $.extend(this, {
911
+ // methods
912
+ "beginUpdate": beginUpdate,
913
+ "endUpdate": endUpdate,
914
+ "setPagingOptions": setPagingOptions,
915
+ "getPagingInfo": getPagingInfo,
916
+ "getItems": getItems,
917
+ "setItems": setItems,
918
+ "setFilter": setFilter,
919
+ "sort": sort,
920
+ "fastSort": fastSort,
921
+ "reSort": reSort,
922
+ "setGrouping": setGrouping,
923
+ "getGrouping": getGrouping,
924
+ "groupBy": groupBy,
925
+ "setAggregators": setAggregators,
926
+ "collapseAllGroups": collapseAllGroups,
927
+ "expandAllGroups": expandAllGroups,
928
+ "collapseGroup": collapseGroup,
929
+ "expandGroup": expandGroup,
930
+ "getGroups": getGroups,
931
+ "getIdxById": getIdxById,
932
+ "getRowById": getRowById,
933
+ "getItemById": getItemById,
934
+ "getItemByIdx": getItemByIdx,
935
+ "mapRowsToIds": mapRowsToIds,
936
+ "mapIdsToRows": mapIdsToRows,
937
+ "setRefreshHints": setRefreshHints,
938
+ "setFilterArgs": setFilterArgs,
939
+ "refresh": refresh,
940
+ "updateItem": updateItem,
941
+ "insertItem": insertItem,
942
+ "addItem": addItem,
943
+ "deleteItem": deleteItem,
944
+ "syncGridSelection": syncGridSelection,
945
+ "syncGridCellCssStyles": syncGridCellCssStyles,
946
+
947
+ // data provider methods
948
+ "getLength": getLength,
949
+ "getItem": getItem,
950
+ "getItemMetadata": getItemMetadata,
951
+
952
+ // events
953
+ "onRowCountChanged": onRowCountChanged,
954
+ "onRowsChanged": onRowsChanged,
955
+ "onPagingInfoChanged": onPagingInfoChanged
956
+ });
957
+ }
958
+
959
+ function AvgAggregator(field) {
960
+ this.field_ = field;
961
+
962
+ this.init = function () {
963
+ this.count_ = 0;
964
+ this.nonNullCount_ = 0;
965
+ this.sum_ = 0;
966
+ };
967
+
968
+ this.accumulate = function (item) {
969
+ var val = item[this.field_];
970
+ this.count_++;
971
+ if (val != null && val !== "" && val !== NaN) {
972
+ this.nonNullCount_++;
973
+ this.sum_ += parseFloat(val);
974
+ }
975
+ };
976
+
977
+ this.storeResult = function (groupTotals) {
978
+ if (!groupTotals.avg) {
979
+ groupTotals.avg = {};
980
+ }
981
+ if (this.nonNullCount_ != 0) {
982
+ groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_;
983
+ }
984
+ };
985
+ }
986
+
987
+ function MinAggregator(field) {
988
+ this.field_ = field;
989
+
990
+ this.init = function () {
991
+ this.min_ = null;
992
+ };
993
+
994
+ this.accumulate = function (item) {
995
+ var val = item[this.field_];
996
+ if (val != null && val !== "" && val !== NaN) {
997
+ if (this.min_ == null || val < this.min_) {
998
+ this.min_ = val;
999
+ }
1000
+ }
1001
+ };
1002
+
1003
+ this.storeResult = function (groupTotals) {
1004
+ if (!groupTotals.min) {
1005
+ groupTotals.min = {};
1006
+ }
1007
+ groupTotals.min[this.field_] = this.min_;
1008
+ }
1009
+ }
1010
+
1011
+ function MaxAggregator(field) {
1012
+ this.field_ = field;
1013
+
1014
+ this.init = function () {
1015
+ this.max_ = null;
1016
+ };
1017
+
1018
+ this.accumulate = function (item) {
1019
+ var val = item[this.field_];
1020
+ if (val != null && val !== "" && val !== NaN) {
1021
+ if (this.max_ == null || val > this.max_) {
1022
+ this.max_ = val;
1023
+ }
1024
+ }
1025
+ };
1026
+
1027
+ this.storeResult = function (groupTotals) {
1028
+ if (!groupTotals.max) {
1029
+ groupTotals.max = {};
1030
+ }
1031
+ groupTotals.max[this.field_] = this.max_;
1032
+ }
1033
+ }
1034
+
1035
+ function SumAggregator(field) {
1036
+ this.field_ = field;
1037
+
1038
+ this.init = function () {
1039
+ this.sum_ = null;
1040
+ };
1041
+
1042
+ this.accumulate = function (item) {
1043
+ var val = item[this.field_];
1044
+ if (val != null && val !== "" && val !== NaN) {
1045
+ this.sum_ += parseFloat(val);
1046
+ }
1047
+ };
1048
+
1049
+ this.storeResult = function (groupTotals) {
1050
+ if (!groupTotals.sum) {
1051
+ groupTotals.sum = {};
1052
+ }
1053
+ groupTotals.sum[this.field_] = this.sum_;
1054
+ }
1055
+ }
1056
+
1057
+ // TODO: add more built-in aggregators
1058
+ // TODO: merge common aggregators in one to prevent needles iterating
1059
+
1060
+ return DataView;
1061
+ }));