riojs 0.0.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/LICENSE +20 -0
  2. data/README.rdoc +24 -0
  3. data/VERSION +1 -0
  4. data/bin/rio +5 -0
  5. data/generators/rio_app/USAGE +19 -0
  6. data/generators/rio_app/rio_app_generator.rb +40 -0
  7. data/generators/rio_app/templates/app.build +10 -0
  8. data/generators/rio_app/templates/app.css +0 -0
  9. data/generators/rio_app/templates/app.js +20 -0
  10. data/generators/rio_app/templates/app_view.html.erb +19 -0
  11. data/generators/rio_app/templates/fixture.js +4 -0
  12. data/generators/rio_app/templates/rio.html.erb +18 -0
  13. data/generators/rio_app/templates/rio_controller.rb +2 -0
  14. data/generators/rio_app/templates/spec.js +7 -0
  15. data/generators/rio_component/USAGE +13 -0
  16. data/generators/rio_component/rio_component_generator.rb +16 -0
  17. data/generators/rio_component/templates/component.css +0 -0
  18. data/generators/rio_component/templates/component.js +11 -0
  19. data/generators/rio_component/templates/fixture.js +3 -0
  20. data/generators/rio_component/templates/spec.js +6 -0
  21. data/generators/rio_model/USAGE +12 -0
  22. data/generators/rio_model/rio_model_generator.rb +21 -0
  23. data/generators/rio_model/templates/fixture.js +3 -0
  24. data/generators/rio_model/templates/model.js +9 -0
  25. data/generators/rio_model/templates/spec.js +6 -0
  26. data/generators/rio_page/USAGE +14 -0
  27. data/generators/rio_page/rio_page_generator.rb +16 -0
  28. data/generators/rio_page/templates/fixture.js +3 -0
  29. data/generators/rio_page/templates/page.css +12 -0
  30. data/generators/rio_page/templates/page.js +12 -0
  31. data/generators/rio_page/templates/page.jst +64 -0
  32. data/generators/rio_page/templates/spec.js +6 -0
  33. data/generators/rio_resource/USAGE +20 -0
  34. data/generators/rio_resource/rio_resource_generator.rb +10 -0
  35. data/generators/rio_resource/templates/controller.rb +3 -0
  36. data/init.rb +4 -0
  37. data/install/config/juggernaut_hosts.yml +18 -0
  38. data/install/lib/tasks/rio.rake +1 -0
  39. data/install/script/rio_server +37 -0
  40. data/lib/rio/autospec.rb +86 -0
  41. data/lib/rio/install.rb +90 -0
  42. data/lib/rio/juggernaut.rb +212 -0
  43. data/lib/rio/path.rb +3 -0
  44. data/lib/rio/rio_compressor.rb +219 -0
  45. data/lib/rio/rio_file_controller.rb +16 -0
  46. data/lib/rio/rio_on_rails.rb +586 -0
  47. data/lib/rio/rio_proxy_controller.rb +60 -0
  48. data/lib/rio/rio_push_controller.rb +48 -0
  49. data/lib/rio/rio_routes.rb +24 -0
  50. data/lib/rio/rio_spec_controller.rb +70 -0
  51. data/lib/riojs.rb +14 -0
  52. data/lib/tasks/rio.rb +63 -0
  53. data/public/images/background-chiffon.png +0 -0
  54. data/public/images/button-gradient-overlay-down.png +0 -0
  55. data/public/images/button-gradient-overlay.png +0 -0
  56. data/public/images/icons/add.png +0 -0
  57. data/public/images/icons/error-big.png +0 -0
  58. data/public/images/icons/warning-big.png +0 -0
  59. data/public/images/rio-logo-big.png +0 -0
  60. data/public/images/rio-logo.png +0 -0
  61. data/public/images/splitter-handle-horizontal.png +0 -0
  62. data/public/images/splitter-handle-vertical.png +0 -0
  63. data/public/images/tab-bar-gradient-overlay.png +0 -0
  64. data/public/images/title-gradient-overlay.png +0 -0
  65. data/public/images/trash.gif +0 -0
  66. data/public/javascripts/components/accordion.js +144 -0
  67. data/public/javascripts/components/alert_box.js +59 -0
  68. data/public/javascripts/components/base.js +47 -0
  69. data/public/javascripts/components/box.js +63 -0
  70. data/public/javascripts/components/button.js +98 -0
  71. data/public/javascripts/components/checkbox.js +44 -0
  72. data/public/javascripts/components/container.js +265 -0
  73. data/public/javascripts/components/grid_view.js +107 -0
  74. data/public/javascripts/components/image.js +19 -0
  75. data/public/javascripts/components/input.js +171 -0
  76. data/public/javascripts/components/label.js +15 -0
  77. data/public/javascripts/components/lightbox.js +160 -0
  78. data/public/javascripts/components/link.js +43 -0
  79. data/public/javascripts/components/list_item.js +44 -0
  80. data/public/javascripts/components/list_view.js +192 -0
  81. data/public/javascripts/components/marquee.js +131 -0
  82. data/public/javascripts/components/menu.js +89 -0
  83. data/public/javascripts/components/notification.js +75 -0
  84. data/public/javascripts/components/overlay.js +134 -0
  85. data/public/javascripts/components/panel.js +146 -0
  86. data/public/javascripts/components/radio.js +46 -0
  87. data/public/javascripts/components/splitter.js +65 -0
  88. data/public/javascripts/components/tab_bar.js +64 -0
  89. data/public/javascripts/components/tab_panel.js +57 -0
  90. data/public/javascripts/components/textarea.js +223 -0
  91. data/public/javascripts/components/toggle_button.js +22 -0
  92. data/public/javascripts/components/tooltip.js +80 -0
  93. data/public/javascripts/lib/application.js +482 -0
  94. data/public/javascripts/lib/attr.js +760 -0
  95. data/public/javascripts/lib/benchmark.js +235 -0
  96. data/public/javascripts/lib/blank.html +39 -0
  97. data/public/javascripts/lib/boot.js +300 -0
  98. data/public/javascripts/lib/clipboard.js +96 -0
  99. data/public/javascripts/lib/collection_entity.js +46 -0
  100. data/public/javascripts/lib/component.js +129 -0
  101. data/public/javascripts/lib/console.js +75 -0
  102. data/public/javascripts/lib/console/apps/console.build +43 -0
  103. data/public/javascripts/lib/console/apps/console.js +28 -0
  104. data/public/javascripts/lib/console/blank.html +39 -0
  105. data/public/javascripts/lib/console/components/benchmark.js +196 -0
  106. data/public/javascripts/lib/console/components/console.js +352 -0
  107. data/public/javascripts/lib/console/components/dependencies_list.js +17 -0
  108. data/public/javascripts/lib/console/components/docs.js +66 -0
  109. data/public/javascripts/lib/console/components/playground.js +30 -0
  110. data/public/javascripts/lib/console/console.html +27 -0
  111. data/public/javascripts/lib/console/console_commands.js +287 -0
  112. data/public/javascripts/lib/console/console_commands.js.rej +21 -0
  113. data/public/javascripts/lib/console/console_mixin.js +22 -0
  114. data/public/javascripts/lib/console/docs/files.html +579 -0
  115. data/public/javascripts/lib/console/docs/index.html +323 -0
  116. data/public/javascripts/lib/console/docs/symbols/Object.html +291 -0
  117. data/public/javascripts/lib/console/docs/symbols/_global_.html +413 -0
  118. data/public/javascripts/lib/console/docs/symbols/rio.AIM.html +490 -0
  119. data/public/javascripts/lib/console/docs/symbols/rio.Application.html +841 -0
  120. data/public/javascripts/lib/console/docs/symbols/rio.Attr.html +1075 -0
  121. data/public/javascripts/lib/console/docs/symbols/rio.Binding.html +272 -0
  122. data/public/javascripts/lib/console/docs/symbols/rio.Component.html +419 -0
  123. data/public/javascripts/lib/console/docs/symbols/rio.Cookie.html +543 -0
  124. data/public/javascripts/lib/console/docs/symbols/rio.DelayedTask#initialize.html +270 -0
  125. data/public/javascripts/lib/console/docs/symbols/rio.DelayedTask.html +391 -0
  126. data/public/javascripts/lib/console/docs/symbols/rio.JsTemplate.html +271 -0
  127. data/public/javascripts/lib/console/docs/symbols/rio.Juggernaut.html +329 -0
  128. data/public/javascripts/lib/console/docs/symbols/rio.Model.html +822 -0
  129. data/public/javascripts/lib/console/docs/symbols/rio.Page.html +383 -0
  130. data/public/javascripts/lib/console/docs/symbols/rio.Template.html +328 -0
  131. data/public/javascripts/lib/console/docs/symbols/rio.Utils.html +617 -0
  132. data/public/javascripts/lib/console/docs/symbols/rio.html +506 -0
  133. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_components_base.js.html +54 -0
  134. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_application.js.html +490 -0
  135. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_attr.js.html +768 -0
  136. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_boot.js.html +308 -0
  137. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_clipboard.js.html +103 -0
  138. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_collection_entity.js.html +53 -0
  139. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_component.js.html +137 -0
  140. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_cookie.js.html +81 -0
  141. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_delayed_task.js.html +68 -0
  142. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_file.js.html +80 -0
  143. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_flash_detect.js.html +129 -0
  144. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_form.js.html +95 -0
  145. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_id.js.html +50 -0
  146. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_inflector.js.html +167 -0
  147. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_js_template.js.html +283 -0
  148. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_juggernaut.js.html +303 -0
  149. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_key_map.js.html +68 -0
  150. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_layout_manager.js.html +175 -0
  151. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_log.js.html +17 -0
  152. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_model.js.html +1074 -0
  153. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_page.js.html +246 -0
  154. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_parameters.js.html +66 -0
  155. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_protohack.js.html +305 -0
  156. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_push.js.html +12 -0
  157. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_rsh.js.html +659 -0
  158. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_swfobject.js.html +12 -0
  159. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_tag.js.html +60 -0
  160. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_template.js.html +64 -0
  161. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_theme.js.html +105 -0
  162. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_undo.js.html +142 -0
  163. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_utils.js.html +87 -0
  164. data/public/javascripts/lib/console/docs/symbols/src/public_javascripts_lib_yaml.js.html +88 -0
  165. data/public/javascripts/lib/console/file-small.png +0 -0
  166. data/public/javascripts/lib/console/green-circle.png +0 -0
  167. data/public/javascripts/lib/console/loading.gif +0 -0
  168. data/public/javascripts/lib/console/pages/console_page.js +149 -0
  169. data/public/javascripts/lib/console/pages/console_page.jst +27 -0
  170. data/public/javascripts/lib/console/red-circle.png +0 -0
  171. data/public/javascripts/lib/cookie.js +74 -0
  172. data/public/javascripts/lib/delayed_task.js +61 -0
  173. data/public/javascripts/lib/dependencies.js +76 -0
  174. data/public/javascripts/lib/environment.js +30 -0
  175. data/public/javascripts/lib/event.simulate.js +137 -0
  176. data/public/javascripts/lib/expressinstall.swf +0 -0
  177. data/public/javascripts/lib/file.js +72 -0
  178. data/public/javascripts/lib/flash_detect.js +122 -0
  179. data/public/javascripts/lib/flashembed.min.js +16 -0
  180. data/public/javascripts/lib/form.js +88 -0
  181. data/public/javascripts/lib/id.js +43 -0
  182. data/public/javascripts/lib/inflector.js +160 -0
  183. data/public/javascripts/lib/instrumenter.js +106 -0
  184. data/public/javascripts/lib/js_template.js +275 -0
  185. data/public/javascripts/lib/jslint.js +4950 -0
  186. data/public/javascripts/lib/juggernaut.js +295 -0
  187. data/public/javascripts/lib/juggernaut.swf +0 -0
  188. data/public/javascripts/lib/key_map.js +60 -0
  189. data/public/javascripts/lib/layout_manager.js +167 -0
  190. data/public/javascripts/lib/model.js +1067 -0
  191. data/public/javascripts/lib/page.js +238 -0
  192. data/public/javascripts/lib/parameters.js +59 -0
  193. data/public/javascripts/lib/png_fix.js +75 -0
  194. data/public/javascripts/lib/protohack.js +297 -0
  195. data/public/javascripts/lib/push.js +5 -0
  196. data/public/javascripts/lib/rio.build +28 -0
  197. data/public/javascripts/lib/rio_development.build +5 -0
  198. data/public/javascripts/lib/rio_lint.js +66 -0
  199. data/public/javascripts/lib/rsh.js +651 -0
  200. data/public/javascripts/lib/spec.js +545 -0
  201. data/public/javascripts/lib/spec_runner.js +242 -0
  202. data/public/javascripts/lib/swfobject.js +5 -0
  203. data/public/javascripts/lib/tag.js +52 -0
  204. data/public/javascripts/lib/undo.js +134 -0
  205. data/public/javascripts/lib/utils.js +80 -0
  206. data/public/javascripts/lib/yaml.js +80 -0
  207. data/public/javascripts/pages/playground_page.js +15 -0
  208. data/public/javascripts/prototype/builder.js +146 -0
  209. data/public/javascripts/prototype/controls.js +1004 -0
  210. data/public/javascripts/prototype/dragdrop.js +1030 -0
  211. data/public/javascripts/prototype/effects.js +1137 -0
  212. data/public/javascripts/prototype/prototype.js +4320 -0
  213. data/public/javascripts/prototype/slider.js +283 -0
  214. data/public/javascripts/prototype/sound.js +67 -0
  215. data/public/javascripts/specs/components/box_spec.js +6 -0
  216. data/public/javascripts/specs/components/checkbox_spec.js +26 -0
  217. data/public/javascripts/specs/components/container_spec.js +6 -0
  218. data/public/javascripts/specs/components/input_spec.js +71 -0
  219. data/public/javascripts/specs/components/panel_spec.js +6 -0
  220. data/public/javascripts/specs/components/radio_spec.js +40 -0
  221. data/public/javascripts/specs/fixtures/components/box.js +3 -0
  222. data/public/javascripts/specs/fixtures/components/checkbox.js +9 -0
  223. data/public/javascripts/specs/fixtures/components/container.js +3 -0
  224. data/public/javascripts/specs/fixtures/components/input.js +12 -0
  225. data/public/javascripts/specs/fixtures/components/menu.js +19 -0
  226. data/public/javascripts/specs/fixtures/components/menu_item.js +18 -0
  227. data/public/javascripts/specs/fixtures/components/radio.js +11 -0
  228. data/public/javascripts/specs/lib/application_spec.js +281 -0
  229. data/public/javascripts/specs/lib/attr_spec.js +1514 -0
  230. data/public/javascripts/specs/lib/benchmark_spec.js +361 -0
  231. data/public/javascripts/specs/lib/collection_entity_spec.js +131 -0
  232. data/public/javascripts/specs/lib/component_spec.js +86 -0
  233. data/public/javascripts/specs/lib/form_spec.js +171 -0
  234. data/public/javascripts/specs/lib/id_spec.js +21 -0
  235. data/public/javascripts/specs/lib/instrumenter_spec.js +5 -0
  236. data/public/javascripts/specs/lib/js_template_spec.js +131 -0
  237. data/public/javascripts/specs/lib/key_map_spec.js +227 -0
  238. data/public/javascripts/specs/lib/model_spec.js +2268 -0
  239. data/public/javascripts/specs/lib/parameters_spec.js +94 -0
  240. data/public/javascripts/specs/lib/spec_spec.js +943 -0
  241. data/public/javascripts/specs/lib/undo_spec.js +105 -0
  242. data/public/javascripts/specs/lib/yaml_spec.js +127 -0
  243. data/public/sounds/basso.wav +0 -0
  244. data/public/sounds/purr.wav +0 -0
  245. data/public/stylesheets/components/accordion.css +24 -0
  246. data/public/stylesheets/components/alert_box.css +35 -0
  247. data/public/stylesheets/components/box.css +0 -0
  248. data/public/stylesheets/components/button.css +39 -0
  249. data/public/stylesheets/components/checkbox.css +9 -0
  250. data/public/stylesheets/components/container.css +3 -0
  251. data/public/stylesheets/components/grid_view.css +52 -0
  252. data/public/stylesheets/components/input.css +10 -0
  253. data/public/stylesheets/components/label.css +3 -0
  254. data/public/stylesheets/components/lightbox.css +31 -0
  255. data/public/stylesheets/components/link.css +4 -0
  256. data/public/stylesheets/components/list_view.css +23 -0
  257. data/public/stylesheets/components/marquee.css +29 -0
  258. data/public/stylesheets/components/menu.css +34 -0
  259. data/public/stylesheets/components/notification.css +52 -0
  260. data/public/stylesheets/components/overlay.css +8 -0
  261. data/public/stylesheets/components/panel.css +36 -0
  262. data/public/stylesheets/components/radio.css +9 -0
  263. data/public/stylesheets/components/splitter.css +35 -0
  264. data/public/stylesheets/components/tab_bar.css +59 -0
  265. data/public/stylesheets/components/tab_panel.css +15 -0
  266. data/public/stylesheets/components/textarea.css +11 -0
  267. data/public/stylesheets/components/tooltip.css +10 -0
  268. data/public/stylesheets/console.css +151 -0
  269. data/public/stylesheets/css_reset.css +55 -0
  270. metadata +343 -0
