sproutcore 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. data/History.txt +4 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +269 -0
  4. data/README.txt +67 -0
  5. data/Rakefile +4 -0
  6. data/app_generators/sproutcore/USAGE +5 -0
  7. data/app_generators/sproutcore/sproutcore_generator.rb +66 -0
  8. data/app_generators/sproutcore/templates/README +77 -0
  9. data/app_generators/sproutcore/templates/environment.yml +4 -0
  10. data/bin/sc-build +145 -0
  11. data/bin/sc-gen +24 -0
  12. data/bin/sc-server +63 -0
  13. data/bin/sproutcore +21 -0
  14. data/clients/sc_docs/controllers/docs.js +118 -0
  15. data/clients/sc_docs/core.js +19 -0
  16. data/clients/sc_docs/english.lproj/body.css +159 -0
  17. data/clients/sc_docs/english.lproj/body.rhtml +33 -0
  18. data/clients/sc_docs/english.lproj/controls.css +0 -0
  19. data/clients/sc_docs/english.lproj/icons/small/next.png +0 -0
  20. data/clients/sc_docs/english.lproj/icons/small/reset.png +0 -0
  21. data/clients/sc_docs/english.lproj/images/gradients.png +0 -0
  22. data/clients/sc_docs/english.lproj/images/indicator.gif +0 -0
  23. data/clients/sc_docs/english.lproj/images/toolbar.png +0 -0
  24. data/clients/sc_docs/english.lproj/no_docs.rhtml +7 -0
  25. data/clients/sc_docs/english.lproj/strings.js +14 -0
  26. data/clients/sc_docs/english.lproj/warning.rhtml +6 -0
  27. data/clients/sc_docs/fixtures/doc.js +11 -0
  28. data/clients/sc_docs/main.js +21 -0
  29. data/clients/sc_docs/models/doc.js +9 -0
  30. data/clients/sc_docs/tests/controllers/docs.rhtml +21 -0
  31. data/clients/sc_docs/tests/models/doc.rhtml +21 -0
  32. data/clients/sc_docs/tests/views/doc_frame.rhtml +21 -0
  33. data/clients/sc_docs/tests/views/doc_label_view.rhtml +21 -0
  34. data/clients/sc_docs/views/doc_frame.js +33 -0
  35. data/clients/sc_docs/views/doc_label.js +20 -0
  36. data/clients/sc_test_runner/controllers/runner.js +175 -0
  37. data/clients/sc_test_runner/core.js +19 -0
  38. data/clients/sc_test_runner/english.lproj/body.css +151 -0
  39. data/clients/sc_test_runner/english.lproj/body.rhtml +35 -0
  40. data/clients/sc_test_runner/english.lproj/controls.css +0 -0
  41. data/clients/sc_test_runner/english.lproj/icons/small/next.png +0 -0
  42. data/clients/sc_test_runner/english.lproj/icons/small/reset.png +0 -0
  43. data/clients/sc_test_runner/english.lproj/images/gradients.png +0 -0
  44. data/clients/sc_test_runner/english.lproj/images/indicator.gif +0 -0
  45. data/clients/sc_test_runner/english.lproj/images/toolbar.png +0 -0
  46. data/clients/sc_test_runner/english.lproj/no_tests.rhtml +6 -0
  47. data/clients/sc_test_runner/english.lproj/strings.js +14 -0
  48. data/clients/sc_test_runner/english.lproj/warning.rhtml +6 -0
  49. data/clients/sc_test_runner/fixtures/test.js +12 -0
  50. data/clients/sc_test_runner/main.js +26 -0
  51. data/clients/sc_test_runner/models/test.js +11 -0
  52. data/clients/sc_test_runner/views/runner_frame.js +72 -0
  53. data/clients/sc_test_runner/views/test_label.js +20 -0
  54. data/config/hoe.rb +70 -0
  55. data/config/requirements.rb +17 -0
  56. data/environment.yml +9 -0
  57. data/frameworks/prototype/prototype.js +4186 -0
  58. data/frameworks/sproutcore/Core.js +378 -0
  59. data/frameworks/sproutcore/README +3 -0
  60. data/frameworks/sproutcore/controllers/array.js +236 -0
  61. data/frameworks/sproutcore/controllers/collection.js +305 -0
  62. data/frameworks/sproutcore/controllers/controller.js +323 -0
  63. data/frameworks/sproutcore/controllers/object.js +372 -0
  64. data/frameworks/sproutcore/drag/drag.js +549 -0
  65. data/frameworks/sproutcore/drag/drag_data_source.js +32 -0
  66. data/frameworks/sproutcore/drag/drag_source.js +64 -0
  67. data/frameworks/sproutcore/drag/drop_target.js +153 -0
  68. data/frameworks/sproutcore/english.lproj/blank.gif +0 -0
  69. data/frameworks/sproutcore/english.lproj/buttons.css +589 -0
  70. data/frameworks/sproutcore/english.lproj/buttons.png +0 -0
  71. data/frameworks/sproutcore/english.lproj/inline_text_editor.css +21 -0
  72. data/frameworks/sproutcore/english.lproj/menu.css +121 -0
  73. data/frameworks/sproutcore/english.lproj/panels/background-fat.jpg +0 -0
  74. data/frameworks/sproutcore/english.lproj/panels/background-thin.jpg +0 -0
  75. data/frameworks/sproutcore/english.lproj/panels/bottom-edge.png +0 -0
  76. data/frameworks/sproutcore/english.lproj/panels/bottom-left-corner.png +0 -0
  77. data/frameworks/sproutcore/english.lproj/panels/bottom-right-corner.png +0 -0
  78. data/frameworks/sproutcore/english.lproj/panels/left-edge.png +0 -0
  79. data/frameworks/sproutcore/english.lproj/panels/overlay.png +0 -0
  80. data/frameworks/sproutcore/english.lproj/panels/right-edge.png +0 -0
  81. data/frameworks/sproutcore/english.lproj/panels/top-edge.png +0 -0
  82. data/frameworks/sproutcore/english.lproj/panels/top-left-corner.png +0 -0
  83. data/frameworks/sproutcore/english.lproj/panels/top-right-corner.png +0 -0
  84. data/frameworks/sproutcore/english.lproj/panes.css +155 -0
  85. data/frameworks/sproutcore/english.lproj/picker.css +22 -0
  86. data/frameworks/sproutcore/english.lproj/strings.js +15 -0
  87. data/frameworks/sproutcore/english.lproj/tab.css +23 -0
  88. data/frameworks/sproutcore/english.lproj/tests.css +67 -0
  89. data/frameworks/sproutcore/english.lproj/theme.css +77 -0
  90. data/frameworks/sproutcore/foundation/animator.js +670 -0
  91. data/frameworks/sproutcore/foundation/application.js +199 -0
  92. data/frameworks/sproutcore/foundation/array.js +348 -0
  93. data/frameworks/sproutcore/foundation/benchmark.js +211 -0
  94. data/frameworks/sproutcore/foundation/binding.js +384 -0
  95. data/frameworks/sproutcore/foundation/date.js +357 -0
  96. data/frameworks/sproutcore/foundation/error.js +39 -0
  97. data/frameworks/sproutcore/foundation/input_manager.js +153 -0
  98. data/frameworks/sproutcore/foundation/json.js +296 -0
  99. data/frameworks/sproutcore/foundation/mock.js +42 -0
  100. data/frameworks/sproutcore/foundation/node_descriptor.js +56 -0
  101. data/frameworks/sproutcore/foundation/object.js +777 -0
  102. data/frameworks/sproutcore/foundation/observable.js +451 -0
  103. data/frameworks/sproutcore/foundation/page.js +63 -0
  104. data/frameworks/sproutcore/foundation/path_module.js +413 -0
  105. data/frameworks/sproutcore/foundation/responder.js +310 -0
  106. data/frameworks/sproutcore/foundation/routes.js +371 -0
  107. data/frameworks/sproutcore/foundation/run_loop.js +21 -0
  108. data/frameworks/sproutcore/foundation/server.js +491 -0
  109. data/frameworks/sproutcore/foundation/set.js +96 -0
  110. data/frameworks/sproutcore/foundation/string.js +149 -0
  111. data/frameworks/sproutcore/foundation/undo_manager.js +186 -0
  112. data/frameworks/sproutcore/foundation/unittest.js +622 -0
  113. data/frameworks/sproutcore/foundation/utils.js +61 -0
  114. data/frameworks/sproutcore/globals/panels.js +182 -0
  115. data/frameworks/sproutcore/globals/popups.js +60 -0
  116. data/frameworks/sproutcore/globals/window.js +381 -0
  117. data/frameworks/sproutcore/lib/index.rhtml +66 -0
  118. data/frameworks/sproutcore/models/collection.js +395 -0
  119. data/frameworks/sproutcore/models/record.js +622 -0
  120. data/frameworks/sproutcore/models/store.js +295 -0
  121. data/frameworks/sproutcore/panes/dialog.js +16 -0
  122. data/frameworks/sproutcore/panes/manager.js +164 -0
  123. data/frameworks/sproutcore/panes/menu.js +45 -0
  124. data/frameworks/sproutcore/panes/overlay.js +231 -0
  125. data/frameworks/sproutcore/panes/pane.js +90 -0
  126. data/frameworks/sproutcore/panes/panel.js +19 -0
  127. data/frameworks/sproutcore/panes/picker.js +45 -0
  128. data/frameworks/sproutcore/tests/controllers/array.rhtml +86 -0
  129. data/frameworks/sproutcore/tests/controllers/controller.rhtml +273 -0
  130. data/frameworks/sproutcore/tests/controllers/object.rhtml +327 -0
  131. data/frameworks/sproutcore/tests/foundation/application.rhtml +125 -0
  132. data/frameworks/sproutcore/tests/foundation/array.rhtml +221 -0
  133. data/frameworks/sproutcore/tests/foundation/object.rhtml +69 -0
  134. data/frameworks/sproutcore/tests/globals/window.rhtml +45 -0
  135. data/frameworks/sproutcore/tests/panes/pane.rhtml +88 -0
  136. data/frameworks/sproutcore/tests/views/collection.rhtml +137 -0
  137. data/frameworks/sproutcore/tests/views/popup_button.rhtml +115 -0
  138. data/frameworks/sproutcore/tests/views/text_field.rhtml +37 -0
  139. data/frameworks/sproutcore/validators/credit_card.js +92 -0
  140. data/frameworks/sproutcore/validators/date.js +36 -0
  141. data/frameworks/sproutcore/validators/email.js +29 -0
  142. data/frameworks/sproutcore/validators/not_empty.js +24 -0
  143. data/frameworks/sproutcore/validators/number.js +55 -0
  144. data/frameworks/sproutcore/validators/password.js +78 -0
  145. data/frameworks/sproutcore/validators/validator.js +304 -0
  146. data/frameworks/sproutcore/views/button.js +425 -0
  147. data/frameworks/sproutcore/views/checkbox_field.js +30 -0
  148. data/frameworks/sproutcore/views/collection.js +1521 -0
  149. data/frameworks/sproutcore/views/container.js +62 -0
  150. data/frameworks/sproutcore/views/error_explanation.js +45 -0
  151. data/frameworks/sproutcore/views/field.js +214 -0
  152. data/frameworks/sproutcore/views/filter_button.js +29 -0
  153. data/frameworks/sproutcore/views/form.js +591 -0
  154. data/frameworks/sproutcore/views/image.js +141 -0
  155. data/frameworks/sproutcore/views/inline_text_editor.js +96 -0
  156. data/frameworks/sproutcore/views/label.js +176 -0
  157. data/frameworks/sproutcore/views/menu_item.js +90 -0
  158. data/frameworks/sproutcore/views/pagination.js +54 -0
  159. data/frameworks/sproutcore/views/popup_button.js +86 -0
  160. data/frameworks/sproutcore/views/popup_menu.js +137 -0
  161. data/frameworks/sproutcore/views/progress.js +100 -0
  162. data/frameworks/sproutcore/views/radio_field.js +107 -0
  163. data/frameworks/sproutcore/views/radio_group.js +48 -0
  164. data/frameworks/sproutcore/views/segmented.js +80 -0
  165. data/frameworks/sproutcore/views/select_field.js +272 -0
  166. data/frameworks/sproutcore/views/spinner.js +11 -0
  167. data/frameworks/sproutcore/views/tab.js +126 -0
  168. data/frameworks/sproutcore/views/text_field.js +179 -0
  169. data/frameworks/sproutcore/views/textarea_field.js +14 -0
  170. data/frameworks/sproutcore/views/toolbar.js +29 -0
  171. data/frameworks/sproutcore/views/view.js +1389 -0
  172. data/frameworks/sproutcore/views/workspace.js +170 -0
  173. data/generators/client/README +3 -0
  174. data/generators/client/USAGE +12 -0
  175. data/generators/client/client_generator.rb +53 -0
  176. data/generators/client/templates/core.js +19 -0
  177. data/generators/client/templates/english.lproj/body.css +0 -0
  178. data/generators/client/templates/english.lproj/body.rhtml +3 -0
  179. data/generators/client/templates/english.lproj/controls.css +0 -0
  180. data/generators/client/templates/english.lproj/strings.js +14 -0
  181. data/generators/client/templates/main.js +37 -0
  182. data/generators/controller/USAGE +16 -0
  183. data/generators/controller/controller_generator.rb +51 -0
  184. data/generators/controller/templates/controller.js +21 -0
  185. data/generators/controller/templates/test.rhtml +21 -0
  186. data/generators/framework/README +7 -0
  187. data/generators/framework/USAGE +12 -0
  188. data/generators/framework/framework_generator.rb +53 -0
  189. data/generators/framework/templates/core.js +20 -0
  190. data/generators/framework/templates/english.lproj/body.css +0 -0
  191. data/generators/framework/templates/english.lproj/body.rhtml +3 -0
  192. data/generators/framework/templates/english.lproj/controls.css +0 -0
  193. data/generators/framework/templates/english.lproj/strings.js +14 -0
  194. data/generators/language/USAGE +16 -0
  195. data/generators/language/language_generator.rb +47 -0
  196. data/generators/language/templates/strings.js +10 -0
  197. data/generators/model/USAGE +24 -0
  198. data/generators/model/model_generator.rb +55 -0
  199. data/generators/model/templates/fixture.js +11 -0
  200. data/generators/model/templates/model.js +20 -0
  201. data/generators/model/templates/test.rhtml +21 -0
  202. data/generators/test/USAGE +16 -0
  203. data/generators/test/templates/test.rhtml +21 -0
  204. data/generators/test/test_generator.rb +47 -0
  205. data/generators/view/USAGE +16 -0
  206. data/generators/view/templates/test.rhtml +21 -0
  207. data/generators/view/templates/view.js +20 -0
  208. data/generators/view/view_generator.rb +51 -0
  209. data/jsdoc/README.txt +119 -0
  210. data/jsdoc/app/DocFile.js +137 -0
  211. data/jsdoc/app/DocTag.js +110 -0
  212. data/jsdoc/app/Doclet.js +63 -0
  213. data/jsdoc/app/Dumper.js +143 -0
  214. data/jsdoc/app/JsDoc.js +103 -0
  215. data/jsdoc/app/JsHilite.js +45 -0
  216. data/jsdoc/app/JsIO.js +163 -0
  217. data/jsdoc/app/JsParse.js +385 -0
  218. data/jsdoc/app/JsPlate.js +130 -0
  219. data/jsdoc/app/JsTestrun.js +129 -0
  220. data/jsdoc/app/JsToke.js +564 -0
  221. data/jsdoc/app/Symbol.js +298 -0
  222. data/jsdoc/app/Transformer.js +14 -0
  223. data/jsdoc/app/Util.js +97 -0
  224. data/jsdoc/app/js.jar +0 -0
  225. data/jsdoc/app/run.js +144 -0
  226. data/jsdoc/plugins/min.js +316 -0
  227. data/jsdoc/plugins/strip.js +20 -0
  228. data/jsdoc/templates/sproutcore/class.tmpl +438 -0
  229. data/jsdoc/templates/sproutcore/default.css +241 -0
  230. data/jsdoc/templates/sproutcore/index.html +13 -0
  231. data/jsdoc/templates/sproutcore/index.tmpl +21 -0
  232. data/jsdoc/templates/sproutcore/prototype.js +4186 -0
  233. data/jsdoc/templates/sproutcore/publish.js +236 -0
  234. data/jsdoc/templates/sproutcore/splash.html +7 -0
  235. data/lib/sproutcore/build_tools/html_builder.rb +88 -0
  236. data/lib/sproutcore/build_tools/resource_builder.rb +194 -0
  237. data/lib/sproutcore/build_tools.rb +44 -0
  238. data/lib/sproutcore/bundle.rb +517 -0
  239. data/lib/sproutcore/bundle_manifest.rb +397 -0
  240. data/lib/sproutcore/generator_helper.rb +170 -0
  241. data/lib/sproutcore/helpers/capture_helper.rb +42 -0
  242. data/lib/sproutcore/helpers/static_helper.rb +80 -0
  243. data/lib/sproutcore/helpers/tag_helper.rb +110 -0
  244. data/lib/sproutcore/helpers/text_helper.rb +336 -0
  245. data/lib/sproutcore/helpers.rb +3 -0
  246. data/lib/sproutcore/jsdoc.rb +40 -0
  247. data/lib/sproutcore/jsmin.rb +247 -0
  248. data/lib/sproutcore/library.rb +258 -0
  249. data/lib/sproutcore/merb/bundle_controller.rb +179 -0
  250. data/lib/sproutcore/merb/router.rb +43 -0
  251. data/lib/sproutcore/merb.rb +27 -0
  252. data/lib/sproutcore/version.rb +9 -0
  253. data/lib/sproutcore/view_helpers/button_views.rb +302 -0
  254. data/lib/sproutcore/view_helpers/core_views.rb +284 -0
  255. data/lib/sproutcore/view_helpers/form_views.rb +258 -0
  256. data/lib/sproutcore/view_helpers/menu_views.rb +94 -0
  257. data/lib/sproutcore/view_helpers.rb +628 -0
  258. data/lib/sproutcore.rb +30 -0
  259. data/script/destroy +14 -0
  260. data/script/generate +14 -0
  261. data/script/txt2html +74 -0
  262. data/setup.rb +1585 -0
  263. data/spec/spec.opts +1 -0
  264. data/spec/spec_helper.rb +7 -0
  265. data/spec/sproutcore_spec.rb +11 -0
  266. data/tasks/deployment.rake +34 -0
  267. data/tasks/environment.rake +7 -0
  268. data/tasks/rspec.rake +21 -0
  269. data/tasks/website.rake +17 -0
  270. metadata +365 -0
