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,622 @@
1
+ // ========================================================================
2
+ // SproutCore
3
+ // copyright 2006-2007 Sprout Systems, Inc.
4
+ // ========================================================================
5
+
6
+ require('foundation/object') ;
7
+
8
+ SC.Record = SC.Object.extend({
9
+
10
+ // ...............................
11
+ // PROPERTIES
12
+ //
13
+
14
+ // override this with the properties you want the record to manage.
15
+ properties: ['guid'],
16
+
17
+ // this is the primary key used to distinguish records. If the keys
18
+ // match, the records are assumed to be identical.
19
+ primaryKey: 'guid',
20
+
21
+ // when a new empty record is created, this will be set to true. It will be
22
+ // set to false again the first time the record is committed.
23
+ newRecord: false,
24
+
25
+ // set to non-zero whenever the record has uncommitted changes.
26
+ changeCount: 0,
27
+
28
+ // set to true when the record is deleted. Will cause it to be removed
29
+ // from any member collections. Once no more objects hold references to it,
30
+ // the property will be disabled.
31
+ isDeleted: false,
32
+
33
+ // ...............................
34
+ // CRUD OPERATIONS
35
+ //
36
+
37
+ // Set this URL to point to the type of resource this record is. Put a
38
+ // '%@' where you expect the primaryKey to be inserted to identify the
39
+ // record.
40
+ resourceURL: null,
41
+
42
+ // The item providing the data for this. Set to either the store or a
43
+ // Server. Setting it to the Store will make refresh and commit effectively
44
+ // null-ops.
45
+ dataSource: SC.Store,
46
+
47
+
48
+ init: function()
49
+ {
50
+ arguments.callee.base.apply(this, arguments);
51
+
52
+ var primaryKeyName = this.get('primaryKey');
53
+ if (!this.get(primaryKeyName))
54
+ {
55
+ // no primary key passed for a new record.
56
+ // we'll need to create one so that it can be cached in SC.Store
57
+ // if this isn't desired behavior, override generateTempPrimaryKey to return false.
58
+ var value = this.generateTempPrimaryKey();
59
+ if (value) this.set(primaryKeyName, value);
60
+ }
61
+ },
62
+
63
+ generateTempPrimaryKey: function()
64
+ {
65
+ return "@" + SC.getGUID(this);
66
+ },
67
+
68
+ // invoked by the UI to request the model object be updated from the server.
69
+ // Override to actually support server changes.
70
+ refresh: function() {
71
+ if (!this.get('newRecord')) this.dataSource.refreshRecords([this]);
72
+ },
73
+
74
+ // invoked by the UI to tell the model this record should be saved. Override
75
+ // to support server changes. Note that this is used to support both the
76
+ // create and update components of CRUD.
77
+ commit: function() {
78
+ // no longer a new record once changes have been committed.
79
+ if (this.get('newRecord')) {
80
+ this.dataSource.createRecords([this]) ;
81
+ } else {
82
+ this.dataSource.commitRecords([this]) ;
83
+ }
84
+ },
85
+
86
+ // this can delete the record. The non-server version just sets isDeleted.
87
+ destroy: function() { this.dataSource.destroyRecords([this]) ; },
88
+
89
+ // ...............................
90
+ // ATTRIBUTES
91
+ //
92
+ // The core attributes hash is used to store the values of a record in a
93
+ // format that can be easily passed to/from the server. The values should
94
+ // generally be stored in their raw string form. References to external
95
+ // records should be stored as primary keys.
96
+ //
97
+ // Normally you do not need to work with the attributes hash directly.
98
+ // Instead you should use get/set on normal record properties. If the
99
+ // property is not defined on the object, then the record will check the
100
+ // attributes hash instead.
101
+ //
102
+ // You can bulk update attributes from the server using the
103
+ // updateAttributes() method.
104
+
105
+ // gets an attribute, converting it to the proper format.
106
+ readAttribute: function(key) {
107
+ if (!this._cachedAttributes) this._cachedAttributes = {} ;
108
+ var ret = this._cachedAttributes[key] ;
109
+ if (ret === undefined) {
110
+ var attr = this._attributes ;
111
+ ret = (attr) ? attr[key] : undefined ;
112
+ if (ret !== undefined) {
113
+ var recordType = this._getRecordType(key+'Type') ;
114
+ ret = this._propertyFromAttribute(ret, recordType) ;
115
+ }
116
+ this._cachedAttributes[key] = ret ;
117
+ }
118
+ return (ret === undefined) ? null : ret;
119
+ },
120
+
121
+ // updates the attribute, converting it back to the property format.
122
+ writeAttribute: function(key, value) {
123
+ var recordType = this._getRecordType(key+'Type') ;
124
+ var ret = this._attributeFromProperty(value, recordType) ;
125
+ if (!this._attributes) this._attributes = {} ;
126
+ this._attributes[key] = ret ;
127
+ if (this._cachedAttributes) delete this._cachedAttributes[key]; // clear cache.
128
+ this.incrementProperty('changeCount') ;
129
+ if (SC.Store) SC.Store.recordDidChange(this) ;
130
+ return value ;
131
+ },
132
+
133
+ // This will take the incoming set of attributes and update internal set. Note that
134
+ // if the attributes have never been set, then the object you pass in may become
135
+ // the new set of attribute. This assumes the attrs you pass in will not be
136
+ // modified later. This method also assumes it is coming from the server, so the
137
+ // change count will be reset.
138
+ updateAttributes: function(newAttrs, replace, isLoaded) {
139
+ var changed = false ;
140
+ if (this._attributes && (replace !== true)) {
141
+ for(var key in newAttrs) {
142
+ if (!newAttrs.hasOwnProperty(key)) continue ;
143
+ if (!changed) changed = (this._attributes[key] != newAttrs[key]) ;
144
+ this._attributes[key] = newAttrs[key] ;
145
+ }
146
+ } else {
147
+ this._attributes = newAttrs ;
148
+ changed = true ;
149
+ }
150
+
151
+ this._cachedAttributes = {} ; // reset cache.
152
+
153
+ if (changed) {
154
+ this.beginPropertyChanges() ;
155
+ this.set('changeCount',0) ;
156
+ this.set('isLoaded',isLoaded) ;
157
+ this.allPropertiesDidChange() ;
158
+ this.endPropertyChanges() ;
159
+
160
+ if (SC.Store) SC.Store.recordDidChange(this) ;
161
+ }
162
+ },
163
+
164
+ // This will return the current set of attributes as a hash you can send back
165
+ // to the server.
166
+ attributes: function() {
167
+ return $H(this._attributes) ;
168
+ }.property(),
169
+
170
+ // If you try to get/set a property not defined by the record, then this method
171
+ // will be called. It will try to get the value from the set of attributes.
172
+ unknownProperty: function( key, value )
173
+ {
174
+ if (value !== undefined) {
175
+
176
+ // if we're modifying the PKEY, then SC.Store needs to relocate where this record is cached.
177
+ // store the old key, update the value, then let the store do the housekeeping...
178
+ var primaryKeyName = this.get('primaryKey');
179
+ if (key == primaryKeyName)
180
+ {
181
+ var oldPrimaryKey = this.get(key);
182
+ var newPrimaryKey = value;
183
+ }
184
+
185
+ this.writeAttribute(key,value);
186
+
187
+ // no need to relocate if there wasn't an old key...
188
+ if ((key == primaryKeyName) && oldPrimaryKey) SC.Store.relocateRecord( oldPrimaryKey, newPrimaryKey, this );
189
+
190
+ } else {
191
+ value = this.readAttribute(key);
192
+ }
193
+ return value;
194
+ },
195
+
196
+ _attributeFromProperty: function(value,recordType) {
197
+ if (value && value instanceof Array) {
198
+ var that = this;
199
+ return value.map(function(v) {
200
+ return that._attributeFromProperty(v,recordType);
201
+ }) ;
202
+ } else {
203
+ var typeConverter = this._pickTypeConverter(recordType) ;
204
+ if (typeConverter) return typeConverter(value,'out') ;
205
+ if (recordType) {
206
+ return (value) ? value.get(recordType.primaryKey()) : null ;
207
+ } else return value ;
208
+ }
209
+ },
210
+
211
+ _propertyFromAttribute: function(value,recordType) {
212
+ if (value && value instanceof Array) {
213
+ var that = this;
214
+ return value.map(function(v) {
215
+ return that._propertyFromAttribute(v,recordType);
216
+ }) ;
217
+ } else {
218
+ var typeConverter = this._pickTypeConverter(recordType) ;
219
+ if (typeConverter) return typeConverter(value,'in') ;
220
+ if (recordType) {
221
+ if (!value) return null ;
222
+ return SC.Store.getRecordFor(value,recordType) ;
223
+ } else return value ;
224
+ }
225
+ },
226
+
227
+ _getRecordType: function(recordTypeKey) {
228
+ var type = this[recordTypeKey] ;
229
+ if (type && (typeof(type) == "string")) {
230
+ type = eval(type) ; // look up type.
231
+ if (type) this[recordTypeKey] = type ;
232
+ }
233
+ return type ;
234
+ },
235
+
236
+ // ...............................
237
+ // SORTING AND COMPARING RECORDS
238
+ //
239
+
240
+ valueForSortKey: function(key) { return this.get(key); },
241
+
242
+ // this will compare the target object with the receiver, using the
243
+ // orderBy parameters.
244
+ compareTo: function(object, orderBy) {
245
+ if (!orderBy) orderBy = [this.get('primaryKey')] ;
246
+ var ret = SC.Record.SORT_SAME ; var loc ;
247
+ for(loc=0; (ret == SC.Record.SORT_SAME && loc<orderBy.length); loc++) {
248
+ var key = orderBy[loc] ;
249
+
250
+ // determine order
251
+ var asc = true ;
252
+ if (key.match(/ DESC$/)) {
253
+ asc = false; key = key.slice(0,-5);
254
+ } else if (key.match(/ ASC$/)) {
255
+ asc = true; key = key.slice(0,-4);
256
+ }
257
+
258
+ // if key contains a .dot then we need to get the value for the key.
259
+ var keys = key.split('.') ;
260
+ key = keys.shift() ;
261
+
262
+ // get values for key.
263
+ var a = this.valueForSortKey(key) ;
264
+ var b = object.valueForSortKey(key) ;
265
+
266
+ // convert the values to comparable values.
267
+ a = this._comparableValueFor(a,keys) ;
268
+ b = this._comparableValueFor(b,keys) ;
269
+
270
+ // compare values
271
+ if (asc) {
272
+ ret = (a<b) ? SC.Record.SORT_BEFORE : ((a>b) ? SC.Record.SORT_AFTER : SC.Record.SORT_SAME) ;
273
+ } else {
274
+ ret = (a>b) ? SC.Record.SORT_BEFORE : ((a<b) ? SC.Record.SORT_AFTER : SC.Record.SORT_SAME) ;
275
+ }
276
+ }
277
+ return ret ;
278
+ },
279
+
280
+ _comparableValueFor: function(value, keys) {
281
+ if (keys && keys.length > 0) {
282
+ var key ; var loc = 0 ;
283
+ while(value && (loc < keys.length)) {
284
+ key = keys[loc];
285
+ value = (value.get) ? value.get(key) : value[key] ;
286
+ loc++ ;
287
+ }
288
+
289
+ // handle records.
290
+ } else value = (value && value._guid) ? value._guid : value ;
291
+ return value ;
292
+ },
293
+
294
+ // used to match records to a set of conditions. By default, this will
295
+ // call matchCondition on each condition.
296
+ matchConditions: function(conditions) {
297
+ for(var key in conditions) {
298
+ var value = conditions[key] ;
299
+ if (value instanceof Array) {
300
+ var loc = value.length ; var isMatch = false ;
301
+ while(--loc >= 0) {
302
+ if (this.matchCondition(key,value[loc])) isMatch = true ;
303
+ }
304
+ if (!isMatch) return false ;
305
+ } else if (!this.matchCondition(key,value)) return false ;
306
+ }
307
+ return true ;
308
+ },
309
+
310
+ // by default this just gets the key value and compares it. Based on the
311
+ // type of the receiver's value, try to massage the condition value into
312
+ // that.
313
+ matchCondition: function(key, value) {
314
+ var recValue = this.get(key) ;
315
+ var isMatch ;
316
+
317
+ // massage value.
318
+ if (value && value.primaryKey) value = value.get(value.primaryKey) ;
319
+
320
+ if (recValue instanceof Array) {
321
+ var loc = recValue.length ;
322
+ while(--loc >= 0) {
323
+ if (this._matchValue(recValue[loc],value)) return true;
324
+ }
325
+ } else return this._matchValue(recValue,value) ;
326
+ return false ;
327
+ },
328
+
329
+ _matchValue: function(recValue,value) {
330
+ // massage recValue a tad
331
+ if (recValue && recValue.primaryKey && typeof(value) == "string") recValue = recValue.get(recValue.primaryKey) ;
332
+ var stringify = (value instanceof RegExp);
333
+ return (stringify) ? recValue.toString().match(value) : recValue==value ;
334
+ },
335
+
336
+ // ...............................
337
+ // PRIVATE
338
+ //
339
+
340
+ toString: function() {
341
+ var that = this ;
342
+ var ret = this.get('properties').map(function(key) {
343
+ var value = that.get(key) ;
344
+ if (typeof(value) == "string") value = '"' + value + '"' ;
345
+ if (value === undefined) value = "(undefined)" ;
346
+ if (value === null) value = "(null)" ;
347
+ return [key,value].join('=') ;
348
+ }) ;
349
+ return 'Record({ ' + ret.join(', ') + ' })' ;
350
+ },
351
+
352
+ propertyObserver: function(observing,target,key,value) {
353
+ //if ((target == this) && this.properties.include(key)) this.incrementProperty('changeCount') ;
354
+ },
355
+
356
+ _cprops: ['properties'],
357
+
358
+ // This method should be used by the server to push updated data into a
359
+ // record. The data should be a hash with strings and arrays. This will
360
+ // use any types you define to convert the values into their correct type.
361
+ // Note that references to external objects should be a string with the
362
+ // primaryKey value of the record.
363
+ updateProperties: function(data,isLoaded) {
364
+ var rec = this ;
365
+
366
+ // for each property, if there is a value in the passed data, convert it to
367
+ // the configured type.
368
+ this.beginPropertyChanges() ;
369
+ if (isLoaded) this.set('isLoaded',true) ;
370
+ try {
371
+ var loc = this.properties.length ;
372
+ while(--loc >= 0) {
373
+ var prop = this.properties[loc] ;
374
+ var newValue = data[prop] ;
375
+
376
+ //if (prop == 'tags') debugger ;
377
+
378
+ // handle null values
379
+ if (newValue === null) {
380
+ if (rec.get(prop) != null) rec.set(prop,null) ;
381
+
382
+ // handle defined, non-null values
383
+ } else if (newValue !== undefined) {
384
+
385
+ var oldValue = rec.get(prop) ;
386
+
387
+ // get type information
388
+ var recordType = rec.get(prop + 'Type') ;
389
+ var typeConverter = this._pickTypeConverter(recordType) ;
390
+ if (typeConverter) recordType = null ;
391
+
392
+ // if array, convert each object.
393
+ var isSame ; var rec = this ;
394
+ if (newValue instanceof Array) {
395
+ newValue = newValue.map(function(nv) {
396
+ return rec._convertValueIn(nv,typeConverter,recordType) ;
397
+ }) ;
398
+ isSame = newValue.isEqual(oldValue) ;
399
+ } else {
400
+ newValue = this._convertValueIn(newValue,typeConverter,recordType);
401
+ isSame = newValue == oldValue ;
402
+ }
403
+
404
+ // set value
405
+ if (!isSame) this.set(prop,newValue) ;
406
+
407
+ }
408
+ }
409
+ }
410
+
411
+ catch(e) {
412
+ console.log(this._guid + ': Exception raised on UPDATE: ' + e) ;
413
+ }
414
+
415
+ this.endPropertyChanges() ;
416
+ this.set('changeCount',0) ;
417
+ },
418
+
419
+ // this is used for the update. It should return a hash with current state
420
+ // of the record. This uses the types to automatically marshall properties.
421
+ getPropertyData: function() {
422
+ var ret = {} ;
423
+ var properties = this.get('properties') || []; var loc = properties.length;
424
+ while(--loc >= 0) {
425
+ var key = properties[loc] ;
426
+ var value = this.get(key) ;
427
+ var recordType = this[key + 'Type'] ;
428
+ var typeConverter = this._pickTypeConverter(recordType) ;
429
+ if (typeConverter) recordType = null ;
430
+
431
+ // if there is a type, use that to make the conversion.
432
+ if (value instanceof Array) {
433
+ var ary = [] ;
434
+ for(var vloc=0;vloc<value.length;vloc++) {
435
+ var v = value[vloc] ;
436
+ ary.push(this._convertValueOut(v,typeConverter,recordType));
437
+ }
438
+ value = ary ;
439
+ } else value = this._convertValueOut(value,typeConverter,recordType);
440
+
441
+ // set key
442
+ ret[key] = value ;
443
+ }
444
+ return ret ;
445
+ },
446
+
447
+ _pickTypeConverter: function(recordType) {
448
+ var typeConverter = null ;
449
+ if (recordType && recordType.isTypeConverter) {
450
+ typeConverter = recordType; recordType = null ;
451
+ } else if(recordType) switch(recordType) {
452
+ case Date:
453
+ typeConverter = SC.Record.Date; recordType = null ;
454
+ break ;
455
+ case Number:
456
+ typeConverter = SC.Record.Number; recordType = null;
457
+ break;
458
+ case String:
459
+ typeConverter = null; recordType = null ;
460
+ break ;
461
+ }
462
+ return typeConverter;
463
+ },
464
+
465
+ _convertValueOut: function(value,typeConverter,recordType) {
466
+ if (typeConverter) return typeConverter(value,'out') ;
467
+ if (recordType) {
468
+ return (value) ? value.get(recordType.primaryKey) : null ;
469
+ } else return value ;
470
+ },
471
+
472
+ _convertValueIn: function(value,typeConverter,recordType) {
473
+ if (typeConverter) return typeConverter(value,'in') ;
474
+ if (recordType) {
475
+ return SC.Store.getRecordFor(value,recordType) ;
476
+ } else return value ;
477
+ },
478
+
479
+ // used by the store
480
+ _storeKey: function() { return this._type._storeKey(); }
481
+
482
+
483
+ }) ;
484
+
485
+ // Class Methods
486
+ SC.Record.mixin({
487
+
488
+ // Constants for sorting
489
+ SORT_BEFORE: -1, SORT_AFTER: 1, SORT_SAME: 0,
490
+
491
+ // Used to find the first object matching the specified conditions. You can pass
492
+ // in either a simple guid or one or more hashes of conditions.
493
+ find: function(guid) {
494
+ var args ;
495
+ if (typeof(guid) == 'object') {
496
+ args = $A(arguments) ;
497
+ args.push(this) ;
498
+ var ret = SC.Store.findRecords.apply(SC.Store,args) ;
499
+ return (ret && ret.length > 0) ? ret[0] : null ;
500
+ } else return SC.Store._getRecordFor(guid,this) ;
501
+ },
502
+
503
+ findOrCreate: function(guid) {
504
+ var ret = this.find(guid) ;
505
+ if (!ret) {
506
+ var opts = (typeof(guid) == "object") ? guid : { guid: guid } ;
507
+ ret = this.create(opts) ;
508
+ SC.Store.addRecord(ret) ;
509
+ }
510
+ return ret ;
511
+ },
512
+
513
+ // Same as find except returns all records matching the passed conditions.
514
+ findAll: function(filter) {
515
+ if (!filter) filter = {} ;
516
+ args = $A(arguments) ; args.push(this) ; // add type
517
+ return SC.Store.findRecords.apply(SC.Store,args) ;
518
+ },
519
+
520
+ // Returns a collection with any passed settings and the receiver as a
521
+ // record type.
522
+ collection: function(opts) {
523
+ if (!opts) opts = {} ;
524
+ opts.recordType = this;
525
+ return SC.Collection.create(opts) ;
526
+ },
527
+
528
+ /// POSSIBLY REMOVE?
529
+
530
+ // defines coreRecordType as the first level of extension from SC.Record.
531
+ // e.g. for SC.Record > Contact > Person, the core record type is Contact.
532
+ extend: function() {
533
+ var ret = SC.Object.extend.apply(this,arguments) ;
534
+ if (ret.coreRecordType == null) ret.coreRecordType = ret ;
535
+ return ret ;
536
+ },
537
+
538
+ // used by the store
539
+ _storeKey: function() {
540
+ return (this.coreRecordType) ? this.coreRecordType._guid : this._guid ;
541
+ },
542
+
543
+ primaryKey: function() { return this.prototype.primaryKey; },
544
+
545
+ // this is set by extend to point to the core record type used to store
546
+ // the record in the pool. The coreRecordType is always the first record
547
+ // type created.
548
+ coreRecordType: null,
549
+
550
+ resourceURL: function() { return this.prototype.resourceURL; },
551
+
552
+ // This will add a property function for your record with a collection
553
+ // of records with the given type that belong to your record.
554
+ hasMany: function(recordTypeString,conditionKey,opts) {
555
+ opts = (opts === undefined) ? {} : Object.clone(opts) ;
556
+ var conditions = opts.conditions || {} ;
557
+ opts.conditions = conditions ;
558
+
559
+ var privateKey = '_' + conditionKey ;
560
+ return function() {
561
+ if (!this[privateKey]) {
562
+ var recordType = eval(recordTypeString);
563
+ conditions[conditionKey] = this ;
564
+ this[privateKey] = recordType.collection(opts) ;
565
+ this[privateKey].refresh() ; // get the initial data set.
566
+ }
567
+ return this[privateKey] ;
568
+ }.property();
569
+ },
570
+
571
+ // This will create a new record with the type. Include the data and an
572
+ // optional data source.
573
+ newRecord: function(attrs,dataSource) {
574
+ if (!dataSource) dataSource = SC.Store ;
575
+ var rec = this.create({ dataSource: dataSource }) ;
576
+ rec.beginPropertyChanges();
577
+ rec.set('newRecord',true);
578
+ for(var key in attrs) {
579
+ if (attrs.hasOwnProperty(key)) rec.set(key,attrs[key]) ;
580
+ }
581
+ rec.endPropertyChanges() ;
582
+ SC.Store.addRecord(rec) ;
583
+ return rec;
584
+ }
585
+
586
+ }) ;
587
+
588
+ // Built in Type Converters. You can also use an SC.Record.
589
+ SC.Record.Date = function(value,direction) {
590
+ if (direction == 'out') {
591
+ if (value instanceof Date) value = value.utcFormat() ;
592
+
593
+ } else if (typeof(value) == "string") {
594
+ // try to parse date. trim any decimal numbers at end since Rails sends
595
+ // this sometimes.
596
+ var ret = Date.parseDate(value.replace(/\.\d+$/,'')) ;
597
+ if (ret) value = ret ;
598
+ }
599
+ return value ;
600
+ }.typeConverter() ;
601
+
602
+ SC.Record.Number = function(value,direction) {
603
+ if (direction == 'out') {
604
+ if (typeof(value) == "number") value = value.toString() ;
605
+
606
+ } else if (typeof(value) == "string") {
607
+ var ret = (value.match('.')) ? parseFloat(value) : parseInt(value,0) ;
608
+ if (ret) value = ret ;
609
+ }
610
+ return value ;
611
+ }.typeConverter() ;
612
+
613
+ SC.Record.Flag = function(value, direction) {
614
+ if (direction == 'out') {
615
+ return value = (value) ? 't' : 'f' ;
616
+ } else if (typeof(value) == "string") {
617
+ return !('false0'.match(value.toLowerCase())) ;
618
+ } else return (value) ? true : false ;
619
+ }.typeConverter() ;
620
+
621
+ SC.Record.Bool = SC.Record.Flag ;
622
+