@@ -0,0 +1,1067 @@
1
+ /**
2
+ @class
3
+ @extends rio.Attr
4
+
5
+ Model is used to create model classes in a rio application. It provides ActiveResource style synchronization with
6
+ a rest-based resource and automated client-server bindings with a push server.
7
+
8
+ @author Jason Tillery
9
+ @copyright 2008-2009 Thinklink LLC
10
+ */
11
+ rio.Model = {
12
+ create: function() {
13
+ var args = $A(arguments);
14
+ if (args.length > 0 && args.last() != undefined && !args.last().ATTR) {
15
+ args[args.size() - 1] = Object.extend({ noExtend: true }, args.last());
16
+ }
17
+ var model = rio.Attr.create.apply(this, args);
18
+ if (model._fields.id == undefined || model.prototype.setId == undefined) {
19
+ model.attrAccessor("id");
20
+ }
21
+ model.attrEvent("destroy");
22
+ Object.extend(model, {
23
+ resource: function(url) {
24
+ model.addMethods({
25
+ isNew: function() {
26
+ return this.getId() == undefined || (this.getId().temporary != undefined && this.getId().temporary());
27
+ },
28
+
29
+ url: function() {
30
+ return model.url() + "/" + this.getId();
31
+ },
32
+
33
+ save: function(options) {
34
+ if (this.__destroying) { return; }
35
+
36
+ if (this.valid && !this.valid()) { return; }
37
+ // var idField = Object.keys(model._fields).detect(function(field) {
38
+ // var val = this["_" + field];
39
+ // return field != "id" && val && val.temporary && val.temporary();
40
+ // }.bind(this));
41
+ // if (idField) {
42
+ // this["_" + idField].doAfterReification(this.save.curry(options).bind(this));
43
+ // return;
44
+ // }
45
+
46
+ // if (this._creating) {
47
+ // if (!this._pendingUpdates) { this._pendingUpdates = []; }
48
+ // this._pendingUpdates.push(this.save.curry(options).bind(this));
49
+ // return;
50
+ // }
51
+ var firstTimeCreating = !this._creating;
52
+ this._creating = true;
53
+ if(this.isNew() && this.beforeCreate && firstTimeCreating){ this.beforeCreate(); }
54
+
55
+ model.addToTransaction(this, options);
56
+ },
57
+
58
+ destroy: function(options) {
59
+ if(this.__destroying) {return;}
60
+
61
+ if (this.beforeDestroy) { this.beforeDestroy(); }
62
+
63
+ this.__destroying = true;
64
+
65
+ model.addToTransaction(this, Object.extend({destroy: true}, options));
66
+ this.removeFromCaches();
67
+
68
+ this.fire("destroy");
69
+ },
70
+
71
+ parameters: function() {
72
+ var modelKey = model.url().match(/^\/?(.*)$/)[1].singularize();
73
+ var parameters = {};
74
+
75
+ var persistentFieldNames = model.persistentFieldNames();
76
+ for (var i=persistentFieldNames.length; i--;) {
77
+ var name = persistentFieldNames[i];
78
+ var currentState = this["_" + name];
79
+ var lastState = this._lastSavedState ? this._lastSavedState[name] : undefined;
80
+
81
+ if (currentState != lastState) {
82
+ parameters[modelKey + "[" + name.underscore() + "]"] = currentState;
83
+ }
84
+ }
85
+ return parameters;
86
+ },
87
+
88
+ afterUpdateField: function() {
89
+ model.updateInCollectionEntites(this);
90
+ },
91
+
92
+ attributeState: function() {
93
+ var state = {};
94
+
95
+ var persistentFieldNames = model.persistentFieldNames();
96
+ for (var i=persistentFieldNames.length; i--;) {
97
+ var f = persistentFieldNames[i];
98
+ state[f] = this[("get-" + f).camelize()]();
99
+ }
100
+ return state;
101
+ },
102
+
103
+ attributeStateChange: function() {
104
+ var state = {};
105
+
106
+ var persistentFieldNames = model.persistentFieldNames();
107
+ for (var i=persistentFieldNames.length; i--;) {
108
+ var f = persistentFieldNames[i];
109
+ var currentState = this["_" + f];
110
+ var lastState = this._lastSavedState ? this._lastSavedState[f] : undefined;
111
+ if (currentState != lastState) {
112
+ state[f] = currentState;
113
+ }
114
+ }
115
+
116
+ return state;
117
+ },
118
+
119
+ removeFromCaches: function() {
120
+ model.removeFromCollectionEntities(this);
121
+ model.removeFromCache(this.getId());
122
+ },
123
+
124
+ toString: function() {
125
+ return "[rio.models.*]";
126
+ }
127
+ });
128
+
129
+ Object.extend(model, {
130
+ undoEnabled: false,
131
+
132
+ url: function() {
133
+ return url;
134
+ },
135
+
136
+ _idCache: {},
137
+ id: function(val, createIfNotFound) {
138
+ if (val) {
139
+ if (this._idCache[val]) { return this._idCache[val]; }
140
+ if (createIfNotFound) {
141
+ var id = new rio.Id(val);
142
+ this._idCache[val] = id;
143
+ return id;
144
+ }
145
+ } else {
146
+ return new rio.Id();
147
+ }
148
+ },
149
+
150
+ reifyId: function(id, val) {
151
+ id.reify(val);
152
+ if (this._idCache[val]) {
153
+ rio.warn("id collision while reifying - " + model + "#" + id);
154
+ }
155
+ this._idCache[val] = id;
156
+ },
157
+
158
+ persistentFieldNames: function() {
159
+ return Object.keys(model._fields).reject(function(f) {
160
+ return model._clientOnlyAttrs.include(f) || model.prototype[("set-" + f).camelize()] == undefined || f == "id";
161
+ });
162
+ },
163
+
164
+ _transaction: [],
165
+ addToTransaction: function(entity, options) {
166
+ options = options || {};
167
+ var existingTransaction = this._transaction.detect(function(t) { return t.entity == entity; });
168
+ if (existingTransaction) {
169
+ if (options.destroy) {
170
+ if (existingTransaction.entity.isNew()) {
171
+ this._transaction.splice(this._transaction.indexOf(existingTransaction), 1);
172
+ // don't need to do this, now that we immediately remove on destroy
173
+ // model.removeFromCollectionEntities(existingTransaction.entity);
174
+ } else {
175
+ existingTransaction.options = options;
176
+ }
177
+ } else {
178
+ existingTransaction.attributeState = entity.attributeStateChange();
179
+ existingTransaction.parameters = entity.parameters();
180
+ }
181
+ // TODO: chain onSuccess/onFailure functions
182
+ } else {
183
+ this._transaction.push({
184
+ entity: entity,
185
+ options: options,
186
+ attributeState: entity.attributeStateChange(),
187
+ parameters: entity.parameters()
188
+ });
189
+ }
190
+ if (!this._transactionQueued) {
191
+ this._transactionQueued = true;
192
+ this.prepareTransaction();
193
+ }
194
+ },
195
+
196
+ /* Simply defers execution. Can override this for testing. */
197
+ prepareTransaction: function() {
198
+ this.executeTransaction.bind(this, rio.Undo.isProcessingUndo(), rio.Undo.isProcessingRedo()).defer();
199
+ },
200
+
201
+ __transactionInProgress: false,
202
+ __queuedTransactions: [],
203
+ executeTransaction: function(undoTransaction, redoTransaction) {
204
+ this._transactionQueued = false;
205
+
206
+ var transaction = this._transaction.clone();
207
+ this._transaction.clear();
208
+
209
+ // Transactions can be queued here, so lets make sure that all entities _lastSavedState's are
210
+ // correct before letting subsequent transaction queue up.
211
+ for (var i=0, len=transaction.length; i<len; i++) {
212
+ var t = transaction[i];
213
+ t.oldLastSavedState = t.entity._lastSavedState;
214
+ t.entity._lastSavedState = t.options.destroy ? undefined : t.entity.attributeState();
215
+ }
216
+
217
+ transaction.undoTransaction = undoTransaction;
218
+ transaction.redoTransaction = redoTransaction;
219
+ if (this.__transactionInProgress) {
220
+ this.__queuedTransactions.push(transaction);
221
+ return;
222
+ } else {
223
+ this._doExecuteTransaction(transaction);
224
+ }
225
+ },
226
+
227
+ _doExecuteTransaction: function(transaction) {
228
+ if (transaction.empty()) { return; }
229
+
230
+ if (model.undoEnabled) {
231
+ var undos = transaction.map(function(t) {
232
+ var undo = { id: t.entity.getId(), state: t.oldLastSavedState, destroy: t.options.destroy };
233
+ // t.entity._lastSavedState = t.options.destroy ? undefined : t.entity.attributeState();
234
+ return undo;
235
+ });
236
+
237
+ var processUndos = function() {
238
+ undos.reverse().each(function(u) {
239
+ if (u.state) {
240
+ if (u.destroy) {
241
+ var instance = new model(Object.extend({ id: u.id }, u.state));
242
+ instance._lastSavedState = undefined;
243
+ instance.save();
244
+ } else {
245
+ model.getFromCache(u.id).updateAttributes(u.state);
246
+ }
247
+ } else {
248
+ model.getFromCache(u.id).destroy();
249
+ }
250
+ });
251
+ };
252
+
253
+ if (transaction.undoTransaction) {
254
+ rio.Undo.registerRedo(processUndos);
255
+ } else {
256
+ rio.Undo.registerUndo(processUndos, transaction.redoTransaction);
257
+ }
258
+ }
259
+
260
+ // // Always update the last saved states
261
+ // for (var i=0, len=transaction.length; i<len; i++) {
262
+ // var t = transaction[i];
263
+ //
264
+ // // LOOKS LIKE A BUG
265
+ // // TODO: SHOULD PROBABLY BE USING THE t.attributeState instead of t.entity.attributeState()
266
+ // t.entity._lastSavedState = t.options.destroy ? undefined : t.entity.attributeState();
267
+ // }
268
+
269
+ var createSuccess = function(entity, onSuccess, json) {
270
+ entity._creating = false;
271
+ var results = model._filterAndProcessJsonWhileAccumulatingCollectionEntities(json);
272
+ var idString = results[0].id;
273
+ if (entity.getId() && entity.getId().temporary && entity.getId().temporary()) {
274
+ var id = parseInt(idString, 10);
275
+ model.reifyId(entity.getId(), id);
276
+ } else {
277
+ // NOTE: An entity will not be updated with any attributes but the ID on create success
278
+ entity._id = model.id(parseInt(idString, 10), true);
279
+ model.putInCache(entity._id, entity);
280
+ }
281
+ onSuccess(entity);
282
+ if (entity.afterCreate) { entity.afterCreate(); }
283
+ // if (entity._pendingUpdates) {
284
+ // entity._pendingUpdates.each(function(u) { u(); });
285
+ // }
286
+ return results[1];
287
+ };
288
+
289
+ var updateSuccess = function(entity, onSuccess) {
290
+ onSuccess(entity);
291
+ };
292
+
293
+ var destroySuccess = function(entity, onSuccess) {
294
+ // don't need to do this, now that we immediately remove on destroy
295
+ // model.removeFromCollectionEntities(entity);
296
+ (onSuccess || Prototype.emptyFunction)();
297
+ };
298
+
299
+ var url;
300
+ var method;
301
+ var parameters;
302
+ var onSuccess;
303
+ var onFailure;
304
+ var onConnectionFailure;
305
+
306
+ if (transaction.length > 1) {
307
+ url = model.url();
308
+ method = "post";
309
+
310
+ parameters = {};
311
+ transaction.each(function(t) {
312
+ var id = t.entity.getId();
313
+ if (t.options.destroy) {
314
+ parameters["transaction[" + id + "]"] = "delete";
315
+ } else {
316
+ for (var f in t.attributeState) {
317
+ var val = t.attributeState[f];
318
+ if (val && val.toString) { val = val.toString(); }
319
+ parameters["transaction[" + id + "][" + f.underscore() + "]"] = val;
320
+ }
321
+ }
322
+ });
323
+
324
+ onSuccess = function(response) {
325
+ var afterFunctions = transaction.map(function(t) {
326
+ var entity = t.entity;
327
+ var options = t.options;
328
+
329
+ options = options || {};
330
+ options.onSuccess = options.onSuccess || Prototype.emptyFunction;
331
+
332
+ var json = response.responseJSON.transaction[t.entity.getId()];
333
+ return (
334
+ options.destroy ? destroySuccess : (entity.isNew() ? createSuccess : updateSuccess)
335
+ )(entity, options.onSuccess, json);
336
+ });
337
+
338
+ afterFunctions.compact().each(function(af) {
339
+ af();
340
+ });
341
+ }.bind(this);
342
+
343
+ onFailure = function() {
344
+ var handled = true;
345
+ transaction.each(function(t) {
346
+ if (t.options.onFailure) {
347
+ t.options.onFailure();
348
+ } else {
349
+ handled = false;
350
+ }
351
+ });
352
+ if (!handled) {
353
+ rio.Application.fail("Failed creating, updating, or destroying.", model + "\n" + Object.toJSON(parameters));
354
+ }
355
+ }.bind(this);
356
+
357
+ onConnectionFailure = function() {
358
+ transaction.each(function(t) {
359
+ if (t.options.onConnectionFailure) {
360
+ t.options.onConnectionFailure();
361
+ }
362
+ });
363
+ };
364
+ } else {
365
+ var entity = transaction.first().entity;
366
+ var options = transaction.first().options;
367
+
368
+ options = options || {};
369
+ options.onSuccess = options.onSuccess || Prototype.emptyFunction;
370
+
371
+ parameters = options.parameters || transaction.first().parameters;
372
+ url = entity.isNew() ? model.url() : entity.url();
373
+ method = options.destroy ? "delete" : (entity.isNew() ? "post" : "put");
374
+ onSuccess = function(response) {
375
+ var after = (
376
+ options.destroy ? destroySuccess : (entity.isNew() ? createSuccess : updateSuccess)
377
+ )(entity, options.onSuccess, response.responseJSON);
378
+ if (after) { after(); }
379
+ };
380
+ onFailure = options.onFailure;
381
+ onConnectionFailure = options.onConnectionFailure;
382
+ }
383
+
384
+ this.__transactionInProgress = true;
385
+ new Ajax.Request(url, {
386
+ asynchronous: true,
387
+ method: method,
388
+ evalJSON: true,
389
+ parameters: $H(parameters).merge({
390
+ 'transaction_key': rio.environment.transactionKey,
391
+ 'authenticity_token': rio.Application.getToken()
392
+ }).toObject(),
393
+ onSuccess: function(response) {
394
+ if (response.status == 0) {
395
+ if (onConnectionFailure) {
396
+ onConnectionFailure();
397
+ } else {
398
+ rio.Application.fail("Connection Failure", model + ":\n" + Object.toJSON(parameters));
399
+ }
400
+ } else {
401
+ onSuccess(response);
402
+ }
403
+ },
404
+ onFailure: function() {
405
+ // if (!entity.__destroying) {
406
+ if (onFailure) {
407
+ onFailure();
408
+ } else {
409
+ rio.Application.fail("Failed creating, updating, or destroying.", model + ":\n" + Object.toJSON(parameters));
410
+ }
411
+ // }
412
+ }.bind(this),
413
+ onComplete: function(response) {
414
+ this.__transactionInProgress = false;
415
+ if (!this.__queuedTransactions.empty()) {
416
+ this._doExecuteTransaction(this.__queuedTransactions.shift());
417
+ }
418
+ }.bind(this)
419
+ });
420
+
421
+ transaction.each(function(t) {
422
+ if (t.entity.isNew()) {
423
+ t.entity._creating = true;
424
+ }
425
+ if (t.options.destroy) {
426
+ t.entity.__destroying = true;
427
+ }
428
+ });
429
+ },
430
+
431
+ create: function(options) {
432
+ var obj = new model(options);
433
+ obj.save(options);
434
+ return obj;
435
+ },
436
+
437
+ find: function(id, options) {
438
+ if (id == undefined) { return; }
439
+ var rioId = id.constructor == rio.Id ? id : model.id(id);
440
+
441
+ options = options || {};
442
+ var asynchronous = options.asynchronous == undefined || options.asynchronous;
443
+
444
+ if (options.onSuccess == undefined) {
445
+ asynchronous = false;
446
+ options.onSuccess = Prototype.emptyFunction;
447
+ }
448
+
449
+ var existing = this.getFromCache(rioId);
450
+ if (existing) {
451
+ options.onSuccess(existing);
452
+ return existing;
453
+ }
454
+
455
+ var entity;
456
+
457
+ new Ajax.Request(this.url() + "/" + id, {
458
+ asynchronous: asynchronous,
459
+ method: 'get',
460
+ evalJSON: true,
461
+ onSuccess: function(response) {
462
+ entity = new model(model._filterAndProcessJson(response.responseJSON));
463
+ options.onSuccess(entity);
464
+ }
465
+ });
466
+ if (!asynchronous) {
467
+ return entity;
468
+ }
469
+ },
470
+
471
+ findAll: function(options) {
472
+ options = options || {};
473
+ var asynchronous = options.asynchronous == undefined || options.asynchronous;
474
+
475
+ if (options.onSuccess == undefined) {
476
+ asynchronous = false;
477
+ options.onSuccess = Prototype.emptyFunction;
478
+ }
479
+
480
+ var idField = Object.keys(options.parameters).detect(function(parameter) {
481
+ var val = options.parameters[parameter];
482
+ return val && val.temporary && val.temporary();
483
+ });
484
+
485
+ var urlToUse = options.url || this.url();
486
+ var parameters = new rio.Parameters(options.parameters || {}, options.nonAjaxParameters);
487
+ // Assume that there are no entities yet (since a param is unreified)
488
+ // New entities will be added to the collectionEntity as they are created or broadcast
489
+ // If this poses a problem, we can always schedule a find for after reification and then reconcile the CE's
490
+ if (idField) {
491
+ var collectionEntity = rio.CollectionEntity.create({
492
+ model: model,
493
+ values: [],
494
+ condition: parameters.conditionFunction()
495
+ });
496
+ this.putCollectionEntity(urlToUse + "#" + Object.toJSON(parameters.ajaxParameters()), collectionEntity);
497
+
498
+ Object.values(model._identityCache).each(function(entity) {
499
+ collectionEntity.add(entity);
500
+ });
501
+
502
+ options.onSuccess(collectionEntity);
503
+ return collectionEntity;
504
+ }
505
+
506
+
507
+ if (this._collectionEntities[urlToUse + "#" + Object.toJSON(parameters.ajaxParameters())]) {
508
+ var found = this._collectionEntities[urlToUse + "#" + Object.toJSON(parameters.ajaxParameters())];
509
+ found.prepare();
510
+ options.onSuccess(found);
511
+ if (!asynchronous) {
512
+ return found;
513
+ } else {
514
+ return;
515
+ }
516
+ }
517
+
518
+ var results;
519
+ rio.Model._findAllRequests.push(
520
+ new Ajax.Request(urlToUse, {
521
+ asynchronous: asynchronous,
522
+ method: 'get',
523
+ evalJSON: true,
524
+ parameters: {conditions: Object.toJSON(parameters.ajaxParameters())},
525
+ onSuccess: function(response) {
526
+ results = this._collectionEntityFromJson(response.responseJSON, parameters, urlToUse);
527
+ results.prepare();
528
+ options.onSuccess(results);
529
+ }.bind(this)
530
+ })
531
+ );
532
+ if (!asynchronous) {
533
+ return results;
534
+ }
535
+ },
536
+
537
+ _hasManyAssociations: {},
538
+ hasMany: function(hasManyName) {
539
+ var options = {};
540
+ if (!Object.isString(hasManyName)) {
541
+ options = hasManyName[1] || {};
542
+ hasManyName = hasManyName[0];
543
+ }
544
+ options.className = hasManyName.singularize().classize();
545
+ options.foreignKey = this.NAME.toLowerCase() + "Id";
546
+ options.parameters = options.parameters || {};
547
+
548
+ this._hasManyAssociations[hasManyName] = options;
549
+
550
+ this.attrAccessor(hasManyName);
551
+ this.clientOnlyAttr(hasManyName);
552
+
553
+ var getName = ("get-" + hasManyName).camelize();
554
+
555
+ this.prototype[getName] = this.prototype[getName].wrap(function(proceed) {
556
+ if (this["_" + hasManyName] == undefined) {
557
+ var parameters = Object.clone(options.parameters);
558
+ parameters[options.foreignKey] = this.getId();
559
+ rio.models[options.className].findAll({
560
+ asynchronous: false,
561
+ parameters: parameters,
562
+ onSuccess: function(entities) {
563
+ this["_" + hasManyName] = entities;
564
+ }.bind(this)
565
+ });
566
+ }
567
+ return proceed.apply(this, $A(arguments).slice(1));
568
+ });
569
+ },
570
+
571
+ belongsTo: function(args) {
572
+ var options = {};
573
+ var associationName;
574
+ if (!Object.isString(args)) {
575
+ associationName = args[0];
576
+ options = args[1] || {};
577
+ } else {
578
+ associationName = args;
579
+ }
580
+ this.attrAccessor(associationName);
581
+ this.clientOnlyAttr(associationName);
582
+
583
+ var className = options.className || associationName.classize();
584
+ var foreignKey = options.foreignKey || associationName + "Id";
585
+ var getName = ("get-" + associationName).camelize();
586
+
587
+ this.prototype[getName] = this.prototype[getName].wrap(function(proceed) {
588
+ if (this["_" + associationName] == undefined) {
589
+ var setAssociation = function() {
590
+ var associationId = this["_" + foreignKey];
591
+ var foundValue = rio.models[className].find(associationId, {
592
+ asynchronous: false
593
+ });
594
+ this[("set-" + associationName).camelize()](foundValue);
595
+ }.bind(this);
596
+ setAssociation();
597
+ this[foreignKey].bind(setAssociation, true);
598
+ }
599
+ return proceed.apply(this, $A(arguments).slice(1));
600
+ });
601
+ },
602
+
603
+ _parametersFromJsonParameters: function(params) {
604
+ return new rio.Parameters(
605
+ Object.keys(params).inject({}, function(acc, p) {
606
+ acc[p.camelize()] = params[p];
607
+ return acc;
608
+ })
609
+ );
610
+ },
611
+
612
+ _processIncludedCollectionEntities: function(json) {
613
+ for (var i=json.length; i--;) {
614
+ var include = json[i];
615
+ params = this._parametersFromJsonParameters(include.parameters);
616
+ var url = rio.models[include.className].url();
617
+ if (this._collectionEntities[url + "#" + Object.toJSON(params.ajaxParameters())] == undefined) {
618
+ rio.models[include.className]._collectionEntityFromJson(include.json, params, url);
619
+ }
620
+ }
621
+ },
622
+
623
+ _filterAndProcessJsonWhileAccumulatingCollectionEntities: function(inJson) {
624
+ if (inJson._set) {
625
+ var ceFunction = function() {
626
+ model._processIncludedCollectionEntities(inJson._set.include);
627
+ };
628
+ return [rio.Model.filterJson(inJson._set.self), ceFunction];
629
+ } else {
630
+ return [rio.Model.filterJson(inJson), Prototype.emptyFunction];
631
+ }
632
+ },
633
+
634
+ _filterAndProcessJson: function(inJson) {
635
+ var results = model._filterAndProcessJsonWhileAccumulatingCollectionEntities(inJson);
636
+ results[1]();
637
+ return results[0];
638
+ },
639
+
640
+ _collectionEntityFromJson: function(json, parameters, url) {
641
+ // In case multiple identical queries were made simultaneously
642
+ if (this._collectionEntities[url + "#" + Object.toJSON(parameters.ajaxParameters())]) {
643
+ return this._collectionEntities[url + "#" + Object.toJSON(parameters.ajaxParameters())];
644
+ }
645
+
646
+ var results = json.map(function(result) {
647
+ // This may cause bugs. It should map the results of _filterAndProcessJsonWhileAccumulatingCollectionEntities
648
+ // and execute them after them map to prevent reification collisions
649
+ var modelJson = model._filterAndProcessJson(result);
650
+ var fromCache = this.getFromCache(model.id(modelJson.id));
651
+ if (fromCache) {
652
+ return fromCache;
653
+ } else {
654
+ var lazyNew = function() {
655
+ var fromCache = this.getFromCache(model.id(modelJson.id));
656
+ if (fromCache) { return fromCache; }
657
+ return new model(modelJson);
658
+ }.bind(this);
659
+ lazyNew.__lazyNew = true;
660
+ return lazyNew;
661
+ }
662
+ }.bind(this));
663
+
664
+ var collectionEntity = rio.CollectionEntity.create({
665
+ model: model,
666
+ values: results,
667
+ condition: parameters.conditionFunction()
668
+ });
669
+ collectionEntity.prepare = function() {
670
+ collectionEntity.prepare = Prototype.emptyFunction;
671
+ // prevent the initialization from double adding the entities
672
+ var oldAdd = this.add;
673
+ try {
674
+ var i;
675
+ this.add = Prototype.emptyFunction;
676
+ var timesToLoop = this.length;
677
+ var toRemove = [];
678
+ for (i=0; i<timesToLoop; i++) {
679
+ if (this[i].__lazyNew) {
680
+ var modelInstance = this[i]();
681
+ if (!this.include(modelInstance)) {
682
+ this[i] = modelInstance;
683
+ } else {
684
+ toRemove.push(i);
685
+ }
686
+ }
687
+ }
688
+ for (i=toRemove.length; i--;) {
689
+ this.splice(toRemove[i], 1);
690
+ }
691
+ } finally {
692
+ this.add = oldAdd;
693
+ }
694
+ };
695
+ (function() {
696
+ // Ultimately we shouldn't need to do this
697
+ // This is required if when parsing eager loaded json collections
698
+ // contain individual entities that need to be added to other collection entities
699
+ // and the entity representing the eager collection is never loaded, hence prepared.
700
+ collectionEntity.prepare();
701
+ }.defer());
702
+ this.putCollectionEntity(url + "#" + Object.toJSON(parameters.ajaxParameters()), collectionEntity);
703
+
704
+ return collectionEntity;
705
+ },
706
+
707
+ _clientOnlyAttrs: [],
708
+
709
+ clientOnlyAttr: function(attrName) {
710
+ this._clientOnlyAttrs.push(attrName);
711
+ },
712
+
713
+ _identityCache: {},
714
+
715
+ getFromCache: function(id) {
716
+ if (id == undefined) { return; }
717
+ return this._identityCache[id.cacheKey ? id.cacheKey() : id] || this._identityCache[model.id(id) && model.id(id).cacheKey()];
718
+ },
719
+
720
+ putInCache: function(id, value) {
721
+ var cacheKey = id.cacheKey ? id.cacheKey() : id;
722
+ if (this._identityCache[cacheKey] == value) {
723
+ return;
724
+ }
725
+ this._identityCache[cacheKey] = value;
726
+ this.addToCollectionEntities(value);
727
+ },
728
+
729
+ removeFromCache: function(id) {
730
+ var cacheKey = id.cacheKey ? id.cacheKey() : id;
731
+ delete this._identityCache[cacheKey];
732
+ },
733
+
734
+ _collectionEntities: {},
735
+ putCollectionEntity: function(key, value) {
736
+ this._collectionEntities[key] = value;
737
+ },
738
+
739
+ addToCollectionEntities: function(value) {
740
+ var ces = this._collectionEntities;
741
+ for (var key in ces) {
742
+ ces[key].add(value);
743
+ }
744
+ },
745
+
746
+ updateInCollectionEntites: function(value) {
747
+ var ces = this._collectionEntities;
748
+ for (var key in ces) {
749
+ ces[key].update(value);
750
+ }
751
+ },
752
+
753
+ removeFromCollectionEntities: function(value) {
754
+ Object.values(this._collectionEntities).each(function(collectionEntity) {
755
+ collectionEntity.remove(value);
756
+ });
757
+ }
758
+
759
+ });
760
+ },
761
+
762
+ hasChannel: function() {
763
+ return false;
764
+ },
765
+
766
+ channel: function() {
767
+ model.addMethods({
768
+ broadcast: function() {
769
+ var args = $A(arguments);
770
+ var methodName = args.shift();
771
+ var body = {
772
+ id: this.getId().toString(),
773
+ method: methodName,
774
+ args: args
775
+ };
776
+
777
+ new Ajax.Request("/push/broadcast", {
778
+ asynchronous: true,
779
+ method: "get",
780
+ evalJSON: false,
781
+ evalJS: false,
782
+ parameters: {
783
+ channel: this.channelName(),
784
+ message: Object.toJSON(body)
785
+ }
786
+ });
787
+ },
788
+
789
+ channelName: function() {
790
+ return model + "." + this.getId();
791
+ }
792
+ });
793
+
794
+ Object.extend(model, {
795
+ hasChannel: function() { return true; },
796
+
797
+ receiveBroadcast: function(options) {
798
+ var instance = model.getFromCache(model.id(options.id));
799
+ if (instance) {
800
+ instance[options.method].apply(instance, options.args);
801
+ }
802
+ }
803
+ });
804
+ }
805
+ });
806
+
807
+ if (args.length > 0 && args.last() != undefined && !args.last().ATTR) {
808
+ var initializers = args.last();
809
+ if (Object.isString(initializers.resource)) {
810
+ model.resource(initializers.resource);
811
+ }
812
+ if (initializers.channel) {
813
+ model.channel();
814
+ }
815
+
816
+ if (initializers.hasMany) {
817
+ initializers.hasMany.each(function(h) {
818
+ model.hasMany(h);
819
+ });
820
+ }
821
+
822
+ if (initializers.belongsTo) {
823
+ initializers.belongsTo.each(function(h) {
824
+ model.belongsTo(h);
825
+ });
826
+ }
827
+
828
+ (initializers.clientOnlyAttrs || []).each(function(clientOnlyAttr) {
829
+ model.clientOnlyAttr(clientOnlyAttr);
830
+ });
831
+
832
+ if (initializers.undoEnabled) {
833
+ model.undoEnabled = true;
834
+ }
835
+
836
+ rio.Model.extend(model, initializers.methods || {});
837
+ }
838
+
839
+ return model;
840
+ },
841
+
842
+ _findAllRequests: [],
843
+ afterActiveQueries: function(fcn) {
844
+ var count = 0;
845
+ this._findAllRequests.each(function(far) {
846
+ if (far._complete) { return; }
847
+ count++;
848
+ far.options.onComplete = (far.options.onComplete || Prototype.emptyFunction).wrap(function(proceed) {
849
+ var ret = proceed(arguments);
850
+ count--;
851
+ if (count == 0) { fcn(); }
852
+ return ret;
853
+ });
854
+ });
855
+ if (count == 0) { fcn(); }
856
+ },
857
+
858
+ filterJson: function(json) {
859
+ if (json.attributes) { return json.attributes; }
860
+ return (rio.environment.includeRootInJson) ? Object.values(json)[0] : json;
861
+ },
862
+
863
+ remoteCreate: function(options) {
864
+ if (options.transactionKey == rio.environment.transactionKey) { return; }
865
+ try {
866
+ var results = rio.Model.doRemoteCreate(options);
867
+ if (results.undoFunction) {
868
+ rio.Undo.registerUndo(results.undoFunction);
869
+ }
870
+ } catch(e) {
871
+ rio.error(e, "Remote create error!");
872
+ }
873
+ },
874
+
875
+ remoteUpdate: function(options) {
876
+ if (options.transactionKey == rio.environment.transactionKey) { return; }
877
+ try {
878
+ var results = rio.Model.doRemoteUpdate(options);
879
+ if (results.undoFunction) {
880
+ rio.Undo.registerUndo(results.undoFunction);
881
+ }
882
+ } catch(e) {
883
+ rio.error(e, "Remote update error!");
884
+ }
885
+ },
886
+
887
+ remoteDestroy: function(options) {
888
+ if (options.transactionKey == rio.environment.transactionKey) { return; }
889
+ try {
890
+ var results = rio.Model.doRemoteDestroy(options);
891
+ if (results.undoFunction) {
892
+ rio.Undo.registerUndo(results.undoFunction);
893
+ }
894
+ } catch(e) {
895
+ rio.error(e, "Remote destroy error!");
896
+ }
897
+ },
898
+
899
+ doRemoteCreate: function(options) {
900
+ var model = rio.models[options.name];
901
+ var fromCache = model.getFromCache(model.id(options.id));
902
+ if (!fromCache) {
903
+ fromCache = new model(model._filterAndProcessJson(options.json));
904
+ } else {
905
+ return {};
906
+ }
907
+
908
+ return {
909
+ undoFunction: model.undoEnabled ? function() {
910
+ model.getFromCache(fromCache.getId()).destroy();
911
+ } : null
912
+ };
913
+ },
914
+
915
+ doRemoteUpdate: function(options) {
916
+ var model = rio.models[options.name];
917
+ var instance = model.getFromCache(model.id(options.id));
918
+ if (!instance) {
919
+ return {};
920
+ }
921
+
922
+ var json = model._filterAndProcessJson(options.json);
923
+
924
+ var results = {};
925
+
926
+ if (model.undoEnabled) {
927
+ var oldState = instance.attributeState();
928
+ var processUndo = function() {
929
+ model.getFromCache(instance.getId()).updateAttributes(oldState);
930
+ };
931
+ results.undoFunction = processUndo;
932
+ }
933
+
934
+ var attributes = Object.keys(json).without("id").inject({}, function(acc, k) {
935
+ acc[k.camelize()] = json[k];
936
+ return acc;
937
+ });
938
+ instance.updateAttributes(attributes, { skipSave: true });
939
+ rio.models[options.name].updateInCollectionEntites(instance);
940
+
941
+ instance._lastSavedState = instance.attributeState();
942
+
943
+ return results;
944
+ },
945
+
946
+ doRemoteDestroy: function(options) {
947
+ var model = rio.models[options.name];
948
+ var fromCache = model.getFromCache(model.id(options.id));
949
+ var results = {};
950
+
951
+ if (fromCache) {
952
+ if (model.undoEnabled) {
953
+ var oldState = fromCache.attributeState();
954
+ var processUndo = function() {
955
+ var instance = new model(Object.extend({id: fromCache.getId()}, oldState));
956
+ instance._lastSavedState = undefined;
957
+ instance.save();
958
+ };
959
+ results.undoFunction = processUndo;
960
+ }
961
+
962
+ fromCache.removeFromCaches();
963
+
964
+ fromCache.fire("destroy");
965
+ }
966
+
967
+ return results;
968
+ },
969
+
970
+ remoteTransaction: function(transactionData) {
971
+ if (transactionData.transactionKey == rio.environment.transactionKey) { return; }
972
+ try {
973
+ // TODO: Two potential bugs here
974
+ //
975
+ // 1) Need to execute this in a rio.Attr.transaction
976
+ // 2) Need to get back the collection entity process functions from _filterAndProcessJsonWhileAccumulatingCollectionEntities
977
+ // and run them at the end. Otherwise we might have reification collisions.
978
+ var resultSets = transactionData.transaction.map(function(t) {
979
+ switch(t.action) {
980
+ case "create":
981
+ return rio.Model.doRemoteCreate(t);
982
+ case "update":
983
+ return rio.Model.doRemoteUpdate(t);
984
+ case "destroy":
985
+ return rio.Model.doRemoteDestroy(t);
986
+ }
987
+ });
988
+
989
+ var undos = resultSets.map(function(r) { return r.undoFunction; }).compact();
990
+
991
+ if (!undos.empty()) {
992
+ rio.Undo.registerUndo(function() {
993
+ undos.each(function(undo) {
994
+ undo();
995
+ });
996
+ });
997
+ }
998
+ } catch(e) {
999
+ rio.error(e, "Remote transaction error!");
1000
+ }
1001
+ },
1002
+
1003
+ extend: function(model, extension) {
1004
+
1005
+ extension.__initialize = extension.initialize || Prototype.emptyFunction;
1006
+ extension.initialize = function(options) {
1007
+ this.__model = model;
1008
+
1009
+ if (options.id == undefined && model.url) {
1010
+ options.id = model.id();
1011
+ this._id = options.id;
1012
+ }
1013
+
1014
+ if (options.id && options.id.constructor != rio.Id && model.id) {
1015
+ options.id = model.id(options.id, true);
1016
+ this.setId(options.id);
1017
+ }
1018
+
1019
+ (this.__initialize.bind(this))(options);
1020
+
1021
+ // Add the hasManyAssocitation#create methods
1022
+ Object.keys(model._hasManyAssociations).each(function(hasManyName) {
1023
+ var options = model._hasManyAssociations[hasManyName];
1024
+ this[hasManyName].create = function(createOptions) {
1025
+ // In the case that the entity is still new, we should preload the association
1026
+ // to make sure no entries are missed
1027
+ // TODO: before we can wrap in isNew, we need to make the spec fixture proxy include created entities
1028
+ // if (this.isNew()) {
1029
+ this[("get-" + hasManyName).camelize()]();
1030
+ // }
1031
+ var parameters = Object.clone(options.parameters);
1032
+ parameters[options.foreignKey] = this.getId();
1033
+ return rio.models[options.className].create(Object.extend(createOptions || {}, parameters));
1034
+ }.bind(this);
1035
+ }.bind(this));
1036
+
1037
+ if (model.hasChannel()) {
1038
+ if (rio.push) {
1039
+ var addChannel = function() {
1040
+ rio.push.addChannel(this.channelName());
1041
+ }.bind(this);
1042
+ if (options.id && options.id.temporary()) {
1043
+ options.id.doAfterReification(addChannel);
1044
+ } else {
1045
+ addChannel();
1046
+ }
1047
+ } else {
1048
+ rio.warn("Attempted to add a channel without an available push server");
1049
+ }
1050
+ }
1051
+
1052
+ options = options || {};
1053
+ if (options.id && model.url) {
1054
+ model.putInCache(options.id, this);
1055
+ }
1056
+ if (options.id && options.id.temporary && !options.id.temporary()) {
1057
+ this._lastSavedState = this.attributeState();
1058
+ }
1059
+ };
1060
+
1061
+ rio.Attr.extend(model, extension);
1062
+ },
1063
+
1064
+ toString: function() {
1065
+ return "Model";
1066
+ }
1067
+ };