@@ -0,0 +1,395 @@
1
+ // ========================================================================
2
+ // SproutCore
3
+ // copyright 2006-2007 Sprout Systems, Inc.
4
+ // ========================================================================
5
+
6
+ require('models/record') ;
7
+ require('models/store') ;
8
+
9
+ // A collection holds a set of records matching the specified conditions. You
10
+ // can set the data source used to find the objects matching the conditions
11
+ // to create collections pulled from the local set of objects or pulled
12
+ // from the server.
13
+ //
14
+ // Collection's automatically update their contents based on the conditions
15
+ // settings you provide as the state of loaded records change.
16
+ //
17
+ SC.Collection = SC.Object.extend({
18
+
19
+ // ........................................
20
+ // CONFIGURABLE PROPERTIES
21
+ //
22
+ // Set these properties to control the records that will be in this list.
23
+
24
+ // indicates the keys to use to order the records. If you want the records
25
+ // ordered by descending order, use a string like 'guid DESC'.
26
+ orderBy: ['guid'],
27
+
28
+ // set this to the range of records you are interested in seeing or null if
29
+ // you want to see all records. Note that collection actually retrieves
30
+ // a list of records slightly larger than what you pass here to allow for
31
+ // members to be added and removed.
32
+ offset: 0, limit: 0,
33
+
34
+ // set this to a hash with conditions options. e.g. { active: true }. If you
35
+ // don't set this property, then all records of the given type will be
36
+ // used.
37
+ conditions: {},
38
+
39
+ // this is the actual array of records in the current collection. This
40
+ // property will change anytime the record members change (but not when
41
+ // the member record properties change)
42
+ records: function() {
43
+ if (this._changedRecords) this._flushChangedRecords() ;
44
+ return this._records ;
45
+ }.property(),
46
+
47
+ // the total count of records matching the conditions settings. The contents
48
+ // of the records array will be clipped to the range value.
49
+ count: function(key, value) {
50
+ if (value !== undefined) {
51
+ this._count = value ;
52
+ } else if (this._changedRecords) this._flushChangedRecords() ;
53
+ return this._count || 0 ;
54
+ }.property(),
55
+
56
+ // set to true when the collection is destroyed
57
+ isDeleted: false, // RO
58
+
59
+ // set this to the data source you want to use to get the records. Use
60
+ // either SC.Store or SC.Server. If you use SC.Server, your recordType
61
+ // must have a resourceURL property. // NC
62
+ dataSource: SC.Store,
63
+
64
+ // set this to the type of record you want in the collection. This should
65
+ // be set when the collection is created and not changed later.
66
+ recordType: SC.Record, // NC
67
+
68
+ // set to true while a refresh is in progress.
69
+ isLoading: false, // RO
70
+
71
+ // set to true if records have changed in a way that might leave the records out
72
+ // of date.
73
+ isDirty: false, // RO
74
+
75
+ // ........................................
76
+ // ACTIONS
77
+ //
78
+
79
+ // call this to force the list to refresh. The refresh may not happen
80
+ // right away, depending on the dataSource.
81
+ refresh: function() {
82
+ var recordType = this.get('recordType') || SC.Record ;
83
+ var offset = (this._limit > 0) ? this._offset : 0 ;
84
+
85
+ if (!this._boundRefreshFunc) {
86
+ this._boundRefreshFunc = this._refreshDidComplete.bind(this) ;
87
+ }
88
+
89
+ // start refresh
90
+ if (!this.dataSource) throw "collection does not have dataSource" ;
91
+ this.beginPropertyChanges();
92
+ if (!this.isLoading) this.set('isLoading',true) ;
93
+ this._refreshing = true ;
94
+
95
+ var order = this.get('orderBy') ;
96
+ if (order && !(order instanceof Array)) order = [order] ;
97
+ this.dataSource.listFor({
98
+ recordType: recordType,
99
+ offset: offset,
100
+ limit: this._limit,
101
+ conditions: this.get('conditions'),
102
+ order: order,
103
+ callback: this._boundRefreshFunc,
104
+ cacheCode: this._cacheCode
105
+ }) ;
106
+ this.endPropertyChanges() ;
107
+ return this;
108
+ },
109
+
110
+ // call this method when you are done with a collection. This will remove
111
+ // it as an observer to changes in the SC.Store so that it can be reclaimed.
112
+ // isDeleted will also be set to true.
113
+ destroy: function() { SC.Store.removeCollection(this); return this; },
114
+
115
+ newRecord: function(settings) {
116
+ if (!settings) settings = {} ;
117
+ settings.newRecord = true ;
118
+ settings.dataSource = this.get('dataSource') ;
119
+ var ret = this.recordType.create(settings);
120
+ SC.Store.addRecord(ret) ; // this will add the record to the collection.
121
+ return ret;
122
+ },
123
+
124
+ // ........................................
125
+ // INTERNAL
126
+ //
127
+
128
+ // this is the real offset, limit, and records used by the collection.
129
+ // this can be a superset of the actual limits seen by outsiders.
130
+ _offset: 0, _limit: 0, _records: null, _members: null, _store: null,
131
+
132
+ init: function() {
133
+ arguments.callee.base.call(this) ;
134
+ SC.Store.addCollection(this) ; // get notified of changes.
135
+ this._computeInteralOffsetAndLimit() ;
136
+ },
137
+
138
+ // This is the callback executed when the data source has found the records
139
+ // matching the passed parameters. The count is the total count of records
140
+ // matching the conditions, ignoring the offset and limit.
141
+ _refreshDidComplete: function(records,count,cacheCode) {
142
+ if (cacheCode) this._cacheCode = cacheCode;
143
+
144
+ if (records) {
145
+ this.beginPropertyChanges() ;
146
+
147
+ // update count
148
+ if (this.get('count') != count) this.set('count',count) ;
149
+
150
+ // update the record store and reslice.
151
+ this.propertyWillChange('records') ;
152
+ records = this._store = records.slice() ;
153
+ this._reslice() ;
154
+ this.propertyDidChange('records') ;
155
+
156
+ this.endPropertyChanges();
157
+ }
158
+
159
+ this._refreshing = false ; // only one refresh at a time.
160
+ },
161
+
162
+ // called by SC.Store whenever an locally stored record has changed state.
163
+ // This method simply indicates that the records property has changed and saved
164
+ // the record for later processing.
165
+ recordDidChange: function(rec) {
166
+ if (!rec && !rec._guid) return ; // probably an error, but recover anyway.
167
+ if (!this._changedRecords) this._changedRecords = {} ;
168
+ this._changedRecords[rec._guid] = rec ;
169
+ this.propertyWillChange('records') ;
170
+ this.propertyDidChange('records') ;
171
+ this.propertyWillChange('count') ;
172
+ this.propertyDidChange('count') ;
173
+ },
174
+
175
+ // this method gets called just before the records property is returned. If
176
+ // there are any changed records queued up, the record changes will be
177
+ // integrated.
178
+ _flushChangedRecords: function() {
179
+ if (!this._changedRecords) return ; // nothing to do.
180
+
181
+ if (this.dataSource != SC.Store) throw "non-local data source is not supported"
182
+
183
+ var current = this._store || [] ;
184
+ var order = this.get('orderBy') || [this.recordType.primaryKey()] ;
185
+ if (!(order instanceof Array)) order = [order] ;
186
+ var conditions = this.get('conditions') ;
187
+
188
+ // get the sorted set of changed records, both sorted and as a hash.
189
+ var records = [] ;
190
+ var changed = this._changedRecords ;
191
+ for(var guid in changed) {
192
+ if (!changed.hasOwnProperty(guid)) continue ;
193
+ records.push(changed[guid]) ;
194
+ }
195
+ records = records.sort(function(a,b){ return a.compareTo(b,order); }) ;
196
+ this._changedRecords = null ;
197
+
198
+ // step through the current set of records. Interpolate changed records, remove records
199
+ // that don't belong.
200
+ var loc = 0 ;
201
+ while(loc < current.length) {
202
+ var working = current[loc] ;
203
+ var compareToPrev, compareToNext ;
204
+
205
+ // is this record one of the changed?
206
+ // if so, then the record may need to be removed from its current position if it is deleted,
207
+ // no longer belongs to the group, or is out of order.
208
+ if (changed[working._guid]) {
209
+
210
+ var belongs = (!working.get('isDeleted')) && working.matchConditions(conditions) ;
211
+ if (belongs) {
212
+
213
+ // comes after prev?
214
+ if (loc>0) {
215
+ belongs = (working.compareTo(current[loc-1],order) >= 0) ;
216
+ }
217
+
218
+ // comes before next?
219
+ if (belongs && (loc+1 < current.length)) {
220
+ belongs = (working.compareTo(current[loc+1],order) <= 0) ;
221
+ }
222
+ }
223
+ if (!belongs) { current.splice(loc,1); continue; }
224
+ }
225
+
226
+ // if we get here, then the current record belongs where it is. next see if any of the changed
227
+ // records need to be inserted here.
228
+ var goAgain = true ;
229
+ while ((records.length > 0) && goAgain) {
230
+ var rec = records[0] ;
231
+ if ((rec != working) && !rec.get('isDeleted') && rec.matchConditions(conditions)) {
232
+ if ((rec == working) || (rec.compareTo(working,order) <= 0)) {
233
+ if (rec != working) current.splice(loc,0,rec) ; // insert only if not the same.
234
+ loc++ ;
235
+ } else goAgain = false ;
236
+ }
237
+ if (goAgain) records.shift() ;
238
+ }
239
+
240
+ // move on to the next record.
241
+ loc++ ;
242
+ }
243
+
244
+ // if we get to the end and there are changed records left to process,
245
+ // they probably need to be added.
246
+ while(records.length > 0) {
247
+ var rec = records.shift() ;
248
+ if (!rec.get('isDeleted') && rec.matchConditions(conditions)) {
249
+ current.push(rec) ;
250
+ }
251
+ }
252
+
253
+ // if ((this == window.tphotos) && (current.length != this._count)) debugger ;
254
+
255
+ // update the count as well.
256
+ this._store = current ;
257
+ this._count = current.length ;
258
+ this._reslice() ;
259
+ },
260
+
261
+ /// SAVED FOR POSTERITY
262
+ _oldRecordsDidChange: function() {
263
+ var state = ((!rec.isDeleted) && rec.matchConditions(this.get('conditions'))) ? 'in' : 'out';
264
+ var records = this._records ;
265
+
266
+ // see if this comes before or after
267
+ if ((this._limit > 0) && (state == "in") && records && (records.length > 0)) {
268
+ var order = this.get('orderBy') ;
269
+ if (rec.compareTo(records[0],order) < 0) {
270
+ state = "before" ;
271
+ } else if (rec.compareTo(records[records.length-1],order) > 0) {
272
+ state = "after" ;
273
+ }
274
+ }
275
+
276
+ // does this currently belong to the collection.
277
+ var belongs = records.include(rec) ;
278
+ var reslice = false ; var refresh = false ;
279
+
280
+ // now adjust appropriately
281
+ switch(state) {
282
+ case 'before':
283
+ // note: this is technically incorrect. If the record comes before
284
+ // that might actually shift everything forward. But there is no
285
+ // way to really tell if the rec is just now being added or not.
286
+ //
287
+ // We let this go since this will only be temporarily wrong as the
288
+ // list will get refreshed.
289
+ //
290
+ if (belongs) { records = records.without(rec) ; reslice = true; }
291
+ refresh = true ;
292
+ break ;
293
+
294
+ case 'after':
295
+ case 'out':
296
+ if (belongs) {
297
+ records = records.without(rec) ;
298
+ reslice = refresh = true ;
299
+ }
300
+ break ;
301
+
302
+ case 'in':
303
+ // resort records
304
+ if (!belongs) {
305
+ records = records.slice();
306
+ records.push(rec) ;
307
+ }
308
+ records = records.sort(function(a,b){ return a.compareTo(b,order); });
309
+ if (!records.isEqual(this._records)) reslice = refresh = true ;
310
+ break;
311
+ }
312
+
313
+ if (reslice) {
314
+ this._records = records ;
315
+ if (this._limit > 0) {
316
+ var start = this.get('offset') - this._offset ;
317
+ var end = start + this.get('limit') ;
318
+ records = records.slice(start,end) ;
319
+ }
320
+ this.set('records',records) ;
321
+ }
322
+
323
+ //if (refresh && !this._refreshing) {
324
+ // this._refreshing = true ; var that = this ;
325
+ // setTimeout(function() { that.refresh(); },1) ;
326
+ //}
327
+ },
328
+
329
+ // Anytime the properties used to filter the collection change, reslice if possible
330
+ // then refresh from the data source.
331
+ propertyObserver: function(observing,target,key,value) {
332
+ if (target != this) return ;
333
+
334
+ // update the internal properties then refresh.
335
+ var needsRefresh = false ;
336
+ var nv ;
337
+ value = this.get(key) ;
338
+ switch(key) {
339
+ case 'offset':
340
+ case 'limit':
341
+ var oldOffset = this._offset ;
342
+ var oldLimit = this._limit ;
343
+ this._computeInteralOffsetAndLimit() ;
344
+ if ((this._offset == oldOffset) && (this._limit == oldLimit)) {
345
+ this.propertyWillChange('records') ;
346
+ this._reslice();
347
+ this.propertyDidChange('records') ;
348
+ } else needsRefresh = true ;
349
+ break ;
350
+
351
+ case 'conditions':
352
+ case 'orderBy':
353
+ needsRefresh = true ;
354
+ break ;
355
+
356
+ default:
357
+ break ;
358
+ }
359
+
360
+ // refresh only once per loop. We don't want to refresh multiple times
361
+ // if the user makes changes to multiple settings.
362
+ if (needsRefresh && !this._refreshing) {
363
+ this._refreshing = true ;
364
+ this._cacheCode = null ;
365
+ this.set('isLoading',true) ;
366
+ setTimeout(this.refresh.bind(this),1);
367
+ }
368
+ },
369
+
370
+ // using the current offset and limit properties, compute the internal offset and limit used for
371
+ // records.
372
+ _computeInteralOffsetAndLimit: function() {
373
+ if (this.dataSource != SC.Store) {
374
+ var v ;
375
+ this._offset = ((v=this.get('offset')) > this.MARGIN) ? (v-this.MARGIN) : 0 ;
376
+ this._limit = ((v=this.get('limit')) > 0) ? (v + this.MARGIN) : 0 ;
377
+ } else this._offset = this._limit = 0 ;
378
+ },
379
+
380
+ // This method will slice the _store records based on the current offset and limit.
381
+ // Is it used internally and thus does not indicate that the computed records
382
+ // property has changed.
383
+ _reslice: function() {
384
+ var offset = this.get('offset') ;
385
+ var limit = this.get('limit') ;
386
+ if ((offset > 0) || (limit > 0)) {
387
+ var start = offset - this._offset ;
388
+ var end = start + ((limit <= 0) ? (this._store || []).length : limit) ;
389
+ this._records = this._store.slice(start,end) ;
390
+ } else this._records = this._store ;
391
+ },
392
+
393
+ MARGIN: 10
394
+
395
+ }) ;