extjs-rails 4.1.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (517) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +36 -0
  4. data/README.md +29 -0
  5. data/Rakefile +2 -0
  6. data/app/assets/javascripts/extjs-rails/extjs-rails.js +1 -0
  7. data/app/assets/javascripts/extjs-rails/index.js.erb +9 -0
  8. data/app/assets/javascripts/extjs-rails/sdk.jsb3 +2017 -0
  9. data/app/assets/javascripts/extjs-rails/src/AbstractComponent.js +3290 -0
  10. data/app/assets/javascripts/extjs-rails/src/AbstractManager.js +144 -0
  11. data/app/assets/javascripts/extjs-rails/src/AbstractPlugin.js +72 -0
  12. data/app/assets/javascripts/extjs-rails/src/Action.js +276 -0
  13. data/app/assets/javascripts/extjs-rails/src/Ajax.js +99 -0
  14. data/app/assets/javascripts/extjs-rails/src/Component.js +1344 -0
  15. data/app/assets/javascripts/extjs-rails/src/ComponentLoader.js +202 -0
  16. data/app/assets/javascripts/extjs-rails/src/ComponentManager.js +47 -0
  17. data/app/assets/javascripts/extjs-rails/src/ComponentQuery.js +521 -0
  18. data/app/assets/javascripts/extjs-rails/src/Editor.js +494 -0
  19. data/app/assets/javascripts/extjs-rails/src/ElementLoader.js +404 -0
  20. data/app/assets/javascripts/extjs-rails/src/FocusManager.js +712 -0
  21. data/app/assets/javascripts/extjs-rails/src/Img.js +111 -0
  22. data/app/assets/javascripts/extjs-rails/src/Layer.js +543 -0
  23. data/app/assets/javascripts/extjs-rails/src/LoadMask.js +432 -0
  24. data/app/assets/javascripts/extjs-rails/src/ModelManager.js +186 -0
  25. data/app/assets/javascripts/extjs-rails/src/PluginManager.js +110 -0
  26. data/app/assets/javascripts/extjs-rails/src/ProgressBar.js +336 -0
  27. data/app/assets/javascripts/extjs-rails/src/Shadow.js +233 -0
  28. data/app/assets/javascripts/extjs-rails/src/ShadowPool.js +43 -0
  29. data/app/assets/javascripts/extjs-rails/src/Template.js +331 -0
  30. data/app/assets/javascripts/extjs-rails/src/XTemplate.js +365 -0
  31. data/app/assets/javascripts/extjs-rails/src/XTemplateCompiler.js +450 -0
  32. data/app/assets/javascripts/extjs-rails/src/XTemplateParser.js +249 -0
  33. data/app/assets/javascripts/extjs-rails/src/ZIndexManager.js +519 -0
  34. data/app/assets/javascripts/extjs-rails/src/app/Application.js +271 -0
  35. data/app/assets/javascripts/extjs-rails/src/app/Controller.js +458 -0
  36. data/app/assets/javascripts/extjs-rails/src/app/EventBus.js +112 -0
  37. data/app/assets/javascripts/extjs-rails/src/button/Button.js +1483 -0
  38. data/app/assets/javascripts/extjs-rails/src/button/Cycle.js +215 -0
  39. data/app/assets/javascripts/extjs-rails/src/button/Split.js +99 -0
  40. data/app/assets/javascripts/extjs-rails/src/chart/Callout.js +140 -0
  41. data/app/assets/javascripts/extjs-rails/src/chart/Chart.js +966 -0
  42. data/app/assets/javascripts/extjs-rails/src/chart/Highlight.js +172 -0
  43. data/app/assets/javascripts/extjs-rails/src/chart/Label.js +241 -0
  44. data/app/assets/javascripts/extjs-rails/src/chart/Legend.js +474 -0
  45. data/app/assets/javascripts/extjs-rails/src/chart/LegendItem.js +217 -0
  46. data/app/assets/javascripts/extjs-rails/src/chart/Mask.js +230 -0
  47. data/app/assets/javascripts/extjs-rails/src/chart/MaskLayer.js +48 -0
  48. data/app/assets/javascripts/extjs-rails/src/chart/Navigation.js +79 -0
  49. data/app/assets/javascripts/extjs-rails/src/chart/Shape.js +106 -0
  50. data/app/assets/javascripts/extjs-rails/src/chart/Tip.js +98 -0
  51. data/app/assets/javascripts/extjs-rails/src/chart/TipSurface.js +42 -0
  52. data/app/assets/javascripts/extjs-rails/src/chart/axis/Abstract.js +73 -0
  53. data/app/assets/javascripts/extjs-rails/src/chart/axis/Axis.js +961 -0
  54. data/app/assets/javascripts/extjs-rails/src/chart/axis/Category.js +130 -0
  55. data/app/assets/javascripts/extjs-rails/src/chart/axis/Gauge.js +203 -0
  56. data/app/assets/javascripts/extjs-rails/src/chart/axis/Numeric.js +235 -0
  57. data/app/assets/javascripts/extjs-rails/src/chart/axis/Radial.js +204 -0
  58. data/app/assets/javascripts/extjs-rails/src/chart/axis/Time.js +135 -0
  59. data/app/assets/javascripts/extjs-rails/src/chart/series/Area.js +803 -0
  60. data/app/assets/javascripts/extjs-rails/src/chart/series/Bar.js +853 -0
  61. data/app/assets/javascripts/extjs-rails/src/chart/series/Cartesian.js +328 -0
  62. data/app/assets/javascripts/extjs-rails/src/chart/series/Column.js +104 -0
  63. data/app/assets/javascripts/extjs-rails/src/chart/series/Gauge.js +490 -0
  64. data/app/assets/javascripts/extjs-rails/src/chart/series/Line.js +1104 -0
  65. data/app/assets/javascripts/extjs-rails/src/chart/series/Pie.js +1072 -0
  66. data/app/assets/javascripts/extjs-rails/src/chart/series/Radar.js +440 -0
  67. data/app/assets/javascripts/extjs-rails/src/chart/series/Scatter.js +668 -0
  68. data/app/assets/javascripts/extjs-rails/src/chart/series/Series.js +443 -0
  69. data/app/assets/javascripts/extjs-rails/src/chart/theme/Base.js +175 -0
  70. data/app/assets/javascripts/extjs-rails/src/chart/theme/Theme.js +253 -0
  71. data/app/assets/javascripts/extjs-rails/src/container/AbstractContainer.js +1062 -0
  72. data/app/assets/javascripts/extjs-rails/src/container/ButtonGroup.js +143 -0
  73. data/app/assets/javascripts/extjs-rails/src/container/Container.js +212 -0
  74. data/app/assets/javascripts/extjs-rails/src/container/DockingContainer.js +265 -0
  75. data/app/assets/javascripts/extjs-rails/src/container/Viewport.js +184 -0
  76. data/app/assets/javascripts/extjs-rails/src/core/dom/Element.form.js +50 -0
  77. data/app/assets/javascripts/extjs-rails/src/core/dom/Element.static-more.js +159 -0
  78. data/app/assets/javascripts/extjs-rails/src/core/examples/index.html +321 -0
  79. data/app/assets/javascripts/extjs-rails/src/core/examples/index.js +6 -0
  80. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/CTO.js +25 -0
  81. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/CoolGuy.js +20 -0
  82. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/Developer.js +43 -0
  83. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/Gun.js +3 -0
  84. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/HumanResource.js +23 -0
  85. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/Musician.js +20 -0
  86. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/Person.js +50 -0
  87. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/ability/CanComposeSongs.js +9 -0
  88. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/ability/CanPlayGuitar.js +9 -0
  89. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/ability/CanSing.js +18 -0
  90. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/deadlock/A.js +3 -0
  91. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/deadlock/B.js +3 -0
  92. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/deadlock/C.js +3 -0
  93. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/deadlock/D.js +3 -0
  94. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/deadlock/E.js +3 -0
  95. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/notdeadlock/A.js +3 -0
  96. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/notdeadlock/B.js +4 -0
  97. data/app/assets/javascripts/extjs-rails/src/core/examples/src/Sample/notdeadlock/C.js +3 -0
  98. data/app/assets/javascripts/extjs-rails/src/core/src/EventManager.js +1167 -0
  99. data/app/assets/javascripts/extjs-rails/src/core/src/EventObject.js +883 -0
  100. data/app/assets/javascripts/extjs-rails/src/core/src/Ext-more.js +1283 -0
  101. data/app/assets/javascripts/extjs-rails/src/core/src/Ext.js +736 -0
  102. data/app/assets/javascripts/extjs-rails/src/core/src/Support.js +662 -0
  103. data/app/assets/javascripts/extjs-rails/src/core/src/class/Base.js +1085 -0
  104. data/app/assets/javascripts/extjs-rails/src/core/src/class/Class.js +676 -0
  105. data/app/assets/javascripts/extjs-rails/src/core/src/class/ClassManager.js +1591 -0
  106. data/app/assets/javascripts/extjs-rails/src/core/src/class/Loader.js +1423 -0
  107. data/app/assets/javascripts/extjs-rails/src/core/src/dom/AbstractElement.alignment.js +173 -0
  108. data/app/assets/javascripts/extjs-rails/src/core/src/dom/AbstractElement.insertion.js +193 -0
  109. data/app/assets/javascripts/extjs-rails/src/core/src/dom/AbstractElement.js +681 -0
  110. data/app/assets/javascripts/extjs-rails/src/core/src/dom/AbstractElement.position.js +356 -0
  111. data/app/assets/javascripts/extjs-rails/src/core/src/dom/AbstractElement.static.js +474 -0
  112. data/app/assets/javascripts/extjs-rails/src/core/src/dom/AbstractElement.style.js +851 -0
  113. data/app/assets/javascripts/extjs-rails/src/core/src/dom/AbstractElement.traversal.js +180 -0
  114. data/app/assets/javascripts/extjs-rails/src/core/src/dom/AbstractHelper.js +291 -0
  115. data/app/assets/javascripts/extjs-rails/src/core/src/dom/AbstractQuery.js +72 -0
  116. data/app/assets/javascripts/extjs-rails/src/core/src/dom/CompositeElement.js +75 -0
  117. data/app/assets/javascripts/extjs-rails/src/core/src/dom/CompositeElementLite.js +436 -0
  118. data/app/assets/javascripts/extjs-rails/src/core/src/dom/DomQuery-aria.js +1049 -0
  119. data/app/assets/javascripts/extjs-rails/src/core/src/env/Browser.js +186 -0
  120. data/app/assets/javascripts/extjs-rails/src/core/src/env/FeatureDetector.js +274 -0
  121. data/app/assets/javascripts/extjs-rails/src/core/src/env/OS.js +141 -0
  122. data/app/assets/javascripts/extjs-rails/src/core/src/lang/Array.js +1205 -0
  123. data/app/assets/javascripts/extjs-rails/src/core/src/lang/Date.js +1463 -0
  124. data/app/assets/javascripts/extjs-rails/src/core/src/lang/Error.js +326 -0
  125. data/app/assets/javascripts/extjs-rails/src/core/src/lang/Function.js +485 -0
  126. data/app/assets/javascripts/extjs-rails/src/core/src/lang/Number.js +175 -0
  127. data/app/assets/javascripts/extjs-rails/src/core/src/lang/Object.js +606 -0
  128. data/app/assets/javascripts/extjs-rails/src/core/src/lang/String.js +333 -0
  129. data/app/assets/javascripts/extjs-rails/src/core/src/misc/JSON.js +236 -0
  130. data/app/assets/javascripts/extjs-rails/src/core/src/perf/Accumulator.js +244 -0
  131. data/app/assets/javascripts/extjs-rails/src/core/src/perf/Monitor.js +197 -0
  132. data/app/assets/javascripts/extjs-rails/src/core/src/util/DelayedTask.js +71 -0
  133. data/app/assets/javascripts/extjs-rails/src/core/src/util/Event.js +215 -0
  134. data/app/assets/javascripts/extjs-rails/src/core/src/util/Format.js +551 -0
  135. data/app/assets/javascripts/extjs-rails/src/core/src/util/TaskManager.js +417 -0
  136. data/app/assets/javascripts/extjs-rails/src/core/src/version/Version.js +364 -0
  137. data/app/assets/javascripts/extjs-rails/src/core/test/unit/README.MD +91 -0
  138. data/app/assets/javascripts/extjs-rails/src/core/test/unit/bootstrap.js +39 -0
  139. data/app/assets/javascripts/extjs-rails/src/core/test/unit/build/build-data.js +34 -0
  140. data/app/assets/javascripts/extjs-rails/src/core/test/unit/build/build.sh +2 -0
  141. data/app/assets/javascripts/extjs-rails/src/core/test/unit/data.js +27 -0
  142. data/app/assets/javascripts/extjs-rails/src/core/test/unit/index.html +15 -0
  143. data/app/assets/javascripts/extjs-rails/src/core/test/unit/other_specs/dom.html +40 -0
  144. data/app/assets/javascripts/extjs-rails/src/core/test/unit/other_specs/dom/extjs-api.js +12 -0
  145. data/app/assets/javascripts/extjs-rails/src/core/test/unit/other_specs/dom/platform-api.js +134 -0
  146. data/app/assets/javascripts/extjs-rails/src/core/test/unit/other_specs/dom/sanity.js +4 -0
  147. data/app/assets/javascripts/extjs-rails/src/core/test/unit/other_specs/dom/touch-api-out.js +128 -0
  148. data/app/assets/javascripts/extjs-rails/src/core/test/unit/other_specs/dom/touch-api.js +89 -0
  149. data/app/assets/javascripts/extjs-rails/src/core/test/unit/other_specs/env/Environment.js +357 -0
  150. data/app/assets/javascripts/extjs-rails/src/core/test/unit/resources/APITest.js +37 -0
  151. data/app/assets/javascripts/extjs-rails/src/core/test/unit/resources/jsb3.js +14 -0
  152. data/app/assets/javascripts/extjs-rails/src/core/test/unit/resources/test-setup.js +137 -0
  153. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/EventManager.js +629 -0
  154. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/Ext-mess.backup +198 -0
  155. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/Ext-more.js +520 -0
  156. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/Ext.js +1550 -0
  157. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/Support.js +250 -0
  158. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/class/Base.js +47 -0
  159. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/class/Class.js +561 -0
  160. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/class/ClassManager.js +555 -0
  161. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/dom/CompositeElementLite.js +409 -0
  162. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/dom/DomHelper.js +444 -0
  163. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/dom/Element.insertion.js +342 -0
  164. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/dom/Element.js +731 -0
  165. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/dom/Element.static.js +201 -0
  166. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/dom/Element.style.js +118 -0
  167. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/dom/Element.traversal.js +336 -0
  168. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/lang/Array.js +1229 -0
  169. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/lang/Date.js +697 -0
  170. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/lang/Error.js +277 -0
  171. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/lang/Function.js +536 -0
  172. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/lang/Number.js +323 -0
  173. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/lang/Object.js +591 -0
  174. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/lang/String.js +451 -0
  175. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/misc/JSON.js +252 -0
  176. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/util/Format.js +521 -0
  177. data/app/assets/javascripts/extjs-rails/src/core/test/unit/spec/version/Version.js +148 -0
  178. data/app/assets/javascripts/extjs-rails/src/core/test/unit/specs.html +19 -0
  179. data/app/assets/javascripts/extjs-rails/src/data/AbstractStore.js +887 -0
  180. data/app/assets/javascripts/extjs-rails/src/data/ArrayStore.js +74 -0
  181. data/app/assets/javascripts/extjs-rails/src/data/Batch.js +236 -0
  182. data/app/assets/javascripts/extjs-rails/src/data/BufferStore.js +13 -0
  183. data/app/assets/javascripts/extjs-rails/src/data/Connection.js +969 -0
  184. data/app/assets/javascripts/extjs-rails/src/data/DirectStore.js +50 -0
  185. data/app/assets/javascripts/extjs-rails/src/data/Errors.js +52 -0
  186. data/app/assets/javascripts/extjs-rails/src/data/Field.js +341 -0
  187. data/app/assets/javascripts/extjs-rails/src/data/IdGenerator.js +198 -0
  188. data/app/assets/javascripts/extjs-rails/src/data/JsonP.js +253 -0
  189. data/app/assets/javascripts/extjs-rails/src/data/JsonPStore.js +51 -0
  190. data/app/assets/javascripts/extjs-rails/src/data/JsonStore.js +60 -0
  191. data/app/assets/javascripts/extjs-rails/src/data/Model.js +1673 -0
  192. data/app/assets/javascripts/extjs-rails/src/data/NodeInterface.js +1294 -0
  193. data/app/assets/javascripts/extjs-rails/src/data/NodeStore.js +254 -0
  194. data/app/assets/javascripts/extjs-rails/src/data/Operation.js +331 -0
  195. data/app/assets/javascripts/extjs-rails/src/data/Request.js +40 -0
  196. data/app/assets/javascripts/extjs-rails/src/data/ResultSet.js +56 -0
  197. data/app/assets/javascripts/extjs-rails/src/data/SequentialIdGenerator.js +61 -0
  198. data/app/assets/javascripts/extjs-rails/src/data/SortTypes.js +125 -0
  199. data/app/assets/javascripts/extjs-rails/src/data/Store.js +2609 -0
  200. data/app/assets/javascripts/extjs-rails/src/data/StoreManager.js +156 -0
  201. data/app/assets/javascripts/extjs-rails/src/data/Tree.js +290 -0
  202. data/app/assets/javascripts/extjs-rails/src/data/TreeStore.js +655 -0
  203. data/app/assets/javascripts/extjs-rails/src/data/Types.js +190 -0
  204. data/app/assets/javascripts/extjs-rails/src/data/UuidGenerator.js +215 -0
  205. data/app/assets/javascripts/extjs-rails/src/data/XmlStore.js +76 -0
  206. data/app/assets/javascripts/extjs-rails/src/data/association/Association.js +243 -0
  207. data/app/assets/javascripts/extjs-rails/src/data/association/BelongsTo.js +291 -0
  208. data/app/assets/javascripts/extjs-rails/src/data/association/HasMany.js +289 -0
  209. data/app/assets/javascripts/extjs-rails/src/data/association/HasOne.js +304 -0
  210. data/app/assets/javascripts/extjs-rails/src/data/proxy/Ajax.js +282 -0
  211. data/app/assets/javascripts/extjs-rails/src/data/proxy/Client.js +21 -0
  212. data/app/assets/javascripts/extjs-rails/src/data/proxy/Direct.js +181 -0
  213. data/app/assets/javascripts/extjs-rails/src/data/proxy/JsonP.js +313 -0
  214. data/app/assets/javascripts/extjs-rails/src/data/proxy/LocalStorage.js +69 -0
  215. data/app/assets/javascripts/extjs-rails/src/data/proxy/Memory.js +156 -0
  216. data/app/assets/javascripts/extjs-rails/src/data/proxy/Proxy.js +433 -0
  217. data/app/assets/javascripts/extjs-rails/src/data/proxy/Rest.js +173 -0
  218. data/app/assets/javascripts/extjs-rails/src/data/proxy/Server.js +460 -0
  219. data/app/assets/javascripts/extjs-rails/src/data/proxy/SessionStorage.js +39 -0
  220. data/app/assets/javascripts/extjs-rails/src/data/proxy/WebStorage.js +546 -0
  221. data/app/assets/javascripts/extjs-rails/src/data/reader/Array.js +68 -0
  222. data/app/assets/javascripts/extjs-rails/src/data/reader/Json.js +383 -0
  223. data/app/assets/javascripts/extjs-rails/src/data/reader/Reader.js +735 -0
  224. data/app/assets/javascripts/extjs-rails/src/data/reader/Xml.js +292 -0
  225. data/app/assets/javascripts/extjs-rails/src/data/validations.js +149 -0
  226. data/app/assets/javascripts/extjs-rails/src/data/writer/Json.js +81 -0
  227. data/app/assets/javascripts/extjs-rails/src/data/writer/Writer.js +147 -0
  228. data/app/assets/javascripts/extjs-rails/src/data/writer/Xml.js +88 -0
  229. data/app/assets/javascripts/extjs-rails/src/dd/DD.js +300 -0
  230. data/app/assets/javascripts/extjs-rails/src/dd/DDProxy.js +204 -0
  231. data/app/assets/javascripts/extjs-rails/src/dd/DDTarget.js +171 -0
  232. data/app/assets/javascripts/extjs-rails/src/dd/DragDrop.js +1101 -0
  233. data/app/assets/javascripts/extjs-rails/src/dd/DragDropManager.js +1264 -0
  234. data/app/assets/javascripts/extjs-rails/src/dd/DragSource.js +392 -0
  235. data/app/assets/javascripts/extjs-rails/src/dd/DragTracker.js +562 -0
  236. data/app/assets/javascripts/extjs-rails/src/dd/DragZone.js +137 -0
  237. data/app/assets/javascripts/extjs-rails/src/dd/DropTarget.js +118 -0
  238. data/app/assets/javascripts/extjs-rails/src/dd/DropZone.js +254 -0
  239. data/app/assets/javascripts/extjs-rails/src/dd/Registry.js +117 -0
  240. data/app/assets/javascripts/extjs-rails/src/dd/ScrollManager.js +218 -0
  241. data/app/assets/javascripts/extjs-rails/src/dd/StatusProxy.js +179 -0
  242. data/app/assets/javascripts/extjs-rails/src/diag/layout/Context.js +523 -0
  243. data/app/assets/javascripts/extjs-rails/src/diag/layout/ContextItem.js +179 -0
  244. data/app/assets/javascripts/extjs-rails/src/direct/Event.js +35 -0
  245. data/app/assets/javascripts/extjs-rails/src/direct/ExceptionEvent.js +16 -0
  246. data/app/assets/javascripts/extjs-rails/src/direct/JsonProvider.js +82 -0
  247. data/app/assets/javascripts/extjs-rails/src/direct/Manager.js +263 -0
  248. data/app/assets/javascripts/extjs-rails/src/direct/PollingProvider.js +156 -0
  249. data/app/assets/javascripts/extjs-rails/src/direct/Provider.js +96 -0
  250. data/app/assets/javascripts/extjs-rails/src/direct/RemotingEvent.js +24 -0
  251. data/app/assets/javascripts/extjs-rails/src/direct/RemotingMethod.js +100 -0
  252. data/app/assets/javascripts/extjs-rails/src/direct/RemotingProvider.js +510 -0
  253. data/app/assets/javascripts/extjs-rails/src/direct/Transaction.js +41 -0
  254. data/app/assets/javascripts/extjs-rails/src/dom/Element.alignment.js +378 -0
  255. data/app/assets/javascripts/extjs-rails/src/dom/Element.anim.js +962 -0
  256. data/app/assets/javascripts/extjs-rails/src/dom/Element.dd.js +40 -0
  257. data/app/assets/javascripts/extjs-rails/src/dom/Element.fx.js +194 -0
  258. data/app/assets/javascripts/extjs-rails/src/dom/Element.js +1407 -0
  259. data/app/assets/javascripts/extjs-rails/src/dom/Element.position.js +501 -0
  260. data/app/assets/javascripts/extjs-rails/src/dom/Element.scroll.js +214 -0
  261. data/app/assets/javascripts/extjs-rails/src/dom/Element.style.js +792 -0
  262. data/app/assets/javascripts/extjs-rails/src/dom/Element.traversal.js +8 -0
  263. data/app/assets/javascripts/extjs-rails/src/dom/Helper.js +466 -0
  264. data/app/assets/javascripts/extjs-rails/src/dom/Query.js +1067 -0
  265. data/app/assets/javascripts/extjs-rails/src/draw/Color.js +303 -0
  266. data/app/assets/javascripts/extjs-rails/src/draw/Component.js +245 -0
  267. data/app/assets/javascripts/extjs-rails/src/draw/CompositeSprite.js +299 -0
  268. data/app/assets/javascripts/extjs-rails/src/draw/Draw.js +1217 -0
  269. data/app/assets/javascripts/extjs-rails/src/draw/Matrix.js +183 -0
  270. data/app/assets/javascripts/extjs-rails/src/draw/Sprite.js +554 -0
  271. data/app/assets/javascripts/extjs-rails/src/draw/SpriteDD.js +87 -0
  272. data/app/assets/javascripts/extjs-rails/src/draw/Surface.js +1001 -0
  273. data/app/assets/javascripts/extjs-rails/src/draw/Text.js +175 -0
  274. data/app/assets/javascripts/extjs-rails/src/draw/engine/ImageExporter.js +106 -0
  275. data/app/assets/javascripts/extjs-rails/src/draw/engine/Svg.js +734 -0
  276. data/app/assets/javascripts/extjs-rails/src/draw/engine/SvgExporter.js +280 -0
  277. data/app/assets/javascripts/extjs-rails/src/draw/engine/Vml.js +916 -0
  278. data/app/assets/javascripts/extjs-rails/src/flash/Component.js +248 -0
  279. data/app/assets/javascripts/extjs-rails/src/form/Basic.js +1018 -0
  280. data/app/assets/javascripts/extjs-rails/src/form/CheckboxGroup.js +441 -0
  281. data/app/assets/javascripts/extjs-rails/src/form/CheckboxManager.js +26 -0
  282. data/app/assets/javascripts/extjs-rails/src/form/FieldAncestor.js +210 -0
  283. data/app/assets/javascripts/extjs-rails/src/form/FieldContainer.js +290 -0
  284. data/app/assets/javascripts/extjs-rails/src/form/FieldSet.js +506 -0
  285. data/app/assets/javascripts/extjs-rails/src/form/Label.js +110 -0
  286. data/app/assets/javascripts/extjs-rails/src/form/Labelable.js +764 -0
  287. data/app/assets/javascripts/extjs-rails/src/form/Panel.js +335 -0
  288. data/app/assets/javascripts/extjs-rails/src/form/RadioGroup.js +124 -0
  289. data/app/assets/javascripts/extjs-rails/src/form/RadioManager.js +26 -0
  290. data/app/assets/javascripts/extjs-rails/src/form/action/Action.js +307 -0
  291. data/app/assets/javascripts/extjs-rails/src/form/action/DirectLoad.js +107 -0
  292. data/app/assets/javascripts/extjs-rails/src/form/action/DirectSubmit.js +119 -0
  293. data/app/assets/javascripts/extjs-rails/src/form/action/Load.js +120 -0
  294. data/app/assets/javascripts/extjs-rails/src/form/action/StandardSubmit.js +34 -0
  295. data/app/assets/javascripts/extjs-rails/src/form/action/Submit.js +257 -0
  296. data/app/assets/javascripts/extjs-rails/src/form/field/Base.js +813 -0
  297. data/app/assets/javascripts/extjs-rails/src/form/field/Checkbox.js +505 -0
  298. data/app/assets/javascripts/extjs-rails/src/form/field/ComboBox.js +1427 -0
  299. data/app/assets/javascripts/extjs-rails/src/form/field/Date.js +578 -0
  300. data/app/assets/javascripts/extjs-rails/src/form/field/Display.js +155 -0
  301. data/app/assets/javascripts/extjs-rails/src/form/field/Field.js +430 -0
  302. data/app/assets/javascripts/extjs-rails/src/form/field/File.js +265 -0
  303. data/app/assets/javascripts/extjs-rails/src/form/field/Hidden.js +75 -0
  304. data/app/assets/javascripts/extjs-rails/src/form/field/HtmlEditor.js +1439 -0
  305. data/app/assets/javascripts/extjs-rails/src/form/field/Number.js +380 -0
  306. data/app/assets/javascripts/extjs-rails/src/form/field/Picker.js +321 -0
  307. data/app/assets/javascripts/extjs-rails/src/form/field/Radio.js +279 -0
  308. data/app/assets/javascripts/extjs-rails/src/form/field/Spinner.js +321 -0
  309. data/app/assets/javascripts/extjs-rails/src/form/field/Text.js +727 -0
  310. data/app/assets/javascripts/extjs-rails/src/form/field/TextArea.js +228 -0
  311. data/app/assets/javascripts/extjs-rails/src/form/field/Time.js +459 -0
  312. data/app/assets/javascripts/extjs-rails/src/form/field/Trigger.js +469 -0
  313. data/app/assets/javascripts/extjs-rails/src/form/field/VTypes.js +172 -0
  314. data/app/assets/javascripts/extjs-rails/src/fx/Anim.js +472 -0
  315. data/app/assets/javascripts/extjs-rails/src/fx/Animator.js +410 -0
  316. data/app/assets/javascripts/extjs-rails/src/fx/CubicBezier.js +77 -0
  317. data/app/assets/javascripts/extjs-rails/src/fx/Easing.js +136 -0
  318. data/app/assets/javascripts/extjs-rails/src/fx/Manager.js +353 -0
  319. data/app/assets/javascripts/extjs-rails/src/fx/PropertyHandler.js +381 -0
  320. data/app/assets/javascripts/extjs-rails/src/fx/Queue.js +123 -0
  321. data/app/assets/javascripts/extjs-rails/src/fx/target/Component.js +118 -0
  322. data/app/assets/javascripts/extjs-rails/src/fx/target/CompositeElement.js +46 -0
  323. data/app/assets/javascripts/extjs-rails/src/fx/target/CompositeElementCSS.js +22 -0
  324. data/app/assets/javascripts/extjs-rails/src/fx/target/CompositeSprite.js +34 -0
  325. data/app/assets/javascripts/extjs-rails/src/fx/target/Element.js +83 -0
  326. data/app/assets/javascripts/extjs-rails/src/fx/target/ElementCSS.js +77 -0
  327. data/app/assets/javascripts/extjs-rails/src/fx/target/Sprite.js +128 -0
  328. data/app/assets/javascripts/extjs-rails/src/fx/target/Target.js +36 -0
  329. data/app/assets/javascripts/extjs-rails/src/grid/CellEditor.js +172 -0
  330. data/app/assets/javascripts/extjs-rails/src/grid/ColumnComponentLayout.js +34 -0
  331. data/app/assets/javascripts/extjs-rails/src/grid/ColumnLayout.js +201 -0
  332. data/app/assets/javascripts/extjs-rails/src/grid/Lockable.js +863 -0
  333. data/app/assets/javascripts/extjs-rails/src/grid/LockingView.js +169 -0
  334. data/app/assets/javascripts/extjs-rails/src/grid/PagingScroller.js +539 -0
  335. data/app/assets/javascripts/extjs-rails/src/grid/Panel.js +393 -0
  336. data/app/assets/javascripts/extjs-rails/src/grid/RowEditor.js +731 -0
  337. data/app/assets/javascripts/extjs-rails/src/grid/RowNumberer.js +76 -0
  338. data/app/assets/javascripts/extjs-rails/src/grid/Scroller.js +5 -0
  339. data/app/assets/javascripts/extjs-rails/src/grid/View.js +44 -0
  340. data/app/assets/javascripts/extjs-rails/src/grid/ViewDropZone.js +41 -0
  341. data/app/assets/javascripts/extjs-rails/src/grid/column/Action.js +318 -0
  342. data/app/assets/javascripts/extjs-rails/src/grid/column/Boolean.js +82 -0
  343. data/app/assets/javascripts/extjs-rails/src/grid/column/Column.js +835 -0
  344. data/app/assets/javascripts/extjs-rails/src/grid/column/Date.js +63 -0
  345. data/app/assets/javascripts/extjs-rails/src/grid/column/Number.js +52 -0
  346. data/app/assets/javascripts/extjs-rails/src/grid/column/Template.js +58 -0
  347. data/app/assets/javascripts/extjs-rails/src/grid/feature/AbstractSummary.js +154 -0
  348. data/app/assets/javascripts/extjs-rails/src/grid/feature/Chunking.js +77 -0
  349. data/app/assets/javascripts/extjs-rails/src/grid/feature/Feature.js +158 -0
  350. data/app/assets/javascripts/extjs-rails/src/grid/feature/Grouping.js +822 -0
  351. data/app/assets/javascripts/extjs-rails/src/grid/feature/GroupingSummary.js +243 -0
  352. data/app/assets/javascripts/extjs-rails/src/grid/feature/RowBody.js +116 -0
  353. data/app/assets/javascripts/extjs-rails/src/grid/feature/RowWrap.js +120 -0
  354. data/app/assets/javascripts/extjs-rails/src/grid/feature/Summary.js +167 -0
  355. data/app/assets/javascripts/extjs-rails/src/grid/header/Container.js +965 -0
  356. data/app/assets/javascripts/extjs-rails/src/grid/header/DragZone.js +69 -0
  357. data/app/assets/javascripts/extjs-rails/src/grid/header/DropZone.js +265 -0
  358. data/app/assets/javascripts/extjs-rails/src/grid/plugin/CellEditing.js +453 -0
  359. data/app/assets/javascripts/extjs-rails/src/grid/plugin/DragDrop.js +254 -0
  360. data/app/assets/javascripts/extjs-rails/src/grid/plugin/Editing.js +561 -0
  361. data/app/assets/javascripts/extjs-rails/src/grid/plugin/HeaderReorderer.js +49 -0
  362. data/app/assets/javascripts/extjs-rails/src/grid/plugin/HeaderResizer.js +292 -0
  363. data/app/assets/javascripts/extjs-rails/src/grid/plugin/RowEditing.js +339 -0
  364. data/app/assets/javascripts/extjs-rails/src/grid/property/Grid.js +351 -0
  365. data/app/assets/javascripts/extjs-rails/src/grid/property/HeaderContainer.js +109 -0
  366. data/app/assets/javascripts/extjs-rails/src/grid/property/Property.js +36 -0
  367. data/app/assets/javascripts/extjs-rails/src/grid/property/Store.js +141 -0
  368. data/app/assets/javascripts/extjs-rails/src/layout/ClassList.js +90 -0
  369. data/app/assets/javascripts/extjs-rails/src/layout/Context.js +1232 -0
  370. data/app/assets/javascripts/extjs-rails/src/layout/ContextItem.js +1470 -0
  371. data/app/assets/javascripts/extjs-rails/src/layout/Layout.js +761 -0
  372. data/app/assets/javascripts/extjs-rails/src/layout/component/Auto.js +220 -0
  373. data/app/assets/javascripts/extjs-rails/src/layout/component/Body.js +80 -0
  374. data/app/assets/javascripts/extjs-rails/src/layout/component/BoundList.js +95 -0
  375. data/app/assets/javascripts/extjs-rails/src/layout/component/Button.js +261 -0
  376. data/app/assets/javascripts/extjs-rails/src/layout/component/Component.js +427 -0
  377. data/app/assets/javascripts/extjs-rails/src/layout/component/Dock.js +1132 -0
  378. data/app/assets/javascripts/extjs-rails/src/layout/component/Draw.js +79 -0
  379. data/app/assets/javascripts/extjs-rails/src/layout/component/FieldSet.js +62 -0
  380. data/app/assets/javascripts/extjs-rails/src/layout/component/ProgressBar.js +54 -0
  381. data/app/assets/javascripts/extjs-rails/src/layout/component/Tab.js +23 -0
  382. data/app/assets/javascripts/extjs-rails/src/layout/component/field/ComboBox.js +52 -0
  383. data/app/assets/javascripts/extjs-rails/src/layout/component/field/Field.js +372 -0
  384. data/app/assets/javascripts/extjs-rails/src/layout/component/field/FieldContainer.js +48 -0
  385. data/app/assets/javascripts/extjs-rails/src/layout/component/field/HtmlEditor.js +54 -0
  386. data/app/assets/javascripts/extjs-rails/src/layout/component/field/Slider.js +58 -0
  387. data/app/assets/javascripts/extjs-rails/src/layout/component/field/Text.js +75 -0
  388. data/app/assets/javascripts/extjs-rails/src/layout/component/field/TextArea.js +51 -0
  389. data/app/assets/javascripts/extjs-rails/src/layout/component/field/Trigger.js +132 -0
  390. data/app/assets/javascripts/extjs-rails/src/layout/container/Absolute.js +121 -0
  391. data/app/assets/javascripts/extjs-rails/src/layout/container/Accordion.js +308 -0
  392. data/app/assets/javascripts/extjs-rails/src/layout/container/Anchor.js +403 -0
  393. data/app/assets/javascripts/extjs-rails/src/layout/container/Auto.js +70 -0
  394. data/app/assets/javascripts/extjs-rails/src/layout/container/Border-old-js +1079 -0
  395. data/app/assets/javascripts/extjs-rails/src/layout/container/Border.js +816 -0
  396. data/app/assets/javascripts/extjs-rails/src/layout/container/Box.js +929 -0
  397. data/app/assets/javascripts/extjs-rails/src/layout/container/Card.js +361 -0
  398. data/app/assets/javascripts/extjs-rails/src/layout/container/CheckboxGroup.js +376 -0
  399. data/app/assets/javascripts/extjs-rails/src/layout/container/Column.js +234 -0
  400. data/app/assets/javascripts/extjs-rails/src/layout/container/Container.js +961 -0
  401. data/app/assets/javascripts/extjs-rails/src/layout/container/Editor.js +74 -0
  402. data/app/assets/javascripts/extjs-rails/src/layout/container/Fit.js +287 -0
  403. data/app/assets/javascripts/extjs-rails/src/layout/container/Form.js +157 -0
  404. data/app/assets/javascripts/extjs-rails/src/layout/container/HBox.js +134 -0
  405. data/app/assets/javascripts/extjs-rails/src/layout/container/Table.js +412 -0
  406. data/app/assets/javascripts/extjs-rails/src/layout/container/VBox.js +139 -0
  407. data/app/assets/javascripts/extjs-rails/src/layout/container/boxOverflow/Menu.js +365 -0
  408. data/app/assets/javascripts/extjs-rails/src/layout/container/boxOverflow/None.js +83 -0
  409. data/app/assets/javascripts/extjs-rails/src/layout/container/boxOverflow/Scroller.js +475 -0
  410. data/app/assets/javascripts/extjs-rails/src/menu/CheckItem.js +169 -0
  411. data/app/assets/javascripts/extjs-rails/src/menu/ColorPicker.js +110 -0
  412. data/app/assets/javascripts/extjs-rails/src/menu/DatePicker.js +100 -0
  413. data/app/assets/javascripts/extjs-rails/src/menu/Item.js +553 -0
  414. data/app/assets/javascripts/extjs-rails/src/menu/KeyNav.js +134 -0
  415. data/app/assets/javascripts/extjs-rails/src/menu/Manager.js +219 -0
  416. data/app/assets/javascripts/extjs-rails/src/menu/Menu.js +579 -0
  417. data/app/assets/javascripts/extjs-rails/src/menu/Separator.js +126 -0
  418. data/app/assets/javascripts/extjs-rails/src/panel/AbstractPanel.js +323 -0
  419. data/app/assets/javascripts/extjs-rails/src/panel/DD.js +99 -0
  420. data/app/assets/javascripts/extjs-rails/src/panel/Header.js +518 -0
  421. data/app/assets/javascripts/extjs-rails/src/panel/Panel.js +2150 -0
  422. data/app/assets/javascripts/extjs-rails/src/panel/Proxy.js +131 -0
  423. data/app/assets/javascripts/extjs-rails/src/panel/Table.js +1018 -0
  424. data/app/assets/javascripts/extjs-rails/src/panel/Tool.js +304 -0
  425. data/app/assets/javascripts/extjs-rails/src/picker/Color.js +219 -0
  426. data/app/assets/javascripts/extjs-rails/src/picker/Date.js +1140 -0
  427. data/app/assets/javascripts/extjs-rails/src/picker/Month.js +490 -0
  428. data/app/assets/javascripts/extjs-rails/src/picker/Time.js +165 -0
  429. data/app/assets/javascripts/extjs-rails/src/resizer/BorderSplitter.js +22 -0
  430. data/app/assets/javascripts/extjs-rails/src/resizer/BorderSplitterTracker.js +210 -0
  431. data/app/assets/javascripts/extjs-rails/src/resizer/Handle.js +29 -0
  432. data/app/assets/javascripts/extjs-rails/src/resizer/ResizeTracker.js +346 -0
  433. data/app/assets/javascripts/extjs-rails/src/resizer/Resizer.js +477 -0
  434. data/app/assets/javascripts/extjs-rails/src/resizer/Splitter.js +242 -0
  435. data/app/assets/javascripts/extjs-rails/src/resizer/SplitterTracker.js +230 -0
  436. data/app/assets/javascripts/extjs-rails/src/selection/CellModel.js +395 -0
  437. data/app/assets/javascripts/extjs-rails/src/selection/CheckboxModel.js +234 -0
  438. data/app/assets/javascripts/extjs-rails/src/selection/DataViewModel.js +160 -0
  439. data/app/assets/javascripts/extjs-rails/src/selection/Model.js +646 -0
  440. data/app/assets/javascripts/extjs-rails/src/selection/RowModel.js +499 -0
  441. data/app/assets/javascripts/extjs-rails/src/selection/TreeModel.js +78 -0
  442. data/app/assets/javascripts/extjs-rails/src/slider/Multi.js +835 -0
  443. data/app/assets/javascripts/extjs-rails/src/slider/Single.js +56 -0
  444. data/app/assets/javascripts/extjs-rails/src/slider/Thumb.js +259 -0
  445. data/app/assets/javascripts/extjs-rails/src/slider/Tip.js +130 -0
  446. data/app/assets/javascripts/extjs-rails/src/state/CookieProvider.js +120 -0
  447. data/app/assets/javascripts/extjs-rails/src/state/LocalStorageProvider.js +72 -0
  448. data/app/assets/javascripts/extjs-rails/src/state/Manager.js +70 -0
  449. data/app/assets/javascripts/extjs-rails/src/state/Provider.js +182 -0
  450. data/app/assets/javascripts/extjs-rails/src/state/Stateful.js +364 -0
  451. data/app/assets/javascripts/extjs-rails/src/tab/Bar.js +258 -0
  452. data/app/assets/javascripts/extjs-rails/src/tab/Panel.js +654 -0
  453. data/app/assets/javascripts/extjs-rails/src/tab/Tab.js +358 -0
  454. data/app/assets/javascripts/extjs-rails/src/tail.js +10 -0
  455. data/app/assets/javascripts/extjs-rails/src/tip/QuickTip.js +335 -0
  456. data/app/assets/javascripts/extjs-rails/src/tip/QuickTipManager.js +239 -0
  457. data/app/assets/javascripts/extjs-rails/src/tip/Tip.js +160 -0
  458. data/app/assets/javascripts/extjs-rails/src/tip/ToolTip.js +691 -0
  459. data/app/assets/javascripts/extjs-rails/src/toolbar/Fill.js +28 -0
  460. data/app/assets/javascripts/extjs-rails/src/toolbar/Item.js +16 -0
  461. data/app/assets/javascripts/extjs-rails/src/toolbar/Paging.js +600 -0
  462. data/app/assets/javascripts/extjs-rails/src/toolbar/Separator.js +23 -0
  463. data/app/assets/javascripts/extjs-rails/src/toolbar/Spacer.js +33 -0
  464. data/app/assets/javascripts/extjs-rails/src/toolbar/TextItem.js +57 -0
  465. data/app/assets/javascripts/extjs-rails/src/toolbar/Toolbar-legacy.js +123 -0
  466. data/app/assets/javascripts/extjs-rails/src/toolbar/Toolbar.js +447 -0
  467. data/app/assets/javascripts/extjs-rails/src/tree/Column.js +90 -0
  468. data/app/assets/javascripts/extjs-rails/src/tree/Panel.js +505 -0
  469. data/app/assets/javascripts/extjs-rails/src/tree/View.js +658 -0
  470. data/app/assets/javascripts/extjs-rails/src/tree/ViewDragZone.js +49 -0
  471. data/app/assets/javascripts/extjs-rails/src/tree/ViewDropZone.js +287 -0
  472. data/app/assets/javascripts/extjs-rails/src/tree/plugin/TreeViewDragDrop.js +244 -0
  473. data/app/assets/javascripts/extjs-rails/src/util/AbstractMixedCollection.js +772 -0
  474. data/app/assets/javascripts/extjs-rails/src/util/Animate.js +426 -0
  475. data/app/assets/javascripts/extjs-rails/src/util/Bindable.js +102 -0
  476. data/app/assets/javascripts/extjs-rails/src/util/CSS.js +185 -0
  477. data/app/assets/javascripts/extjs-rails/src/util/ClickRepeater.js +238 -0
  478. data/app/assets/javascripts/extjs-rails/src/util/ComponentDragger.js +126 -0
  479. data/app/assets/javascripts/extjs-rails/src/util/Cookies.js +91 -0
  480. data/app/assets/javascripts/extjs-rails/src/util/ElementContainer.js +293 -0
  481. data/app/assets/javascripts/extjs-rails/src/util/Filter.js +159 -0
  482. data/app/assets/javascripts/extjs-rails/src/util/Floating.js +321 -0
  483. data/app/assets/javascripts/extjs-rails/src/util/Grouper.js +26 -0
  484. data/app/assets/javascripts/extjs-rails/src/util/HashMap.js +356 -0
  485. data/app/assets/javascripts/extjs-rails/src/util/History.js +285 -0
  486. data/app/assets/javascripts/extjs-rails/src/util/Inflector.js +297 -0
  487. data/app/assets/javascripts/extjs-rails/src/util/KeyMap.js +427 -0
  488. data/app/assets/javascripts/extjs-rails/src/util/KeyNav.js +239 -0
  489. data/app/assets/javascripts/extjs-rails/src/util/LruCache.js +257 -0
  490. data/app/assets/javascripts/extjs-rails/src/util/Memento.js +131 -0
  491. data/app/assets/javascripts/extjs-rails/src/util/MixedCollection.js +239 -0
  492. data/app/assets/javascripts/extjs-rails/src/util/Observable.js +1003 -0
  493. data/app/assets/javascripts/extjs-rails/src/util/Offset.js +60 -0
  494. data/app/assets/javascripts/extjs-rails/src/util/Point.js +93 -0
  495. data/app/assets/javascripts/extjs-rails/src/util/ProtoElement.js +205 -0
  496. data/app/assets/javascripts/extjs-rails/src/util/Queue.js +76 -0
  497. data/app/assets/javascripts/extjs-rails/src/util/Region.js +388 -0
  498. data/app/assets/javascripts/extjs-rails/src/util/Renderable.js +1079 -0
  499. data/app/assets/javascripts/extjs-rails/src/util/Sortable.js +248 -0
  500. data/app/assets/javascripts/extjs-rails/src/util/Sorter.js +179 -0
  501. data/app/assets/javascripts/extjs-rails/src/util/TextMetrics.js +148 -0
  502. data/app/assets/javascripts/extjs-rails/src/view/AbstractView.js +1103 -0
  503. data/app/assets/javascripts/extjs-rails/src/view/BoundList.js +207 -0
  504. data/app/assets/javascripts/extjs-rails/src/view/BoundListKeyNav.js +92 -0
  505. data/app/assets/javascripts/extjs-rails/src/view/DragZone.js +106 -0
  506. data/app/assets/javascripts/extjs-rails/src/view/DropZone.js +240 -0
  507. data/app/assets/javascripts/extjs-rails/src/view/Table.js +1229 -0
  508. data/app/assets/javascripts/extjs-rails/src/view/TableChunker.js +139 -0
  509. data/app/assets/javascripts/extjs-rails/src/view/View.js +636 -0
  510. data/app/assets/javascripts/extjs-rails/src/window/MessageBox.js +880 -0
  511. data/app/assets/javascripts/extjs-rails/src/window/Window-legacy.js +56 -0
  512. data/app/assets/javascripts/extjs-rails/src/window/Window.js +793 -0
  513. data/extjs-rails.gemspec +24 -0
  514. data/lib/extjs-rails.rb +7 -0
  515. data/lib/extjs-rails/engine.rb +7 -0
  516. data/lib/extjs-rails/version.rb +5 -0
  517. metadata +660 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @author Ed Spencer
