sproutcore 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
+