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,6 @@
1
+ describe(rio.components.Panel, {
2
+ beforeEach: function() {
3
+
4
+ }
5
+
6
+ });
@@ -0,0 +1,40 @@
1
+ describe(rio.components.Radio, {
2
+ beforeEach: function() {
3
+ this.radio1 = rio.components.Radio.example("a1");
4
+ this.radio2 = rio.components.Radio.example("a2");
5
+ insertComponent(this.radio1);
6
+ insertComponent(this.radio2);
7
+ },
8
+
9
+ "should have an html radio element": function() {
10
+ this.radio1.html().childElements()[0].tagName.shouldEqual("INPUT");
11
+ this.radio1.html().childElements()[0].type.shouldEqual("radio");
12
+ },
13
+
14
+ "should have the proper checked attribute": function() {
15
+ this.radio1.html().childElements()[0].checked.shouldBeFalse();
16
+ this.radio2.html().childElements()[0].checked.shouldBeTrue();
17
+ },
18
+
19
+ "should update the checked attribute when updating the html checkbox": function() {
20
+ this.radio1.html().childElements()[0].simulate("click");
21
+ this.radio1.getChecked().shouldBeTrue();
22
+ },
23
+
24
+ "should update the html checked attribute when update the checked attribute": function() {
25
+ this.radio1.setChecked(true);
26
+ this.radio1.html().childElements()[0].checked.shouldBeTrue();
27
+ },
28
+
29
+ "should update the other radio buttons html checked attributes when updating the checked attribute": function() {
30
+ this.radio1.setChecked(true);
31
+ this.radio2.html().childElements()[0].checked.shouldBeFalse();
32
+ },
33
+
34
+ "should update the other radio buttons checked attributes when updating the checked attribute": function() {
35
+ this.radio1.html().childElements()[0].simulate("click");
36
+ this.radio2.getChecked().shouldBeFalse();
37
+ }
38
+
39
+
40
+ });
@@ -0,0 +1,3 @@
1
+ rio.components.Box.setExamples({
2
+
3
+ });
@@ -0,0 +1,9 @@
1
+ rio.components.Checkbox.setExamples({
2
+ checked: {
3
+ checked: true
4
+ },
5
+
6
+ unchecked: {
7
+ checked: false
8
+ }
9
+ });
@@ -0,0 +1,3 @@
1
+ rio.components.Container.setExamples({
2
+
3
+ });
@@ -0,0 +1,12 @@
1
+ rio.components.Input.setExamples({
2
+
3
+ empty: {
4
+ },
5
+
6
+ helloWorld: {
7
+ value: "hello",
8
+ className: "world",
9
+ hoverClassName: "hover",
10
+ focusClassName: "focus"
11
+ }
12
+ });
@@ -0,0 +1,19 @@
1
+ rio.components.Menu.setExamples({
2
+
3
+ empty: {
4
+ },
5
+
6
+ oneItem: {
7
+ items: [
8
+ example(rio.components.MenuItem, "hello")
9
+ ]
10
+ },
11
+
12
+ manyItems: {
13
+ items: [
14
+ example(rio.components.MenuItem, "file"),
15
+ example(rio.components.MenuItem, "edit"),
16
+ example(rio.components.MenuItem, "view")
17
+ ]
18
+ }
19
+ });
@@ -0,0 +1,18 @@
1
+ rio.components.MenuItem.setExamples({
2
+
3
+ hello: {
4
+ text: "Hello"
5
+ },
6
+
7
+ file: {
8
+ text: "File"
9
+ },
10
+
11
+ edit: {
12
+ text: "Edit"
13
+ },
14
+
15
+ view: {
16
+ text: "View"
17
+ }
18
+ });
@@ -0,0 +1,11 @@
1
+ rio.components.Radio.setExamples({
2
+ a1: {
3
+ checked: false,
4
+ name: "a"
5
+ },
6
+
7
+ a2: {
8
+ checked: true,
9
+ name: "a"
10
+ }
11
+ });
@@ -0,0 +1,281 @@
1
+ /*
2
+
3
+ Untested methods:
4
+
5
+ applyHistoryEntry
6
+ addHistoryEntry
7
+ navigateTo
8
+ clearPage
9
+ reboot
10
+ getCurrentLocation
11
+ getCurrentPage
12
+ setCurrentPage
13
+ rootUrl
14
+
15
+ #include
16
+ #require
17
+ #includeCss
18
+ #getToken
19
+ #setToken
20
+ #fail
21
+
22
+ */
23
+
24
+ describe(rio.Application, {
25
+
26
+ "with no routes": {
27
+ beforeEach: function() {
28
+ this.lPressed = false;
29
+ var application = rio.Application.create({
30
+ methods: {
31
+ keyMap: function() {
32
+ return [
33
+ {
34
+ map: { key: 'l' },
35
+ handler: function() {
36
+ this.lPressed = true;
37
+ }.bind(this)
38
+ }
39
+ ];
40
+ }.bind(this)
41
+ }
42
+ });
43
+ this.applicationInstance = new application();
44
+ },
45
+
46
+ "should have no routes": function() {
47
+ this.applicationInstance.noRoutes().shouldBeTrue();
48
+ },
49
+
50
+ "should avoid animation if IE": function() {
51
+ stub(Prototype.Browser, "IE").withValue(true);
52
+ this.applicationInstance.avoidAnimation().shouldBeTrue();
53
+ },
54
+
55
+ "should not avoid animation if not IE": function() {
56
+ stub(Prototype.Browser, "IE").withValue(false);
57
+ this.applicationInstance.avoidAnimation().shouldBeFalse();
58
+ },
59
+
60
+ "should reload the page on refresh": function() {
61
+ /* You can't stub document.location.reload in firefox, so just skip the test */
62
+ if (Prototype.Browser.Gecko) { return; }
63
+
64
+ stub(document.location, "reload").shouldBeCalled();
65
+ this.applicationInstance.refresh();
66
+ },
67
+
68
+ "should execute keymaps on keypress": function() {
69
+ this.applicationInstance.launch();
70
+
71
+ var keyEvent = { keyCode: 76 };
72
+ this.applicationInstance.keyDown(keyEvent);
73
+ this.lPressed.shouldBeTrue();
74
+ }
75
+ },
76
+
77
+ "with routes": {
78
+ beforeEach: function() {
79
+ stub(dhtmlHistory, "getCurrentLocation").andReturn("");
80
+
81
+
82
+ var page = rio.Page.create({
83
+ methods: {
84
+ buildHtml: function() {
85
+ return "";
86
+ },
87
+
88
+ render: function() {
89
+
90
+ }
91
+ }
92
+ });
93
+ this.lPressed = false;
94
+ this.myPage = new page();
95
+ this.application = rio.Application.create({
96
+ routes: {
97
+ "other": "otherPage",
98
+ ":something/hello": "helloPage",
99
+ "hello/*world": "worldPage",
100
+ "": "myPage"
101
+ },
102
+ methods: {
103
+ myPage: function() {
104
+ return this.myPage;
105
+ }.bind(this),
106
+
107
+ keyMap: function() {
108
+ return [
109
+ {
110
+ map: { key: 'l' },
111
+ handler: function() {
112
+ this.lPressed = true;
113
+ }.bind(this)
114
+ }
115
+ ];
116
+ }.bind(this)
117
+ }
118
+ });
119
+ this.applicationInstance = new this.application();
120
+ },
121
+
122
+ "should not have no routes": function() {
123
+ this.applicationInstance.noRoutes().shouldBeFalse();
124
+ },
125
+
126
+ "should fire its current page's resize event if the window is resized": function() {
127
+ this.applicationInstance.launch();
128
+ stub(this.myPage, "resize").shouldBeCalled();
129
+ this.applicationInstance.resize();
130
+ },
131
+
132
+ "should fire its current page's keyPress event on keyPress": function() {
133
+ this.applicationInstance.launch();
134
+
135
+ var expectedEvent = { a: 1 };
136
+ stub(this.myPage, "keyPress").withValue(function(actualEvent) {
137
+ (expectedEvent === actualEvent).shouldBeTrue();
138
+ }.shouldBeCalled());
139
+ this.applicationInstance.keyPress(expectedEvent);
140
+ },
141
+
142
+ "should execute keymaps on keypress": function() {
143
+ this.applicationInstance.launch();
144
+
145
+ var keyEvent = { keyCode: 76 };
146
+ this.applicationInstance.keyDown(keyEvent);
147
+ this.lPressed.shouldBeTrue();
148
+ },
149
+
150
+ "should fire its current page's _keyDown event on keyDown": function() {
151
+ this.applicationInstance.launch();
152
+
153
+ var expectedEvent = { a: 1 };
154
+ stub(this.myPage, "_keyDown").withValue(function(actualEvent) {
155
+ (expectedEvent === actualEvent).shouldBeTrue();
156
+ }.shouldBeCalled());
157
+ this.applicationInstance.keyDown(expectedEvent);
158
+ },
159
+
160
+ "should not be launched before launch": function() {
161
+ this.applicationInstance.launched().shouldBeFalse();
162
+ },
163
+
164
+ "should be launched after launch": function() {
165
+ this.applicationInstance.launch();
166
+ this.applicationInstance.launched().shouldBeTrue();
167
+ },
168
+
169
+ "should initialize the dhtmlHistory on launch": function() {
170
+ stub(window.dhtmlHistory, "initialize").shouldBeCalled();
171
+ this.applicationInstance.launch();
172
+ },
173
+
174
+ "should add applyHistoryEntry as a listener to the dhtmlHistory on launch": function() {
175
+ stub(this.applicationInstance, "applyHistoryEntry").shouldBeCalled();
176
+ stub(window.dhtmlHistory, "addListener").withValue(function(listener) {
177
+ listener();
178
+ }.shouldBeCalled());
179
+ this.applicationInstance.launch();
180
+ },
181
+
182
+ "should immediately execute Application#afterLaunch calls if rio.app exists and has been launched": function() {
183
+ stub(rio, "_afterLaunchFunctions").withValue([]);
184
+ stub(rio, "app").withValue(this.applicationInstance);
185
+ this.applicationInstance.launch();
186
+ rio.Application.afterLaunch(function() {}.shouldBeCalled());
187
+ rio.Application.afterLaunch(function() {}.shouldBeCalled());
188
+ },
189
+
190
+ "should not immediately execute Application#afterLaunch calls if rio.app does not exist": function() {
191
+ stub(rio, "_afterLaunchFunctions").withValue([]);
192
+ stub(rio, "app").withValue();
193
+ rio.Application.afterLaunch(function() {}.shouldNotBeCalled());
194
+ },
195
+
196
+ "should not immediately execute Application#afterLaunch calls if rio.app exists but is not launched": function() {
197
+ stub(rio, "_afterLaunchFunctions").withValue([]);
198
+ stub(rio, "app").withValue(this.applicationInstance);
199
+ rio.Application.afterLaunch(function() {}.shouldNotBeCalled());
200
+ },
201
+
202
+ "should process the Application class afterLaunch functions on launch": function() {
203
+ stub(rio, "app").withValue(this.applicationInstance);
204
+ rio.Application.afterLaunch(function() {}.shouldBeCalled());
205
+ rio.Application.afterLaunch(function() {}.shouldBeCalled());
206
+ this.applicationInstance.launch();
207
+ },
208
+
209
+ "should match exact matches": function() {
210
+ this.applicationInstance.matchRoutePath("other").shouldEqual("other");
211
+ this.applicationInstance.matchRoutePath("").shouldEqual("");
212
+ },
213
+
214
+ "should treat :part as wild cards separated by slashes": function() {
215
+ this.applicationInstance.matchRoutePath("123/hello").shouldEqual(":something/hello");
216
+ },
217
+
218
+ "should treat *part as optional": function() {
219
+ this.applicationInstance.matchRoutePath("hello/123").shouldEqual("hello/*world");
220
+ this.applicationInstance.matchRoutePath("hello").shouldEqual("hello/*world");
221
+ },
222
+
223
+ "should fail to create routes if the * is not on the last parameter": function() {
224
+ var f = function() {}.shouldBeCalled();
225
+ try {
226
+ rio.Application.create({
227
+ routes: {
228
+ "*hello/world": "worldPage"
229
+ }
230
+ });
231
+ } catch(e) {
232
+ f();
233
+ }
234
+ },
235
+
236
+ "should match anything to '' route": function() {
237
+ this.applicationInstance.matchRoutePath("asdf").shouldEqual("");
238
+ },
239
+
240
+ "should return the match route target name": function() {
241
+ this.applicationInstance.matchRouteTarget("asdf").shouldEqual("myPage");
242
+ this.applicationInstance.matchRouteTarget("123/hello").shouldEqual("helloPage");
243
+ },
244
+
245
+ "should return anything after a slash trailing a match as remainingPath": function() {
246
+ this.applicationInstance.remainingPath("asdf").shouldEqual("asdf");
247
+ this.applicationInstance.remainingPath("123/hello/3/something").shouldEqual("3/something");
248
+ this.applicationInstance.remainingPath("other/page").shouldEqual("page");
249
+ this.applicationInstance.remainingPath("hello/anything").shouldEqual("anything");
250
+ },
251
+
252
+ "should return an empty hash for path parts that match path's with no wild-cards": function() {
253
+ var parts = this.applicationInstance.pathParts("asdf");
254
+ (parts.constructor == Object).shouldBeTrue();
255
+ Object.keys(parts).shouldBeEmpty();
256
+ },
257
+
258
+ "should return a hash from the wildcard name to its value in the path for path parts": function() {
259
+ this.applicationInstance.pathParts("123/hello").something.shouldEqual("123");
260
+ },
261
+
262
+ "should return a hash from the optional name to its value in the path for path parts": function() {
263
+ this.applicationInstance.pathParts("hello/456").world.shouldEqual("456");
264
+ },
265
+
266
+ "should fail the application if a route can't be found": function() {
267
+ stub(rio.Application, "fail").shouldBeCalled();
268
+ var app = new (rio.Application.create({
269
+ routes: {
270
+ "world": "asdf"
271
+ }
272
+ }))({});
273
+ try {
274
+ app.navigateTo("hello");
275
+ } catch(e) {
276
+ /* It's going to error here */
277
+ }
278
+ }
279
+ }
280
+
281
+ });
@@ -0,0 +1,1514 @@
1
+ describe(rio.Attr, {
2
+
3
+ "should support declaritive attrReader": function() {
4
+ var attr = rio.Attr.create();
5
+ attr.attrReader("something");
6
+ rio.Attr.extend(attr, {});
7
+
8
+ var attrInstance = new attr({ something: "some value" });
9
+
10
+ attrInstance.getSomething().shouldEqual("some value");
11
+ },
12
+
13
+ "should support declaritive attrReader with default value": function() {
14
+ var attr = rio.Attr.create();
15
+ attr.attrReader("something", "some default");
16
+ rio.Attr.extend(attr, {});
17
+
18
+ var attrInstance = new attr();
19
+
20
+ attrInstance.getSomething().shouldEqual("some default");
21
+ },
22
+
23
+ "should support declaritive attrAccessor": function() {
24
+ var attr = rio.Attr.create();
25
+ attr.attrAccessor("something");
26
+ rio.Attr.extend(attr, {});
27
+
28
+ var attrInstance = new attr({ something: "some value" });
29
+
30
+ attrInstance.getSomething().shouldEqual("some value");
31
+ attrInstance.setSomething("another value");
32
+ attrInstance.getSomething().shouldEqual("another value");
33
+ },
34
+
35
+ "should support attr inheritance": function() {
36
+ var attr = rio.Attr.create();
37
+ attr.attrReader("something");
38
+ rio.Attr.extend(attr, {});
39
+
40
+ var subAttr = rio.Attr.create(attr);
41
+
42
+ var attrInstance = new subAttr({ something: "some value" });
43
+
44
+ attrInstance.getSomething().shouldEqual("some value");
45
+ },
46
+
47
+ "should support concise attrReader syntax": function() {
48
+ var attr = rio.Attr.create({
49
+ attrReaders: ["something", "anotherThing"]
50
+ });
51
+
52
+ var attrInstance = new attr({ something: "some value", anotherThing: "another thing" });
53
+ attrInstance.getSomething().shouldEqual("some value");
54
+ attrInstance.getAnotherThing().shouldEqual("another thing");
55
+ },
56
+
57
+ "should support concise attrReader syntax also generating an is- accessor": function() {
58
+ var attr = rio.Attr.create({
59
+ attrReaders: ["something"]
60
+ });
61
+
62
+ var attrInstance = new attr({ something: "some value" });
63
+ attrInstance.isSomething().shouldEqual("some value");
64
+ },
65
+
66
+ "should support concise attrReader syntax with default values": function() {
67
+ var attr = rio.Attr.create({
68
+ attrReaders: [["something", "some default"]]
69
+ });
70
+
71
+ var attrInstance = new attr();
72
+
73
+ attrInstance.getSomething().shouldEqual("some default");
74
+ },
75
+
76
+ "should support concise attrReader syntax with default array values that are cloned when creating new objects": function() {
77
+ var attr = rio.Attr.create({
78
+ attrReaders: [["something", [1]]]
79
+ });
80
+
81
+ var a = new attr();
82
+ a.getSomething().push(2);
83
+ a.getSomething().length.shouldEqual(2);
84
+ shouldBeUndefined(new attr().getSomething().length.shouldEqual(1));
85
+ },
86
+
87
+ "should support concise attrReader syntax with default object values that are cloned when creating new objects": function() {
88
+ var attr = rio.Attr.create({
89
+ attrReaders: [["something", {a: 1}]]
90
+ });
91
+
92
+ var a = new attr();
93
+ a.getSomething().b = 2;
94
+ shouldBeUndefined(new attr().getSomething().b);
95
+ },
96
+
97
+ "should support concise attrAccessor syntax": function() {
98
+ var attr = rio.Attr.create({
99
+ attrAccessors: ["something", "anotherThing"]
100
+ });
101
+
102
+ var attrInstance = new attr({ something: "some value", anotherThing: "another thing" });
103
+
104
+ attrInstance.getSomething().shouldEqual("some value");
105
+ attrInstance.getAnotherThing().shouldEqual("another thing");
106
+
107
+ attrInstance.setSomething("some value 2");
108
+ attrInstance.getSomething().shouldEqual("some value 2");
109
+ },
110
+
111
+ "should support concise attrAccessor syntax with default values": function() {
112
+ var attr = rio.Attr.create({
113
+ attrAccessors: [["something", "some default"]]
114
+ });
115
+
116
+ var attrInstance = new attr();
117
+
118
+ attrInstance.getSomething().shouldEqual("some default");
119
+
120
+ attrInstance.setSomething("some value");
121
+ attrInstance.getSomething().shouldEqual("some value");
122
+ },
123
+
124
+ "should support concise instance method extension": function() {
125
+ var attr = rio.Attr.create({
126
+ methods: {
127
+ hello: function() {
128
+ return "hello world";
129
+ }
130
+ }
131
+ });
132
+
133
+ var attrInstance = new attr();
134
+
135
+ attrInstance.hello().shouldEqual("hello world");
136
+ },
137
+
138
+ "should support concise class method extension": function() {
139
+ var attr = rio.Attr.create({
140
+ classMethods: {
141
+ hello: function() {
142
+ return "hello world";
143
+ }
144
+ }
145
+ });
146
+
147
+ attr.hello().shouldEqual("hello world");
148
+ },
149
+
150
+ /* BEGIN BINDING RELATED SPECS */
151
+ "with a bindable attribute named a": {
152
+ beforeEach: function() {
153
+ this.attr = rio.Attr.create({ attrAccessors: ["a"] });
154
+ this.attrB = rio.Attr.create({ attrAccessors: ["b"] });
155
+ },
156
+
157
+ "should immediately execute a binding on a": function() {
158
+ var attrInstance = new this.attr({ a: "asdf" });
159
+ attrInstance.bind("a", function(newVal) {
160
+ newVal.shouldEqual("asdf");
161
+ }.shouldBeCalled());
162
+ },
163
+
164
+ "should pass the current value as the old value on bind": function() {
165
+ var attrInstance = new this.attr({ a: "asdf" });
166
+ attrInstance.bind("a", function(newVal, oldVal) {
167
+ oldVal.shouldEqual("asdf");
168
+ }.shouldBeCalled());
169
+ },
170
+
171
+ "should not immediately execute a binding when passing true for skipInitialExecution": function() {
172
+ var attrInstance = new this.attr({ a: "asdf" });
173
+ attrInstance.bind("a", function() {}.shouldNotBeCalled(), true);
174
+ },
175
+
176
+ "should allow binding a function to a using attr.bind('a', fcn) syntax": function() {
177
+ var attrInstance = new this.attr();
178
+ attrInstance.bind("a", function(newVal) {
179
+ newVal.shouldEqual("asdf");
180
+ }.shouldBeCalled(), true);
181
+
182
+ attrInstance.setA("asdf");
183
+ },
184
+
185
+ "should also pass the old value to the function on set": function() {
186
+ var attrInstance = new this.attr({ a: "something old" });
187
+ attrInstance.bind("a", function(newVal, oldVal) {
188
+ newVal.shouldEqual("asdf");
189
+ oldVal.shouldEqual("something old");
190
+ }.shouldBeCalled(), true);
191
+
192
+ attrInstance.setA("asdf");
193
+ },
194
+
195
+ "should not execute bindings during a #transaction until the transaction has completed": function() {
196
+ var attrInstance = new this.attr({ a: "asdf" });
197
+ var lastCalledWith;
198
+ attrInstance.bind("a", function(val) {
199
+ lastCalledWith = val;
200
+ });
201
+
202
+ lastCalledWith.shouldEqual("asdf");
203
+ rio.Attr.transaction(function() {
204
+ attrInstance.setA("qwer");
205
+ lastCalledWith.shouldNotEqual("qwer");
206
+ }.shouldBeCalled());
207
+ lastCalledWith.shouldEqual("qwer");
208
+ },
209
+
210
+ "should not reexecute bindings during a second #transaction": function() {
211
+ var attrInstance = new this.attr({ a: "asdf" });
212
+ var lastCalledWith;
213
+ attrInstance.bind("a", function(val) {
214
+ lastCalledWith = val;
215
+ });
216
+
217
+ lastCalledWith.shouldEqual("asdf");
218
+ rio.Attr.transaction(function() {
219
+ attrInstance.setA("qwer");
220
+ lastCalledWith.shouldNotEqual("qwer");
221
+ }.shouldBeCalled());
222
+ lastCalledWith.shouldEqual("qwer");
223
+
224
+ lastCalledWith = undefined;
225
+ rio.Attr.transaction(function() {
226
+
227
+ });
228
+ shouldBeUndefined(lastCalledWith);
229
+ },
230
+
231
+ "should not execute bindings during nested #transactions until all transactions have completed": function() {
232
+ var attrInstance = new this.attr({ a: "asdf" });
233
+ var lastCalledWith;
234
+ attrInstance.bind("a", function(val) {
235
+ lastCalledWith = val;
236
+ });
237
+
238
+ lastCalledWith.shouldEqual("asdf");
239
+ rio.Attr.transaction(function() {
240
+ rio.Attr.transaction(function() {
241
+ attrInstance.setA("qwer");
242
+ lastCalledWith.shouldNotEqual("qwer");
243
+ }.shouldBeCalled());
244
+ lastCalledWith.shouldNotEqual("qwer");
245
+ }.shouldBeCalled());
246
+
247
+ lastCalledWith.shouldEqual("qwer");
248
+ },
249
+
250
+ "should not execute bindings during a #transaction if the transaction fails": function() {
251
+ var attrInstance = new this.attr({ a: "asdf" });
252
+ var lastCalledWith;
253
+ attrInstance.bind("a", function(val) {
254
+ lastCalledWith = val;
255
+ });
256
+
257
+ lastCalledWith.shouldEqual("asdf");
258
+ try {
259
+ rio.Attr.transaction(function() {
260
+ attrInstance.setA("qwer");
261
+ throw("EEEK");
262
+ }.shouldBeCalled());
263
+ } catch(e) {
264
+ /* swallow this exception as part of the test */
265
+ }
266
+
267
+ lastCalledWith.shouldEqual("asdf");
268
+ },
269
+
270
+ "should not execute bindings during a #transaction if the transaction fails but should allow a binding to fire immediately after": function() {
271
+ var attrInstance = new this.attr({ a: "asdf" });
272
+ var lastCalledWith;
273
+ attrInstance.bind("a", function(val) {
274
+ lastCalledWith = val;
275
+ });
276
+
277
+ lastCalledWith.shouldEqual("asdf");
278
+ try {
279
+ rio.Attr.transaction(function() {
280
+ attrInstance.setA("qwer");
281
+ throw("EEEK");
282
+ }.shouldBeCalled());
283
+ } catch(e) {
284
+ /* swallow this exception as part of the test */
285
+ }
286
+ lastCalledWith.shouldEqual("asdf");
287
+
288
+ attrInstance.setA("zxcv");
289
+ lastCalledWith.shouldEqual("zxcv");
290
+ },
291
+
292
+ "should return an unbind function when binding a function to an a": function() {
293
+ var attrInstance = new this.attr();
294
+ var unbindFunction = attrInstance.bind("a", function() {}.shouldNotBeCalled(), true);
295
+ unbindFunction();
296
+ attrInstance.setA("asdf");
297
+ },
298
+
299
+ "should allow binding on a path 'a.b' and fire bindings when a's b value changes": function() {
300
+ var attrBInstance = new this.attrB();
301
+ var attrInstance = new this.attr({ a: attrBInstance });
302
+ attrInstance.bind("a.b", function(newVal) {
303
+ newVal.shouldEqual("asdf");
304
+ }.shouldBeCalled(), true);
305
+
306
+ attrBInstance.setB("asdf");
307
+ },
308
+
309
+ "should allow binding on a path 'a.b' and fire bindings immediately with the old value and new value the same": function() {
310
+ var attrInstance = new this.attr({ a: new this.attrB({ b: "asdf" }) });
311
+ attrInstance.bind("a.b", function(newVal, oldVal) {
312
+ oldVal.shouldEqual("asdf");
313
+ newVal.shouldEqual("asdf");
314
+ }.shouldBeCalled());
315
+ },
316
+
317
+ "should allow binding on a path 'a.b' and fire bindings when a's b value changes with the old value as well": function() {
318
+ var attrBInstance = new this.attrB({ b: "old" });
319
+ var attrInstance = new this.attr({ a: attrBInstance });
320
+ attrInstance.bind("a.b", function(newVal, oldVal) {
321
+ oldVal.shouldEqual("old");
322
+ }.shouldBeCalled(), true);
323
+
324
+ attrBInstance.setB("asdf");
325
+ },
326
+
327
+ "should allow binding on a path 'a.b' and fire bindings when a changes": function() {
328
+ var attrInstance = new this.attr();
329
+ attrInstance.bind("a.b", function(newVal) {
330
+ newVal.shouldEqual("asdf");
331
+ }.shouldBeCalled(), true);
332
+
333
+ attrInstance.setA(new this.attrB({ b: "asdf" }));
334
+ },
335
+
336
+ "should allow binding on a path 'a.b' and fire bindings when a changes with the old value also": function() {
337
+ var attrInstance = new this.attr({ a: new this.attrB({ b: "old" }) });
338
+ attrInstance.bind("a.b", function(newVal, oldVal) {
339
+ oldVal.shouldEqual("old");
340
+ }.shouldBeCalled(), true);
341
+
342
+ attrInstance.setA(new this.attrB({ b: "asdf" }));
343
+ },
344
+
345
+ "should properly unbind a path": function() {
346
+ var attrInstanceB = new this.attrB({ b: 0 });
347
+ var attrInstance = new this.attr({ a: attrInstanceB });
348
+ attrInstance.bind("a.b", function() {}.shouldBeCalled().times(2), true);
349
+
350
+ attrInstanceB.setB(1);
351
+ attrInstance.setA(new this.attrB({ b: 2 }));
352
+ attrInstanceB.setB(3);
353
+ },
354
+
355
+ "should properly unbind a long path": function() {
356
+ var attrInstanceA2 = new this.attr({ a: "asdf" });
357
+ var attrInstanceB = new this.attrB({ b: attrInstanceA2 });
358
+ var attrInstance = new this.attr({ a: attrInstanceB });
359
+ attrInstance.bind("a.b.a", function() {}.shouldBeCalled().times(2), true);
360
+
361
+ attrInstanceA2.setA(1);
362
+ attrInstance.setA(new this.attrB({ b: new this.attr({ a: 2 }) }));
363
+ attrInstanceA2.setA(3);
364
+ },
365
+
366
+ "should bind collection bindings at the end of a path": function() {
367
+ var arr = [1];
368
+ var attrInstanceB = new this.attrB({ b: arr });
369
+ var attrInstance = new this.attr({ a: attrInstanceB });
370
+ attrInstance.bind("a.b", {
371
+ insert: function(val) {
372
+ val.shouldEqual(2);
373
+ }.shouldBeCalled().once()
374
+ });
375
+
376
+ arr.push(2);
377
+ },
378
+
379
+ "should rebind collection bindings at the end of a path": function() {
380
+ var arr = [1];
381
+ var attrInstanceB = new this.attrB({ b: arr });
382
+ var attrInstance = new this.attr({ a: attrInstanceB });
383
+ attrInstance.bind("a.b", {
384
+ insert: function(val) {}.shouldNotBeCalled()
385
+ });
386
+
387
+ attrInstanceB.setB([]);
388
+ arr.push(2);
389
+ },
390
+
391
+ "should unbind collection bindings at the end of a long path": function() {
392
+ var arr = [1];
393
+ var attrInstanceA2 = new this.attr({ a: arr });
394
+ var attrInstanceB = new this.attrB({ b: attrInstanceA2 });
395
+ var attrInstance = new this.attr({ a: attrInstanceB });
396
+ attrInstance.bind("a.b.a", {
397
+ insert: function(val) { }.shouldNotBeCalled()
398
+ });
399
+
400
+ attrInstanceB.setB(new this.attr({ a: [] }));
401
+ arr.push(2);
402
+ },
403
+
404
+
405
+ /* This is a confusing test. */
406
+ "should allow you to pass the same list bindings to a binding path and a standard binding": function() {
407
+ var subInstance = new this.attrB({ b: "asdf" });
408
+ var attrInstance = new this.attr({
409
+ a: subInstance
410
+ });
411
+
412
+ var otherInstance = new this.attr({
413
+ a: [2,3]
414
+ });
415
+
416
+ var setValue = "asdf";
417
+ var insertValue = "";
418
+ var listBindings = {
419
+ set: function(newVal) {
420
+ newVal.shouldEqual(setValue);
421
+ }.shouldBeCalled().times(3),
422
+
423
+ insert: function(val) {
424
+ val.shouldEqual(insertValue);
425
+ }.shouldBeCalled().times(3)
426
+ };
427
+
428
+ attrInstance.bind("a.b", listBindings);
429
+
430
+ setValue = [2, 3];
431
+ otherInstance.a.bind(listBindings);
432
+
433
+ insertValue = 6;
434
+ otherInstance.getA().push(6);
435
+
436
+ setValue = [1,2,3];
437
+ subInstance.setB(setValue);
438
+
439
+ insertValue = 7;
440
+ otherInstance.getA().push(7);
441
+
442
+ insertValue = 4;
443
+ setValue.push(insertValue);
444
+ },
445
+
446
+ "should allow binding a collection attrAccessor to an object with set": function() {
447
+ var attrInstance = new this.attr();
448
+
449
+ attrInstance.bind("a", {
450
+ set: function(newVal) {
451
+ newVal.shouldEqual([1,2,3]);
452
+ }.shouldBeCalled()
453
+ }, true);
454
+
455
+ attrInstance.setA([1,2,3]);
456
+ },
457
+
458
+ "should allow binding a collection attrAccessor to an object with insert responding to push": function() {
459
+ var arr = [1,2,3];
460
+ var attrInstance = new this.attr({ a: arr });
461
+
462
+ attrInstance.bind("a", {
463
+ insert: function(newVal, index) {
464
+ newVal.shouldEqual(4);
465
+ index.shouldEqual(3);
466
+ }.shouldBeCalled()
467
+ });
468
+ arr.push(4);
469
+ },
470
+
471
+ "should allow binding a collection attrAccessor to an object with insert responding to splice": function() {
472
+ var arr = [1,2,3];
473
+ var attrInstance = new this.attr({ a: arr });
474
+
475
+ attrInstance.bind("a", {
476
+ insert: function(newVal, index) {
477
+ newVal.shouldEqual(1.5);
478
+ index.shouldEqual(1);
479
+ }.shouldBeCalled()
480
+ });
481
+ arr.splice(1, 0, 1.5);
482
+ },
483
+
484
+ "should allow binding a collection attrAccessor to an object with remove responding to splice": function() {
485
+ var arr = [1,2,3];
486
+ var attrInstance = new this.attr({ a: arr });
487
+
488
+ attrInstance.bind("a", {
489
+ remove: function(removeVal) {
490
+ removeVal.shouldEqual(2);
491
+ }.shouldBeCalled()
492
+ });
493
+ arr.splice(1, 1);
494
+ },
495
+
496
+ "should allow binding a collection attrAccessor to an object with remove responding to pop": function() {
497
+ var arr = [1,2,3];
498
+ var attrInstance = new this.attr({ a: arr });
499
+
500
+ attrInstance.bind("a", {
501
+ remove: function(removeVal) {
502
+ removeVal.shouldEqual(3);
503
+ }.shouldBeCalled()
504
+ });
505
+ arr.pop().shouldEqual(3);
506
+ },
507
+
508
+ "should allow binding a collection attrAccessor to an object with remove not responding to pop if already empty": function() {
509
+ var arr = [];
510
+ var attrInstance = new this.attr({ a: arr });
511
+
512
+ attrInstance.bind("a", {
513
+ remove: function(removeVal) {}.shouldNotBeCalled()
514
+ });
515
+ shouldBeUndefined(arr.pop());
516
+ },
517
+
518
+ "should allow binding a collection attrAccessor to an object with empty responding to pop": function() {
519
+ var arr = [1];
520
+ var attrInstance = new this.attr({ a: arr });
521
+
522
+ var lastEmptyValue;
523
+ attrInstance.bind("a", {
524
+ empty: function(val) {
525
+ lastEmptyValue = val;
526
+ }.shouldBeCalled()
527
+ });
528
+ lastEmptyValue.shouldBeFalse();
529
+ arr.pop();
530
+ lastEmptyValue.shouldBeTrue();
531
+ },
532
+
533
+ "should allow binding a collection attrAccessor to an object with empty not responding to pop if emptyness doesn't change": function() {
534
+ var arr = [1, 2];
535
+ var attrInstance = new this.attr({ a: arr });
536
+
537
+ var lastEmptyValue;
538
+ attrInstance.bind("a", {
539
+ empty: function(val) {
540
+ lastEmptyValue = val;
541
+ }.shouldBeCalled()
542
+ });
543
+ lastEmptyValue.shouldBeFalse();
544
+ lastEmptyValue = undefined;
545
+ arr.pop();
546
+ shouldBeUndefined(lastEmptyValue);
547
+ },
548
+
549
+ "should allow binding a collection attrAccessor to an object with empty": function() {
550
+ var attrInstance = new this.attr({ a: [] });
551
+ attrInstance.bind("a", {
552
+ empty: function(empty) {
553
+ empty.shouldBeTrue();
554
+ }.shouldBeCalled().once()
555
+ });
556
+ },
557
+
558
+ "should allow binding a collection attrAccessor to an object with empty responding true to set": function() {
559
+ var attrInstance = new this.attr();
560
+ attrInstance.bind("a", {
561
+ empty: function(empty) {
562
+ empty.shouldBeTrue();
563
+ }.shouldBeCalled().once()
564
+ });
565
+ attrInstance.setA([]);
566
+ },
567
+
568
+ "should allow binding a collection attrAccessor to an object with empty responding false to set": function() {
569
+ var attrInstance = new this.attr();
570
+ attrInstance.bind("a", {
571
+ empty: function(empty) {
572
+ empty.shouldBeFalse();
573
+ }.shouldBeCalled().once()
574
+ });
575
+ attrInstance.setA([1,2,3]);
576
+ },
577
+
578
+ "should allow binding a collection attrAccessor to an object with empty responding to insert": function() {
579
+ var arr = [];
580
+ var attrInstance = new this.attr({ a: arr });
581
+
582
+ /* When bind is called the empty binding will fire. Use this expectation variable to set an expectation on the subsequent firing. */
583
+ var expectation = Prototype.emptyFunction;
584
+ attrInstance.bind("a", {
585
+ empty: function(empty) {
586
+ expectation(empty);
587
+ }
588
+ });
589
+ expectation = function(empty) { empty.shouldBeFalse(); }.shouldBeCalled();
590
+ arr.push(4);
591
+ },
592
+
593
+ "should allow binding a collection attrAccessor to an object with empty responding to splice adding": function() {
594
+ var arr = [];
595
+ var attrInstance = new this.attr({ a: arr });
596
+
597
+ /* When bind is called the empty binding will fire. Use this expectation variable to set an expectation on the subsequent firing. */
598
+ var expectation = Prototype.emptyFunction;
599
+ attrInstance.bind("a", {
600
+ empty: function(empty) {
601
+ expectation(empty);
602
+ }
603
+ });
604
+ expectation = function(empty) { empty.shouldBeFalse(); }.shouldBeCalled();
605
+
606
+ arr.splice(0, 0, "a");
607
+ },
608
+
609
+ "should allow binding a collection attrAccessor to an object with empty responding to splice removing": function() {
610
+ var arr = [1];
611
+ var attrInstance = new this.attr({ a: arr });
612
+
613
+ /* When bind is called the empty binding will fire. Use this expectation variable to set an expectation on the subsequent firing. */
614
+ var expectation = Prototype.emptyFunction;
615
+ attrInstance.bind("a", {
616
+ empty: function(empty) {
617
+ expectation(empty);
618
+ }
619
+ });
620
+ expectation = function(empty) { empty.shouldBeTrue(); }.shouldBeCalled();
621
+
622
+ arr.splice(0, 1);
623
+ },
624
+
625
+ "should allow binding a collection attrAccessor to an object with empty not responding to splice adding or removing that don't change emptyness": function() {
626
+ var arr = [1];
627
+ var attrInstance = new this.attr({ a: arr });
628
+
629
+ /* When bind is called the empty binding will fire. Use this expectation variable to set an expectation on the subsequent firing. */
630
+ var expectation = Prototype.emptyFunction;
631
+ attrInstance.bind("a", {
632
+ empty: function(empty) {
633
+ expectation(empty);
634
+ }
635
+ });
636
+ expectation = function() { }.shouldNotBeCalled();
637
+
638
+ arr.splice(0, 0, "a");
639
+ arr.splice(0, 1);
640
+ },
641
+
642
+ "should allow binding a collection attrAccessor to an object with set responding to clear": function() {
643
+ var arr = [1,2,3];
644
+ var attrInstance = new this.attr({ a: arr });
645
+
646
+ var expectation = Prototype.emptyFunction;
647
+ attrInstance.bind("a", {
648
+ set: function(val) {
649
+ expectation(val);
650
+ }
651
+ });
652
+ expectation = function(val) {
653
+ val.shouldBeEmpty();
654
+ }.shouldBeCalled();
655
+
656
+ arr.clear();
657
+ },
658
+
659
+ "should allow binding a collection attrAccessor to an object with set not responding to clear if already empty": function() {
660
+ var arr = [];
661
+ var attrInstance = new this.attr({ a: arr });
662
+
663
+ var expectation = Prototype.emptyFunction;
664
+ attrInstance.bind("a", {
665
+ set: function(val) {
666
+ expectation(val);
667
+ }
668
+ });
669
+ expectation = function(val) {}.shouldNotBeCalled();
670
+
671
+ arr.clear();
672
+ },
673
+
674
+ "should allow binding a collection attrAccessor to an object with empty responding to clear": function() {
675
+ var arr = [1, 2, 3];
676
+ var attrInstance = new this.attr({ a: arr });
677
+
678
+ var expectation = Prototype.emptyFunction;
679
+ var lastEmptyValue;
680
+ attrInstance.bind("a", {
681
+ empty: function(val) {
682
+ lastEmptyValue = val;
683
+ }
684
+ });
685
+
686
+ arr.clear();
687
+ lastEmptyValue.shouldBeTrue();
688
+ },
689
+
690
+ "should allow binding a collection attrAccessor to an object with empty not responding to clear if already empty": function() {
691
+ var arr = [];
692
+ var attrInstance = new this.attr({ a: arr });
693
+
694
+ var expectation = Prototype.emptyFunction;
695
+ var lastEmptyValue;
696
+ attrInstance.bind("a", {
697
+ empty: function(val) {
698
+ lastEmptyValue = val;
699
+ }
700
+ });
701
+
702
+ lastEmptyValue = undefined;
703
+ arr.clear();
704
+ shouldBeUndefined(lastEmptyValue);
705
+ },
706
+
707
+ "should unbind collection clear binding when the value of an attrAccessor changes": function() {
708
+ var arr = [1,2,3];
709
+ var attrInstance = new this.attr({ a: arr });
710
+
711
+ var expectation = Prototype.emptyFunction;
712
+ attrInstance.bind("a", {
713
+ set: function(val) {
714
+ expectation(val);
715
+ }
716
+ });
717
+ expectation = function(val) {}.shouldBeCalled().once();
718
+
719
+ attrInstance.setA([]);
720
+ arr.clear();
721
+ arr.clear();
722
+ },
723
+
724
+ "should unbind collection bindings when the value of an attrAccessor changes": function() {
725
+ var arr = [1,2,3];
726
+ var attrInstance = new this.attr({ a: arr });
727
+
728
+ attrInstance.bind("a", {
729
+ insert: function(insertVal) {}.shouldBeCalled().once()
730
+ });
731
+
732
+ attrInstance.setA([5,6,7,8]);
733
+ attrInstance.setA(arr);
734
+
735
+ arr.push(4);
736
+ },
737
+
738
+ "should unbind collection bindings when the value of an attrAccessor changes and the collection was bound in multiple attr's": function() {
739
+ var arr = [1,2,3];
740
+ var attrInstance1 = new this.attr({ a: arr });
741
+ var attrInstance2 = new this.attr({ a: arr });
742
+
743
+ attrInstance1.bind("a", {
744
+ insert: function(insertVal) {}.shouldBeCalled().once()
745
+ });
746
+ attrInstance2.bind("a", {
747
+ insert: function(insertVal) {}.shouldBeCalled().once()
748
+ });
749
+
750
+ attrInstance1.setA([5,6,7,8]);
751
+ attrInstance1.setA(arr);
752
+
753
+ attrInstance2.setA([5,6,7,8]);
754
+ attrInstance2.setA(arr);
755
+
756
+ arr.push(4);
757
+ },
758
+
759
+ "should unbind collection bindings when calling the unbind function": function() {
760
+ var arr = [1,2,3];
761
+ var attrInstance = new this.attr({ a: arr });
762
+
763
+ var unbind = attrInstance.bind("a", {
764
+ insert: function(insertVal) {}.shouldBeCalled().once()
765
+ });
766
+
767
+ arr.push(4);
768
+ unbind();
769
+ arr.push(5);
770
+ },
771
+
772
+ "should not execute collection bindings for push during a #transaction until the transaction completes": function() {
773
+ var arr = [];
774
+ var attrInstance = new this.attr({ a: arr });
775
+
776
+ var insertLastCalledWith;
777
+ var emptyLastCalledWith;
778
+ attrInstance.bind("a", {
779
+ insert: function(val) {
780
+ insertLastCalledWith = val;
781
+ },
782
+
783
+ empty: function(val) {
784
+ emptyLastCalledWith = val;
785
+ }
786
+ });
787
+
788
+ shouldBeUndefined(insertLastCalledWith);
789
+ emptyLastCalledWith.shouldBeTrue();
790
+
791
+ rio.Attr.transaction(function() {
792
+ arr.push(4);
793
+
794
+ shouldBeUndefined(insertLastCalledWith);
795
+ emptyLastCalledWith.shouldBeTrue();
796
+ }.shouldBeCalled());
797
+
798
+ insertLastCalledWith.shouldEqual(4);
799
+ emptyLastCalledWith.shouldBeFalse();
800
+ },
801
+
802
+ "should not execute collection bindings for pop during a #transaction until the transaction completes": function() {
803
+ var arr = [1];
804
+ var attrInstance = new this.attr({ a: arr });
805
+
806
+ var removeLastCalledWith;
807
+ var emptyLastCalledWith;
808
+ attrInstance.bind("a", {
809
+ remove: function(val) {
810
+ removeLastCalledWith = val;
811
+ },
812
+
813
+ empty: function(val) {
814
+ emptyLastCalledWith = val;
815
+ }
816
+ });
817
+
818
+ shouldBeUndefined(removeLastCalledWith);
819
+ emptyLastCalledWith.shouldBeFalse();
820
+
821
+ rio.Attr.transaction(function() {
822
+ arr.pop().shouldEqual(1);
823
+
824
+ shouldBeUndefined(removeLastCalledWith);
825
+ emptyLastCalledWith.shouldBeFalse();
826
+ }.shouldBeCalled());
827
+
828
+ removeLastCalledWith.shouldEqual(1);
829
+ emptyLastCalledWith.shouldBeTrue();
830
+ },
831
+
832
+ "should not execute collection bindings for splice during a #transaction until the transaction completes": function() {
833
+ var arr = [];
834
+ var attrInstance = new this.attr({ a: arr });
835
+
836
+ var insertLastCalledWith;
837
+ var removeLastCalledWith;
838
+ var emptyLastCalledWith;
839
+ attrInstance.bind("a", {
840
+ insert: function(val) {
841
+ insertLastCalledWith = val;
842
+ },
843
+
844
+ remove: function(val) {
845
+ removeLastCalledWith = val;
846
+ },
847
+
848
+ empty: function(val) {
849
+ emptyLastCalledWith = val;
850
+ }
851
+ });
852
+
853
+ shouldBeUndefined(insertLastCalledWith);
854
+ shouldBeUndefined(removeLastCalledWith);
855
+ emptyLastCalledWith.shouldBeTrue();
856
+
857
+ rio.Attr.transaction(function() {
858
+ arr.splice(0, 0, 2);
859
+ arr.splice(1, 0, 5);
860
+
861
+ arr.splice(0, 1);
862
+
863
+ shouldBeUndefined(insertLastCalledWith);
864
+ shouldBeUndefined(removeLastCalledWith);
865
+ emptyLastCalledWith.shouldBeTrue();
866
+ }.shouldBeCalled());
867
+
868
+ insertLastCalledWith.shouldEqual(5);
869
+ removeLastCalledWith.shouldEqual(2);
870
+ emptyLastCalledWith.shouldBeFalse();
871
+ },
872
+
873
+ "should not execute collection bindings for clear during a #transaction until the transaction completes": function() {
874
+ var arr = [1, 2, 3];
875
+ var attrInstance = new this.attr({ a: arr });
876
+
877
+ var setLastCalledWith;
878
+ var emptyLastCalledWith;
879
+ attrInstance.bind("a", {
880
+ set: function(val) {
881
+ setLastCalledWith = val;
882
+ },
883
+
884
+ empty: function(val) {
885
+ emptyLastCalledWith = val;
886
+ }
887
+ });
888
+
889
+ setLastCalledWith = undefined;
890
+ emptyLastCalledWith.shouldBeFalse();
891
+
892
+ rio.Attr.transaction(function() {
893
+ arr.clear();
894
+
895
+ shouldBeUndefined(setLastCalledWith);
896
+ emptyLastCalledWith.shouldBeFalse();
897
+ }.shouldBeCalled());
898
+
899
+ setLastCalledWith.shouldEqual([]);
900
+ emptyLastCalledWith.shouldBeTrue();
901
+ },
902
+
903
+ "has a binding object at attrInstance.a that": {
904
+ beforeEach: function() {
905
+ this.attrInstance = new this.attr({ a: "asdf" });
906
+ },
907
+
908
+ "should expose the value of a": function() {
909
+ this.attrInstance.a.value().shouldEqual("asdf");
910
+ },
911
+
912
+ "should allow binding a function to it": function() {
913
+ this.attrInstance.a.bind(function(newVal) {
914
+ newVal.shouldEqual("qwer");
915
+ }.shouldBeCalled(), true);
916
+
917
+ this.attrInstance.setA("qwer");
918
+ },
919
+
920
+ "should provide an update method that changes the underlying attribute value": function() {
921
+ this.attrInstance.a.update("qwer");
922
+ this.attrInstance.getA().shouldEqual("qwer");
923
+ },
924
+
925
+ "provides a bindTo method that": {
926
+ beforeEach: function() {
927
+ this.otherInstance = new this.attrB({ b: "qwer" });
928
+ },
929
+
930
+ "should bind itself to another binding initializing the binding value with 'asdf'": function() {
931
+ this.attrInstance.a.bindTo(this.otherInstance.b);
932
+ this.attrInstance.getA().shouldEqual("asdf");
933
+ this.otherInstance.getB().shouldEqual("asdf");
934
+ },
935
+
936
+ "should bind itself to another binding and update the other binding when it updates": function() {
937
+ this.attrInstance.a.bindTo(this.otherInstance.b);
938
+ this.attrInstance.setA("1234");
939
+ this.otherInstance.getB().shouldEqual("1234");
940
+ },
941
+
942
+ "should bind itself to another binding and update itself when the other binding is updated": function() {
943
+ this.attrInstance.a.bindTo(this.otherInstance.b);
944
+ this.otherInstance.setB("4321");
945
+ this.attrInstance.getA().shouldEqual("4321");
946
+ },
947
+
948
+ "should provid an unbind object that cleans up the bindings in both directions": function() {
949
+ var unbind = this.attrInstance.a.bindTo(this.otherInstance.b);
950
+ this.attrInstance.setA('initial');
951
+ unbind();
952
+
953
+ this.attrInstance.setA('newA');
954
+ this.otherInstance.getB().shouldEqual('initial');
955
+
956
+ this.otherInstance.setB('newB');
957
+ this.attrInstance.getA().shouldEqual('newA');
958
+ }
959
+ }
960
+ },
961
+
962
+ "will bind a to another attrAccessor if its binding is passed in on initialization and": {
963
+ beforeEach: function() {
964
+ this.subInstance = new this.attrB({ b: "1234" });
965
+ this.otherInstance = new this.attrB({ b: this.subInstance });
966
+ this.attrInstance = new this.attr({ a: this.otherInstance.binding("b.b") });
967
+ },
968
+
969
+ "should initialize a with the value of the other binding": function() {
970
+ this.attrInstance.getA().shouldEqual("1234");
971
+ },
972
+
973
+ "should update a when the sub binding value is updated": function() {
974
+ this.subInstance.setB("4321");
975
+ this.attrInstance.getA().shouldEqual("4321");
976
+ },
977
+
978
+ "should update the sub binding value when a is updated": function() {
979
+ this.attrInstance.setA("4321");
980
+ this.subInstance.getB().shouldEqual("4321");
981
+ },
982
+
983
+ "should update a when the binding value is changed": function() {
984
+ this.otherInstance.setB(new this.attrB({ b: "9876" }));
985
+ this.attrInstance.getA().shouldEqual("9876");
986
+ }
987
+ },
988
+
989
+ "has a freeze method that": {
990
+ "sets frozen to true": function() {
991
+ var attrInstance = new this.attr({ a: 1 });
992
+ attrInstance.freeze();
993
+ attrInstance.frozen().shouldBeTrue();
994
+ },
995
+
996
+ "makes all attrAccessor set methods no-ops": function() {
997
+ var attrInstance = new this.attr({ a: 1 });
998
+ attrInstance.freeze();
999
+ attrInstance.setA(2);
1000
+ attrInstance.getA().shouldEqual(1);
1001
+ }
1002
+ },
1003
+
1004
+ "has an unfreeze method that": {
1005
+ "sets frozen to false": function() {
1006
+ var attrInstance = new this.attr({ a: 1 });
1007
+ attrInstance.freeze();
1008
+ attrInstance.unfreeze();
1009
+ attrInstance.frozen().shouldBeFalse();
1010
+ },
1011
+
1012
+ "makes all attrAccessor set methods no-ops": function() {
1013
+ var attrInstance = new this.attr({ a: 1 });
1014
+ attrInstance.freeze();
1015
+ attrInstance.unfreeze();
1016
+ attrInstance.setA(2);
1017
+ attrInstance.getA().shouldEqual(2);
1018
+ }
1019
+ },
1020
+
1021
+ "supports an invert method on bindings that": {
1022
+ "should have value true for underlying false": function() {
1023
+ new this.attr({ a: false }).a.invert().value().shouldEqual(true);
1024
+ },
1025
+
1026
+ "should have value false for underlying true": function() {
1027
+ new this.attr({ a: true }).a.invert().value().shouldEqual(false);
1028
+ },
1029
+
1030
+ "should have value true for underlying falsy values": function() {
1031
+ new this.attr({ a: 0 }).a.invert().value().shouldEqual(true);
1032
+ new this.attr({ a: "" }).a.invert().value().shouldEqual(true);
1033
+ new this.attr({ a: null }).a.invert().value().shouldEqual(true);
1034
+ },
1035
+
1036
+ "should have value false for underlying truthy values": function() {
1037
+ new this.attr({ a: 1 }).a.invert().value().shouldEqual(false);
1038
+ new this.attr({ a: "asdf" }).a.invert().value().shouldEqual(false);
1039
+ new this.attr({ a: [1] }).a.invert().value().shouldEqual(false);
1040
+ },
1041
+
1042
+ "should have value true for underlying undefined": function() {
1043
+ new this.attr().a.invert().value().shouldEqual(true);
1044
+ },
1045
+
1046
+ "should update the underlying value to false for true": function() {
1047
+ var attrInstance = new this.attr();
1048
+ attrInstance.a.invert().update(true);
1049
+ attrInstance.getA().shouldEqual(false);
1050
+ },
1051
+
1052
+ "should update the underlying value to true for false": function() {
1053
+ var attrInstance = new this.attr();
1054
+ attrInstance.a.invert().update(false);
1055
+ attrInstance.getA().shouldEqual(true);
1056
+ },
1057
+
1058
+ "should update the underlying value to false for truthy values": function() {
1059
+ var attrInstance = new this.attr();
1060
+ attrInstance.a.invert().update(1);
1061
+ attrInstance.getA().shouldEqual(false);
1062
+ attrInstance.a.invert().update("asdf");
1063
+ attrInstance.getA().shouldEqual(false);
1064
+ attrInstance.a.invert().update([1]);
1065
+ attrInstance.getA().shouldEqual(false);
1066
+ },
1067
+
1068
+ "should update the underlying value to true for falsy values": function() {
1069
+ var attrInstance = new this.attr();
1070
+ attrInstance.a.invert().update(0);
1071
+ attrInstance.getA().shouldEqual(true);
1072
+ attrInstance.a.invert().update("");
1073
+ attrInstance.getA().shouldEqual(true);
1074
+ attrInstance.a.invert().update(null);
1075
+ attrInstance.getA().shouldEqual(true);
1076
+ },
1077
+
1078
+ "should update the underlying value to true for undefined": function() {
1079
+ var attrInstance = new this.attr();
1080
+ attrInstance.a.invert().update();
1081
+ attrInstance.getA().shouldEqual(true);
1082
+ },
1083
+
1084
+ "should honor true for skipInitialExecution flag on bind": function() {
1085
+ new this.attr().a.invert().bind(function() {}.shouldNotBeCalled(), true);
1086
+ },
1087
+
1088
+ "should honor false for skipInitialExecution flag on bind": function() {
1089
+ new this.attr().a.invert().bind(function() {}.shouldBeCalled(), false);
1090
+ },
1091
+
1092
+ "should honor a default of false for skipInitialExecution flag on bind": function() {
1093
+ new this.attr().a.invert().bind(function() {}.shouldBeCalled());
1094
+ },
1095
+
1096
+ "should invert true to false for observers on bind": function() {
1097
+ new this.attr({ a: true }).a.invert().bind(function(a) { a.shouldEqual(false); }.shouldBeCalled());
1098
+ },
1099
+
1100
+ "should invert false to true for observers on bind": function() {
1101
+ new this.attr({ a: false }).a.invert().bind(function(a) { a.shouldEqual(true); }.shouldBeCalled());
1102
+ },
1103
+
1104
+ "should invert truthy values to false for observers on bind": function() {
1105
+ new this.attr({ a: 1 }).a.invert().bind(function(a) { a.shouldEqual(false); }.shouldBeCalled());
1106
+ new this.attr({ a: "asdf" }).a.invert().bind(function(a) { a.shouldEqual(false); }.shouldBeCalled());
1107
+ new this.attr({ a: [1] }).a.invert().bind(function(a) { a.shouldEqual(false); }.shouldBeCalled());
1108
+ },
1109
+
1110
+ "should invert falsy values to true for observers on bind": function() {
1111
+ new this.attr({ a: 0 }).a.invert().bind(function(a) { a.shouldEqual(true); }.shouldBeCalled());
1112
+ new this.attr({ a: "" }).a.invert().bind(function(a) { a.shouldEqual(true); }.shouldBeCalled());
1113
+ new this.attr({ a: null }).a.invert().bind(function(a) { a.shouldEqual(true); }.shouldBeCalled());
1114
+ },
1115
+
1116
+ "should invert undefined to true for observers on bind": function() {
1117
+ new this.attr().a.invert().bind(function(a) { a.shouldEqual(true); }.shouldBeCalled());
1118
+ },
1119
+
1120
+ "should return an unbinding function from bind": function() {
1121
+ var attrInstance = new this.attr();
1122
+ var unbinding = attrInstance.a.invert().bind(function() {}.shouldNotBeCalled(), true);
1123
+ unbinding();
1124
+ attrInstance.setA(1);
1125
+ },
1126
+
1127
+ "should update a second attribute with true for undelying false on bindTo": function() {
1128
+ var attrInstance = new this.attr({ a: false });
1129
+ var otherAttrInstance = new this.attr();
1130
+
1131
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1132
+
1133
+ otherAttrInstance.getA().shouldEqual(true);
1134
+ },
1135
+
1136
+ "should update a second attribute with false for undelying true on bindTo": function() {
1137
+ var attrInstance = new this.attr({ a: true });
1138
+ var otherAttrInstance = new this.attr();
1139
+
1140
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1141
+
1142
+ otherAttrInstance.getA().shouldEqual(false);
1143
+ },
1144
+
1145
+ "should update a second attribute with true for undelying falsy on bindTo": function() {
1146
+ var attrInstance = new this.attr({ a: 0 });
1147
+ var otherAttrInstance = new this.attr();
1148
+
1149
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1150
+
1151
+ otherAttrInstance.getA().shouldEqual(true);
1152
+ },
1153
+
1154
+ "should update a second attribute with false for undelying truthy on bindTo": function() {
1155
+ var attrInstance = new this.attr({ a: 1 });
1156
+ var otherAttrInstance = new this.attr();
1157
+
1158
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1159
+
1160
+ otherAttrInstance.getA().shouldEqual(false);
1161
+ },
1162
+
1163
+ "should update a second attribute with true for undelying undefined on bindTo": function() {
1164
+ var attrInstance = new this.attr();
1165
+ var otherAttrInstance = new this.attr();
1166
+
1167
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1168
+
1169
+ otherAttrInstance.getA().shouldEqual(true);
1170
+ },
1171
+
1172
+ "should update itself with true from a second attribute with false on bindTo": function() {
1173
+ var attrInstance = new this.attr();
1174
+ var otherAttrInstance = new this.attr();
1175
+
1176
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1177
+
1178
+ otherAttrInstance.setA(false);
1179
+ attrInstance.getA().shouldEqual(true);
1180
+ },
1181
+
1182
+ "should update itself with false from a second attribute with true on bindTo": function() {
1183
+ var attrInstance = new this.attr();
1184
+ var otherAttrInstance = new this.attr();
1185
+
1186
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1187
+
1188
+ otherAttrInstance.setA(true);
1189
+ attrInstance.getA().shouldEqual(false);
1190
+ },
1191
+
1192
+ "should update itself with true from a second attribute with falsy on bindTo": function() {
1193
+ var attrInstance = new this.attr();
1194
+ var otherAttrInstance = new this.attr();
1195
+
1196
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1197
+
1198
+ otherAttrInstance.setA(0);
1199
+ attrInstance.getA().shouldEqual(true);
1200
+ },
1201
+
1202
+ "should update itself with false from a second attribute with truthy on bindTo": function() {
1203
+ var attrInstance = new this.attr();
1204
+ var otherAttrInstance = new this.attr();
1205
+
1206
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1207
+
1208
+ otherAttrInstance.setA(1);
1209
+ attrInstance.getA().shouldEqual(false);
1210
+ },
1211
+
1212
+ "should update itself with true from a second attribute with undefined on bindTo": function() {
1213
+ var attrInstance = new this.attr();
1214
+ var otherAttrInstance = new this.attr();
1215
+
1216
+ attrInstance.a.invert().bindTo(otherAttrInstance.a);
1217
+
1218
+ otherAttrInstance.setA();
1219
+ attrInstance.getA().shouldEqual(true);
1220
+ },
1221
+
1222
+ "should return an unbinding function from bindTo": function() {
1223
+ var attrInstance = new this.attr();
1224
+ var otherAttrInstance = new this.attr();
1225
+
1226
+ var unbinding = attrInstance.a.invert().bindTo(otherAttrInstance.a);
1227
+
1228
+ unbinding();
1229
+
1230
+ attrInstance.setA(3);
1231
+ otherAttrInstance.setA(false);
1232
+ attrInstance.getA().shouldEqual(3);
1233
+
1234
+ otherAttrInstance.setA(3);
1235
+ attrInstance.setA(true);
1236
+ otherAttrInstance.getA().shouldEqual(3);
1237
+ },
1238
+
1239
+ "should has attribute BINDING true": function() {
1240
+ new this.attr().a.invert().BINDING.shouldBeTrue();
1241
+ }
1242
+ }
1243
+
1244
+ },
1245
+
1246
+ "with two bindable attributes": {
1247
+ beforeEach: function() {
1248
+ this.attr = rio.Attr.create({ attrAccessors: ["a", "helloWorld"] });
1249
+ },
1250
+
1251
+ "should allow multiple attributes to be updated with updateAttributes": function() {
1252
+ var attrInstance = new this.attr({ a: 1, helloWorld: 2 });
1253
+ attrInstance.updateAttributes({ a: 3, helloWorld: 4 });
1254
+ attrInstance.getA().shouldEqual(3);
1255
+ attrInstance.getHelloWorld().shouldEqual(4);
1256
+ },
1257
+
1258
+ "should update all attributes before firing bindings on updateAttributes": function() {
1259
+ var attrInstance = new this.attr({ a: 1, helloWorld: 2 });
1260
+ var f = function() {
1261
+ attrInstance.getA().shouldEqual(3);
1262
+ attrInstance.getHelloWorld().shouldEqual(4);
1263
+ }.shouldBeCalled().times(2);
1264
+ attrInstance.a.bind(f, true);
1265
+ attrInstance.helloWorld.bind(f, true);
1266
+ attrInstance.updateAttributes({ a: 3, helloWorld: 4 });
1267
+ },
1268
+
1269
+ "should update all attributes of multiple Attr instances on Attr#updateAttributes": function() {
1270
+ var attrInstance1 = new this.attr({ a: 1, helloWorld: 2 });
1271
+ var attrInstance2 = new this.attr({ a: 3, helloWorld: 4 });
1272
+ rio.Attr.updateAttributes([
1273
+ { object: attrInstance1, attributes: { a: 5, helloWorld: 6 } },
1274
+ { object: attrInstance2, attributes: { a: 7, helloWorld: 8 } }
1275
+ ]);
1276
+ attrInstance1.getA().shouldEqual(5);
1277
+ attrInstance1.getHelloWorld().shouldEqual(6);
1278
+ attrInstance2.getA().shouldEqual(7);
1279
+ attrInstance2.getHelloWorld().shouldEqual(8);
1280
+ },
1281
+
1282
+ "should update all attributes of multiple Attr instances before firing bindings on Attr#updateAttributes": function() {
1283
+ var attrInstance1 = new this.attr({ a: 1, helloWorld: 2 });
1284
+ var attrInstance2 = new this.attr({ a: 3, helloWorld: 4 });
1285
+ var f = function() {
1286
+ attrInstance1.getA().shouldEqual(5);
1287
+ attrInstance1.getHelloWorld().shouldEqual(6);
1288
+ attrInstance2.getA().shouldEqual(7);
1289
+ attrInstance2.getHelloWorld().shouldEqual(8);
1290
+ }.shouldBeCalled().times(4);
1291
+ attrInstance1.a.bind(f, true);
1292
+ attrInstance1.helloWorld.bind(f, true);
1293
+ attrInstance2.a.bind(f, true);
1294
+ attrInstance2.helloWorld.bind(f, true);
1295
+ rio.Attr.updateAttributes([
1296
+ { object: attrInstance1, attributes: { a: 5, helloWorld: 6 } },
1297
+ { object: attrInstance2, attributes: { a: 7, helloWorld: 8 } }
1298
+ ]);
1299
+ }
1300
+ },
1301
+
1302
+ /* END BINDING RELATED SPECS */
1303
+
1304
+ "with a declared event called poof": {
1305
+ beforeEach: function() {
1306
+ this.attr = rio.Attr.create({ attrEvents: ["poof"] });
1307
+ this.attrInstance = new this.attr();
1308
+ },
1309
+
1310
+ "should observe and fire the event": function() {
1311
+ this.attrInstance.observe("poof", function() {}.shouldBeCalled());
1312
+ this.attrInstance.fire("poof");
1313
+ },
1314
+
1315
+ "should support stop observing functionality": function() {
1316
+ var f = function() {}.shouldNotBeCalled();
1317
+ this.attrInstance.observe("poof", f);
1318
+ this.attrInstance.stopObserving("poof", f);
1319
+ this.attrInstance.fire("poof");
1320
+ },
1321
+
1322
+ "should return a stop observing function on observing an event": function() {
1323
+ var f = function() {}.shouldBeCalled().once();
1324
+ var stopObserving = this.attrInstance.observe("poof", f);
1325
+ this.attrInstance.fire("poof");
1326
+ stopObserving();
1327
+ this.attrInstance.fire("poof");
1328
+ },
1329
+
1330
+ "should pass on any parameters to the observers": function() {
1331
+ this.attrInstance.observe("poof", function(a,b,c) {
1332
+ a.shouldEqual(1);
1333
+ b.shouldEqual(2);
1334
+ c.shouldEqual(3);
1335
+ }.shouldBeCalled());
1336
+ this.attrInstance.fire("poof", 1, 2, 3);
1337
+ },
1338
+
1339
+ "should create an observer when initialized with an onPoof argument": function() {
1340
+ var attrInstance = new this.attr({
1341
+ onPoof: function(a) {
1342
+ a.shouldEqual(1);
1343
+ }.shouldBeCalled()
1344
+ });
1345
+ attrInstance.fire("poof", 1);
1346
+ }
1347
+ },
1348
+
1349
+ "should set the attr NAME property": function() {
1350
+ var attr = rio.Attr.create("SomeAttr");
1351
+
1352
+ attr.NAME.shouldEqual("SomeAttr");
1353
+ },
1354
+
1355
+ "should return NAME from toString": function() {
1356
+ var attr = rio.Attr.create("SomeAttr");
1357
+
1358
+ attr.toString().shouldEqual("SomeAttr");
1359
+ },
1360
+
1361
+ "inheritance": {
1362
+ "on initialize": {
1363
+ "should call the initialize method of the super class if the subclass has no initialize method": function() {
1364
+ var SuperAttr = rio.Attr.create("Super", {
1365
+ methods: {
1366
+ initialize: function() {}.shouldBeCalled()
1367
+ }
1368
+ });
1369
+
1370
+ var SubAttr = rio.Attr.create(SuperAttr, "Sub");
1371
+ new SubAttr();
1372
+ },
1373
+
1374
+ "should pass in $super and options if both super and sub classes provide initializers": function() {
1375
+ var SuperAttr = rio.Attr.create("Super", {
1376
+ methods: {
1377
+ initialize: function(options) {
1378
+ options.hello.shouldEqual("world");
1379
+ options.foo.shouldEqual("bar");
1380
+ }.shouldBeCalled()
1381
+ }
1382
+ });
1383
+
1384
+ var SubAttr = rio.Attr.create(SuperAttr, "Sub", {
1385
+ methods: {
1386
+ initialize: function($super, options) {
1387
+ $super(Object.extend({
1388
+ foo: "bar"
1389
+ }, options));
1390
+ options.hello.shouldEqual("world");
1391
+ }
1392
+ }
1393
+ });
1394
+ new SubAttr({hello: "world"});
1395
+ },
1396
+
1397
+ "should handle 3 level heirarchy": function() {
1398
+ var SuperAttr = rio.Attr.create("Super", {
1399
+ methods: {
1400
+ initialize: function(options) {
1401
+ options.hello.shouldEqual("world");
1402
+ options.foo.shouldEqual("bar");
1403
+ options.bar.shouldEqual("baz");
1404
+ }.shouldBeCalled()
1405
+ }
1406
+ });
1407
+
1408
+ var SubAttr = rio.Attr.create(SuperAttr, "Sub", {
1409
+ methods: {
1410
+ initialize: function($super, options) {
1411
+ $super(Object.extend({
1412
+ foo: "bar"
1413
+ }, options));
1414
+ options.hello.shouldEqual("world");
1415
+ options.bar.shouldEqual("baz");
1416
+ }
1417
+ }
1418
+ });
1419
+
1420
+ var SubSubAttr = rio.Attr.create(SubAttr, "SubSub", {
1421
+ methods: {
1422
+ initialize: function($super, options) {
1423
+ $super(Object.extend({
1424
+ bar: "baz"
1425
+ }, options));
1426
+ options.hello.shouldEqual("world");
1427
+ }
1428
+ }
1429
+ });
1430
+ new SubSubAttr({hello: "world"});
1431
+ },
1432
+
1433
+ "should handle 3 level heirarchy, where the middle level omits initialize": function() {
1434
+ var SuperAttr = rio.Attr.create("Super", {
1435
+ methods: {
1436
+ initialize: function(options) {
1437
+ options.hello.shouldEqual("world");
1438
+ options.bar.shouldEqual("baz");
1439
+ }.shouldBeCalled()
1440
+ }
1441
+ });
1442
+
1443
+ var SubAttr = rio.Attr.create(SuperAttr, "Sub");
1444
+
1445
+ var SubSubAttr = rio.Attr.create(SubAttr, "SubSub", {
1446
+ methods: {
1447
+ initialize: function($super, options) {
1448
+ $super(Object.extend({
1449
+ bar: "baz"
1450
+ }, options));
1451
+ options.hello.shouldEqual("world");
1452
+ }
1453
+ }
1454
+ });
1455
+ new SubSubAttr({hello: "world"});
1456
+ }
1457
+ },
1458
+
1459
+ beforeEach: function() {
1460
+ var SuperAttr = rio.Attr.create("Super", {
1461
+ attrAccessors: ["a", ["hello", "super"]],
1462
+ attrHtmls: ["super"],
1463
+ methods: { buildSuperHtml: function() { return "foo"; } }
1464
+ });
1465
+
1466
+ var SubAttr = rio.Attr.create(SuperAttr, "Sub", {
1467
+ attrAccessors: ["b"],
1468
+ attrHtmls: ["sub"],
1469
+ methods: { buildSubHtml: function() { return "bar"; } }
1470
+ });
1471
+
1472
+ var SubSubAttr = rio.Attr.create(SubAttr, "SubSub", {
1473
+ attrAccessors: ["c", ["hello", "subsub"]],
1474
+ attrHtmls: ["subSub"],
1475
+ methods: { buildSubSubHtml: function() { return "baz"; } }
1476
+ });
1477
+
1478
+ this.attr = new SubSubAttr({ a: 1, b: 2, c: 3 });
1479
+ this.superAttr = new SuperAttr();
1480
+ },
1481
+
1482
+
1483
+ "should inherit the attributes of its parent": function() {
1484
+ this.attr.getB().shouldEqual(2);
1485
+ this.attr.getC().shouldEqual(3);
1486
+ },
1487
+
1488
+ "should inherit the attributes of its grandparent": function() {
1489
+ this.attr.getA().shouldEqual(1);
1490
+ this.attr.getB().shouldEqual(2);
1491
+ this.attr.getC().shouldEqual(3);
1492
+ },
1493
+
1494
+ "should inherit the attrHtmls of it's parent": function() {
1495
+ this.attr.superHtml().shouldEqual("foo");
1496
+ this.attr.subHtml().shouldEqual("bar");
1497
+ },
1498
+
1499
+ "should inherit the attrHtmls of it's grandparent": function() {
1500
+ this.attr.superHtml().shouldEqual("foo");
1501
+ this.attr.subHtml().shouldEqual("bar");
1502
+ this.attr.subSubHtml().shouldEqual("baz");
1503
+ },
1504
+
1505
+ "should use it's own default value for an attribute if defined": function() {
1506
+ this.attr.getHello().shouldEqual("subsub");
1507
+ },
1508
+
1509
+ "should not override default value for instances of superclasses": function() {
1510
+ this.superAttr.getHello().shouldEqual("super");
1511
+ }
1512
+
1513
+ }
1514
+ });