3
+ *
4
+ * Simple class that represents a Request that will be made by any {@link Ext.data.proxy.Server} subclass.
5
+ * All this class does is standardize the representation of a Request as used by any ServerProxy subclass,
6
+ * it does not contain any actual logic or perform the request itself.
7
+ */
8
+ Ext.define('Ext.data.Request', {
9
+ /**
10
+ * @cfg {String} action
11
+ * The name of the action this Request represents. Usually one of 'create', 'read', 'update' or 'destroy'.
12
+ */
13
+ action: undefined,
14
+
15
+ /**
16
+ * @cfg {Object} params
17
+ * HTTP request params. The Proxy and its Writer have access to and can modify this object.
18
+ */
19
+ params: undefined,
20
+
21
+ /**
22
+ * @cfg {String} method
23
+ * The HTTP method to use on this Request. Should be one of 'GET', 'POST', 'PUT' or 'DELETE'.
24
+ */
25
+ method: 'GET',
26
+
27
+ /**
28
+ * @cfg {String} url
29
+ * The url to access on this Request
30
+ */
31
+ url: undefined,
32
+
33
+ /**
34
+ * Creates the Request object.
35
+ * @param {Object} [config] Config object.
36
+ */
37
+ constructor: function(config) {
38
+ Ext.apply(this, config);
39
+ }
40
+ });
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @author Ed Spencer
3
+ *
4
+ * Simple wrapper class that represents a set of records returned by a Proxy.
5
+ */
6
+ Ext.define('Ext.data.ResultSet', {
7
+ /**
8
+ * @cfg {Boolean} loaded
9
+ * True if the records have already been loaded. This is only meaningful when dealing with
10
+ * SQL-backed proxies.
11
+ */
12
+ loaded: true,
13
+
14
+ /**
15
+ * @cfg {Number} count
16
+ * The number of records in this ResultSet. Note that total may differ from this number.
17
+ */
18
+ count: 0,
19
+
20
+ /**
21
+ * @cfg {Number} total
22
+ * The total number of records reported by the data source. This ResultSet may form a subset of
23
+ * those records (see {@link #count}).
24
+ */
25
+ total: 0,
26
+
27
+ /**
28
+ * @cfg {Boolean} success
29
+ * True if the ResultSet loaded successfully, false if any errors were encountered.
30
+ */
31
+ success: false,
32
+
33
+ /**
34
+ * @cfg {Ext.data.Model[]} records (required)
35
+ * The array of record instances.
36
+ */
37
+
38
+ /**
39
+ * Creates the resultSet
40
+ * @param {Object} [config] Config object.
41
+ */
42
+ constructor: function(config) {
43
+ Ext.apply(this, config);
44
+
45
+ /**
46
+ * @property {Number} totalRecords
47
+ * Copy of this.total.
48
+ * @deprecated Will be removed in Ext JS 5.0. Use {@link #total} instead.
49
+ */
50
+ this.totalRecords = this.total;
51
+
52
+ if (config.count === undefined) {
53
+ this.count = this.records.length;
54
+ }
55
+ }
56
+ });
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @author Don Griffin
3
+ *
4
+ * This class is a sequential id generator. A simple use of this class would be like so:
5
+ *
6
+ * Ext.define('MyApp.data.MyModel', {
7
+ * extend: 'Ext.data.Model',
8
+ * idgen: 'sequential'
9
+ * });
10
+ * // assign id's of 1, 2, 3, etc.
11
+ *
12
+ * An example of a configured generator would be:
13
+ *
14
+ * Ext.define('MyApp.data.MyModel', {
15
+ * extend: 'Ext.data.Model',
16
+ * idgen: {
17
+ * type: 'sequential',
18
+ * prefix: 'ID_',
19
+ * seed: 1000
20
+ * }
21
+ * });
22
+ * // assign id's of ID_1000, ID_1001, ID_1002, etc.
23
+ *
24
+ */
25
+ Ext.define('Ext.data.SequentialIdGenerator', {
26
+ extend: 'Ext.data.IdGenerator',
27
+ alias: 'idgen.sequential',
28
+
29
+ constructor: function() {
30
+ var me = this;
31
+
32
+ me.callParent(arguments);
33
+
34
+ me.parts = [ me.prefix, ''];
35
+ },
36
+
37
+ /**
38
+ * @cfg {String} prefix
39
+ * The string to place in front of the sequential number for each generated id. The
40
+ * default is blank.
41
+ */
42
+ prefix: '',
43
+
44
+ /**
45
+ * @cfg {Number} seed
46
+ * The number at which to start generating sequential id's. The default is 1.
47
+ */
48
+ seed: 1,
49
+
50
+ /**
51
+ * Generates and returns the next id.
52
+ * @return {String} The next id.
53
+ */
54
+ generate: function () {
55
+ var me = this,
56
+ parts = me.parts;
57
+
58
+ parts[1] = me.seed++;
59
+ return parts.join('');
60
+ }
61
+ });
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @class Ext.data.SortTypes
3
+ * This class defines a series of static methods that are used on a
4
+ * {@link Ext.data.Field} for performing sorting. The methods cast the
5
+ * underlying values into a data type that is appropriate for sorting on
6
+ * that particular field. If a {@link Ext.data.Field#type} is specified,
7
+ * the sortType will be set to a sane default if the sortType is not
8
+ * explicitly defined on the field. The sortType will make any necessary
9
+ * modifications to the value and return it.
10
+ * <ul>
11
+ * <li><b>asText</b> - Removes any tags and converts the value to a string</li>
12
+ * <li><b>asUCText</b> - Removes any tags and converts the value to an uppercase string</li>
13
+ * <li><b>asUCText</b> - Converts the value to an uppercase string</li>
14
+ * <li><b>asDate</b> - Converts the value into Unix epoch time</li>
15
+ * <li><b>asFloat</b> - Converts the value to a floating point number</li>
16
+ * <li><b>asInt</b> - Converts the value to an integer number</li>
17
+ * </ul>
18
+ * <p>
19
+ * It is also possible to create a custom sortType that can be used throughout
20
+ * an application.
21
+ * <pre><code>
22
+ Ext.apply(Ext.data.SortTypes, {
23
+ asPerson: function(person){
24
+ // expects an object with a first and last name property
25
+ return person.lastName.toUpperCase() + person.firstName.toLowerCase();
26
+ }
27
+ });
28
+
29
+ Ext.define('Employee', {
30
+ extend: 'Ext.data.Model',
31
+ fields: [{
32
+ name: 'person',
33
+ sortType: 'asPerson'
34
+ }, {
35
+ name: 'salary',
36
+ type: 'float' // sortType set to asFloat
37
+ }]
38
+ });
39
+ * </code></pre>
40
+ * </p>
41
+ * @singleton
42
+ * @docauthor Evan Trimboli <evan@sencha.com>
43
+ */
44
+ Ext.define('Ext.data.SortTypes', {
45
+
46
+ singleton: true,
47
+
48
+ /**
49
+ * Default sort that does nothing
50
+ * @param {Object} s The value being converted
51
+ * @return {Object} The comparison value
52
+ */
53
+ none : function(s) {
54
+ return s;
55
+ },
56
+
57
+ /**
58
+ * The regular expression used to strip tags
59
+ * @type {RegExp}
60
+ * @property
61
+ */
62
+ stripTagsRE : /<\/?[^>]+>/gi,
63
+
64
+ /**
65
+ * Strips all HTML tags to sort on text only
66
+ * @param {Object} s The value being converted
67
+ * @return {String} The comparison value
68
+ */
69
+ asText : function(s) {
70
+ return String(s).replace(this.stripTagsRE, "");
71
+ },
72
+
73
+ /**
74
+ * Strips all HTML tags to sort on text only - Case insensitive
75
+ * @param {Object} s The value being converted
76
+ * @return {String} The comparison value
77
+ */
78
+ asUCText : function(s) {
79
+ return String(s).toUpperCase().replace(this.stripTagsRE, "");
80
+ },
81
+
82
+ /**
83
+ * Case insensitive string
84
+ * @param {Object} s The value being converted
85
+ * @return {String} The comparison value
86
+ */
87
+ asUCString : function(s) {
88
+ return String(s).toUpperCase();
89
+ },
90
+
91
+ /**
92
+ * Date sorting
93
+ * @param {Object} s The value being converted
94
+ * @return {Number} The comparison value
95
+ */
96
+ asDate : function(s) {
97
+ if(!s){
98
+ return 0;
99
+ }
100
+ if(Ext.isDate(s)){
101
+ return s.getTime();
102
+ }
103
+ return Date.parse(String(s));
104
+ },
105
+
106
+ /**
107
+ * Float sorting
108
+ * @param {Object} s The value being converted
109
+ * @return {Number} The comparison value
110
+ */
111
+ asFloat : function(s) {
112
+ var val = parseFloat(String(s).replace(/,/g, ""));
113
+ return isNaN(val) ? 0 : val;
114
+ },
115
+
116
+ /**
117
+ * Integer sorting
118
+ * @param {Object} s The value being converted
119
+ * @return {Number} The comparison value
120
+ */
121
+ asInt : function(s) {
122
+ var val = parseInt(String(s).replace(/,/g, ""), 10);
123
+ return isNaN(val) ? 0 : val;
124
+ }
125
+ });
@@ -0,0 +1,2609 @@
1
+ /**
2
+ * The Store class encapsulates a client side cache of {@link Ext.data.Model Model} objects. Stores load data via a
3
+ * {@link Ext.data.proxy.Proxy Proxy}, and also provide functions for {@link #sort sorting}, {@link #filter filtering}
4
+ * and querying the {@link Ext.data.Model model} instances contained within it.
5
+ *
6
+ * Creating a Store is easy - we just tell it the Model and the Proxy to use to load and save its data:
7
+ *
8
+ * // Set up a {@link Ext.data.Model model} to use in our Store
9
+ * Ext.define('User', {
10
+ * extend: 'Ext.data.Model',
11
+ * fields: [
12
+ * {name: 'firstName', type: 'string'},
13
+ * {name: 'lastName', type: 'string'},
14
+ * {name: 'age', type: 'int'},
15
+ * {name: 'eyeColor', type: 'string'}
16
+ * ]
17
+ * });
18
+ *
19
+ * var myStore = Ext.create('Ext.data.Store', {
20
+ * model: 'User',
21
+ * proxy: {
22
+ * type: 'ajax',
23
+ * url: '/users.json',
24
+ * reader: {
25
+ * type: 'json',
26
+ * root: 'users'
27
+ * }
28
+ * },
29
+ * autoLoad: true
30
+ * });
31
+ *
32
+ * In the example above we configured an AJAX proxy to load data from the url '/users.json'. We told our Proxy to use a
33
+ * {@link Ext.data.reader.Json JsonReader} to parse the response from the server into Model object - {@link
34
+ * Ext.data.reader.Json see the docs on JsonReader} for details.
35
+ *
36
+ * ## Inline data
37
+ *
38
+ * Stores can also load data inline. Internally, Store converts each of the objects we pass in as {@link #cfg-data} into
39
+ * Model instances:
40
+ *
41
+ * Ext.create('Ext.data.Store', {
42
+ * model: 'User',
43
+ * data : [
44
+ * {firstName: 'Ed', lastName: 'Spencer'},
45
+ * {firstName: 'Tommy', lastName: 'Maintz'},
46
+ * {firstName: 'Aaron', lastName: 'Conran'},
47
+ * {firstName: 'Jamie', lastName: 'Avins'}
48
+ * ]
49
+ * });
50
+ *
51
+ * Loading inline data using the method above is great if the data is in the correct format already (e.g. it doesn't
52
+ * need to be processed by a {@link Ext.data.reader.Reader reader}). If your inline data requires processing to decode
53
+ * the data structure, use a {@link Ext.data.proxy.Memory MemoryProxy} instead (see the {@link Ext.data.proxy.Memory
54
+ * MemoryProxy} docs for an example).
55
+ *
56
+ * Additional data can also be loaded locally using {@link #method-add}.
57
+ *
58
+ * ## Loading Nested Data
59
+ *
60
+ * Applications often need to load sets of associated data - for example a CRM system might load a User and her Orders.
61
+ * Instead of issuing an AJAX request for the User and a series of additional AJAX requests for each Order, we can load
62
+ * a nested dataset and allow the Reader to automatically populate the associated models. Below is a brief example, see
63
+ * the {@link Ext.data.reader.Reader} intro docs for a full explanation:
64
+ *
65
+ * var store = Ext.create('Ext.data.Store', {
66
+ * autoLoad: true,
67
+ * model: "User",
68
+ * proxy: {
69
+ * type: 'ajax',
70
+ * url: 'users.json',
71
+ * reader: {
72
+ * type: 'json',
73
+ * root: 'users'
74
+ * }
75
+ * }
76
+ * });
77
+ *
78
+ * Which would consume a response like this:
79
+ *
80
+ * {
81
+ * "users": [{
82
+ * "id": 1,
83
+ * "name": "Ed",
84
+ * "orders": [{
85
+ * "id": 10,
86
+ * "total": 10.76,
87
+ * "status": "invoiced"
88
+ * },{
89
+ * "id": 11,
90
+ * "total": 13.45,
91
+ * "status": "shipped"
92
+ * }]
93
+ * }]
94
+ * }
95
+ *
96
+ * See the {@link Ext.data.reader.Reader} intro docs for a full explanation.
97
+ *
98
+ * ## Filtering and Sorting
99
+ *
100
+ * Stores can be sorted and filtered - in both cases either remotely or locally. The {@link #sorters} and {@link #cfg-
101
+ * filters} are held inside {@link Ext.util.MixedCollection MixedCollection} instances to make them easy to manage.
102
+ * Usually it is sufficient to either just specify sorters and filters in the Store configuration or call {@link #sort}
103
+ * or {@link #filter}:
104
+ *
105
+ * var store = Ext.create('Ext.data.Store', {
106
+ * model: 'User',
107
+ * sorters: [{
108
+ * property: 'age',
109
+ * direction: 'DESC'
110
+ * }, {
111
+ * property: 'firstName',
112
+ * direction: 'ASC'
113
+ * }],
114
+ *
115
+ * filters: [{
116
+ * property: 'firstName',
117
+ * value: /Ed/
118
+ * }]
119
+ * });
120
+ *
121
+ * The new Store will keep the configured sorters and filters in the MixedCollection instances mentioned above. By
122
+ * default, sorting and filtering are both performed locally by the Store - see {@link #remoteSort} and {@link
123
+ * #remoteFilter} to allow the server to perform these operations instead.
124
+ *
125
+ * Filtering and sorting after the Store has been instantiated is also easy. Calling {@link #filter} adds another filter
126
+ * to the Store and automatically filters the dataset (calling {@link #filter} with no arguments simply re-applies all
127
+ * existing filters). Note that by default {@link #sortOnFilter} is set to true, which means that your sorters are
128
+ * automatically reapplied if using local sorting.
129
+ *
130
+ * store.filter('eyeColor', 'Brown');
131
+ *
132
+ * Change the sorting at any time by calling {@link #sort}:
133
+ *
134
+ * store.sort('height', 'ASC');
135
+ *
136
+ * Note that all existing sorters will be removed in favor of the new sorter data (if {@link #sort} is called with no
137
+ * arguments, the existing sorters are just reapplied instead of being removed). To keep existing sorters and add new
138
+ * ones, just add them to the MixedCollection:
139
+ *
140
+ * store.sorters.add(new Ext.util.Sorter({
141
+ * property : 'shoeSize',
142
+ * direction: 'ASC'
143
+ * }));
144
+ *
145
+ * store.sort();
146
+ *
147
+ * ## Registering with StoreManager
148
+ *
149
+ * Any Store that is instantiated with a {@link #storeId} will automatically be registed with the {@link
150
+ * Ext.data.StoreManager StoreManager}. This makes it easy to reuse the same store in multiple views:
151
+ *
152
+ * //this store can be used several times
153
+ * Ext.create('Ext.data.Store', {
154
+ * model: 'User',
155
+ * storeId: 'usersStore'
156
+ * });
157
+ *
158
+ * new Ext.List({
159
+ * store: 'usersStore',
160
+ * //other config goes here
161
+ * });
162
+ *
163
+ * new Ext.view.View({
164
+ * store: 'usersStore',
165
+ * //other config goes here
166
+ * });
167
+ *
168
+ * ## Further Reading
169
+ *
170
+ * Stores are backed up by an ecosystem of classes that enables their operation. To gain a full understanding of these
171
+ * pieces and how they fit together, see:
172
+ *
173
+ * - {@link Ext.data.proxy.Proxy Proxy} - overview of what Proxies are and how they are used
174
+ * - {@link Ext.data.Model Model} - the core class in the data package
175
+ * - {@link Ext.data.reader.Reader Reader} - used by any subclass of {@link Ext.data.proxy.Server ServerProxy} to read a response
176
+ *
177
+ * @author Ed Spencer
178
+ */
179
+ Ext.define('Ext.data.Store', {
180
+ extend: 'Ext.data.AbstractStore',
181
+
182
+ alias: 'store.store',
183
+
184
+ // Required classes must be loaded before the definition callback runs
185
+ // The class definition callback creates a dummy Store which requires that
186
+ // all the classes below have been loaded.
187
+ requires: [
188
+ 'Ext.data.StoreManager',
189
+ 'Ext.data.Model',
190
+ 'Ext.data.proxy.Ajax',
191
+ 'Ext.data.proxy.Memory',
192
+ 'Ext.data.reader.Json',
193
+ 'Ext.data.writer.Json',
194
+ 'Ext.util.LruCache'
195
+ ],
196
+
197
+ uses: [
198
+ 'Ext.ModelManager',
199
+ 'Ext.util.Grouper'
200
+ ],
201
+
202
+ /**
203
+ * @cfg {Boolean} remoteSort
204
+ * True to defer any sorting operation to the server. If false, sorting is done locally on the client.
205
+ */
206
+ remoteSort: false,
207
+
208
+ /**
209
+ * @cfg {Boolean} remoteFilter
210
+ * True to defer any filtering operation to the server. If false, filtering is done locally on the client.
211
+ */
212
+ remoteFilter: false,
213
+
214
+ /**
215
+ * @cfg {Boolean} remoteGroup
216
+ * True if the grouping should apply on the server side, false if it is local only. If the
217
+ * grouping is local, it can be applied immediately to the data. If it is remote, then it will simply act as a
218
+ * helper, automatically sending the grouping information to the server.
219
+ */
220
+ remoteGroup : false,
221
+
222
+ /**
223
+ * @cfg {String/Ext.data.proxy.Proxy/Object} proxy
224
+ * The Proxy to use for this Store. This can be either a string, a config object or a Proxy instance -
225
+ * see {@link #setProxy} for details.
226
+ */
227
+
228
+ /**
229
+ * @cfg {Object[]/Ext.data.Model[]} data
230
+ * Array of Model instances or data objects to load locally. See "Inline data" above for details.
231
+ */
232
+
233
+ /**
234
+ * @cfg {String} groupField
235
+ * The field by which to group data in the store. Internally, grouping is very similar to sorting - the
236
+ * groupField and {@link #groupDir} are injected as the first sorter (see {@link #sort}). Stores support a single
237
+ * level of grouping, and groups can be fetched via the {@link #getGroups} method.
238
+ */
239
+ groupField: undefined,
240
+
241
+ /**
242
+ * @cfg {String} groupDir
243
+ * The direction in which sorting should be applied when grouping. Supported values are "ASC" and "DESC".
244
+ */
245
+ groupDir: "ASC",
246
+
247
+ /**
248
+ * @cfg {Number} trailingBufferZone
249
+ * When {@link #buffered}, the number of extra records to keep cached on the trailing side of scrolling buffer
250
+ * as scrolling proceeds. A larger number means fewer replenishments from the server.
251
+ */
252
+ trailingBufferZone: 25,
253
+
254
+ /**
255
+ * @cfg {Number} leadingBufferZone
256
+ * When {@link #buffered}, the number of extra rows to keep cached on the leading side of scrolling buffer
257
+ * as scrolling proceeds. A larger number means fewer replenishments from the server.
258
+ */
259
+ leadingBufferZone: 200,
260
+
261
+ /**
262
+ * @cfg {Number} pageSize
263
+ * The number of records considered to form a 'page'. This is used to power the built-in
264
+ * paging using the nextPage and previousPage functions when the grid is paged using a
265
+ * {@link Ext.toolbar.Paging PagingScroller} Defaults to 25.
266
+ *
267
+ * If this Store is {@link #buffered}, pages are loaded into a page cache before the Store's
268
+ * data is updated from the cache. The pageSize is the number of rows loaded into the cache in one request.
269
+ * This will not affect the rendering of a buffered grid, but a larger page size will mean fewer loads.
270
+ *
271
+ * In a buffered grid, scrolling is monitored, and the page cache is kept primed with data ahead of the
272
+ * direction of scroll to provide rapid access to data when scrolling causes it to be required. Several pages
273
+ * in advance may be requested depending on various parameters.
274
+ *
275
+ * It is recommended to tune the {@link #pageSize}, {@link #trailingBufferZone} and
276
+ * {@link #leadingBufferZone} configurations based upon the conditions pertaining in your deployed application.
277
+ *
278
+ * The provided SDK example `examples/grid/infinite-scroll-grid-tuner.html` can be used to experiment with
279
+ * different settings including simulating Ajax latency.
280
+ */
281
+ pageSize: undefined,
282
+
283
+ /**
284
+ * @property {Number} currentPage
285
+ * The page that the Store has most recently loaded (see {@link #loadPage})
286
+ */
287
+ currentPage: 1,
288
+
289
+ /**
290
+ * @cfg {Boolean} clearOnPageLoad
291
+ * True to empty the store when loading another page via {@link #loadPage},
292
+ * {@link #nextPage} or {@link #previousPage}. Setting to false keeps existing records, allowing
293
+ * large data sets to be loaded one page at a time but rendered all together.
294
+ */
295
+ clearOnPageLoad: true,
296
+
297
+ /**
298
+ * @property {Boolean} loading
299
+ * True if the Store is currently loading via its Proxy
300
+ * @private
301
+ */
302
+ loading: false,
303
+
304
+ /**
305
+ * @cfg {Boolean} sortOnFilter
306
+ * For local filtering only, causes {@link #sort} to be called whenever {@link #filter} is called,
307
+ * causing the sorters to be reapplied after filtering. Defaults to true
308
+ */
309
+ sortOnFilter: true,
310
+
311
+ /**
312
+ * @cfg {Boolean} buffered
313
+ * Allows the Store to prefetch and cache in a **page cache**, pages of Records, and to then satisfy
314
+ * loading requirements from this page cache.
315
+ *
316
+ * To use buffered Stores, initiate the process by loading the first page. The number of rows rendered are
317
+ * determined automatically, and the range of pages needed to keep the cache primed for scrolling is
318
+ * requested and cached.
319
+ * Example:
320
+ *
321
+ * // Load page 1
322
+ * myStore.loadPage(1);
323
+ *
324
+ * A {@link Ext.grid.PagingScroller PagingScroller} is instantiated which will monitor the scrolling in the grid, and
325
+ * refresh the view's rows from the page cache as needed. It will also pull new data into the page
326
+ * cache when scrolling of the view draws upon data near either end of the prefetched data.
327
+ *
328
+ * The margins which trigger view refreshing from the prefetched data are {@link Ext.grid.PagingScroller#numFromEdge},
329
+ * {@link Ext.grid.PagingScroller#leadingBufferZone} and {@link Ext.grid.PagingScroller#trailingBufferZone}.
330
+ *
331
+ * The margins which trigger loading more data into the page cache are, {@link #leadingBufferZone} and
332
+ * {@link #trailingBufferZone}.
333
+ *
334
+ * By defult, only 5 pages of data are cached in the page cache, with pages "scrolling" out of the buffer
335
+ * as the view moves down through the dataset. This can be increased by changing the {@link #purgePageSize}
336
+ * value. Setting this value to zero means that no pages are *ever* scrolled out of the page cache, and
337
+ * that eventually the whole dataset may become present in the page cache. This is sometimes desirable
338
+ * as long as datasets do not reach astronomical proportions.
339
+ *
340
+ * Selection state may be maintained across page boundaries by configuring the SelectionModel not to discard
341
+ * records from its collection when those Records cycle out of the Store's primary collection. This is done
342
+ * by configuring the SelectionModel like this:
343
+ *
344
+ * selModel: {
345
+ * pruneRemoved: false
346
+ * }
347
+ *
348
+ */
349
+ buffered: false,
350
+
351
+ /**
352
+ * @cfg {Number} purgePageCount
353
+ * *Valid only when used with a {@link Ext.data.Store#buffered buffered} Store.*
354
+ *
355
+ * The number of pages *additional to the required buffered range* to keep in the prefetch cache before purging least recently used records.
356
+ *
357
+ * For example, if the height of the view area and the configured {@link trailingBufferZone} and {@link #leadingBufferZone} require that there
358
+ * are three pages in the cache, then a `purgePageCount` of 5 ensures that up to 8 pages can be in the page cache any any one time.
359
+ *
360
+ * A value of 0 indicates to never purge the prefetched data.
361
+ */
362
+ purgePageCount: 5,
363
+
364
+ /**
365
+ * @cfg {Boolean} [clearRemovedOnLoad=true]
366
+ * True to clear anything in the {@link #removed} record collection when the store loads.
367
+ */
368
+ clearRemovedOnLoad: true,
369
+
370
+ defaultPageSize: 25,
371
+
372
+ statics: {
373
+ recordIdFn: function(record) {
374
+ return record.internalId;
375
+ },
376
+ recordIndexFn: function(record) {
377
+ return record.index;
378
+ }
379
+ },
380
+
381
+ onClassExtended: function(cls, data, hooks) {
382
+ var model = data.model,
383
+ onBeforeClassCreated;
384
+
385
+ if (typeof model == 'string') {
386
+ onBeforeClassCreated = hooks.onBeforeCreated;
387
+
388
+ hooks.onBeforeCreated = function() {
389
+ var me = this,
390
+ args = arguments;
391
+
392
+ Ext.require(model, function() {
393
+ onBeforeClassCreated.apply(me, args);
394
+ });
395
+ };
396
+ }
397
+ },
398
+
399
+ /**
400
+ * Creates the store.
401
+ * @param {Object} [config] Config object
402
+ */
403
+ constructor: function(config) {
404
+ // Clone the config so we don't modify the original config object
405
+ config = Ext.Object.merge({}, config);
406
+
407
+ var me = this,
408
+ groupers = config.groupers || me.groupers,
409
+ groupField = config.groupField || me.groupField,
410
+ proxy,
411
+ data;
412
+
413
+ /**
414
+ * @event beforeprefetch
415
+ * Fires before a prefetch occurs. Return false to cancel.
416
+ * @param {Ext.data.Store} this
417
+ * @param {Ext.data.Operation} operation The associated operation
418
+ */
419
+ /**
420
+ * @event groupchange
421
+ * Fired whenever the grouping in the grid changes
422
+ * @param {Ext.data.Store} store The store
423
+ * @param {Ext.util.Grouper[]} groupers The array of grouper objects
424
+ */
425
+ /**
426
+ * @event prefetch
427
+ * Fires whenever records have been prefetched
428
+ * @param {Ext.data.Store} this
429
+ * @param {Ext.data.Model[]} records An array of records.
430
+ * @param {Boolean} successful True if the operation was successful.
431
+ * @param {Ext.data.Operation} operation The associated operation
432
+ */
433
+ data = config.data || me.data;
434
+
435
+ /**
436
+ * @property {Ext.util.MixedCollection} data
437
+ * The MixedCollection that holds this store's local cache of records.
438
+ */
439
+ me.data = new Ext.util.MixedCollection(false, Ext.data.Store.recordIdFn);
440
+
441
+ if (data) {
442
+ me.inlineData = data;
443
+ delete config.data;
444
+ }
445
+
446
+ if (!groupers && groupField) {
447
+ groupers = [{
448
+ property : groupField,
449
+ direction: config.groupDir || me.groupDir
450
+ }];
451
+ }
452
+ delete config.groupers;
453
+
454
+ /**
455
+ * @property {Ext.util.MixedCollection} groupers
456
+ * The collection of {@link Ext.util.Grouper Groupers} currently applied to this Store.
457
+ */
458
+ me.groupers = new Ext.util.MixedCollection();
459
+ me.groupers.addAll(me.decodeGroupers(groupers));
460
+
461
+ this.callParent([config]);
462
+ // don't use *config* anymore from here on... use *me* instead...
463
+
464
+ if (me.buffered) {
465
+
466
+ // Create our page map.
467
+ // Whenever it gets cleared, it means we re no longer interested in
468
+ // any outstanding page prefetches, so cancel tham all
469
+ me.pageMap = new me.PageMap({
470
+ pageSize: me.pageSize,
471
+ maxSize: me.purgePageCount,
472
+ listeners: {
473
+ clear: me.cancelAllPrefetches,
474
+ scope: me
475
+ }
476
+ });
477
+ me.pageRequests = {};
478
+
479
+ me.sortOnLoad = false;
480
+ me.filterOnLoad = false;
481
+ }
482
+
483
+ if (me.groupers.items.length) {
484
+ me.sort(me.groupers.items, 'prepend', false);
485
+ }
486
+
487
+ proxy = me.proxy;
488
+ data = me.inlineData;
489
+
490
+ // Page size for non-buffered Store defaults to 25
491
+ // For a buffered Store, the default page size is taken from the initial call to prefetch.
492
+ if (!me.buffered && !me.pageSize) {
493
+ me.pageSize = me.defaultPageSize;
494
+ }
495
+
496
+ if (data) {
497
+ if (proxy instanceof Ext.data.proxy.Memory) {
498
+ proxy.data = data;
499
+ me.read();
500
+ } else {
501
+ me.add.apply(me, [data]);
502
+ }
503
+
504
+ me.sort();
505
+ delete me.inlineData;
506
+ } else if (me.autoLoad) {
507
+ Ext.defer(me.load, 10, me, [ typeof me.autoLoad === 'object' ? me.autoLoad : undefined ]);
508
+ // Remove the defer call, we may need reinstate this at some point, but currently it's not obvious why it's here.
509
+ // this.load(typeof this.autoLoad == 'object' ? this.autoLoad : undefined);
510
+ }
511
+ },
512
+
513
+ destroy: function() {
514
+ // Release cached pages.
515
+ // Will also ccancel outstanding prefetch requests, and cause a generation change
516
+ // so that incoming prefetch data will be ignored.
517
+ if (this.pageMap) {
518
+ this.pageMap.clear();
519
+ }
520
+ this.callParent(arguments);
521
+ },
522
+
523
+ onBeforeSort: function() {
524
+ var groupers = this.groupers;
525
+ if (groupers.getCount() > 0) {
526
+ this.sort(groupers.items, 'prepend', false);
527
+ }
528
+ },
529
+
530
+ /**
531
+ * @private
532
+ * Normalizes an array of grouper objects, ensuring that they are all Ext.util.Grouper instances
533
+ * @param {Object[]} groupers The groupers array
534
+ * @return {Ext.util.Grouper[]} Array of Ext.util.Grouper objects
535
+ */
536
+ decodeGroupers: function(groupers) {
537
+ if (!Ext.isArray(groupers)) {
538
+ if (groupers === undefined) {
539
+ groupers = [];
540
+ } else {
541
+ groupers = [groupers];
542
+ }
543
+ }
544
+
545
+ var length = groupers.length,
546
+ Grouper = Ext.util.Grouper,
547
+ config, i, result = [];
548
+
549
+ for (i = 0; i < length; i++) {
550
+ config = groupers[i];
551
+
552
+ if (!(config instanceof Grouper)) {
553
+ if (Ext.isString(config)) {
554
+ config = {
555
+ property: config
556
+ };
557
+ }
558
+
559
+ config = Ext.apply({
560
+ root : 'data',
561
+ direction: "ASC"
562
+ }, config);
563
+
564
+ //support for 3.x style sorters where a function can be defined as 'fn'
565
+ if (config.fn) {
566
+ config.sorterFn = config.fn;
567
+ }
568
+
569
+ //support a function to be passed as a sorter definition
570
+ if (typeof config == 'function') {
571
+ config = {
572
+ sorterFn: config
573
+ };
574
+ }
575
+
576
+ // return resulting Groupers in a separate array so as not to mutate passed in data objects.
577
+ result.push(new Grouper(config));
578
+ } else {
579
+ result.push(config);
580
+ }
581
+ }
582
+ return result;
583
+ },
584
+
585
+ /**
586
+ * Groups data inside the store.
587
+ * @param {String/Object[]} groupers Either a string name of one of the fields in this Store's
588
+ * configured {@link Ext.data.Model Model}, or an Array of grouper configurations.
589
+ * @param {String} [direction="ASC"] The overall direction to group the data by.
590
+ */
591
+ group: function(groupers, direction) {
592
+ var me = this,
593
+ hasNew = false,
594
+ grouper,
595
+ newGroupers;
596
+
597
+ if (Ext.isArray(groupers)) {
598
+ newGroupers = groupers;
599
+ } else if (Ext.isObject(groupers)) {
600
+ newGroupers = [groupers];
601
+ } else if (Ext.isString(groupers)) {
602
+ grouper = me.groupers.get(groupers);
603
+
604
+ if (!grouper) {
605
+ grouper = {
606
+ property : groupers,
607
+ direction: direction
608
+ };
609
+ newGroupers = [grouper];
610
+ } else if (direction === undefined) {
611
+ grouper.toggle();
612
+ } else {
613
+ grouper.setDirection(direction);
614
+ }
615
+ }
616
+
617
+ if (newGroupers && newGroupers.length) {
618
+ hasNew = true;
619
+ newGroupers = me.decodeGroupers(newGroupers);
620
+ me.groupers.clear();
621
+ me.groupers.addAll(newGroupers);
622
+ }
623
+
624
+ if (me.remoteGroup) {
625
+ me.load({
626
+ scope: me,
627
+ callback: me.fireGroupChange
628
+ });
629
+ } else {
630
+ // need to explicitly force a sort if we have groupers
631
+ me.sort(null, null, null, hasNew);
632
+ me.fireGroupChange();
633
+ }
634
+ },
635
+
636
+ /**
637
+ * Clear any groupers in the store
638
+ */
639
+ clearGrouping: function() {
640
+ var me = this,
641
+ groupers = me.groupers.items,
642
+ gLen = groupers.length,
643
+ grouper, g;
644
+
645
+ for (g = 0; g < gLen; g++) {
646
+ grouper = groupers[g];
647
+
648
+ me.sorters.remove(grouper);
649
+ }
650
+
651
+ me.groupers.clear();
652
+ if (me.remoteGroup) {
653
+ me.load({
654
+ scope: me,
655
+ callback: me.fireGroupChange
656
+ });
657
+ } else {
658
+ me.sort();
659
+ me.fireEvent('groupchange', me, me.groupers);
660
+ }
661
+ },
662
+
663
+ /**
664
+ * Checks if the store is currently grouped
665
+ * @return {Boolean} True if the store is grouped.
666
+ */
667
+ isGrouped: function() {
668
+ return this.groupers.getCount() > 0;
669
+ },
670
+
671
+ /**
672
+ * Fires the groupchange event. Abstracted out so we can use it
673
+ * as a callback
674
+ * @private
675
+ */
676
+ fireGroupChange: function() {
677
+ this.fireEvent('groupchange', this, this.groupers);
678
+ },
679
+
680
+ /**
681
+ * Returns an array containing the result of applying grouping to the records in this store.
682
+ * See {@link #groupField}, {@link #groupDir} and {@link #getGroupString}. Example for a store
683
+ * containing records with a color field:
684
+ *
685
+ * var myStore = Ext.create('Ext.data.Store', {
686
+ * groupField: 'color',
687
+ * groupDir : 'DESC'
688
+ * });
689
+ *
690
+ * myStore.getGroups(); // returns:
691
+ * [
692
+ * {
693
+ * name: 'yellow',
694
+ * children: [
695
+ * // all records where the color field is 'yellow'
696
+ * ]
697
+ * },
698
+ * {
699
+ * name: 'red',
700
+ * children: [
701
+ * // all records where the color field is 'red'
702
+ * ]
703
+ * }
704
+ * ]
705
+ *
706
+ * Group contents are effected by filtering.
707
+ *
708
+ * @param {String} [groupName] Pass in an optional groupName argument to access a specific
709
+ * group as defined by {@link #getGroupString}.
710
+ * @return {Object/Object[]} The grouped data
711
+ */
712
+ getGroups: function(requestGroupString) {
713
+ var records = this.data.items,
714
+ length = records.length,
715
+ groups = [],
716
+ pointers = {},
717
+ record,
718
+ groupStr,
719
+ group,
720
+ i;
721
+
722
+ for (i = 0; i < length; i++) {
723
+ record = records[i];
724
+ groupStr = this.getGroupString(record);
725
+ group = pointers[groupStr];
726
+
727
+ if (group === undefined) {
728
+ group = {
729
+ name: groupStr,
730
+ children: []
731
+ };
732
+
733
+ groups.push(group);
734
+ pointers[groupStr] = group;
735
+ }
736
+
737
+ group.children.push(record);
738
+ }
739
+
740
+ return requestGroupString ? pointers[requestGroupString] : groups;
741
+ },
742
+
743
+ /**
744
+ * @private
745
+ * For a given set of records and a Grouper, returns an array of arrays - each of which is the set of records
746
+ * matching a certain group.
747
+ */
748
+ getGroupsForGrouper: function(records, grouper) {
749
+ var length = records.length,
750
+ groups = [],
751
+ oldValue,
752
+ newValue,
753
+ record,
754
+ group,
755
+ i;
756
+
757
+ for (i = 0; i < length; i++) {
758
+ record = records[i];
759
+ newValue = grouper.getGroupString(record);
760
+
761
+ if (newValue !== oldValue) {
762
+ group = {
763
+ name: newValue,
764
+ grouper: grouper,
765
+ records: []
766
+ };
767
+ groups.push(group);
768
+ }
769
+
770
+ group.records.push(record);
771
+
772
+ oldValue = newValue;
773
+ }
774
+
775
+ return groups;
776
+ },
777
+
778
+ /**
779
+ * @private
780
+ * This is used recursively to gather the records into the configured Groupers. The data MUST have been sorted for
781
+ * this to work properly (see {@link #getGroupData} and {@link #getGroupsForGrouper}) Most of the work is done by
782
+ * {@link #getGroupsForGrouper} - this function largely just handles the recursion.
783
+ *
784
+ * @param {Ext.data.Model[]} records The set or subset of records to group
785
+ * @param {Number} grouperIndex The grouper index to retrieve
786
+ * @return {Object[]} The grouped records
787
+ */
788
+ getGroupsForGrouperIndex: function(records, grouperIndex) {
789
+ var me = this,
790
+ groupers = me.groupers,
791
+ grouper = groupers.getAt(grouperIndex),
792
+ groups = me.getGroupsForGrouper(records, grouper),
793
+ length = groups.length,
794
+ i;
795
+
796
+ if (grouperIndex + 1 < groupers.length) {
797
+ for (i = 0; i < length; i++) {
798
+ groups[i].children = me.getGroupsForGrouperIndex(groups[i].records, grouperIndex + 1);
799
+ }
800
+ }
801
+
802
+ for (i = 0; i < length; i++) {
803
+ groups[i].depth = grouperIndex;
804
+ }
805
+
806
+ return groups;
807
+ },
808
+
809
+ /**
810
+ * @private
811
+ * Returns records grouped by the configured {@link #groupers grouper} configuration. Sample return value (in
812
+ * this case grouping by genre and then author in a fictional books dataset):
813
+ *
814
+ * [
815
+ * {
816
+ * name: 'Fantasy',
817
+ * depth: 0,
818
+ * records: [
819
+ * //book1, book2, book3, book4
820
+ * ],
821
+ * children: [
822
+ * {
823
+ * name: 'Rowling',
824
+ * depth: 1,
825
+ * records: [
826
+ * //book1, book2
827
+ * ]
828
+ * },
829
+ * {
830
+ * name: 'Tolkein',
831
+ * depth: 1,
832
+ * records: [
833
+ * //book3, book4
834
+ * ]
835
+ * }
836
+ * ]
837
+ * }
838
+ * ]
839
+ *
840
+ * @param {Boolean} [sort=true] True to call {@link #sort} before finding groups. Sorting is required to make grouping
841
+ * function correctly so this should only be set to false if the Store is known to already be sorted correctly.
842
+ * @return {Object[]} The group data
843
+ */
844
+ getGroupData: function(sort) {
845
+ var me = this;
846
+ if (sort !== false) {
847
+ me.sort();
848
+ }
849
+
850
+ return me.getGroupsForGrouperIndex(me.data.items, 0);
851
+ },
852
+
853
+ /**
854
+ * Returns the string to group on for a given model instance. The default implementation of this method returns
855
+ * the model's {@link #groupField}, but this can be overridden to group by an arbitrary string. For example, to
856
+ * group by the first letter of a model's 'name' field, use the following code:
857
+ *
858
+ * Ext.create('Ext.data.Store', {
859
+ * groupDir: 'ASC',
860
+ * getGroupString: function(instance) {
861
+ * return instance.get('name')[0];
862
+ * }
863
+ * });
864
+ *
865
+ * @param {Ext.data.Model} instance The model instance
866
+ * @return {String} The string to compare when forming groups
867
+ */
868
+ getGroupString: function(instance) {
869
+ var group = this.groupers.first();
870
+ if (group) {
871
+ return instance.get(group.property);
872
+ }
873
+ return '';
874
+ },
875
+
876
+ /**
877
+ * Inserts Model instances into the Store at the given index and fires the {@link #event-add} event.
878
+ * See also {@link #method-add}.
879
+ *
880
+ * @param {Number} index The start index at which to insert the passed Records.
881
+ * @param {Ext.data.Model[]} records An Array of Ext.data.Model objects to add to the store.
882
+ */
883
+ insert: function(index, records) {
884
+ var me = this,
885
+ sync = false,
886
+ i,
887
+ record,
888
+ len;
889
+
890
+ records = [].concat(records);
891
+ for (i = 0,len = records.length; i < len; i++) {
892
+ record = me.createModel(records[i]);
893
+ record.set(me.modelDefaults);
894
+ // reassign the model in the array in case it wasn't created yet
895
+ records[i] = record;
896
+
897
+ me.data.insert(index + i, record);
898
+ record.join(me);
899
+
900
+ sync = sync || record.phantom === true;
901
+ }
902
+
903
+ if (me.snapshot) {
904
+ me.snapshot.addAll(records);
905
+ }
906
+
907
+ if (me.requireSort) {
908
+ // suspend events so the usual data changed events don't get fired.
909
+ me.suspendEvents();
910
+ me.sort();
911
+ me.resumeEvents();
912
+ }
913
+
914
+ me.fireEvent('add', me, records, index);
915
+ me.fireEvent('datachanged', me);
916
+ if (me.autoSync && sync && !me.autoSyncSuspended) {
917
+ me.sync();
918
+ }
919
+ },
920
+
921
+ /**
922
+ * Adds Model instance to the Store. This method accepts either:
923
+ *
924
+ * - An array of Model instances or Model configuration objects.
925
+ * - Any number of Model instance or Model configuration object arguments.
926
+ *
927
+ * The new Model instances will be added at the end of the existing collection.
928
+ *
929
+ * Sample usage:
930
+ *
931
+ * myStore.add({some: 'data'}, {some: 'other data'});
932
+ *
933
+ * Note that if this Store is sorted, the new Model instances will be inserted
934
+ * at the correct point in the Store to maintain the sort order.
935
+ *
936
+ * @param {Ext.data.Model[]/Ext.data.Model...} model An array of Model instances
937
+ * or Model configuration objects, or variable number of Model instance or config arguments.
938
+ * @return {Ext.data.Model[]} The model instances that were added
939
+ */
940
+ add: function(records) {
941
+ //accept both a single-argument array of records, or any number of record arguments
942
+ if (!Ext.isArray(records)) {
943
+ records = Array.prototype.slice.apply(arguments);
944
+ } else {
945
+ // Create an array copy
946
+ records = records.slice(0);
947
+ }
948
+
949
+ var me = this,
950
+ i = 0,
951
+ length = records.length,
952
+ record,
953
+ isSorted = me.sorters && me.sorters.items.length;
954
+
955
+ // If this Store is sorted, and they only passed one Record (99% or use cases)
956
+ // then it's much more efficient to add it sorted than to append and then sort.
957
+ if (isSorted && length === 1) {
958
+ return [ me.addSorted(me.createModel(records[0])) ];
959
+ }
960
+
961
+ for (; i < length; i++) {
962
+ record = me.createModel(records[i]);
963
+ // reassign the model in the array in case it wasn't created yet
964
+ records[i] = record;
965
+ }
966
+
967
+ // If this sort is sorted, set the flag used by the insert method to sort
968
+ // before firing events.
969
+ if (isSorted) {
970
+ me.requireSort = true;
971
+ }
972
+
973
+ me.insert(me.data.length, records);
974
+ delete me.requireSort;
975
+
976
+ return records;
977
+ },
978
+
979
+ /**
980
+ * (Local sort only) Inserts the passed Record into the Store at the index where it
981
+ * should go based on the current sort information.
982
+ *
983
+ * @param {Ext.data.Record} record
984
+ */
985
+ addSorted: function(record) {
986
+ var me = this,
987
+ index = me.data.findInsertionIndex(record, me.generateComparator());
988
+
989
+ me.insert(index, record);
990
+ return record;
991
+ },
992
+
993
+ /**
994
+ * Converts a literal to a model, if it's not a model already
995
+ * @private
996
+ * @param {Ext.data.Model/Object} record The record to create
997
+ * @return {Ext.data.Model}
998
+ */
999
+ createModel: function(record) {
1000
+ if (!record.isModel) {
1001
+ record = Ext.ModelManager.create(record, this.model);
1002
+ }
1003
+
1004
+ return record;
1005
+ },
1006
+
1007
+ /**
1008
+ * Calls the specified function for each {@link Ext.data.Model record} in the store.
1009
+ *
1010
+ * When store is filtered, only loops over the filtered records.
1011
+ *
1012
+ * @param {Function} fn The function to call. The {@link Ext.data.Model Record} is passed as the first parameter.
1013
+ * Returning `false` aborts and exits the iteration.
1014
+ * @param {Object} [scope] The scope (this reference) in which the function is executed.
1015
+ * Defaults to the current {@link Ext.data.Model record} in the iteration.
1016
+ */
1017
+ each: function(fn, scope) {
1018
+ var data = this.data.items,
1019
+ dLen = data.length,
1020
+ record, d;
1021
+
1022
+ for (d = 0; d < dLen; d++) {
1023
+ record = data[d];
1024
+ if (fn.call(scope || record, record, d, dLen) === false) {
1025
+ break;
1026
+ }
1027
+ }
1028
+ },
1029
+
1030
+ /**
1031
+ * Removes the given record from the Store, firing the 'remove' event for each instance that is removed,
1032
+ * plus a single 'datachanged' event after removal.
1033
+ *
1034
+ * @param {Ext.data.Model/Ext.data.Model[]} records Model instance or array of instances to remove.
1035
+ */
1036
+ remove: function(records, /* private */ isMove) {
1037
+ if (!Ext.isArray(records)) {
1038
+ records = [records];
1039
+ }
1040
+
1041
+ /*
1042
+ * Pass the isMove parameter if we know we're going to be re-inserting this record
1043
+ */
1044
+ isMove = isMove === true;
1045
+ var me = this,
1046
+ sync = false,
1047
+ i = 0,
1048
+ length = records.length,
1049
+ isPhantom,
1050
+ index,
1051
+ record;
1052
+
1053
+ for (; i < length; i++) {
1054
+ record = records[i];
1055
+ index = me.data.indexOf(record);
1056
+
1057
+ if (me.snapshot) {
1058
+ me.snapshot.remove(record);
1059
+ }
1060
+
1061
+ if (index > -1) {
1062
+ isPhantom = record.phantom === true;
1063
+
1064
+ if (!isMove && !isPhantom) {
1065
+ // don't push phantom records onto removed
1066
+ me.removed.push(record);
1067
+ }
1068
+
1069
+ record.unjoin(me);
1070
+ me.data.remove(record);
1071
+ sync = sync || !isPhantom;
1072
+
1073
+ me.fireEvent('remove', me, record, index);
1074
+ }
1075
+ }
1076
+
1077
+ me.fireEvent('datachanged', me);
1078
+ if (!isMove && me.autoSync && sync && !me.autoSyncSuspended) {
1079
+ me.sync();
1080
+ }
1081
+ },
1082
+
1083
+ /**
1084
+ * Removes the model instance at the given index
1085
+ * @param {Number} index The record index
1086
+ */
1087
+ removeAt: function(index) {
1088
+ var record = this.getAt(index);
1089
+
1090
+ if (record) {
1091
+ this.remove(record);
1092
+ }
1093
+ },
1094
+
1095
+ /**
1096
+ * Loads data into the Store via the configured {@link #proxy}. This uses the Proxy to make an
1097
+ * asynchronous call to whatever storage backend the Proxy uses, automatically adding the retrieved
1098
+ * instances into the Store and calling an optional callback if required. Example usage:
1099
+ *
1100
+ * store.load({
1101
+ * scope: this,
1102
+ * callback: function(records, operation, success) {
1103
+ * // the {@link Ext.data.Operation operation} object
1104
+ * // contains all of the details of the load operation
1105
+ * console.log(records);
1106
+ * }
1107
+ * });
1108
+ *
1109
+ * If the callback scope does not need to be set, a function can simply be passed:
1110
+ *
1111
+ * store.load(function(records, operation, success) {
1112
+ * console.log('loaded records');
1113
+ * });
1114
+ *
1115
+ * @param {Object/Function} [options] config object, passed into the Ext.data.Operation object before loading.
1116
+ * Additionally `addRecords: true` can be specified to add these records to the existing records, default is
1117
+ * to remove the Store's existing records first.
1118
+ */
1119
+ load: function(options) {
1120
+ var me = this;
1121
+
1122
+ options = options || {};
1123
+
1124
+ if (typeof options == 'function') {
1125
+ options = {
1126
+ callback: options
1127
+ };
1128
+ }
1129
+
1130
+ options.groupers = options.groupers || me.groupers.items;
1131
+ options.page = options.page || me.currentPage;
1132
+ options.start = (options.start !== undefined) ? options.start : (me.currentPage - 1) * me.pageSize;
1133
+ options.limit = options.limit || me.pageSize;
1134
+ options.addRecords = options.addRecords || false;
1135
+
1136
+ if (me.buffered) {
1137
+ return me.loadToPrefetch(options);
1138
+ }
1139
+ return me.callParent([options]);
1140
+ },
1141
+
1142
+ /**
1143
+ * @private
1144
+ * Called internally when a Proxy has completed a load request
1145
+ */
1146
+ onProxyLoad: function(operation) {
1147
+ var me = this,
1148
+ resultSet = operation.getResultSet(),
1149
+ records = operation.getRecords(),
1150
+ successful = operation.wasSuccessful();
1151
+
1152
+ if (resultSet) {
1153
+ me.totalCount = resultSet.total;
1154
+ }
1155
+
1156
+ if (successful) {
1157
+ me.loadRecords(records, operation);
1158
+ }
1159
+
1160
+ me.loading = false;
1161
+ me.fireEvent('load', me, records, successful);
1162
+
1163
+ //TODO: deprecate this event, it should always have been 'load' instead. 'load' is now documented, 'read' is not.
1164
+ //People are definitely using this so can't deprecate safely until 2.x
1165
+ me.fireEvent('read', me, records, operation.wasSuccessful());
1166
+
1167
+ //this is a callback that would have been passed to the 'read' function and is optional
1168
+ Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
1169
+ },
1170
+
1171
+ //inherit docs
1172
+ getNewRecords: function() {
1173
+ return this.data.filterBy(this.filterNew).items;
1174
+ },
1175
+
1176
+ //inherit docs
1177
+ getUpdatedRecords: function() {
1178
+ return this.data.filterBy(this.filterUpdated).items;
1179
+ },
1180
+
1181
+ /**
1182
+ * Filters the loaded set of records by a given set of filters.
1183
+ *
1184
+ * By default, the passed filter(s) are *added* to the collection of filters being used to filter this Store.
1185
+ *
1186
+ * To remove existing filters before applying a new set of filters use
1187
+ *
1188
+ * // Clear the filter collection without updating the UI
1189
+ * store.clearFilter(true);
1190
+ *
1191
+ * see (@link #clearFilter}.
1192
+ *
1193
+ * Alternatively, if filters are configured with an `id`, then existing filters store may be *replaced* by new
1194
+ * filters having the same `id`.
1195
+ *
1196
+ * Filtering by single field:
1197
+ *
1198
+ * store.filter("email", /\.com$/);
1199
+ *
1200
+ * Using multiple filters:
1201
+ *
1202
+ * store.filter([
1203
+ * {property: "email", value: /\.com$/},
1204
+ * {filterFn: function(item) { return item.get("age") > 10; }}
1205
+ * ]);
1206
+ *
1207
+ * Using Ext.util.Filter instances instead of config objects
1208
+ * (note that we need to specify the {@link Ext.util.Filter#root root} config option in this case):
1209
+ *
1210
+ * store.filter([
1211
+ * Ext.create('Ext.util.Filter', {property: "email", value: /\.com$/, root: 'data'}),
1212
+ * Ext.create('Ext.util.Filter', {filterFn: function(item) { return item.get("age") > 10; }, root: 'data'})
1213
+ * ]);
1214
+ *
1215
+ * When store is filtered, most of the methods for accessing store data will be working only
1216
+ * within the set of filtered records. Two notable exceptions are {@link #queryBy} and
1217
+ * {@link #getById}.
1218
+ *
1219
+ * @param {Object[]/Ext.util.Filter[]/String} filters The set of filters to apply to the data.
1220
+ * These are stored internally on the store, but the filtering itself is done on the Store's
1221
+ * {@link Ext.util.MixedCollection MixedCollection}. See MixedCollection's
1222
+ * {@link Ext.util.MixedCollection#filter filter} method for filter syntax.
1223
+ * Alternatively, pass in a property string
1224
+ * @param {String} [value] value to filter by (only if using a property string as the first argument)
1225
+ */
1226
+ filter: function(filters, value) {
1227
+ if (Ext.isString(filters)) {
1228
+ filters = {
1229
+ property: filters,
1230
+ value: value
1231
+ };
1232
+ }
1233
+
1234
+ var me = this,
1235
+ decoded = me.decodeFilters(filters),
1236
+ i = 0,
1237
+ doLocalSort = me.sorters.length && me.sortOnFilter && !me.remoteSort,
1238
+ length = decoded.length;
1239
+
1240
+ for (; i < length; i++) {
1241
+ me.filters.replace(decoded[i]);
1242
+ }
1243
+
1244
+ if (me.remoteFilter) {
1245
+ // For a buffered Store, we have to clear the prefetch cache because the dataset will change upon filtering.
1246
+ // Then we must prefetch the new page 1, and when that arrives, reload the visible part of the Store
1247
+ // via the guaranteedrange event
1248
+ if (me.buffered) {
1249
+ me.pageMap.clear();
1250
+ me.loadPage(1);
1251
+ } else {
1252
+ // Reset to the first page, the filter is likely to produce a smaller data set
1253
+ me.currentPage = 1;
1254
+ //the load function will pick up the new filters and request the filtered data from the proxy
1255
+ me.load();
1256
+ }
1257
+ } else {
1258
+ /**
1259
+ * @property {Ext.util.MixedCollection} snapshot
1260
+ * A pristine (unfiltered) collection of the records in this store. This is used to reinstate
1261
+ * records when a filter is removed or changed
1262
+ */
1263
+ if (me.filters.getCount()) {
1264
+ me.snapshot = me.snapshot || me.data.clone();
1265
+ me.data = me.data.filter(me.filters.items);
1266
+
1267
+ if (doLocalSort) {
1268
+ me.sort();
1269
+ } else {
1270
+ // fire datachanged event if it hasn't already been fired by doSort
1271
+ me.fireEvent('datachanged', me);
1272
+ me.fireEvent('refresh', me);
1273
+ }
1274
+ }
1275
+ }
1276
+ },
1277
+
1278
+ /**
1279
+ * Reverts to a view of the Record cache with no filtering applied.
1280
+ * @param {Boolean} suppressEvent If `true` the filter is cleared silently.
1281
+ *
1282
+ * For a locally filtered Store, this means that the filter collection is cleared without firing the
1283
+ * {@link #datachanged} event.
1284
+ *
1285
+ * For a remotely filtered Store, this means that the filter collection is cleared, but the store
1286
+ * is not reloaded from the server.
1287
+ */
1288
+ clearFilter: function(suppressEvent) {
1289
+ var me = this;
1290
+
1291
+ me.filters.clear();
1292
+
1293
+ if (me.remoteFilter) {
1294
+
1295
+ // In a buffered Store, the meaing of suppressEvent is to simply clear the filters collection
1296
+ if (suppressEvent) {
1297
+ return;
1298
+ }
1299
+
1300
+ // For a buffered Store, we have to clear the prefetch cache because the dataset will change upon filtering.
1301
+ // Then we must prefetch the new page 1, and when that arrives, reload the visible part of the Store
1302
+ // via the guaranteedrange event
1303
+ if (me.buffered) {
1304
+ me.pageMap.clear();
1305
+ me.loadPage(1);
1306
+ } else {
1307
+ // Reset to the first page, clearing a filter will destroy the context of the current dataset
1308
+ me.currentPage = 1;
1309
+ me.load();
1310
+ }
1311
+ } else if (me.isFiltered()) {
1312
+ me.data = me.snapshot.clone();
1313
+ delete me.snapshot;
1314
+
1315
+ if (suppressEvent !== true) {
1316
+ me.fireEvent('datachanged', me);
1317
+ me.fireEvent('refresh', me);
1318
+ }
1319
+ }
1320
+ },
1321
+
1322
+ /**
1323
+ * Returns true if this store is currently filtered
1324
+ * @return {Boolean}
1325
+ */
1326
+ isFiltered: function() {
1327
+ var snapshot = this.snapshot;
1328
+ return !! snapshot && snapshot !== this.data;
1329
+ },
1330
+
1331
+ /**
1332
+ * Filters by a function. The specified function will be called for each
1333
+ * Record in this Store. If the function returns `true` the Record is included,
1334
+ * otherwise it is filtered out.
1335
+ *
1336
+ * When store is filtered, most of the methods for accessing store data will be working only
1337
+ * within the set of filtered records. Two notable exceptions are {@link #queryBy} and
1338
+ * {@link #getById}.
1339
+ *
1340
+ * @param {Function} fn The function to be called. It will be passed the following parameters:
1341
+ * @param {Ext.data.Model} fn.record The record to test for filtering. Access field values
1342
+ * using {@link Ext.data.Model#get}.
1343
+ * @param {Object} fn.id The ID of the Record passed.
1344
+ * @param {Object} [scope] The scope (this reference) in which the function is executed.
1345
+ * Defaults to this Store.
1346
+ */
1347
+ filterBy: function(fn, scope) {
1348
+ var me = this;
1349
+
1350
+ me.snapshot = me.snapshot || me.data.clone();
1351
+ me.data = me.queryBy(fn, scope || me);
1352
+ me.fireEvent('datachanged', me);
1353
+ me.fireEvent('refresh', me);
1354
+ },
1355
+
1356
+ /**
1357
+ * Query all the cached records in this Store using a filtering function. The specified function
1358
+ * will be called with each record in this Store. If the function returns `true` the record is
1359
+ * included in the results.
1360
+ *
1361
+ * This method is not effected by filtering, it will always look from all records inside the store
1362
+ * no matter if filter is applied or not.
1363
+ *
1364
+ * @param {Function} fn The function to be called. It will be passed the following parameters:
1365
+ * @param {Ext.data.Model} fn.record The record to test for filtering. Access field values
1366
+ * using {@link Ext.data.Model#get}.
1367
+ * @param {Object} fn.id The ID of the Record passed.
1368
+ * @param {Object} [scope] The scope (this reference) in which the function is executed
1369
+ * Defaults to this Store.
1370
+ * @return {Ext.util.MixedCollection} Returns an Ext.util.MixedCollection of the matched records
1371
+ */
1372
+ queryBy: function(fn, scope) {
1373
+ var me = this,
1374
+ data = me.snapshot || me.data;
1375
+ return data.filterBy(fn, scope || me);
1376
+ },
1377
+
1378
+ /**
1379
+ * Loads an array of data straight into the Store.
1380
+ *
1381
+ * Using this method is great if the data is in the correct format already (e.g. it doesn't need to be
1382
+ * processed by a reader). If your data requires processing to decode the data structure, use a
1383
+ * {@link Ext.data.proxy.Memory MemoryProxy} instead.
1384
+ *
1385
+ * @param {Ext.data.Model[]/Object[]} data Array of data to load. Any non-model instances will be cast
1386
+ * into model instances first.
1387
+ * @param {Boolean} [append=false] True to add the records to the existing records in the store, false
1388
+ * to remove the old ones first.
1389
+ */
1390
+ loadData: function(data, append) {
1391
+ var model = this.model,
1392
+ length = data.length,
1393
+ newData = [],
1394
+ i,
1395
+ record;
1396
+
1397
+ //make sure each data element is an Ext.data.Model instance
1398
+ for (i = 0; i < length; i++) {
1399
+ record = data[i];
1400
+
1401
+ if (!(record instanceof Ext.data.Model)) {
1402
+ record = Ext.ModelManager.create(record, model);
1403
+ }
1404
+ newData.push(record);
1405
+ }
1406
+
1407
+ this.loadRecords(newData, {addRecords: append});
1408
+ },
1409
+
1410
+
1411
+ /**
1412
+ * Loads data via the bound Proxy's reader
1413
+ *
1414
+ * Use this method if you are attempting to load data and want to utilize the configured data reader.
1415
+ *
1416
+ * @param {Object[]} data The full JSON object you'd like to load into the Data store.
1417
+ * @param {Boolean} [append=false] True to add the records to the existing records in the store, false
1418
+ * to remove the old ones first.
1419
+ */
1420
+ loadRawData : function(data, append) {
1421
+ var me = this,
1422
+ result = me.proxy.reader.read(data),
1423
+ records = result.records;
1424
+
1425
+ if (result.success) {
1426
+ me.totalCount = result.total;
1427
+ me.loadRecords(records, { addRecords: append });
1428
+ me.fireEvent('load', me, records, true);
1429
+ }
1430
+ },
1431
+
1432
+
1433
+ /**
1434
+ * Loads an array of {@link Ext.data.Model model} instances into the store, fires the datachanged event. This should only usually
1435
+ * be called internally when loading from the {@link Ext.data.proxy.Proxy Proxy}, when adding records manually use {@link #method-add} instead
1436
+ * @param {Ext.data.Model[]} records The array of records to load
1437
+ * @param {Object} options {addRecords: true} to add these records to the existing records, false to remove the Store's existing records first
1438
+ */
1439
+ loadRecords: function(records, options) {
1440
+ var me = this,
1441
+ i = 0,
1442
+ length = records.length,
1443
+ start = (options = options || {}).start,
1444
+ snapshot = me.snapshot;
1445
+
1446
+ if (!options.addRecords) {
1447
+ delete me.snapshot;
1448
+ me.clearData(true);
1449
+ } else if (snapshot) {
1450
+ snapshot.addAll(records);
1451
+ }
1452
+
1453
+ me.data.addAll(records);
1454
+
1455
+ if (typeof start != 'undefined') {
1456
+ for (; i < length; i++) {
1457
+ records[i].index = start + i;
1458
+ records[i].join(me);
1459
+ }
1460
+ } else {
1461
+ for (; i < length; i++) {
1462
+ records[i].join(me);
1463
+ }
1464
+ }
1465
+
1466
+ /*
1467
+ * this rather inelegant suspension and resumption of events is required because both the filter and sort functions
1468
+ * fire an additional datachanged event, which is not wanted. Ideally we would do this a different way. The first
1469
+ * datachanged event is fired by the call to this.add, above.
1470
+ */
1471
+ me.suspendEvents();
1472
+
1473
+ if (me.filterOnLoad && !me.remoteFilter) {
1474
+ me.filter();
1475
+ }
1476
+
1477
+ if (me.sortOnLoad && !me.remoteSort) {
1478
+ me.sort();
1479
+ }
1480
+
1481
+ me.resumeEvents();
1482
+ me.fireEvent('datachanged', me);
1483
+ me.fireEvent('refresh', me);
1484
+ },
1485
+
1486
+ // PAGING METHODS
1487
+ /**
1488
+ * Loads a given 'page' of data by setting the start and limit values appropriately. Internally this just causes a normal
1489
+ * load operation, passing in calculated 'start' and 'limit' params
1490
+ * @param {Number} page The number of the page to load
1491
+ * @param {Object} options See options for {@link #method-load}
1492
+ */
1493
+ loadPage: function(page, options) {
1494
+ var me = this;
1495
+
1496
+ me.currentPage = page;
1497
+
1498
+ // Copy options into a new object so as not to mutate passed in objects
1499
+ options = Ext.apply({
1500
+ page: page,
1501
+ start: (page - 1) * me.pageSize,
1502
+ limit: me.pageSize,
1503
+ addRecords: !me.clearOnPageLoad
1504
+ }, options);
1505
+
1506
+ if (me.buffered) {
1507
+ return me.loadToPrefetch(options);
1508
+ }
1509
+ me.read(options);
1510
+ },
1511
+
1512
+ /**
1513
+ * Loads the next 'page' in the current data set
1514
+ * @param {Object} options See options for {@link #method-load}
1515
+ */
1516
+ nextPage: function(options) {
1517
+ this.loadPage(this.currentPage + 1, options);
1518
+ },
1519
+
1520
+ /**
1521
+ * Loads the previous 'page' in the current data set
1522
+ * @param {Object} options See options for {@link #method-load}
1523
+ */
1524
+ previousPage: function(options) {
1525
+ this.loadPage(this.currentPage - 1, options);
1526
+ },
1527
+
1528
+ // private
1529
+ clearData: function(isLoad) {
1530
+ var me = this,
1531
+ records = me.data.items,
1532
+ i = records.length;
1533
+
1534
+ while (i--) {
1535
+ records[i].unjoin(me);
1536
+ }
1537
+ me.data.clear();
1538
+ if (isLoad !== true || me.clearRemovedOnLoad) {
1539
+ me.removed = [];
1540
+ }
1541
+ },
1542
+
1543
+ loadToPrefetch: function(options) {
1544
+ var me = this,
1545
+ waitForInitialRange = function() {
1546
+ if (me.rangeCached(options.start, options.limit - 1)) {
1547
+ me.pageMap.un('pageAdded', waitForInitialRange);
1548
+ me.guaranteeRange(options.start, (me.viewSize || me.pageSize) - 1);
1549
+ }
1550
+ };
1551
+
1552
+ // Wait for the requested range to become available in the page map
1553
+ me.pageMap.on('pageAdded', waitForInitialRange);
1554
+ return me.prefetch(options || {});
1555
+ },
1556
+
1557
+ // Buffering
1558
+ /**
1559
+ * Prefetches data into the store using its configured {@link #proxy}.
1560
+ * @param {Object} options (Optional) config object, passed into the Ext.data.Operation object before loading.
1561
+ * See {@link #method-load}
1562
+ */
1563
+ prefetch: function(options) {
1564
+ var me = this,
1565
+ pageSize = me.pageSize,
1566
+ operation;
1567
+
1568
+ // Check pageSize has not been tampered with. That would break page caching
1569
+ if (pageSize) {
1570
+ if (me.lastPageSize && pageSize != me.lastPageSize) {
1571
+ Ext.error.raise("pageSize cannot be dynamically altered");
1572
+ }
1573
+ if (!me.pageMap.pageSize) {
1574
+ me.pageMap.pageSize = pageSize;
1575
+ }
1576
+ }
1577
+
1578
+ // Allow first prefetch call to imply the required page size.
1579
+ else {
1580
+ me.pageSize = me.pageMap.pageSize = pageSize = options.limit;
1581
+ }
1582
+
1583
+ // So that we can check for tampering next time through
1584
+ me.lastPageSize = pageSize;
1585
+
1586
+ // Always get whole pages.
1587
+ if (!options.page) {
1588
+ options.page = me.getPageFromRecordIndex(options.start);
1589
+ options.start = (options.page - 1) * pageSize;
1590
+ options.limit = Math.ceil(options.limit / pageSize) * pageSize;
1591
+ }
1592
+
1593
+ // Currently not requesting this page, then request it...
1594
+ if (!me.pageRequests[options.page]) {
1595
+
1596
+ // Copy options into a new object so as not to mutate passed in objects
1597
+ options = Ext.apply({
1598
+ action : 'read',
1599
+ filters: me.filters.items,
1600
+ sorters: me.sorters.items,
1601
+
1602
+ // Generation # of the page map to which the requested records belong.
1603
+ // If page map is cleared while this request is in flight, the generation will increment and the payload will be rejected
1604
+ generation: me.pageMap.generation
1605
+ }, options);
1606
+
1607
+ operation = new Ext.data.Operation(options);
1608
+
1609
+ if (me.fireEvent('beforeprefetch', me, operation) !== false) {
1610
+ me.loading = true;
1611
+ me.pageRequests[options.page] = me.proxy.read(operation, me.onProxyPrefetch, me);
1612
+ }
1613
+ }
1614
+
1615
+ return me;
1616
+ },
1617
+
1618
+ /**
1619
+ * @private
1620
+ * Cancels all pending prefetch requests.
1621
+ *
1622
+ * This is called when the page map is cleared.
1623
+ *
1624
+ * Any requests which still make it through will be for the previous page map generation
1625
+ * (generation is incremented upon clear), and so will be rejected upon arrival.
1626
+ */
1627
+ cancelAllPrefetches: function() {
1628
+ var me = this,
1629
+ reqs = me.pageRequests,
1630
+ req,
1631
+ page;
1632
+
1633
+ // If any requests return, we no longer respond to them.
1634
+ if (me.pageMap.events.pageadded) {
1635
+ me.pageMap.events.pageadded.clearListeners();
1636
+ }
1637
+
1638
+ // Cancel all outstanding requests
1639
+ for (page in reqs) {
1640
+ if (reqs.hasOwnProperty(page)) {
1641
+ req = reqs[page];
1642
+ delete reqs[page];
1643
+ delete req.callback;
1644
+ }
1645
+ }
1646
+ },
1647
+
1648
+ /**
1649
+ * Prefetches a page of data.
1650
+ * @param {Number} page The page to prefetch
1651
+ * @param {Object} options (Optional) config object, passed into the Ext.data.Operation object before loading.
1652
+ * See {@link #method-load}
1653
+ */
1654
+ prefetchPage: function(page, options) {
1655
+ var me = this,
1656
+ pageSize = me.pageSize || me.defaultPageSize,
1657
+ start = (page - 1) * me.pageSize,
1658
+ total = me.totalCount;
1659
+
1660
+ // No more data to prefetch.
1661
+ if (total !== undefined && me.getCount() === total) {
1662
+ return;
1663
+ }
1664
+
1665
+ // Copy options into a new object so as not to mutate passed in objects
1666
+ me.prefetch(Ext.apply({
1667
+ page : page,
1668
+ start : start,
1669
+ limit : pageSize
1670
+ }, options));
1671
+ },
1672
+
1673
+ /**
1674
+ * Called after the configured proxy completes a prefetch operation.
1675
+ * @private
1676
+ * @param {Ext.data.Operation} operation The operation that completed
1677
+ */
1678
+ onProxyPrefetch: function(operation) {
1679
+ var me = this,
1680
+ resultSet = operation.getResultSet(),
1681
+ records = operation.getRecords(),
1682
+ successful = operation.wasSuccessful(),
1683
+ page = operation.page;
1684
+
1685
+ // Only cache the data if the operation was invoked for the current generation of the page map.
1686
+ // If the generation has changed since the request was fired off, it will have been cancelled.
1687
+ if (operation.generation === me.pageMap.generation) {
1688
+
1689
+ if (resultSet) {
1690
+ me.totalCount = resultSet.total;
1691
+ me.fireEvent('totalcountchange', me.totalCount);
1692
+ }
1693
+
1694
+ // Remove the loaded page from the outstanding pages hash
1695
+ if (page !== undefined) {
1696
+ delete me.pageRequests[page];
1697
+ }
1698
+
1699
+ // Add the page into the page map.
1700
+ // pageAdded event may trigger the onGuaranteedRange
1701
+ if (successful) {
1702
+ me.cachePage(records, operation.page);
1703
+ }
1704
+
1705
+ me.loading = false;
1706
+ me.fireEvent('prefetch', me, records, successful, operation);
1707
+
1708
+ //this is a callback that would have been passed to the 'read' function and is optional
1709
+ Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
1710
+ }
1711
+ },
1712
+
1713
+ /**
1714
+ * Caches the records in the prefetch and stripes them with their server-side
1715
+ * index.
1716
+ * @private
1717
+ * @param {Ext.data.Model[]} records The records to cache
1718
+ * @param {Ext.data.Operation} The associated operation
1719
+ */
1720
+ cachePage: function(records, page) {
1721
+ var me = this;
1722
+
1723
+ if (!Ext.isDefined(me.totalCount)) {
1724
+ me.totalCount = records.length;
1725
+ me.fireEvent('totalcountchange', me.totalCount);
1726
+ }
1727
+
1728
+ // Add the fetched page into the pageCache
1729
+ me.pageMap.addPage(page, records);
1730
+ },
1731
+
1732
+ /**
1733
+ * Determines if the passed range is available in the page cache.
1734
+ * @private
1735
+ * @param {Number} start The start index
1736
+ * @param {Number} end The end index in the range
1737
+ */
1738
+ rangeCached: function(start, end) {
1739
+ return this.pageMap && this.pageMap.hasRange(start, end);
1740
+ },
1741
+
1742
+ /**
1743
+ * Determines if the passed page is available in the page cache.
1744
+ * @private
1745
+ * @param {Number} page The page to find in the page cache.
1746
+ */
1747
+ pageCached: function(page) {
1748
+ return this.pageMap && this.pageMap.hasPage(page);
1749
+ },
1750
+
1751
+ /**
1752
+ * Determines if the passed range is available in the page cache.
1753
+ * @private
1754
+ * @deprecated 4.1.0 use {@link #rangeCached} instead
1755
+ * @param {Number} start The start index
1756
+ * @param {Number} end The end index in the range
1757
+ */
1758
+ rangeSatisfied: function(start, end) {
1759
+ return this.rangeCached(start, end);
1760
+ },
1761
+
1762
+ /**
1763
+ * Determines the page from a record index
1764
+ * @param {Number} index The record index
1765
+ * @return {Number} The page the record belongs to
1766
+ */
1767
+ getPageFromRecordIndex: function(index) {
1768
+ return Math.floor(index / this.pageSize) + 1;
1769
+ },
1770
+
1771
+ /**
1772
+ * Handles a guaranteed range being loaded
1773
+ * @private
1774
+ */
1775
+ onGuaranteedRange: function(options) {
1776
+ var me = this,
1777
+ totalCount = me.getTotalCount(),
1778
+ start = options.prefetchStart,
1779
+ end = ((totalCount - 1) < options.prefetchEnd) ? totalCount - 1 : options.prefetchEnd,
1780
+ range;
1781
+
1782
+ end = Math.max(0, end);
1783
+
1784
+ //<debug>
1785
+ if (start > end) {
1786
+ Ext.log({
1787
+ level: 'warn',
1788
+ msg: 'Start (' + start + ') was greater than end (' + end +
1789
+ ') for the range of records requested (' + start + '-' +
1790
+ options.prefetchEnd + ')' + (this.storeId ? ' from store "' + this.storeId + '"' : '')
1791
+ });
1792
+ }
1793
+ //</debug>
1794
+
1795
+ range = me.pageMap.getRange(start, end);
1796
+ me.fireEvent('guaranteedrange', range, start, end);
1797
+ if (options.cb) {
1798
+ options.cb.call(options.scope || me, range, start, end);
1799
+ }
1800
+ },
1801
+
1802
+ /**
1803
+ * Ensures that the specified range of rows is present in the cache.
1804
+ *
1805
+ * Converts the row range to a page range and then only load pages which are not already
1806
+ * present in the page cache.
1807
+ */
1808
+ prefetchRange: function(start, end) {
1809
+ var me = this,
1810
+ startPage, endPage, page;
1811
+ if (!me.rangeCached(start, end)) {
1812
+ startPage = me.getPageFromRecordIndex(start);
1813
+ endPage = me.getPageFromRecordIndex(end);
1814
+
1815
+ // Ensure that the page cache's max size is correct.
1816
+ // Our purgePageCount is the number of additional pages *outside of the required range* which
1817
+ // may be kept in the cache. A purgePageCount of zero means unlimited.
1818
+ me.pageMap.maxSize = me.purgePageCount ? (endPage - startPage + 1) + me.purgePageCount : 0;
1819
+
1820
+ // We have the range, but ensure that we have a "buffer" of pages around it.
1821
+ for (page = startPage; page <= endPage; page++) {
1822
+ if (!me.pageCached(page)) {
1823
+ me.prefetchPage(page);
1824
+ }
1825
+ }
1826
+ }
1827
+ },
1828
+
1829
+ /**
1830
+ * Guarantee a specific range, this will load the store with a range (that
1831
+ * must be the pageSize or smaller) and take care of any loading that may
1832
+ * be necessary.
1833
+ */
1834
+ guaranteeRange: function(start, end, cb, scope) {
1835
+ // Sanity check end point to be within dataset range
1836
+ end = (end > this.totalCount) ? this.totalCount - 1 : end;
1837
+
1838
+ var me = this,
1839
+ lastRequestStart = me.lastRequestStart,
1840
+ options = {
1841
+ prefetchStart: start,
1842
+ prefetchEnd: end,
1843
+ cb: cb,
1844
+ scope: scope
1845
+ },
1846
+ pageAddHandler;
1847
+
1848
+ me.lastRequestStart = start;
1849
+
1850
+ // If data request can be satisfied from the page cache
1851
+ if (me.rangeCached(start, end)) {
1852
+
1853
+ // Attempt to keep the page cache primed with pages which encompass the live data range
1854
+ if (start < lastRequestStart) {
1855
+ start = Math.max(start - me.leadingBufferZone, 0);
1856
+ end = Math.min(end + me.trailingBufferZone, me.totalCount - 1);
1857
+ } else {
1858
+ start = Math.max(Math.min(start - me.trailingBufferZone, me.totalCount - me.pageSize), 0);
1859
+ end = Math.min(end + me.leadingBufferZone, me.totalCount - 1);
1860
+ }
1861
+
1862
+ // If the prefetch window calculated round the requested range is not already satisfied in the page cache,
1863
+ // then arrange to prefetch it.
1864
+ if (!me.rangeCached(start, end)) {
1865
+ // We have the range, but ensure that we have a "buffer" of pages around it.
1866
+ me.prefetchRange(start, end);
1867
+ }
1868
+ me.onGuaranteedRange(options);
1869
+ }
1870
+ // At least some of the requested range needs loading from server
1871
+ else {
1872
+ // Private event used by the LoadMask class to perform masking when the range required for rendering is not found in the cache
1873
+ me.fireEvent('cachemiss', me, start, end);
1874
+
1875
+ // Calculate a prefetch range which is centered on the requested data
1876
+ start = Math.min(Math.max(Math.floor(start - ((me.leadingBufferZone + me.trailingBufferZone) / 2)), 0), me.totalCount - me.pageSize);
1877
+ end = Math.min(Math.max(Math.ceil (end + ((me.leadingBufferZone + me.trailingBufferZone) / 2)), 0), me.totalCount - 1);
1878
+
1879
+ // Add a pageAdded listener, and as soon as the requested range is loaded, fire the rangeguaranteed event
1880
+ pageAddHandler = function(page, records) {
1881
+ if (me.rangeCached(options.prefetchStart, options.prefetchEnd)) {
1882
+ // Private event used by the LoadMask class to unmask when the range required for rendering has been loaded into the cache
1883
+ me.fireEvent('cachefilled', me, start, end);
1884
+ me.pageMap.un('pageAdded', pageAddHandler);
1885
+ me.onGuaranteedRange(options);
1886
+ }
1887
+ };
1888
+ me.pageMap.on('pageAdded', pageAddHandler);
1889
+
1890
+ // Prioritize the request for the *exact range that the UI is asking for*.
1891
+ // When a page request is in flight, it will not be requested again by checking the me.pageRequests hash,
1892
+ // so the request after this will only request the *remaining* unrequested pages .
1893
+ me.prefetchRange(options.prefetchStart, options.prefetchEnd);
1894
+
1895
+ // Load the pages that need loading.
1896
+ me.prefetchRange(start, end);
1897
+ }
1898
+ },
1899
+
1900
+ // because prefetchData is stored by index
1901
+ // this invalidates all of the prefetchedData
1902
+ sort: function() {
1903
+ var me = this,
1904
+ prefetchData = me.pageMap;
1905
+
1906
+ if (me.buffered) {
1907
+ if (me.remoteSort) {
1908
+ prefetchData.clear();
1909
+ me.callParent(arguments);
1910
+ } else {
1911
+ me.callParent(arguments);
1912
+ }
1913
+ } else {
1914
+ me.callParent(arguments);
1915
+ }
1916
+ },
1917
+
1918
+ // overriden to provide striping of the indexes as sorting occurs.
1919
+ // this cannot be done inside of sort because datachanged has already
1920
+ // fired and will trigger a repaint of the bound view.
1921
+ doSort: function(sorterFn) {
1922
+ var me = this,
1923
+ range,
1924
+ ln,
1925
+ i;
1926
+ if (me.remoteSort) {
1927
+
1928
+ // For a buffered Store, we have to clear the prefetch cache since it is keyed by the index within the dataset.
1929
+ // Then we must prefetch the new page 1, and when that arrives, reload the visible part of the Store
1930
+ // via the guaranteedrange event
1931
+ if (me.buffered) {
1932
+ me.pageMap.clear();
1933
+ me.loadPage(1);
1934
+ } else {
1935
+ //the load function will pick up the new sorters and request the sorted data from the proxy
1936
+ me.load();
1937
+ }
1938
+ } else {
1939
+ me.data.sortBy(sorterFn);
1940
+ if (!me.buffered) {
1941
+ range = me.getRange();
1942
+ ln = range.length;
1943
+ i = 0;
1944
+ for (; i < ln; i++) {
1945
+ range[i].index = i;
1946
+ }
1947
+ }
1948
+ me.fireEvent('datachanged', me);
1949
+ me.fireEvent('refresh', me);
1950
+ }
1951
+ },
1952
+
1953
+ /**
1954
+ * Finds the index of the first matching Record in this store by a specific field value.
1955
+ *
1956
+ * When store is filtered, finds records only within filter.
1957
+ *
1958
+ * @param {String} fieldName The name of the Record field to test.
1959
+ * @param {String/RegExp} value Either a string that the field value
1960
+ * should begin with, or a RegExp to test against the field.
1961
+ * @param {Number} [startIndex=0] The index to start searching at
1962
+ * @param {Boolean} [anyMatch=false] True to match any part of the string, not just the beginning
1963
+ * @param {Boolean} [caseSensitive=false] True for case sensitive comparison
1964
+ * @param {Boolean} [exactMatch=false] True to force exact match (^ and $ characters added to the regex).
1965
+ * @return {Number} The matched index or -1
1966
+ */
1967
+ find: function(property, value, start, anyMatch, caseSensitive, exactMatch) {
1968
+ var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
1969
+ return fn ? this.data.findIndexBy(fn, null, start) : -1;
1970
+ },
1971
+
1972
+ /**
1973
+ * Finds the first matching Record in this store by a specific field value.
1974
+ *
1975
+ * When store is filtered, finds records only within filter.
1976
+ *
1977
+ * @param {String} fieldName The name of the Record field to test.
1978
+ * @param {String/RegExp} value Either a string that the field value
1979
+ * should begin with, or a RegExp to test against the field.
1980
+ * @param {Number} [startIndex=0] The index to start searching at
1981
+ * @param {Boolean} [anyMatch=false] True to match any part of the string, not just the beginning
1982
+ * @param {Boolean} [caseSensitive=false] True for case sensitive comparison
1983
+ * @param {Boolean} [exactMatch=false] True to force exact match (^ and $ characters added to the regex).
1984
+ * @return {Ext.data.Model} The matched record or null
1985
+ */
1986
+ findRecord: function() {
1987
+ var me = this,
1988
+ index = me.find.apply(me, arguments);
1989
+ return index !== -1 ? me.getAt(index) : null;
1990
+ },
1991
+
1992
+ /**
1993
+ * @private
1994
+ * Returns a filter function used to test a the given property's value. Defers most of the work to
1995
+ * Ext.util.MixedCollection's createValueMatcher function.
1996
+ *
1997
+ * @param {String} property The property to create the filter function for
1998
+ * @param {String/RegExp} value The string/regex to compare the property value to
1999
+ * @param {Boolean} [anyMatch=false] True if we don't care if the filter value is not the full value.
2000
+ * @param {Boolean} [caseSensitive=false] True to create a case-sensitive regex.
2001
+ * @param {Boolean} [exactMatch=false] True to force exact match (^ and $ characters added to the regex).
2002
+ * Ignored if anyMatch is true.
2003
+ */
2004
+ createFilterFn: function(property, value, anyMatch, caseSensitive, exactMatch) {
2005
+ if (Ext.isEmpty(value)) {
2006
+ return false;
2007
+ }
2008
+ value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
2009
+ return function(r) {
2010
+ return value.test(r.data[property]);
2011
+ };
2012
+ },
2013
+
2014
+ /**
2015
+ * Finds the index of the first matching Record in this store by a specific field value.
2016
+ *
2017
+ * When store is filtered, finds records only within filter.
2018
+ *
2019
+ * @param {String} fieldName The name of the Record field to test.
2020
+ * @param {Object} value The value to match the field against.
2021
+ * @param {Number} [startIndex=0] The index to start searching at
2022
+ * @return {Number} The matched index or -1
2023
+ */
2024
+ findExact: function(property, value, start) {
2025
+ return this.data.findIndexBy(function(rec) {
2026
+ return rec.isEqual(rec.get(property), value);
2027
+ },
2028
+ this, start);
2029
+ },
2030
+
2031
+ /**
2032
+ * Find the index of the first matching Record in this Store by a function.
2033
+ * If the function returns `true` it is considered a match.
2034
+ *
2035
+ * When store is filtered, finds records only within filter.
2036
+ *
2037
+ * @param {Function} fn The function to be called. It will be passed the following parameters:
2038
+ * @param {Ext.data.Model} fn.record The record to test for filtering. Access field values
2039
+ * using {@link Ext.data.Model#get}.
2040
+ * @param {Object} fn.id The ID of the Record passed.
2041
+ * @param {Object} [scope] The scope (this reference) in which the function is executed.
2042
+ * Defaults to this Store.
2043
+ * @param {Number} [startIndex=0] The index to start searching at
2044
+ * @return {Number} The matched index or -1
2045
+ */
2046
+ findBy: function(fn, scope, start) {
2047
+ return this.data.findIndexBy(fn, scope, start);
2048
+ },
2049
+
2050
+ /**
2051
+ * Collects unique values for a particular dataIndex from this store.
2052
+ *
2053
+ * @param {String} dataIndex The property to collect
2054
+ * @param {Boolean} [allowNull] Pass true to allow null, undefined or empty string values
2055
+ * @param {Boolean} [bypassFilter] Pass true to collect from all records, even ones which are filtered.
2056
+ * @return {Object[]} An array of the unique values
2057
+ */
2058
+ collect: function(dataIndex, allowNull, bypassFilter) {
2059
+ var me = this,
2060
+ data = (bypassFilter === true && me.snapshot) ? me.snapshot : me.data;
2061
+
2062
+ return data.collect(dataIndex, 'data', allowNull);
2063
+ },
2064
+
2065
+ /**
2066
+ * Gets the number of records in store.
2067
+ *
2068
+ * If using paging, this may not be the total size of the dataset. If the data object
2069
+ * used by the Reader contains the dataset size, then the {@link #getTotalCount} function returns
2070
+ * the dataset size. **Note**: see the Important note in {@link #method-load}.
2071
+ *
2072
+ * When store is filtered, it's the number of records matching the filter.
2073
+ *
2074
+ * @return {Number} The number of Records in the Store.
2075
+ */
2076
+ getCount: function() {
2077
+ return this.data.length || 0;
2078
+ },
2079
+
2080
+ /**
2081
+ * Returns the total number of {@link Ext.data.Model Model} instances that the {@link Ext.data.proxy.Proxy Proxy}
2082
+ * indicates exist. This will usually differ from {@link #getCount} when using paging - getCount returns the
2083
+ * number of records loaded into the Store at the moment, getTotalCount returns the number of records that
2084
+ * could be loaded into the Store if the Store contained all data
2085
+ * @return {Number} The total number of Model instances available via the Proxy. 0 returned if
2086
+ * no value has been set via the reader.
2087
+ */
2088
+ getTotalCount: function() {
2089
+ return this.totalCount || 0;
2090
+ },
2091
+
2092
+ /**
2093
+ * Get the Record at the specified index.
2094
+ *
2095
+ * The index is effected by filtering.
2096
+ *
2097
+ * @param {Number} index The index of the Record to find.
2098
+ * @return {Ext.data.Model} The Record at the passed index. Returns undefined if not found.
2099
+ */
2100
+ getAt: function(index) {
2101
+ return this.data.getAt(index);
2102
+ },
2103
+
2104
+ /**
2105
+ * Returns a range of Records between specified indices.
2106
+ *
2107
+ * This method is effected by filtering.
2108
+ *
2109
+ * @param {Number} [startIndex=0] The starting index
2110
+ * @param {Number} [endIndex] The ending index. Defaults to the last Record in the Store.
2111
+ * @return {Ext.data.Model[]} An array of Records
2112
+ */
2113
+ getRange: function(start, end) {
2114
+ return this.data.getRange(start, end);
2115
+ },
2116
+
2117
+ /**
2118
+ * Get the Record with the specified id.
2119
+ *
2120
+ * This method is not effected by filtering, lookup will be performed from all records
2121
+ * inside the store, filtered or not.
2122
+ *
2123
+ * @param {Mixed} id The id of the Record to find.
2124
+ * @return {Ext.data.Model} The Record with the passed id. Returns null if not found.
2125
+ */
2126
+ getById: function(id) {
2127
+ return (this.snapshot || this.data).findBy(function(record) {
2128
+ return record.getId() === id;
2129
+ });
2130
+ },
2131
+
2132
+ /**
2133
+ * Get the index of the record within the store.
2134
+ *
2135
+ * When store is filtered, records outside of filter will not be found.
2136
+ *
2137
+ * @param {Ext.data.Model} record The Ext.data.Model object to find.
2138
+ * @return {Number} The index of the passed Record. Returns -1 if not found.
2139
+ */
2140
+ indexOf: function(record) {
2141
+ return this.data.indexOf(record);
2142
+ },
2143
+
2144
+
2145
+ /**
2146
+ * Get the index within the entire dataset. From 0 to the totalCount.
2147
+ *
2148
+ * Like #indexOf, this method is effected by filtering.
2149
+ *
2150
+ * @param {Ext.data.Model} record The Ext.data.Model object to find.
2151
+ * @return {Number} The index of the passed Record. Returns -1 if not found.
2152
+ */
2153
+ indexOfTotal: function(record) {
2154
+ var index = record.index;
2155
+ if (index || index === 0) {
2156
+ return index;
2157
+ }
2158
+ return this.indexOf(record);
2159
+ },
2160
+
2161
+ /**
2162
+ * Get the index within the store of the Record with the passed id.
2163
+ *
2164
+ * Like #indexOf, this method is effected by filtering.
2165
+ *
2166
+ * @param {String} id The id of the Record to find.
2167
+ * @return {Number} The index of the Record. Returns -1 if not found.
2168
+ */
2169
+ indexOfId: function(id) {
2170
+ return this.indexOf(this.getById(id));
2171
+ },
2172
+
2173
+ /**
2174
+ * Removes all items from the store.
2175
+ * @param {Boolean} silent Prevent the `clear` event from being fired.
2176
+ */
2177
+ removeAll: function(silent) {
2178
+ var me = this;
2179
+
2180
+ me.clearData();
2181
+ if (me.snapshot) {
2182
+ me.snapshot.clear();
2183
+ }
2184
+
2185
+ // Special handling to synch the PageMap only for removeAll
2186
+ // TODO: handle other store/data modifications WRT buffered Stores.
2187
+ if (me.pageMap) {
2188
+ me.pageMap.clear();
2189
+ }
2190
+ if (silent !== true) {
2191
+ me.fireEvent('clear', me);
2192
+ }
2193
+ },
2194
+
2195
+ /*
2196
+ * Aggregation methods
2197
+ */
2198
+
2199
+ /**
2200
+ * Convenience function for getting the first model instance in the store.
2201
+ *
2202
+ * When store is filtered, will return first item within the filter.
2203
+ *
2204
+ * @param {Boolean} [grouped] True to perform the operation for each group
2205
+ * in the store. The value returned will be an object literal with the key being the group
2206
+ * name and the first record being the value. The grouped parameter is only honored if
2207
+ * the store has a groupField.
2208
+ * @return {Ext.data.Model/undefined} The first model instance in the store, or undefined
2209
+ */
2210
+ first: function(grouped) {
2211
+ var me = this;
2212
+
2213
+ if (grouped && me.isGrouped()) {
2214
+ return me.aggregate(function(records) {
2215
+ return records.length ? records[0] : undefined;
2216
+ }, me, true);
2217
+ } else {
2218
+ return me.data.first();
2219
+ }
2220
+ },
2221
+
2222
+ /**
2223
+ * Convenience function for getting the last model instance in the store.
2224
+ *
2225
+ * When store is filtered, will return last item within the filter.
2226
+ *
2227
+ * @param {Boolean} [grouped] True to perform the operation for each group
2228
+ * in the store. The value returned will be an object literal with the key being the group
2229
+ * name and the last record being the value. The grouped parameter is only honored if
2230
+ * the store has a groupField.
2231
+ * @return {Ext.data.Model/undefined} The last model instance in the store, or undefined
2232
+ */
2233
+ last: function(grouped) {
2234
+ var me = this;
2235
+
2236
+ if (grouped && me.isGrouped()) {
2237
+ return me.aggregate(function(records) {
2238
+ var len = records.length;
2239
+ return len ? records[len - 1] : undefined;
2240
+ }, me, true);
2241
+ } else {
2242
+ return me.data.last();
2243
+ }
2244
+ },
2245
+
2246
+ /**
2247
+ * Sums the value of `property` for each {@link Ext.data.Model record} between `start`
2248
+ * and `end` and returns the result.
2249
+ *
2250
+ * When store is filtered, only sums items within the filter.
2251
+ *
2252
+ * @param {String} field A field in each record
2253
+ * @param {Boolean} [grouped] True to perform the operation for each group
2254
+ * in the store. The value returned will be an object literal with the key being the group
2255
+ * name and the sum for that group being the value. The grouped parameter is only honored if
2256
+ * the store has a groupField.
2257
+ * @return {Number} The sum
2258
+ */
2259
+ sum: function(field, grouped) {
2260
+ var me = this;
2261
+
2262
+ if (grouped && me.isGrouped()) {
2263
+ return me.aggregate(me.getSum, me, true, [field]);
2264
+ } else {
2265
+ return me.getSum(me.data.items, field);
2266
+ }
2267
+ },
2268
+
2269
+ // @private, see sum
2270
+ getSum: function(records, field) {
2271
+ var total = 0,
2272
+ i = 0,
2273
+ len = records.length;
2274
+
2275
+ for (; i < len; ++i) {
2276
+ total += records[i].get(field);
2277
+ }
2278
+
2279
+ return total;
2280
+ },
2281
+
2282
+ /**
2283
+ * Gets the count of items in the store.
2284
+ *
2285
+ * When store is filtered, only items within the filter are counted.
2286
+ *
2287
+ * @param {Boolean} [grouped] True to perform the operation for each group
2288
+ * in the store. The value returned will be an object literal with the key being the group
2289
+ * name and the count for each group being the value. The grouped parameter is only honored if
2290
+ * the store has a groupField.
2291
+ * @return {Number} the count
2292
+ */
2293
+ count: function(grouped) {
2294
+ var me = this;
2295
+
2296
+ if (grouped && me.isGrouped()) {
2297
+ return me.aggregate(function(records) {
2298
+ return records.length;
2299
+ }, me, true);
2300
+ } else {
2301
+ return me.getCount();
2302
+ }
2303
+ },
2304
+
2305
+ /**
2306
+ * Gets the minimum value in the store.
2307
+ *
2308
+ * When store is filtered, only items within the filter are aggregated.
2309
+ *
2310
+ * @param {String} field The field in each record
2311
+ * @param {Boolean} [grouped] True to perform the operation for each group
2312
+ * in the store. The value returned will be an object literal with the key being the group
2313
+ * name and the minimum in the group being the value. The grouped parameter is only honored if
2314
+ * the store has a groupField.
2315
+ * @return {Object} The minimum value, if no items exist, undefined.
2316
+ */
2317
+ min: function(field, grouped) {
2318
+ var me = this;
2319
+
2320
+ if (grouped && me.isGrouped()) {
2321
+ return me.aggregate(me.getMin, me, true, [field]);
2322
+ } else {
2323
+ return me.getMin(me.data.items, field);
2324
+ }
2325
+ },
2326
+
2327
+ // @private, see min
2328
+ getMin: function(records, field) {
2329
+ var i = 1,
2330
+ len = records.length,
2331
+ value, min;
2332
+
2333
+ if (len > 0) {
2334
+ min = records[0].get(field);
2335
+ }
2336
+
2337
+ for (; i < len; ++i) {
2338
+ value = records[i].get(field);
2339
+ if (value < min) {
2340
+ min = value;
2341
+ }
2342
+ }
2343
+ return min;
2344
+ },
2345
+
2346
+ /**
2347
+ * Gets the maximum value in the store.
2348
+ *
2349
+ * When store is filtered, only items within the filter are aggregated.
2350
+ *
2351
+ * @param {String} field The field in each record
2352
+ * @param {Boolean} [grouped] True to perform the operation for each group
2353
+ * in the store. The value returned will be an object literal with the key being the group
2354
+ * name and the maximum in the group being the value. The grouped parameter is only honored if
2355
+ * the store has a groupField.
2356
+ * @return {Object} The maximum value, if no items exist, undefined.
2357
+ */
2358
+ max: function(field, grouped) {
2359
+ var me = this;
2360
+
2361
+ if (grouped && me.isGrouped()) {
2362
+ return me.aggregate(me.getMax, me, true, [field]);
2363
+ } else {
2364
+ return me.getMax(me.data.items, field);
2365
+ }
2366
+ },
2367
+
2368
+ // @private, see max
2369
+ getMax: function(records, field) {
2370
+ var i = 1,
2371
+ len = records.length,
2372
+ value,
2373
+ max;
2374
+
2375
+ if (len > 0) {
2376
+ max = records[0].get(field);
2377
+ }
2378
+
2379
+ for (; i < len; ++i) {
2380
+ value = records[i].get(field);
2381
+ if (value > max) {
2382
+ max = value;
2383
+ }
2384
+ }
2385
+ return max;
2386
+ },
2387
+
2388
+ /**
2389
+ * Gets the average value in the store.
2390
+ *
2391
+ * When store is filtered, only items within the filter are aggregated.
2392
+ *
2393
+ * @param {String} field The field in each record
2394
+ * @param {Boolean} [grouped] True to perform the operation for each group
2395
+ * in the store. The value returned will be an object literal with the key being the group
2396
+ * name and the group average being the value. The grouped parameter is only honored if
2397
+ * the store has a groupField.
2398
+ * @return {Object} The average value, if no items exist, 0.
2399
+ */
2400
+ average: function(field, grouped) {
2401
+ var me = this;
2402
+ if (grouped && me.isGrouped()) {
2403
+ return me.aggregate(me.getAverage, me, true, [field]);
2404
+ } else {
2405
+ return me.getAverage(me.data.items, field);
2406
+ }
2407
+ },
2408
+
2409
+ // @private, see average
2410
+ getAverage: function(records, field) {
2411
+ var i = 0,
2412
+ len = records.length,
2413
+ sum = 0;
2414
+
2415
+ if (records.length > 0) {
2416
+ for (; i < len; ++i) {
2417
+ sum += records[i].get(field);
2418
+ }
2419
+ return sum / len;
2420
+ }
2421
+ return 0;
2422
+ },
2423
+
2424
+ /**
2425
+ * Runs the aggregate function for all the records in the store.
2426
+ *
2427
+ * When store is filtered, only items within the filter are aggregated.
2428
+ *
2429
+ * @param {Function} fn The function to execute. The function is called with a single parameter,
2430
+ * an array of records for that group.
2431
+ * @param {Object} [scope] The scope to execute the function in. Defaults to the store.
2432
+ * @param {Boolean} [grouped] True to perform the operation for each group
2433
+ * in the store. The value returned will be an object literal with the key being the group
2434
+ * name and the group average being the value. The grouped parameter is only honored if
2435
+ * the store has a groupField.
2436
+ * @param {Array} [args] Any arguments to append to the function call
2437
+ * @return {Object} An object literal with the group names and their appropriate values.
2438
+ */
2439
+ aggregate: function(fn, scope, grouped, args) {
2440
+ args = args || [];
2441
+ if (grouped && this.isGrouped()) {
2442
+ var groups = this.getGroups(),
2443
+ i = 0,
2444
+ len = groups.length,
2445
+ out = {},
2446
+ group;
2447
+
2448
+ for (; i < len; ++i) {
2449
+ group = groups[i];
2450
+ out[group.name] = fn.apply(scope || this, [group.children].concat(args));
2451
+ }
2452
+ return out;
2453
+ } else {
2454
+ return fn.apply(scope || this, [this.data.items].concat(args));
2455
+ }
2456
+ },
2457
+
2458
+ /**
2459
+ * Commits all Records with {@link #getModifiedRecords outstanding changes}. To handle updates for changes,
2460
+ * subscribe to the Store's {@link #update update event}, and perform updating when the third parameter is
2461
+ * Ext.data.Record.COMMIT.
2462
+ */
2463
+ commitChanges : function(){
2464
+ var me = this,
2465
+ recs = me.getModifiedRecords(),
2466
+ len = recs.length,
2467
+ i = 0;
2468
+
2469
+ for (; i < len; i++){
2470
+ recs[i].commit();
2471
+ }
2472
+
2473
+ // Since removals are cached in a simple array we can simply reset it here.
2474
+ // Adds and updates are managed in the data MixedCollection and should already be current.
2475
+ me.removed.length = 0;
2476
+ },
2477
+
2478
+ filterNewOnly: function(item){
2479
+ return item.phantom === true;
2480
+ },
2481
+
2482
+ // Ideally in the future this will use getModifiedRecords, where there will be a param
2483
+ // to getNewRecords & getUpdatedRecords to indicate whether to get only the valid
2484
+ // records or grab all of them
2485
+ getRejectRecords: function(){
2486
+ var newRecs = this.data.filterBy(this.filterNewOnly).items;
2487
+ return newRecs.concat(this.getUpdatedRecords());
2488
+ },
2489
+
2490
+ /**
2491
+ * {@link Ext.data.Model#reject Rejects} outstanding changes on all {@link #getModifiedRecords modified records}
2492
+ * and re-insert any records that were removed locally. Any phantom records will be removed.
2493
+ */
2494
+ rejectChanges : function() {
2495
+ var me = this,
2496
+ recs = me.getRejectRecords(),
2497
+ len = recs.length,
2498
+ i = 0,
2499
+ rec;
2500
+
2501
+ for (; i < len; i++) {
2502
+ rec = recs[i];
2503
+ rec.reject();
2504
+ if (rec.phantom) {
2505
+ me.remove(rec);
2506
+ }
2507
+ }
2508
+
2509
+ recs = me.removed;
2510
+ len = recs.length;
2511
+
2512
+ for (i = 0; i < len; i++) {
2513
+ rec = recs[i];
2514
+ me.insert(rec.index || 0, rec);
2515
+ rec.reject();
2516
+ }
2517
+
2518
+ // Since removals are cached in a simple array we can simply reset it here.
2519
+ // Adds and updates are managed in the data MixedCollection and should already be current.
2520
+ me.removed.length = 0;
2521
+ }
2522
+ }, function() {
2523
+ // A dummy empty store with a fieldless Model defined in it.
2524
+ // Just for binding to Views which are instantiated with no Store defined.
2525
+ // They will be able to run and render fine, and be bound to a generated Store later.
2526
+ Ext.regStore('ext-empty-store', {fields: [], proxy: 'memory'});
2527
+
2528
+ // Private class for use by only Store when configured buffered: true
2529
+ this.prototype.PageMap = new Ext.Class({
2530
+ extend: 'Ext.util.LruCache',
2531
+
2532
+ // Maintain a generation counter, so that the Store can reject incoming pages destined for the previous generation
2533
+ clear: function(initial) {
2534
+ this.generation = (this.generation ||0) + 1;
2535
+ this.callParent(arguments);
2536
+ },
2537
+
2538
+ getPageFromRecordIndex: this.prototype.getPageFromRecordIndex,
2539
+
2540
+ addPage: function(page, records) {
2541
+ this.add(page, records);
2542
+ this.fireEvent('pageAdded', page, records);
2543
+ },
2544
+
2545
+ getPage: function(page) {
2546
+ return this.get(page);
2547
+ },
2548
+
2549
+ hasRange: function(start, end) {
2550
+ var page = this.getPageFromRecordIndex(start),
2551
+ endPage = this.getPageFromRecordIndex(end);
2552
+
2553
+ for (; page <= endPage; page++) {
2554
+ if (!this.hasPage(page)) {
2555
+ return false;
2556
+ }
2557
+ }
2558
+ return true;
2559
+ },
2560
+
2561
+ hasPage: function(page) {
2562
+ return !!this.map[page];
2563
+ },
2564
+
2565
+ getRange: function(start, end) {
2566
+ if (!this.hasRange(start, end)) {
2567
+ Ext.Error.raise('PageMap asked for range which it does not have');
2568
+ }
2569
+ var me = this,
2570
+ startPage = me.getPageFromRecordIndex(start),
2571
+ endPage = me.getPageFromRecordIndex(end),
2572
+ dataStart = (startPage - 1) * me.pageSize,
2573
+ dataEnd = (endPage * me.pageSize) - 1,
2574
+ page = startPage,
2575
+ result = [],
2576
+ sliceBegin, sliceEnd, doSlice,
2577
+ i = 0, len;
2578
+
2579
+ for (; page <= endPage; page++) {
2580
+
2581
+ // First and last pages will need slicing to cut into the actual wanted records
2582
+ if (page == startPage) {
2583
+ sliceBegin = start - dataStart;
2584
+ doSlice = true;
2585
+ } else {
2586
+ sliceBegin = 0;
2587
+ doSlice = false;
2588
+ }
2589
+ if (page == endPage) {
2590
+ sliceEnd = me.pageSize - (dataEnd - end);
2591
+ doSlice = true;
2592
+ }
2593
+
2594
+ // First and last pages will need slicing
2595
+ if (doSlice) {
2596
+ Ext.Array.push(result, Ext.Array.slice(me.getPage(page), sliceBegin, sliceEnd));
2597
+ } else {
2598
+ Ext.Array.push(result, me.getPage(page));
2599
+ }
2600
+ }
2601
+
2602
+ // Inject the dataset ordinal position into the record as the index
2603
+ for (len = result.length; i < len; i++) {
2604
+ result[i].index = start++;
2605
+ }
2606
+ return result;
2607
+ }
2608
+ });
2609
+ });