spontaneous 0.1.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (556) hide show
  1. data/Gemfile +49 -0
  2. data/Gemfile.lock +146 -0
  3. data/LICENSE +0 -0
  4. data/README +0 -0
  5. data/Rakefile +284 -0
  6. data/Readme.markdown +7 -0
  7. data/application/css/add_alias_dialogue.scss +27 -0
  8. data/application/css/definitions.scss +249 -0
  9. data/application/css/developer.scss +9 -0
  10. data/application/css/editing.scss +649 -0
  11. data/application/css/login.scss +91 -0
  12. data/application/css/min/54ee0ed3c7fac7632bd5c020d69e9a2503e0c88c.css +1 -0
  13. data/application/css/min/c256adc144e2bdd0b0539356b04eb62db01e1dc3.css +1 -0
  14. data/application/css/popover.scss +335 -0
  15. data/application/css/schema_error.scss +90 -0
  16. data/application/css/spontaneous.scss +111 -0
  17. data/application/css/unsupported.scss +16 -0
  18. data/application/css/v2.scss +1606 -0
  19. data/application/css/variables.scss +80 -0
  20. data/application/js/add_alias_dialogue.js +59 -0
  21. data/application/js/add_home_dialogue.js +59 -0
  22. data/application/js/ajax.js +99 -0
  23. data/application/js/authentication.js +22 -0
  24. data/application/js/box.js +104 -0
  25. data/application/js/box_container.js +82 -0
  26. data/application/js/compatibility.js +132 -0
  27. data/application/js/conflicted_field_dialogue.js +92 -0
  28. data/application/js/content.js +224 -0
  29. data/application/js/content_area.js +44 -0
  30. data/application/js/dialogue.js +196 -0
  31. data/application/js/dom.js +71 -0
  32. data/application/js/edit_dialogue.js +137 -0
  33. data/application/js/edit_panel.js +232 -0
  34. data/application/js/editing.js +42 -0
  35. data/application/js/entry.js +13 -0
  36. data/application/js/extensions.js +104 -0
  37. data/application/js/field.js +4 -0
  38. data/application/js/field_preview.js +55 -0
  39. data/application/js/field_types/date_field.js +16 -0
  40. data/application/js/field_types/file_field.js +71 -0
  41. data/application/js/field_types/image_field.js +358 -0
  42. data/application/js/field_types/markdown_field.js +656 -0
  43. data/application/js/field_types/string_field.js +185 -0
  44. data/application/js/image.js +72 -0
  45. data/application/js/init.js +34 -0
  46. data/application/js/load.js +4 -0
  47. data/application/js/location.js +157 -0
  48. data/application/js/login.js +53 -0
  49. data/application/js/min/492a209de8ee955fa9c729a765377495001e11b1.js +17 -0
  50. data/application/js/min/80f684d77c940887a1d4a63e3a96102e993baa98.js +88 -0
  51. data/application/js/min/b8abf302a824c35385ff517b34111e1710ff3b37.js +2 -0
  52. data/application/js/min/c7140ec9475e5bf868b901e0621338d7d162358b.js +3 -0
  53. data/application/js/min/f07f2bd6630ee31e1c2288ec223383d8f0658ba6.js +2 -0
  54. data/application/js/page.js +43 -0
  55. data/application/js/page_browser.js +147 -0
  56. data/application/js/page_entry.js +47 -0
  57. data/application/js/popover.js +99 -0
  58. data/application/js/popover_view.js +56 -0
  59. data/application/js/preview.js +64 -0
  60. data/application/js/progress.js +358 -0
  61. data/application/js/properties.js +90 -0
  62. data/application/js/publish.js +187 -0
  63. data/application/js/require.js +129 -0
  64. data/application/js/sharded_upload.js +206 -0
  65. data/application/js/side_bar.js +30 -0
  66. data/application/js/spontaneous.js +6 -0
  67. data/application/js/state.js +64 -0
  68. data/application/js/status_bar.js +47 -0
  69. data/application/js/top_bar.js +368 -0
  70. data/application/js/types.js +98 -0
  71. data/application/js/upload.js +88 -0
  72. data/application/js/upload_manager.js +319 -0
  73. data/application/js/user.js +37 -0
  74. data/application/js/vendor/.DS_Store +0 -0
  75. data/application/js/vendor/JS.Class-2.1.5/CHANGELOG +283 -0
  76. data/application/js/vendor/JS.Class-2.1.5/MIT-LICENSE +30 -0
  77. data/application/js/vendor/JS.Class-2.1.5/README +30 -0
  78. data/application/js/vendor/JS.Class-2.1.5/min/command.js +1 -0
  79. data/application/js/vendor/JS.Class-2.1.5/min/comparable.js +1 -0
  80. data/application/js/vendor/JS.Class-2.1.5/min/constant_scope.js +1 -0
  81. data/application/js/vendor/JS.Class-2.1.5/min/core.js +1 -0
  82. data/application/js/vendor/JS.Class-2.1.5/min/decorator.js +1 -0
  83. data/application/js/vendor/JS.Class-2.1.5/min/enumerable.js +1 -0
  84. data/application/js/vendor/JS.Class-2.1.5/min/forwardable.js +1 -0
  85. data/application/js/vendor/JS.Class-2.1.5/min/hash.js +1 -0
  86. data/application/js/vendor/JS.Class-2.1.5/min/linked_list.js +1 -0
  87. data/application/js/vendor/JS.Class-2.1.5/min/loader.js +1 -0
  88. data/application/js/vendor/JS.Class-2.1.5/min/method_chain.js +1 -0
  89. data/application/js/vendor/JS.Class-2.1.5/min/observable.js +1 -0
  90. data/application/js/vendor/JS.Class-2.1.5/min/package.js +1 -0
  91. data/application/js/vendor/JS.Class-2.1.5/min/proxy.js +1 -0
  92. data/application/js/vendor/JS.Class-2.1.5/min/ruby.js +1 -0
  93. data/application/js/vendor/JS.Class-2.1.5/min/set.js +1 -0
  94. data/application/js/vendor/JS.Class-2.1.5/min/stack_trace.js +1 -0
  95. data/application/js/vendor/JS.Class-2.1.5/min/state.js +1 -0
  96. data/application/js/vendor/JS.Class-2.1.5/min/stdlib.js +16 -0
  97. data/application/js/vendor/JS.Class-2.1.5/src/command.js +93 -0
  98. data/application/js/vendor/JS.Class-2.1.5/src/comparable.js +37 -0
  99. data/application/js/vendor/JS.Class-2.1.5/src/constant_scope.js +48 -0
  100. data/application/js/vendor/JS.Class-2.1.5/src/core.js +1060 -0
  101. data/application/js/vendor/JS.Class-2.1.5/src/decorator.js +50 -0
  102. data/application/js/vendor/JS.Class-2.1.5/src/enumerable.js +505 -0
  103. data/application/js/vendor/JS.Class-2.1.5/src/forwardable.js +22 -0
  104. data/application/js/vendor/JS.Class-2.1.5/src/hash.js +334 -0
  105. data/application/js/vendor/JS.Class-2.1.5/src/linked_list.js +114 -0
  106. data/application/js/vendor/JS.Class-2.1.5/src/loader.js +553 -0
  107. data/application/js/vendor/JS.Class-2.1.5/src/method_chain.js +172 -0
  108. data/application/js/vendor/JS.Class-2.1.5/src/observable.js +55 -0
  109. data/application/js/vendor/JS.Class-2.1.5/src/package.js +472 -0
  110. data/application/js/vendor/JS.Class-2.1.5/src/proxy.js +58 -0
  111. data/application/js/vendor/JS.Class-2.1.5/src/ruby.js +44 -0
  112. data/application/js/vendor/JS.Class-2.1.5/src/set.js +332 -0
  113. data/application/js/vendor/JS.Class-2.1.5/src/stack_trace.js +151 -0
  114. data/application/js/vendor/JS.Class-2.1.5/src/state.js +95 -0
  115. data/application/js/vendor/JS.Class-2.1.5/src/stdlib.js +2612 -0
  116. data/application/js/vendor/crypto-2.3.0-crypto.js +160 -0
  117. data/application/js/vendor/crypto-2.3.0-sha1.js +91 -0
  118. data/application/js/vendor/diff_match_patch.js +2153 -0
  119. data/application/js/vendor/jquery-1.4.2.min.js +154 -0
  120. data/application/js/vendor/jquery-1.4.3.min.js +166 -0
  121. data/application/js/vendor/jquery-1.5.1.min.js +16 -0
  122. data/application/js/vendor/jquery-1.5.1rc1.min.js +24 -0
  123. data/application/js/vendor/jquery-1.6.2.min.js +18 -0
  124. data/application/js/vendor/jquery-ui-1.8.6.custom.min.js +265 -0
  125. data/application/js/vendor/jquery-ui-1.8.9.custom.min.js +415 -0
  126. data/application/js/vendor/jquery-ui-1.8.custom.min.js +106 -0
  127. data/application/js/vendor/jquery.hotkeys-0.7.9.js +248 -0
  128. data/application/js/vendor/jquery.hotkeys-0.7.9.min.js +19 -0
  129. data/application/js/vendor/jsdiff.js +169 -0
  130. data/application/js/views/box_view.js +229 -0
  131. data/application/js/views/page_piece_view.js +45 -0
  132. data/application/js/views/page_view.js +238 -0
  133. data/application/js/views/piece_view.js +178 -0
  134. data/application/js/views.js +110 -0
  135. data/application/static/editing-0-noise.png +0 -0
  136. data/application/static/editing-1-noise.png +0 -0
  137. data/application/static/editing-texture-1.png +0 -0
  138. data/application/static/editing-texture.png +0 -0
  139. data/application/static/editing-toolbar-shadow-bottom.png +0 -0
  140. data/application/static/editing-toolbar-shadow-top.png +0 -0
  141. data/application/static/favicon.ico +0 -0
  142. data/application/static/inner-glow.png +0 -0
  143. data/application/static/item-buttons.png +0 -0
  144. data/application/static/location-arrow.png +0 -0
  145. data/application/static/logo-400px-transparent.png +0 -0
  146. data/application/static/missing.png +0 -0
  147. data/application/static/orange-down-arrow.png +0 -0
  148. data/application/static/page-browser-next.png +0 -0
  149. data/application/static/paper-texture-dark.png +0 -0
  150. data/application/static/px.gif +0 -0
  151. data/application/static/select-arrow-root.png +0 -0
  152. data/application/static/select-arrow.png +0 -0
  153. data/application/static/slot-down-arrow.png +0 -0
  154. data/application/static/splash.png +0 -0
  155. data/application/static/spontaneous.png +0 -0
  156. data/application/static/spot.png +0 -0
  157. data/application/static/spot.svg +40 -0
  158. data/application/static/texture.png +0 -0
  159. data/application/views/index.erubis +46 -0
  160. data/application/views/login.erubis +69 -0
  161. data/application/views/schema_modification_error.html.erb +61 -0
  162. data/application/views/unsupported.erubis +23 -0
  163. data/bin/limit-upload +5 -0
  164. data/bin/spot +10 -0
  165. data/bin/unlimit-upload +3 -0
  166. data/config/nginx.conf +60 -0
  167. data/db/migrations/20100610142136_init.rb +66 -0
  168. data/db/migrations/20101130104334_timestamps.rb +44 -0
  169. data/db/migrations/20101202113205_site_publishing_flags.rb +12 -0
  170. data/db/migrations/20101206124543_aliases.rb +16 -0
  171. data/db/migrations/20110201133550_visibility.rb +27 -0
  172. data/db/migrations/20110209152710_users_and_groups.rb +58 -0
  173. data/db/migrations/20110215133910_boxes.rb +25 -0
  174. data/db/migrations/20110521114145_remove_slots_and_entries.rb +21 -0
  175. data/db/migrations/20110604192145_rename_schema_id_columns.rb +22 -0
  176. data/db/migrations/20110805141925_rename_site_to_state.rb +11 -0
  177. data/lib/cutaneous/context_helper.rb +82 -0
  178. data/lib/cutaneous/first_pass_parser.rb +23 -0
  179. data/lib/cutaneous/first_pass_renderer.rb +18 -0
  180. data/lib/cutaneous/parser_core.rb +18 -0
  181. data/lib/cutaneous/preview_context.rb +31 -0
  182. data/lib/cutaneous/preview_renderer.rb +15 -0
  183. data/lib/cutaneous/publish_context.rb +9 -0
  184. data/lib/cutaneous/renderer.rb +122 -0
  185. data/lib/cutaneous/request_context.rb +8 -0
  186. data/lib/cutaneous/second_pass_parser.rb +23 -0
  187. data/lib/cutaneous/second_pass_renderer.rb +18 -0
  188. data/lib/cutaneous.rb +47 -0
  189. data/lib/sequel/plugins/content_table_inheritance.rb +196 -0
  190. data/lib/sequel/plugins/yajl_serialization.rb +154 -0
  191. data/lib/spontaneous/application/feature.rb +9 -0
  192. data/lib/spontaneous/application/plugin.rb +13 -0
  193. data/lib/spontaneous/application.rb +8 -0
  194. data/lib/spontaneous/box.rb +232 -0
  195. data/lib/spontaneous/box_style.rb +64 -0
  196. data/lib/spontaneous/change.rb +107 -0
  197. data/lib/spontaneous/cli/adapter.rb +13 -0
  198. data/lib/spontaneous/cli/base.rb +184 -0
  199. data/lib/spontaneous/cli/console.rb +0 -0
  200. data/lib/spontaneous/cli/media.rb +13 -0
  201. data/lib/spontaneous/cli/server.rb +50 -0
  202. data/lib/spontaneous/cli/site.rb +46 -0
  203. data/lib/spontaneous/cli/sync.rb +42 -0
  204. data/lib/spontaneous/cli/tasks.rb +9 -0
  205. data/lib/spontaneous/cli.rb +83 -0
  206. data/lib/spontaneous/collections/box_set.rb +56 -0
  207. data/lib/spontaneous/collections/change_set.rb +43 -0
  208. data/lib/spontaneous/collections/entry_set.rb +83 -0
  209. data/lib/spontaneous/collections/field_set.rb +53 -0
  210. data/lib/spontaneous/collections/prototype_set.rb +131 -0
  211. data/lib/spontaneous/collections/style_set.rb +13 -0
  212. data/lib/spontaneous/config.rb +156 -0
  213. data/lib/spontaneous/constants.rb +24 -0
  214. data/lib/spontaneous/content.rb +113 -0
  215. data/lib/spontaneous/content_query.rb +17 -0
  216. data/lib/spontaneous/errors.rb +48 -0
  217. data/lib/spontaneous/extensions/array.rb +18 -0
  218. data/lib/spontaneous/extensions/class.rb +17 -0
  219. data/lib/spontaneous/extensions/hash.rb +18 -0
  220. data/lib/spontaneous/extensions/json.rb +26 -0
  221. data/lib/spontaneous/extensions/kernel.rb +7 -0
  222. data/lib/spontaneous/extensions/object.rb +30 -0
  223. data/lib/spontaneous/extensions/object_space.rb +12 -0
  224. data/lib/spontaneous/extensions/string.rb +44 -0
  225. data/lib/spontaneous/facet.rb +47 -0
  226. data/lib/spontaneous/field_types/date_field.rb +12 -0
  227. data/lib/spontaneous/field_types/field.rb +252 -0
  228. data/lib/spontaneous/field_types/image_field.rb +329 -0
  229. data/lib/spontaneous/field_types/markdown_field.rb +37 -0
  230. data/lib/spontaneous/field_types/string_field.rb +14 -0
  231. data/lib/spontaneous/field_types.rb +40 -0
  232. data/lib/spontaneous/generators/page/inline.html.cut +1 -0
  233. data/lib/spontaneous/generators/page/page.html.cut.tt +4 -0
  234. data/lib/spontaneous/generators/page/page.rb.tt +9 -0
  235. data/lib/spontaneous/generators/page.rb +38 -0
  236. data/lib/spontaneous/generators/site/.gitignore +4 -0
  237. data/lib/spontaneous/generators/site/Gemfile.tt +31 -0
  238. data/lib/spontaneous/generators/site/Rakefile.tt +6 -0
  239. data/lib/spontaneous/generators/site/config/back.ru +7 -0
  240. data/lib/spontaneous/generators/site/config/boot.rb +19 -0
  241. data/lib/spontaneous/generators/site/config/database.yml.tt +21 -0
  242. data/lib/spontaneous/generators/site/config/deploy.rb.tt +0 -0
  243. data/lib/spontaneous/generators/site/config/environment.rb.tt +8 -0
  244. data/lib/spontaneous/generators/site/config/environments/development.rb.tt +15 -0
  245. data/lib/spontaneous/generators/site/config/environments/production.rb.tt +5 -0
  246. data/lib/spontaneous/generators/site/config/front.ru +8 -0
  247. data/lib/spontaneous/generators/site/config/user_levels.yml +22 -0
  248. data/lib/spontaneous/generators/site/lib/site.rb.tt +4 -0
  249. data/lib/spontaneous/generators/site/lib/tasks/site.rake.tt +8 -0
  250. data/lib/spontaneous/generators/site/public/css/site.css +0 -0
  251. data/lib/spontaneous/generators/site/public/favicon.ico +0 -0
  252. data/lib/spontaneous/generators/site/public/js/.empty_directory +0 -0
  253. data/lib/spontaneous/generators/site/public/js/site.js +0 -0
  254. data/lib/spontaneous/generators/site/public/robots.txt +0 -0
  255. data/lib/spontaneous/generators/site/schema/.map +1 -0
  256. data/lib/spontaneous/generators/site/schema/page.rb.tt +8 -0
  257. data/lib/spontaneous/generators/site/schema/piece.rb.tt +4 -0
  258. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +13 -0
  259. data/lib/spontaneous/generators/site.rb +77 -0
  260. data/lib/spontaneous/generators.rb +23 -0
  261. data/lib/spontaneous/image_size.rb +117 -0
  262. data/lib/spontaneous/json.rb +33 -0
  263. data/lib/spontaneous/layout.rb +15 -0
  264. data/lib/spontaneous/loader.rb +280 -0
  265. data/lib/spontaneous/logger.rb +369 -0
  266. data/lib/spontaneous/media.rb +84 -0
  267. data/lib/spontaneous/page.rb +92 -0
  268. data/lib/spontaneous/page_controller.rb +18 -0
  269. data/lib/spontaneous/page_piece.rb +77 -0
  270. data/lib/spontaneous/paths.rb +30 -0
  271. data/lib/spontaneous/permissions/access_group.rb +50 -0
  272. data/lib/spontaneous/permissions/access_key.rb +35 -0
  273. data/lib/spontaneous/permissions/user.rb +167 -0
  274. data/lib/spontaneous/permissions/user_level.rb +177 -0
  275. data/lib/spontaneous/permissions.rb +55 -0
  276. data/lib/spontaneous/piece.rb +30 -0
  277. data/lib/spontaneous/plugins/aliases.rb +128 -0
  278. data/lib/spontaneous/plugins/allowed_types.rb +173 -0
  279. data/lib/spontaneous/plugins/application/facets.rb +25 -0
  280. data/lib/spontaneous/plugins/application/paths.rb +137 -0
  281. data/lib/spontaneous/plugins/application/render.rb +29 -0
  282. data/lib/spontaneous/plugins/application/serialisation.rb +16 -0
  283. data/lib/spontaneous/plugins/application/state.rb +86 -0
  284. data/lib/spontaneous/plugins/boxes.rb +84 -0
  285. data/lib/spontaneous/plugins/controllers.rb +52 -0
  286. data/lib/spontaneous/plugins/entries.rb +193 -0
  287. data/lib/spontaneous/plugins/entry.rb +51 -0
  288. data/lib/spontaneous/plugins/fields.rb +103 -0
  289. data/lib/spontaneous/plugins/instance_code.rb +18 -0
  290. data/lib/spontaneous/plugins/layouts.rb +87 -0
  291. data/lib/spontaneous/plugins/media.rb +41 -0
  292. data/lib/spontaneous/plugins/page/formats.rb +67 -0
  293. data/lib/spontaneous/plugins/page/request.rb +89 -0
  294. data/lib/spontaneous/plugins/page_search.rb +64 -0
  295. data/lib/spontaneous/plugins/page_tree.rb +25 -0
  296. data/lib/spontaneous/plugins/paths.rb +125 -0
  297. data/lib/spontaneous/plugins/permissions.rb +63 -0
  298. data/lib/spontaneous/plugins/prototypes.rb +84 -0
  299. data/lib/spontaneous/plugins/publishing.rb +255 -0
  300. data/lib/spontaneous/plugins/render.rb +24 -0
  301. data/lib/spontaneous/plugins/schema_hierarchy.rb +76 -0
  302. data/lib/spontaneous/plugins/schema_id.rb +60 -0
  303. data/lib/spontaneous/plugins/schema_title.rb +33 -0
  304. data/lib/spontaneous/plugins/serialisation.rb +67 -0
  305. data/lib/spontaneous/plugins/site/instance.rb +22 -0
  306. data/lib/spontaneous/plugins/site/map.rb +19 -0
  307. data/lib/spontaneous/plugins/site/publishing.rb +74 -0
  308. data/lib/spontaneous/plugins/site/revisions.rb +28 -0
  309. data/lib/spontaneous/plugins/site/selectors.rb +41 -0
  310. data/lib/spontaneous/plugins/site_map.rb +34 -0
  311. data/lib/spontaneous/plugins/styles.rb +119 -0
  312. data/lib/spontaneous/plugins/supertype.rb +11 -0
  313. data/lib/spontaneous/plugins/visibility.rb +151 -0
  314. data/lib/spontaneous/plugins.rb +20 -0
  315. data/lib/spontaneous/prototypes/box_prototype.rb +168 -0
  316. data/lib/spontaneous/prototypes/field_prototype.rb +112 -0
  317. data/lib/spontaneous/prototypes/layout_prototype.rb +17 -0
  318. data/lib/spontaneous/prototypes/style_prototype.rb +42 -0
  319. data/lib/spontaneous/proxy_object.rb +12 -0
  320. data/lib/spontaneous/publishing/fire_and_forget.rb +57 -0
  321. data/lib/spontaneous/publishing/immediate.rb +197 -0
  322. data/lib/spontaneous/publishing/threaded.rb +25 -0
  323. data/lib/spontaneous/publishing.rb +10 -0
  324. data/lib/spontaneous/rack/around_back.rb +44 -0
  325. data/lib/spontaneous/rack/around_front.rb +29 -0
  326. data/lib/spontaneous/rack/around_preview.rb +26 -0
  327. data/lib/spontaneous/rack/assets.rb +98 -0
  328. data/lib/spontaneous/rack/back.rb +729 -0
  329. data/lib/spontaneous/rack/front.rb +41 -0
  330. data/lib/spontaneous/rack/http.rb +18 -0
  331. data/lib/spontaneous/rack/media.rb +29 -0
  332. data/lib/spontaneous/rack/public.rb +232 -0
  333. data/lib/spontaneous/rack/reloader.rb +42 -0
  334. data/lib/spontaneous/rack/static.rb +25 -0
  335. data/lib/spontaneous/rack.rb +55 -0
  336. data/lib/spontaneous/render/context.rb +100 -0
  337. data/lib/spontaneous/render/development_renderer.rb +14 -0
  338. data/lib/spontaneous/render/engine.rb +19 -0
  339. data/lib/spontaneous/render/format/html.rb +5 -0
  340. data/lib/spontaneous/render/format.rb +70 -0
  341. data/lib/spontaneous/render/preview_renderer.rb +18 -0
  342. data/lib/spontaneous/render/published_renderer.rb +54 -0
  343. data/lib/spontaneous/render/publishing_renderer.rb +13 -0
  344. data/lib/spontaneous/render/renderer.rb +46 -0
  345. data/lib/spontaneous/render.rb +173 -0
  346. data/lib/spontaneous/revision.rb +7 -0
  347. data/lib/spontaneous/schema/schema_modification.rb +260 -0
  348. data/lib/spontaneous/schema/uid.rb +221 -0
  349. data/lib/spontaneous/schema.rb +295 -0
  350. data/lib/spontaneous/server.rb +65 -0
  351. data/lib/spontaneous/site.rb +87 -0
  352. data/lib/spontaneous/state.rb +53 -0
  353. data/lib/spontaneous/style.rb +144 -0
  354. data/lib/spontaneous/tasks/database.rake +9 -0
  355. data/lib/spontaneous/tasks.rb +5 -0
  356. data/lib/spontaneous/version.rb +6 -0
  357. data/lib/spontaneous.rb +179 -0
  358. data/spontaneous.gemspec.tmpl +66 -0
  359. data/test/disabled/test_slots.rb +287 -0
  360. data/test/experimental/test_formats.rb +92 -0
  361. data/test/experimental/test_plugins.rb +64 -0
  362. data/test/fixtures/application/css/test.less +5 -0
  363. data/test/fixtures/application/js/test.js +1 -0
  364. data/test/fixtures/application/static/favicon.ico +1 -0
  365. data/test/fixtures/application/static/test.html +1 -0
  366. data/test/fixtures/application/views/index.erubis +1 -0
  367. data/test/fixtures/back/public/test.html +1 -0
  368. data/test/fixtures/back/templates/layouts/standard.html.cut +1 -0
  369. data/test/fixtures/config/config/environment.rb +4 -0
  370. data/test/fixtures/config/config/environments/development.rb +13 -0
  371. data/test/fixtures/config/config/environments/production.rb +22 -0
  372. data/test/fixtures/config/config/environments/staging.rb +2 -0
  373. data/test/fixtures/example_application/Gemfile +6 -0
  374. data/test/fixtures/example_application/Gemfile.lock +76 -0
  375. data/test/fixtures/example_application/Rakefile +6 -0
  376. data/test/fixtures/example_application/config/back.rb +15 -0
  377. data/test/fixtures/example_application/config/back.ru +8 -0
  378. data/test/fixtures/example_application/config/back.yml +8 -0
  379. data/test/fixtures/example_application/config/boot.rb +16 -0
  380. data/test/fixtures/example_application/config/database.yml +24 -0
  381. data/test/fixtures/example_application/config/environment.rb +4 -0
  382. data/test/fixtures/example_application/config/environments/development.rb +16 -0
  383. data/test/fixtures/example_application/config/environments/production.rb +21 -0
  384. data/test/fixtures/example_application/config/environments/staging.rb +1 -0
  385. data/test/fixtures/example_application/config/front.rb +8 -0
  386. data/test/fixtures/example_application/config/front.ru +8 -0
  387. data/test/fixtures/example_application/config/front.yml +8 -0
  388. data/test/fixtures/example_application/config/schema.yml +48 -0
  389. data/test/fixtures/example_application/config/unicorn.rb +1 -0
  390. data/test/fixtures/example_application/config/user_levels.yml +19 -0
  391. data/test/fixtures/example_application/public/css/test.css +0 -0
  392. data/test/fixtures/example_application/public/favicon.ico +1 -0
  393. data/test/fixtures/example_application/public/js/test.js +0 -0
  394. data/test/fixtures/example_application/public/test.html +1 -0
  395. data/test/fixtures/example_application/schema/client_project.rb +18 -0
  396. data/test/fixtures/example_application/schema/client_projects.rb +8 -0
  397. data/test/fixtures/example_application/schema/home_page.rb +22 -0
  398. data/test/fixtures/example_application/schema/info_page.rb +13 -0
  399. data/test/fixtures/example_application/schema/inline_image.rb +11 -0
  400. data/test/fixtures/example_application/schema/page.rb +4 -0
  401. data/test/fixtures/example_application/schema/piece.rb +3 -0
  402. data/test/fixtures/example_application/schema/project.rb +21 -0
  403. data/test/fixtures/example_application/schema/project_image.rb +18 -0
  404. data/test/fixtures/example_application/schema/projects_page.rb +12 -0
  405. data/test/fixtures/example_application/schema/text.rb +8 -0
  406. data/test/fixtures/example_application/templates/client_project/images.html.cut +1 -0
  407. data/test/fixtures/example_application/templates/client_project.html.cut +4 -0
  408. data/test/fixtures/example_application/templates/client_projects.html.cut +6 -0
  409. data/test/fixtures/example_application/templates/info_page/inline.html.cut +0 -0
  410. data/test/fixtures/example_application/templates/inline_image.html.cut +1 -0
  411. data/test/fixtures/example_application/templates/layouts/home.html.cut +15 -0
  412. data/test/fixtures/example_application/templates/layouts/info.html.cut +3 -0
  413. data/test/fixtures/example_application/templates/layouts/project.html.cut +13 -0
  414. data/test/fixtures/example_application/templates/layouts/projects.html.cut +11 -0
  415. data/test/fixtures/example_application/templates/layouts/standard.html.cut +0 -0
  416. data/test/fixtures/example_application/templates/project/inline.html.cut +5 -0
  417. data/test/fixtures/example_application/templates/project.html.cut +5 -0
  418. data/test/fixtures/example_application/templates/project_image.html.cut +1 -0
  419. data/test/fixtures/example_application/templates/text.html.cut +1 -0
  420. data/test/fixtures/images/rose.greyscale.jpg +0 -0
  421. data/test/fixtures/images/rose.jpg +0 -0
  422. data/test/fixtures/images/size.gif +0 -0
  423. data/test/fixtures/images/size.jpg +0 -0
  424. data/test/fixtures/images/size.png24 +0 -0
  425. data/test/fixtures/images/size.png8 +0 -0
  426. data/test/fixtures/layouts/layouts/custom1.html.cut +1 -0
  427. data/test/fixtures/layouts/layouts/custom1.pdf.cut +0 -0
  428. data/test/fixtures/layouts/layouts/custom1.xml.cut +0 -0
  429. data/test/fixtures/layouts/layouts/custom2.html.cut +1 -0
  430. data/test/fixtures/layouts/layouts/custom3.html.cut +0 -0
  431. data/test/fixtures/layouts/layouts/standard.html.cut +1 -0
  432. data/test/fixtures/media/101/003/rose.jpg +0 -0
  433. data/test/fixtures/permissions/config/user_levels.yml +9 -0
  434. data/test/fixtures/permissions/media/image.jpg +0 -0
  435. data/test/fixtures/plugins/schema_plugin/init.rb +1 -0
  436. data/test/fixtures/plugins/schema_plugin/schema/external.rb +5 -0
  437. data/test/fixtures/plugins/schema_plugin/templates/external.html.cut +1 -0
  438. data/test/fixtures/plugins/schema_plugin/templates/from_plugin.html.cut +0 -0
  439. data/test/fixtures/plugins/schema_plugin/templates/layouts/from_plugin.html.cut +0 -0
  440. data/test/fixtures/public/templates/layouts/default.html.cut +1 -0
  441. data/test/fixtures/public/templates/layouts/default.pdf.cut +1 -0
  442. data/test/fixtures/public/templates/layouts/default.rss.cut +1 -0
  443. data/test/fixtures/public/templates/layouts/dynamic.html.cut +1 -0
  444. data/test/fixtures/public/templates/layouts/standard.html.cut +0 -0
  445. data/test/fixtures/schema/before.yml +24 -0
  446. data/test/fixtures/schema/resolvable.yml +12 -0
  447. data/test/fixtures/schema/schema.yml +7 -0
  448. data/test/fixtures/serialisation/class_hash.yaml.erb +53 -0
  449. data/test/fixtures/serialisation/root_hash.yaml.erb +184 -0
  450. data/test/fixtures/sharding/rose.jpg +0 -0
  451. data/test/fixtures/sharding/xaa +0 -0
  452. data/test/fixtures/sharding/xab +0 -0
  453. data/test/fixtures/sharding/xac +0 -0
  454. data/test/fixtures/sharding/xad +0 -0
  455. data/test/fixtures/sharding/xae +0 -0
  456. data/test/fixtures/sharding/xaf +0 -0
  457. data/test/fixtures/sharding/xag +0 -0
  458. data/test/fixtures/styles/box_a/runny.html.cut +0 -0
  459. data/test/fixtures/styles/box_a.html.cut +1 -0
  460. data/test/fixtures/styles/named2.html.cut +1 -0
  461. data/test/fixtures/styles/orange/apple.html.cut +1 -0
  462. data/test/fixtures/styles/template_class/named1.html.cut +1 -0
  463. data/test/fixtures/styles/template_class/results.html.cut +1 -0
  464. data/test/fixtures/styles/template_class/walky.html.cut +0 -0
  465. data/test/fixtures/styles/template_class.epub.cut +0 -0
  466. data/test/fixtures/styles/template_class.html.cut +1 -0
  467. data/test/fixtures/styles/template_class.pdf.cut +0 -0
  468. data/test/fixtures/styles/template_sub_class1.html.cut +1 -0
  469. data/test/fixtures/templates/aliases/a/a_style.html.cut +0 -0
  470. data/test/fixtures/templates/aliases/a/page.html.cut +0 -0
  471. data/test/fixtures/templates/aliases/a_alias/a_alias_style.html.cut +0 -0
  472. data/test/fixtures/templates/aliases/layouts/b.html.cut +1 -0
  473. data/test/fixtures/templates/aliases/layouts/b_alias.html.cut +1 -0
  474. data/test/fixtures/templates/aliases/layouts/c_alias.html.cut +1 -0
  475. data/test/fixtures/templates/boxes/blank_content/things.html.cut +1 -0
  476. data/test/fixtures/templates/boxes/my_box_class/christy.html.cut +1 -0
  477. data/test/fixtures/templates/boxes/thangs.html.cut +1 -0
  478. data/test/fixtures/templates/boxes/with_template_box.html.cut +1 -0
  479. data/test/fixtures/templates/content/include.html.cut +1 -0
  480. data/test/fixtures/templates/content/include_dir.html.cut +1 -0
  481. data/test/fixtures/templates/content/included.epub.cut +1 -0
  482. data/test/fixtures/templates/content/included.html.cut +1 -0
  483. data/test/fixtures/templates/content/partial/included.html.cut +1 -0
  484. data/test/fixtures/templates/content/preprocess.html.cut +1 -0
  485. data/test/fixtures/templates/content/second.html.cut +1 -0
  486. data/test/fixtures/templates/content/template.epub.cut +1 -0
  487. data/test/fixtures/templates/content/template.html.cut +1 -0
  488. data/test/fixtures/templates/default_style_class.html.cut +1 -0
  489. data/test/fixtures/templates/direct.html.cut +1 -0
  490. data/test/fixtures/templates/extended/grandparent.html.cut +10 -0
  491. data/test/fixtures/templates/extended/main.html.cut +6 -0
  492. data/test/fixtures/templates/extended/parent.html.cut +10 -0
  493. data/test/fixtures/templates/layouts/entries.html.cut +7 -0
  494. data/test/fixtures/templates/layouts/page_style.html.cut +1 -0
  495. data/test/fixtures/templates/layouts/params.html.cut +1 -0
  496. data/test/fixtures/templates/layouts/preview_render.html.cut +2 -0
  497. data/test/fixtures/templates/layouts/standard_page.html.cut +1 -0
  498. data/test/fixtures/templates/layouts/subdir_style.html.cut +1 -0
  499. data/test/fixtures/templates/layouts/template_params.html.cut +1 -0
  500. data/test/fixtures/templates/page_class/inline_style.html.cut +1 -0
  501. data/test/fixtures/templates/preview_render/inline.html.cut +0 -0
  502. data/test/fixtures/templates/publishing/layouts/dynamic.html.cut +1 -0
  503. data/test/fixtures/templates/publishing/layouts/static.html.cut +1 -0
  504. data/test/fixtures/templates/template_class/anonymous_style.html.cut +4 -0
  505. data/test/fixtures/templates/template_class/another_template.html.cut +0 -0
  506. data/test/fixtures/templates/template_class/complex_template.html.cut +6 -0
  507. data/test/fixtures/templates/template_class/complex_template.pdf.cut +6 -0
  508. data/test/fixtures/templates/template_class/default_template_style.html.cut +4 -0
  509. data/test/fixtures/templates/template_class/images_with_template.html.cut +5 -0
  510. data/test/fixtures/templates/template_class/slots_template.html.cut +5 -0
  511. data/test/fixtures/templates/template_class/slots_template.pdf.cut +5 -0
  512. data/test/fixtures/templates/template_class/this_template.epub.cut +1 -0
  513. data/test/fixtures/templates/template_class/this_template.html.cut +1 -0
  514. data/test/fixtures/templates/template_class/this_template.pdf.cut +1 -0
  515. data/test/fixtures/templates/with_default_style_class.html.cut +1 -0
  516. data/test/functional/test_application.rb +176 -0
  517. data/test/functional/test_back.rb +902 -0
  518. data/test/functional/test_front.rb +571 -0
  519. data/test/javascript/test_dom.rb +94 -0
  520. data/test/javascript/test_markdown.rb +97 -0
  521. data/test/slow/test_publishing.rb +987 -0
  522. data/test/slow/test_visibility.rb +250 -0
  523. data/test/support/custom_matchers.rb +77 -0
  524. data/test/support/timing.rb +23 -0
  525. data/test/test_helper.rb +164 -0
  526. data/test/test_javascript.rb +34 -0
  527. data/test/ui/test_page_editing.rb +167 -0
  528. data/test/ui_helper.rb +114 -0
  529. data/test/unit/test_alias.rb +254 -0
  530. data/test/unit/test_authentication.rb +510 -0
  531. data/test/unit/test_boxes.rb +497 -0
  532. data/test/unit/test_config.rb +156 -0
  533. data/test/unit/test_content.rb +221 -0
  534. data/test/unit/test_content_inheritance.rb +103 -0
  535. data/test/unit/test_extensions.rb +14 -0
  536. data/test/unit/test_fields.rb +392 -0
  537. data/test/unit/test_generators.rb +97 -0
  538. data/test/unit/test_image_size.rb +25 -0
  539. data/test/unit/test_images.rb +265 -0
  540. data/test/unit/test_layouts.rb +111 -0
  541. data/test/unit/test_logger.rb +80 -0
  542. data/test/unit/test_media.rb +70 -0
  543. data/test/unit/test_page.rb +244 -0
  544. data/test/unit/test_permissions.rb +834 -0
  545. data/test/unit/test_piece.rb +80 -0
  546. data/test/unit/test_prototype_set.rb +192 -0
  547. data/test/unit/test_prototypes.rb +102 -0
  548. data/test/unit/test_render.rb +359 -0
  549. data/test/unit/test_schema.rb +1009 -0
  550. data/test/unit/test_serialisation.rb +215 -0
  551. data/test/unit/test_site.rb +145 -0
  552. data/test/unit/test_structure.rb +85 -0
  553. data/test/unit/test_styles.rb +417 -0
  554. data/test/unit/test_templates.rb +224 -0
  555. data/test/unit/test_type_hierarchy.rb +28 -0
  556. metadata +1017 -0
@@ -0,0 +1,1060 @@
1
+ /**
2
+ * JS.Class: Ruby-style JavaScript
3
+ * Copyright (c) 2007-2010 James Coglan
4
+ *
5
+ * http://jsclass.jcoglan.com
6
+ * http://github.com/jcoglan/js.class
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ *
26
+ * Parts of this software are derived from the following open-source projects:
27
+ *
28
+ * - The Prototype framework, (c) 2005-2010 Sam Stephenson
29
+ * - Alex Arnell's Inheritance library, (c) 2006, Alex Arnell
30
+ * - Base, (c) 2006-9, Dean Edwards
31
+ */
32
+
33
+ /**
34
+ * == core ==
35
+ **/
36
+
37
+ /** section: core
38
+ * JS
39
+ *
40
+ * The `JS` object is used as a namespace by the rest of the JS.Class framework, and hosts
41
+ * various utility methods used throughout. None of these methods should be taken as being
42
+ * public API, they are all 'plumbing' and may be removed or changed at any time.
43
+ **/
44
+ this.JS = this.JS || {};
45
+
46
+ /**
47
+ * JS.extend(target, extensions) -> Object
48
+ * - target (Object): object to be extended
49
+ * - extensions (Object): object containing key/value pairs to add to target
50
+ *
51
+ * Adds the properties of the second argument to the first, and returns the first. Will not
52
+ * needlessly overwrite fields with identical values; if an object has inherited a property
53
+ * we should not add the property to the object itself.
54
+ **/
55
+ JS.extend = function(target, extensions) {
56
+ extensions = extensions || {};
57
+ for (var prop in extensions) {
58
+ if (target[prop] === extensions[prop]) continue;
59
+ target[prop] = extensions[prop];
60
+ }
61
+ return target;
62
+ };
63
+
64
+ JS.extend(JS, {
65
+
66
+ /**
67
+ * JS.makeFunction() -> Function
68
+ *
69
+ * Returns a function for use as a constructor. These functions are used as the basis for
70
+ * classes. The constructor calls the object's `initialize()` method if it exists.
71
+ **/
72
+ makeFunction: function() {
73
+ return function() {
74
+ return this.initialize
75
+ ? (this.initialize.apply(this, arguments) || this)
76
+ : this;
77
+ };
78
+ },
79
+
80
+ /**
81
+ * JS.makeBridge(klass) -> Object
82
+ * - klass (JS.Class): class from which you want to inherit
83
+ *
84
+ * Takes a class and returns an instance of it, without calling the class's constructor.
85
+ * Used for forging prototype links between objects using JavaScript's inheritance model.
86
+ **/
87
+ makeBridge: function(klass) {
88
+ var bridge = function() {};
89
+ bridge.prototype = klass.prototype;
90
+ return new bridge;
91
+ },
92
+
93
+ /**
94
+ * JS.bind(object, func) -> Function
95
+ * - object (Object): object to bind the function to
96
+ * - func (Function): function that the bound function should call
97
+ *
98
+ * Takes a function and an object, and returns a new function that calls the original
99
+ * function with `this` set to refer to the `object`. Used to implement `JS.Kernel#method`,
100
+ * amongst other things.
101
+ **/
102
+ bind: function() {
103
+ var args = JS.array(arguments),
104
+ method = args.shift(),
105
+ object = args.shift() || null;
106
+
107
+ return function() {
108
+ return method.apply(object, args.concat(JS.array(arguments)));
109
+ };
110
+ },
111
+
112
+ /**
113
+ * JS.callsSuper(func) -> Boolean
114
+ * - func (Function): function to test for super() calls
115
+ *
116
+ * Takes a function and returns `true` iff the function makes a call to `callSuper()`.
117
+ * Result is cached on the function itself since functions are immutable and decompiling
118
+ * them is expensive. We use this to determine whether to wrap the function when it's
119
+ * added to a class; wrapping impedes performance and should be avoided where possible.
120
+ **/
121
+ callsSuper: function(func) {
122
+ return func.SUPER === undefined
123
+ ? func.SUPER = /\bcallSuper\b/.test(func.toString())
124
+ : func.SUPER;
125
+ },
126
+
127
+ /**
128
+ * JS.mask(func) -> Function
129
+ * - func (Function): function to obfuscate
130
+ *
131
+ * Disguises a function so that we cannot tell if it uses `callSuper()`. Sometimes we don't
132
+ * want such functions to be wrapped by the inheritance system. Modifies the function's
133
+ * `toString()` method and returns the function.
134
+ **/
135
+ mask: function(func) {
136
+ var string = func.toString().replace(/callSuper/g, 'super');
137
+ func.toString = function() { return string };
138
+ return func;
139
+ },
140
+
141
+ /**
142
+ * JS.array(iterable) -> Array
143
+ * - iterable (Object): object you want to cast to an array
144
+ *
145
+ * Takes any iterable object (something with a `length` property) and returns a native
146
+ * JavaScript `Array` containing the same elements.
147
+ **/
148
+ array: function(iterable) {
149
+ if (!iterable) return [];
150
+ if (iterable.toArray) return iterable.toArray();
151
+
152
+ var length = iterable.length,
153
+ results = [];
154
+
155
+ while (length--) results[length] = iterable[length];
156
+ return results;
157
+ },
158
+
159
+ /**
160
+ * JS.indexOf(haystack, needle) -> Number
161
+ * - haystack (Array): array to search
162
+ * - needle (Object): object to search for
163
+ *
164
+ * Returns the index of the `needle` in the `haystack`, which is typically an `Array` or an
165
+ * array-like object. Returns -1 if no matching element is found. We need this as older
166
+ * IE versions don't implement `Array#indexOf`.
167
+ **/
168
+ indexOf: function(haystack, needle) {
169
+ for (var i = 0, n = haystack.length; i < n; i++) {
170
+ if (haystack[i] === needle) return i;
171
+ }
172
+ return -1;
173
+ },
174
+
175
+ /**
176
+ * JS.isFn(object) -> Boolean
177
+ * - object (Object): object to test
178
+ *
179
+ * Returns `true` iff the argument is a `Function`.
180
+ **/
181
+ isFn: function(object) {
182
+ return object instanceof Function;
183
+ },
184
+
185
+ /**
186
+ * JS.isType(object, type) -> Boolean
187
+ * - object (Object): object whose type we wish to check
188
+ * - type (JS.Module): type to match against
189
+ *
190
+ * Returns `true` iff `object` is of the given `type`.
191
+ **/
192
+ isType: function(object, type) {
193
+ if (!object || !type) return false;
194
+ return (type instanceof Function && object instanceof type) ||
195
+ (typeof type === 'string' && typeof object === type) ||
196
+ (object.isA && object.isA(type));
197
+ },
198
+
199
+ /**
200
+ * JS.ignore(key, object) -> Boolean
201
+ * - key (String): name of field being added to an object
202
+ * - object (Object): value of the given field
203
+ *
204
+ * Used to determine whether a key-value pair should be added to a class or module. Pairs
205
+ * may be ignored if they have some special function, like `include` or `extend`.
206
+ **/
207
+ ignore: function(key, object) {
208
+ return /^(include|extend)$/.test(key) && typeof object === 'object';
209
+ }
210
+ });
211
+
212
+
213
+ /** section: core
214
+ * class JS.Module
215
+ * includes JS.Kernel
216
+ *
217
+ * `Module` is the core class in JS.Class. A module is simply an object that stores methods,
218
+ * and is responsible for handling method lookups, inheritance relationships and the like.
219
+ * All of Ruby's inheritance semantics are handled using modules in JS.Class.
220
+ *
221
+ * The basic object/module/class model in Ruby is expressed in the diagram at
222
+ * http://ruby-doc.org/core/classes/Class.html -- `Class` inherits from `Module`, which
223
+ * inherits from `Object` (as do all custom classes). `Kernel` is a `Module` which is mixed
224
+ * into `Object` to provide methods common to all objects.
225
+ *
226
+ * In JS.Class, there is no `Object` class, but we do have `Module`, `Class` and `Kernel`.
227
+ * All top-level (parentless) classes include the `JS.Kernel` module, so all classes in effect
228
+ * inherit from `Kernel`. All classes are instances of `JS.Class`, and all modules instances
229
+ * of `JS.Module`. `Module` is a top-level class, from which `Class` inherits.
230
+ *
231
+ * The following diagram shows this relationship; vertical lines indicate parent/child
232
+ * class relationships, horizontal lines indicate module inclusions. (`C`) means a class,
233
+ * (`M`) a module.
234
+ *
235
+ *
236
+ * ============== ============== =================== ==============
237
+ * | M | Kernel |----->| C | Module | | C | ParentClass |<-----| M | Kernel |
238
+ * ============== ============== =================== ==============
239
+ * ^ ^
240
+ * | |
241
+ * | |
242
+ * ============= ==================
243
+ * | C | Class | | C | ChildClass |
244
+ * ============= ==================
245
+ *
246
+ *
247
+ * All objects have a metamodule attached to them; this handles storage of singleton
248
+ * methods as metaclasses do in Ruby. This is handled by mixing the object's class into
249
+ * the object's metamodule.
250
+ *
251
+ *
252
+ * class
253
+ * =================
254
+ * | C | SomeClass |------------------------------------------------
255
+ * ================= |
256
+ * | |
257
+ * V |
258
+ * ==================== ================================= |
259
+ * | <SomeClass:0xb7> |<>----| M | <Module:<SomeClass:0xb7>> |<-----
260
+ * ==================== =================================
261
+ * instance metamodule
262
+ *
263
+ *
264
+ * Similarly, inheritance of class methods is handled by mixing the parent class's
265
+ * metamodule into the child class's metamodule, like so:
266
+ *
267
+ *
268
+ * =================== ============================
269
+ * | C | ParentClass |<>----| M | <Module:ParentClass> |------
270
+ * =================== ============================ |
271
+ * ^ |
272
+ * | |
273
+ * | |
274
+ * =================== =========================== |
275
+ * | C | ChildClass |<>----| M | <Module:ChildClass> |<------
276
+ * =================== ===========================
277
+ *
278
+ *
279
+ * The parent-child relationships are also implemented using module inclusion, with some
280
+ * extra checks and optimisations. Also, bear in mind that although `Class` appears to be a
281
+ * subclass of `Module`, this particular parent-child relationship is faked using manual
282
+ * delegation; every class has a hidden module attached to it that handles all the method
283
+ * storage and lookup responsibilities.
284
+ **/
285
+ JS.Module = JS.makeFunction();
286
+ JS.extend(JS.Module.prototype, {
287
+ END_WITHOUT_DOT: /([^\.])$/,
288
+
289
+ /**
290
+ * new JS.Module(name, methods, options)
291
+ * - name (String): the name of the module, used for debugging
292
+ * - methods (Object): list of methods for the class
293
+ * - options (Object): configuration options
294
+ *
295
+ * The `name` argument is optional and may be omitted; `name` is not used to assign
296
+ * the class to a variable, it is only uses as metadata. The `options` object is used
297
+ * to specify the target object that the module is storing methods for.
298
+ *
299
+ * var Runnable = new JS.Module('Runnable', {
300
+ * run: function(args) {
301
+ * // ...
302
+ * }
303
+ * });
304
+ **/
305
+ initialize: function(name, methods, options) {
306
+ this.__mod__ = this; // Mirror property found in Class. Think of this as toModule()
307
+ this.__inc__ = []; // List of modules included in this module
308
+ this.__fns__ = {}; // Object storing methods belonging to this module
309
+ this.__dep__ = []; // List modules and classes that depend on this module
310
+ this.__mct__ = {}; // Cache table for method call lookups
311
+
312
+ if (typeof name === 'string') {
313
+ this.__nom__ = this.displayName = name;
314
+ } else {
315
+ this.__nom__ = this.displayName = '';
316
+ options = methods;
317
+ methods = name;
318
+ }
319
+
320
+ options = options || {};
321
+
322
+ // Object to resolve methods onto
323
+ this.__res__ = options._resolve || null;
324
+
325
+ if (methods) this.include(methods, false);
326
+
327
+ if (JS.Module.__chainq__) JS.Module.__chainq__.push(this);
328
+ },
329
+
330
+ /**
331
+ * JS.Module#setName(name) -> undefined
332
+ * - name (String): the name for the module
333
+ *
334
+ * Sets the `displayName` of the module to the given value. Should be the fully-qualified
335
+ * name, including names of the containing modules.
336
+ **/
337
+ setName: function(name) {
338
+ this.__nom__ = this.displayName = name || '';
339
+ for (var key in this.__mod__.__fns__)
340
+ this.__name__(key);
341
+ if (name && this.__meta__) this.__meta__.setName(name + '.');
342
+ },
343
+
344
+ /**
345
+ * JS.Module#__name__(name) -> undefined
346
+ * - name (String): the name of the method to assign a `displayName` to
347
+ *
348
+ * Assigns the `displayName` property to the named method using Ruby conventions for naming
349
+ * instance and singleton methods. If the named field points to another `Module`, the name
350
+ * change is applied recursively.
351
+ **/
352
+ __name__: function(name) {
353
+ if (!this.__nom__) return;
354
+ var object = this.__mod__.__fns__[name] || {};
355
+ name = this.__nom__.replace(this.END_WITHOUT_DOT, '$1#') + name;
356
+ if (JS.isFn(object.setName)) return object.setName(name);
357
+ if (JS.isFn(object)) object.displayName = name;
358
+ },
359
+
360
+ /**
361
+ * JS.Module#define(name, func[, resolve = true[, options = {}]]) -> undefined
362
+ * - name (String): the name of the method
363
+ * - func (Function): a function implementing the method
364
+ * - resolve (Boolean): sets whether to refresh method tables afterward
365
+ * - options (Object): execution options
366
+ *
367
+ * Adds an instance method to the module with the given `name`. The `options` parameter is
368
+ * for internal use to make sure callbacks fire on the correct objects, e.g. a class
369
+ * uses a hidden module to store its methods, but callbacks should fire on the class,
370
+ * not the module.
371
+ **/
372
+ define: function(name, func, resolve, options) {
373
+ var notify = (options || {})._notify || this;
374
+ this.__fns__[name] = func;
375
+ this.__name__(name);
376
+ if (JS.Module._notify && notify && JS.isFn(func))
377
+ JS.Module._notify(name, notify);
378
+ if (resolve !== false) this.resolve();
379
+ },
380
+
381
+ /**
382
+ * JS.Module#instanceMethod(name) -> Function
383
+ * - name (String): the name of the method
384
+ *
385
+ * Returns the named instance method from the module as an unbound function.
386
+ **/
387
+ instanceMethod: function(name) {
388
+ var method = this.lookup(name).pop();
389
+ return JS.isFn(method) ? method : null;
390
+ },
391
+
392
+ /**
393
+ * JS.Module#instanceMethods([includeSuper = true[, results]]) -> Array
394
+ * - includeSuper (Boolean): whether to include ancestor methods
395
+ * - results (Array): list of found method names (internal use)
396
+ *
397
+ * Returns an array of all the method names from the module. Pass `false` to ignore methods
398
+ * inherited from ancestors.
399
+ **/
400
+ instanceMethods: function(includeSuper, results) {
401
+ var self = this.__mod__,
402
+ results = results || [],
403
+ ancestors = self.ancestors(),
404
+ n = ancestors.length,
405
+ name;
406
+
407
+ for (name in self.__fns__) {
408
+ if (self.__fns__.hasOwnProperty(name) &&
409
+ JS.isFn(self.__fns__[name]) &&
410
+ JS.indexOf(results, name) === -1)
411
+ results.push(name);
412
+ }
413
+ if (includeSuper === false) return results;
414
+
415
+ while (n--) ancestors[n].instanceMethods(false, results);
416
+ return results;
417
+ },
418
+
419
+ /**
420
+ * JS.Module#include(module[, resolve = true[, options = {}]]) -> undefined
421
+ * - module (JS.Module): the module to mix in
422
+ * - resolve (Boolean): sets whether to refresh method tables afterward
423
+ * - options (Object): flags to control execution
424
+ *
425
+ * Mixes `module` into the receiver or, if `module` is plain old object (rather than a
426
+ * `JS.Module`) adds methods directly into the receiver. The `options` and `resolve` arguments
427
+ * are mostly for internal use; `options` specifies objects that callbacks should fire on,
428
+ * and `resolve` tells the module whether to resolve methods onto its target after adding
429
+ * the methods.
430
+ **/
431
+ include: function(module, resolve, options) {
432
+ resolve = (resolve !== false);
433
+ if (!module) return resolve ? this.resolve() : this.uncache();
434
+ options = options || {};
435
+
436
+ if (module.__mod__) module = module.__mod__;
437
+
438
+ var inc = module.include,
439
+ ext = module.extend,
440
+ includer = options._included || this,
441
+ modules, method, i, n;
442
+
443
+ if (module.__inc__ && module.__fns__) {
444
+ // module is a Module instance: make links and fire callbacks
445
+
446
+ this.__inc__.push(module);
447
+ module.__dep__.push(this);
448
+ if (options._extended) module.extended && module.extended(options._extended);
449
+ else module.included && module.included(includer);
450
+
451
+ } else {
452
+ // module is a normal object: add methods directly to this module
453
+
454
+ if (options._recall) {
455
+ // Second call: add all the methods
456
+ for (method in module) {
457
+ if (JS.ignore(method, module[method])) continue;
458
+ this.define(method, module[method], false, {_notify: includer || options._extended || this});
459
+ }
460
+ } else {
461
+ // First call: handle include and extend blocks
462
+
463
+ // Handle inclusions
464
+ if (typeof inc === 'object' || JS.isType(inc, JS.Module)) {
465
+ modules = [].concat(inc);
466
+ for (i = 0, n = modules.length; i < n; i++)
467
+ includer.include(modules[i], resolve, options);
468
+ }
469
+
470
+ // Handle extensions
471
+ if (typeof ext === 'object' || JS.isType(ext, JS.Module)) {
472
+ modules = [].concat(ext);
473
+ for (i = 0, n = modules.length; i < n; i++)
474
+ includer.extend(modules[i], false);
475
+ includer.extend();
476
+ }
477
+
478
+ // Make a second call to include(). This allows mixins to modify the
479
+ // include() method and affect the addition of methods to this module
480
+ options._recall = true;
481
+ return includer.include(module, resolve, options);
482
+ }
483
+ }
484
+ resolve ? this.resolve() : this.uncache();
485
+ },
486
+
487
+ /**
488
+ * JS.Module#includes(module) -> Boolean
489
+ * - module (JS.Module): a module to check for inclusion
490
+ *
491
+ * Returns `true` iff the receiver includes (i.e. inherits from) the given `module`, or
492
+ * if the receiver and given `module` are the same object. Recurses over the receiver's
493
+ * inheritance tree, could get expensive.
494
+ **/
495
+ includes: function(module) {
496
+ var self = this.__mod__,
497
+ i = self.__inc__.length;
498
+
499
+ if (Object === module || self === module || self.__res__ === module.prototype)
500
+ return true;
501
+
502
+ while (i--) {
503
+ if (self.__inc__[i].includes(module))
504
+ return true;
505
+ }
506
+ return false;
507
+ },
508
+
509
+ /**
510
+ * JS.Module#match(object) -> Boolean
511
+ * - object (Object): object to type-check
512
+ *
513
+ * Returns `true` if the receiver is in the inheritance chain of `object`.
514
+ **/
515
+ match: function(object) {
516
+ return object.isA && object.isA(this);
517
+ },
518
+
519
+ /**
520
+ * JS.Module#ancestors([results]) -> Array
521
+ * - results (Array): list of found ancestors (internal use)
522
+ *
523
+ * Returns an array of the module's ancestor modules/classes, with the most distant
524
+ * first and the receiver last. This is the opposite order to that given by Ruby, but
525
+ * this order makes it easier to eliminate duplicates and preserve Ruby's inheritance
526
+ * semantics with respect to the diamond problem. The `results` parameter is for internal
527
+ * use; we recurse over the tree passing the same array around rather than generating
528
+ * lots of arrays and concatenating.
529
+ **/
530
+ ancestors: function(results) {
531
+ var self = this.__mod__,
532
+ cachable = (results === undefined),
533
+ klass = (self.__res__||{}).klass,
534
+ result = (klass && self.__res__ === klass.prototype) ? klass : self,
535
+ i, n;
536
+
537
+ if (cachable && self.__anc__) return self.__anc__.slice();
538
+ results = results || [];
539
+
540
+ // Recurse over inclusions first
541
+ for (i = 0, n = self.__inc__.length; i < n; i++)
542
+ self.__inc__[i].ancestors(results);
543
+
544
+ // If this module is not already in the list, add it
545
+ if (JS.indexOf(results, result) === -1) results.push(result);
546
+
547
+ if (cachable) self.__anc__ = results.slice();
548
+ return results;
549
+ },
550
+
551
+ /**
552
+ * JS.Module#lookup(name) -> Array
553
+ * - name (String): the name of the method to search for
554
+ *
555
+ * Returns an array of all the methods in the module's inheritance tree with the given
556
+ * `name`. Methods are returned in the same order as the modules in `JS.Module#ancestors`,
557
+ * so the last method in the list will be called first, the penultimate on the first
558
+ * `callSuper()`, and so on back through the list.
559
+ **/
560
+ lookup: function(name) {
561
+ var self = this.__mod__,
562
+ cache = self.__mct__;
563
+
564
+ if (cache[name]) return cache[name].slice();
565
+
566
+ var ancestors = self.ancestors(),
567
+ results = [],
568
+ i, n, method;
569
+
570
+ for (i = 0, n = ancestors.length; i < n; i++) {
571
+ method = ancestors[i].__mod__.__fns__[name];
572
+ if (method) results.push(method);
573
+ }
574
+ cache[name] = results.slice();
575
+ return results;
576
+ },
577
+
578
+ /**
579
+ * JS.Module#make(name, func) -> Function
580
+ * - name (String): the name of the method being produced
581
+ * - func (Function): a function implementing the method
582
+ *
583
+ * Returns a version of the function ready to be added to a prototype object. Functions
584
+ * that use `callSuper()` must be wrapped to support that behaviour, other functions can
585
+ * be used raw.
586
+ **/
587
+ make: function(name, func) {
588
+ if (!JS.isFn(func) || !JS.callsSuper(func)) return func;
589
+ var module = this;
590
+ return function() {
591
+ return module.chain(this, name, arguments);
592
+ };
593
+ },
594
+
595
+ /**
596
+ * JS.Module#chain(self, name, args) -> Object
597
+ * - self (Object): the receiver of the call
598
+ * - name (String): the name of the method being called
599
+ * - args (Array): list of arguments to begin the call
600
+ *
601
+ * Performs calls to functions that use `callSuper()`. Ancestor methods are looked up
602
+ * dynamically at call-time; this allows `callSuper()` to be late-bound as in Ruby at the
603
+ * cost of a little performance. Arguments to the call are stored so they can be passed
604
+ * up the call stack automatically without the developer needing to pass them by hand.
605
+ **/
606
+ chain: JS.mask( function(self, name, args) {
607
+ var callees = this.lookup(name), // List of method implementations
608
+ stackIndex = callees.length - 1, // Current position in the call stack
609
+ currentSuper = self.callSuper, // Current super method attached to the receiver
610
+ params = JS.array(args), // Copy of argument list
611
+ result;
612
+
613
+ // Set up the callSuper() method
614
+ self.callSuper = function() {
615
+
616
+ // Overwrite arguments specified explicitly
617
+ var i = arguments.length;
618
+ while (i--) params[i] = arguments[i];
619
+
620
+ // Step up the stack, call and step back down
621
+ stackIndex -= 1;
622
+ var returnValue = callees[stackIndex].apply(self, params);
623
+ stackIndex += 1;
624
+
625
+ return returnValue;
626
+ };
627
+
628
+ // Call the last method in the stack
629
+ result = callees.pop().apply(self, params);
630
+
631
+ // Remove or reassign callSuper() method
632
+ currentSuper ? self.callSuper = currentSuper : delete self.callSuper;
633
+
634
+ return result;
635
+ } ),
636
+
637
+ /**
638
+ * JS.Module#resolve([target = this]) -> undefined
639
+ * - target (Object): the object to reflect methods onto
640
+ *
641
+ * Copies methods from the module onto the `target` object, wrapping methods where
642
+ * necessary. The target will typically be a native JavaScript prototype object used
643
+ * to represent a class. Recurses over this module's ancestors to make sure all applicable
644
+ * methods exist.
645
+ **/
646
+ resolve: function(target) {
647
+ var self = this.__mod__,
648
+ target = target || self,
649
+ resolved = target.__res__, i, n, key, made;
650
+
651
+ // Resolve all dependent modules if the target is this module
652
+ if (target === self) {
653
+ self.uncache(false);
654
+ i = self.__dep__.length;
655
+ while (i--) self.__dep__[i].resolve();
656
+ }
657
+
658
+ if (!resolved) return;
659
+
660
+ // Recurse over this module's ancestors
661
+ for (i = 0, n = self.__inc__.length; i < n; i++)
662
+ self.__inc__[i].resolve(target);
663
+
664
+ // Wrap and copy methods to the target
665
+ for (key in self.__fns__) {
666
+ made = target.make(key, self.__fns__[key]);
667
+ if (resolved[key] !== made) resolved[key] = made;
668
+ }
669
+ },
670
+
671
+ /**
672
+ * JS.Module#uncache([recursive = true]) -> undefined
673
+ * - recursive (Boolean): whether to clear the cache of all dependent modules
674
+ *
675
+ * Clears the ancestor and method table cahces for the module. This is used to invalidate
676
+ * caches when modules are modified, to avoid some of the bugs that exist in Ruby.
677
+ **/
678
+ uncache: function(recursive) {
679
+ var self = this.__mod__,
680
+ i = self.__dep__.length;
681
+
682
+ self.__anc__ = null;
683
+ self.__mct__ = {};
684
+ if (recursive === false) return;
685
+ while (i--) self.__dep__[i].uncache();
686
+ }
687
+ });
688
+
689
+
690
+ /** section: core
691
+ * class JS.Class < JS.Module
692
+ *
693
+ * `Class` is a subclass of `JS.Module`; classes not only store methods but also spawn
694
+ * new objects. In addition, classes have an extra type of inheritance on top of mixins,
695
+ * in that each class can have a single parent class from which it will inherit both
696
+ * instance and singleton methods.
697
+ *
698
+ * Refer to `JS.Module` for details of how inheritance is implemented in JS.Class. Though
699
+ * `Class` is supposed to appear to be a subclass of `Module`, this relationship is
700
+ * implemented by letting each `Class` hold a reference to an anonymous `Module` and
701
+ * using manual delegation where necessary.
702
+ **/
703
+ JS.Class = JS.makeFunction();
704
+ JS.extend(JS.Class.prototype = JS.makeBridge(JS.Module), {
705
+
706
+ /**
707
+ * new JS.Class(name, parent, methods)
708
+ * - name (String): the name of the class, used for debugging
709
+ * - parent (JS.Class): the parent class to inherit from
710
+ * - methods (Object): list of methods for the class
711
+ *
712
+ * The `name` and `parent` arguments are both optional and may be omitted. `name`
713
+ * is not used to assign the class to a variable, it is only uses as metadata.
714
+ * The default parent class is `Object`, and all classes include the JS.Kernel
715
+ * module.
716
+ **/
717
+ initialize: function(name, parent, methods) {
718
+ if (typeof name === 'string') {
719
+ this.__nom__ = this.displayName = name;
720
+ } else {
721
+ this.__nom__ = this.displayName = '';
722
+ methods = parent;
723
+ parent = name;
724
+ }
725
+ var klass = JS.extend(JS.makeFunction(), this);
726
+ klass.klass = klass.constructor = this.klass;
727
+
728
+ if (!JS.isFn(parent)) {
729
+ methods = parent;
730
+ parent = Object;
731
+ }
732
+
733
+ // Set up parent-child relationship, then add methods. Setting up a parent
734
+ // class in JavaScript wipes the existing prototype object.
735
+ klass.inherit(parent);
736
+ klass.include(methods, false);
737
+ klass.resolve();
738
+
739
+ // Fire inherited() callback on ancestors
740
+ do {
741
+ parent.inherited && parent.inherited(klass);
742
+ } while (parent = parent.superclass);
743
+
744
+ return klass;
745
+ },
746
+
747
+ /**
748
+ * JS.Class#inherit(klass) -> undefined
749
+ * - klass (JS.Class): the class to inherit from
750
+ *
751
+ * Sets up the parent-child relationship to the parent class. This is a destructive action
752
+ * in that the existing prototype will be discarded; always call this before adding any
753
+ * methods to the class.
754
+ **/
755
+ inherit: function(klass) {
756
+ this.superclass = klass;
757
+
758
+ // Mix the parent's metamodule into this class's metamodule
759
+ if (this.__eigen__ && klass.__eigen__) this.extend(klass.__eigen__(), true);
760
+
761
+ this.subclasses = [];
762
+ (klass.subclasses || []).push(this);
763
+
764
+ // Bootstrap JavaScript's prototypal inheritance model
765
+ var p = this.prototype = JS.makeBridge(klass);
766
+ p.klass = p.constructor = this;
767
+
768
+ // Set up a module to store methods and delegate calls to
769
+ // -- Class does not really subclass Module, instead each
770
+ // Class has a Module that it delegates to
771
+ this.__mod__ = new JS.Module(this.__nom__, {}, {_resolve: this.prototype});
772
+ this.include(JS.Kernel, false);
773
+
774
+ if (klass !== Object) this.include(klass.__mod__ || new JS.Module(klass.prototype,
775
+ {_resolve: klass.prototype}), false);
776
+ },
777
+
778
+ /**
779
+ * JS.Class#include(module[, resolve = true[, options = {}]]) -> undefined
780
+ * - module (JS.Module): the module to mix in
781
+ * - resolve (Boolean): sets whether to refresh method tables afterward
782
+ * - options (Object): flags to control execution
783
+ *
784
+ * Mixes a `module` into the class if it's a `JS.Module` instance, or adds instance
785
+ * methods to the class itself if given a plain old object. Overrides `JS.Module#include`
786
+ * to make sure callbacks fire on the class rather than its delegating module.
787
+ **/
788
+ include: function(module, resolve, options) {
789
+ if (!module) return;
790
+
791
+ var mod = this.__mod__,
792
+ options = options || {};
793
+
794
+ options._included = this;
795
+ return mod.include(module, resolve, options);
796
+ },
797
+
798
+ /**
799
+ * JS.Class#define(name, func[, resolve = true[, options = {}]]) -> undefined
800
+ * - name (String): the name of the method
801
+ * - func (Function): a function to implement the method
802
+ * - resolve (Boolean): sets whether to refresh method tables afterward
803
+ * - options (Object): options for internal use
804
+ *
805
+ * Adds an instance method to the class with the given `name`. The `options` parameter is
806
+ * for internal use to make sure callbacks fire on the correct objects, e.g. a class
807
+ * uses a hidden module to store its methods, but callbacks should fire on the class,
808
+ * not the module.
809
+ **/
810
+ define: function(name, func, resolve, options) {
811
+ var module = this.__mod__;
812
+ options = options || {};
813
+ options._notify = this;
814
+ module.define(name, func, resolve, options);
815
+ }
816
+ });
817
+
818
+
819
+ // This file bootstraps the framework by redefining Module and Class using their
820
+ // own prototypes and mixing in methods from Kernel, making these classes appear
821
+ // to be instances of themselves.
822
+
823
+ JS.Module = new JS.Class('Module', JS.Module.prototype);
824
+ JS.Class = new JS.Class('Class', JS.Module, JS.Class.prototype);
825
+ JS.Module.klass = JS.Module.constructor =
826
+ JS.Class.klass = JS.Class.constructor = JS.Class;
827
+
828
+ JS.extend(JS.Module, {
829
+ _observers: [],
830
+
831
+ __chainq__: [],
832
+
833
+ methodAdded: function(block, context) {
834
+ this._observers.push([block, context]);
835
+ },
836
+
837
+ _notify: function(name, object) {
838
+ var obs = this._observers, i = obs.length;
839
+ while (i--) obs[i][0].call(obs[i][1] || null, name, object);
840
+ }
841
+ });
842
+
843
+
844
+ /** section: core
845
+ * mixin JS.Kernel
846
+ *
847
+ * `Kernel` is the base module; all classes include the `Kernel`, so its methods become
848
+ * available to all objects instantiated by JS.Class. As in Ruby, the core `Object`
849
+ * methods are implemented here rather than in the base `Object` class. JS.Class does
850
+ * not in fact have an `Object` class and does not modify the builtin JavaScript `Object`
851
+ * class either.
852
+ **/
853
+ JS.Kernel = JS.extend(new JS.Module('Kernel', {
854
+ /**
855
+ * JS.Kernel#__eigen__() -> JS.Module
856
+ *
857
+ * Returns the object's metamodule, analogous to calling `(class << self; self; end)`
858
+ * in Ruby. Ruby's metaclasses are `Class`es, not just `Module`s, but so far I've not found
859
+ * a compelling reason to enforce this. You cannot instantiate or subclass metaclasses
860
+ * in Ruby, they only really exist to store methods so a module will suffice.
861
+ **/
862
+ __eigen__: function() {
863
+ if (this.__meta__) return this.__meta__;
864
+
865
+ var me = this.__nom__,
866
+ klass = this.klass.__nom__,
867
+ name = me || (klass ? '#<' + klass + '>' : ''),
868
+ module = this.__meta__ = new JS.Module(name ? name + '.' : '', {}, {_resolve: this});
869
+
870
+ module.include(this.klass.__mod__, false);
871
+ return module;
872
+ },
873
+
874
+ /**
875
+ * JS.Kernel#equals(object) -> Boolean
876
+ * - object (Object): object to compare to the receiver
877
+ *
878
+ * Returns `true` iff `object` is the same object as the receiver. Override to provide a
879
+ * more meaningful comparison for use in sets, hashtables etc.
880
+ **/
881
+ equals: function(object) {
882
+ return this === object;
883
+ },
884
+
885
+ /**
886
+ * JS.Kernel#extend(module[, resolve = true]) -> undefined
887
+ * - module (JS.Module): module with which to extend the object
888
+ * - resolve (Boolean): whether to refresh method tables afterward
889
+ *
890
+ * Extends the object using the methods from `module`. If `module` is an instance of
891
+ * `JS.Module`, it becomes part of the object's inheritance chain and any methods added
892
+ * directly to the object will take precedence. Pass `false` as a second argument
893
+ * to prevent the method resolution process from firing.
894
+ **/
895
+ extend: function(module, resolve) {
896
+ return this.__eigen__().include(module, resolve, {_extended: this});
897
+ },
898
+
899
+ /**
900
+ * JS.Kernel#hash() -> String
901
+ *
902
+ * Returns a hexadecimal hashcode for the object for use in hashtables. By default,
903
+ * this is a random number guaranteed to be unique to the object. If you override
904
+ * this method, make sure that `a.equals(b)` implies `a.hash() === b.hash()`.
905
+ **/
906
+ hash: function() {
907
+ return this.__hashcode__ = this.__hashcode__ || JS.Kernel.getHashCode();
908
+ },
909
+
910
+ /**
911
+ * JS.Kernel#isA(type) -> Boolean
912
+ * - type (JS.Module): module or class to check the object's type against
913
+ *
914
+ * Returns `true` iff the object is an instance of `type` or one of its
915
+ * subclasses, or if the object's class includes the module `type`.
916
+ **/
917
+ isA: function(moduleOrClass) {
918
+ return this.__eigen__().includes(moduleOrClass);
919
+ },
920
+
921
+ /**
922
+ * JS.Kernel#method(name) -> Function
923
+ * - name (String): the name of the required method
924
+ *
925
+ * Returns the named method from the object as a bound function.
926
+ **/
927
+ method: function(name) {
928
+ var self = this,
929
+ cache = self.__mcache__ = self.__mcache__ || {};
930
+
931
+ if ((cache[name] || {}).fn === self[name]) return cache[name].bd;
932
+ return (cache[name] = {fn: self[name], bd: JS.bind(self[name], self)}).bd;
933
+ },
934
+
935
+ /**
936
+ * JS.Kernel#methods() -> Array
937
+ *
938
+ * Returns a list of all the method names defined on the object.
939
+ **/
940
+ methods: function() {
941
+ return this.__eigen__().instanceMethods(true);
942
+ },
943
+
944
+ /**
945
+ * JS.Kernel#tap(block[, context]) -> this
946
+ * - block (Function): block of code to execute
947
+ * - context (Object): sets the binding of `this` within `block`
948
+ *
949
+ * Executes the given function passing the object as a parameter, and returns the
950
+ * object rather than the result of the function. Designed to 'tap into' a method
951
+ * chain to inspect intermediate values. From the Ruby docs:
952
+ *
953
+ * list .tap(function(x) { console.log("original: ", x) })
954
+ * .toArray() .tap(function(x) { console.log("array: ", x) })
955
+ * .select(condition) .tap(function(x) { console.log("evens: ", x) })
956
+ * .map(square) .tap(function(x) { console.log("squares: ", x) })
957
+ **/
958
+ tap: function(block, context) {
959
+ block.call(context || null, this);
960
+ return this;
961
+ }
962
+ }),
963
+
964
+ {
965
+ __hashIndex__: 0,
966
+
967
+ getHashCode: function() {
968
+ this.__hashIndex__ += 1;
969
+ return (Math.floor(new Date().getTime() / 1000) + this.__hashIndex__).toString(16);
970
+ }
971
+ });
972
+
973
+ JS.Module.include(JS.Kernel);
974
+ JS.extend(JS.Module, JS.Kernel.__fns__);
975
+ JS.Class.include(JS.Kernel);
976
+ JS.extend(JS.Class, JS.Kernel.__fns__);
977
+
978
+
979
+ /** section: core
980
+ * class JS.Interface
981
+ *
982
+ * `Interface` is a class used to encapsulate sets of methods and check whether objects
983
+ * implement them. Think of interfaces as a means of duck-typing rather than as they are
984
+ * used in Java.
985
+ **/
986
+ JS.Interface = new JS.Class({
987
+ /**
988
+ * new JS.Interface(methods)
989
+ * - methods (Array): a list of method names
990
+ *
991
+ * An `Interface` is instantiated using a list of method names; these methods are the
992
+ * API the interface can be used to check for.
993
+ *
994
+ * var HistoryInterface = new JS.Interface([
995
+ * 'getInitialState',
996
+ * 'changeState'
997
+ * ]);
998
+ **/
999
+ initialize: function(methods) {
1000
+ this.test = function(object, returnName) {
1001
+ var n = methods.length;
1002
+ while (n--) {
1003
+ if (!JS.isFn(object[methods[n]]))
1004
+ return returnName ? methods[n] : false;
1005
+ }
1006
+ return true;
1007
+ };
1008
+ },
1009
+
1010
+ /**
1011
+ * JS.Interface#test(object[, returnName = false]) -> Boolean | String
1012
+ * - object (Object): object whose API we wish to check
1013
+ * - returnName (Boolean): if true, return the first name found to be missing
1014
+ *
1015
+ * Checks whether `object` implements the interface, returning `true` or `false`. If
1016
+ * the second argument is `true`, returns the name of the first method found to be
1017
+ * missing from the object's API.
1018
+ **/
1019
+
1020
+ extend: {
1021
+ /**
1022
+ * JS.Interface.ensure(object, iface1[, iface2]) -> undefined
1023
+ * - object (Object): object whose API we wish to check
1024
+ * - iface (JS.Interface): interface the object should implement
1025
+ *
1026
+ * Throws an `Error` unless `object` implements the required interface(s).
1027
+ **/
1028
+ ensure: function() {
1029
+ var args = JS.array(arguments), object = args.shift(), face, result;
1030
+ while (face = args.shift()) {
1031
+ result = face.test(object, true);
1032
+ if (result !== true) throw new Error('object does not implement ' + result + '()');
1033
+ }
1034
+ }
1035
+ }
1036
+ });
1037
+
1038
+
1039
+ /** section: core
1040
+ * class JS.Singleton
1041
+ *
1042
+ * `Singleton` is a class used to construct custom objects with all the inheritance features
1043
+ * of `JS.Class`, the methods from `JS.Kernel`, etc. It constructs an anonymous class from the
1044
+ * objects you provide and returns an instance of this class.
1045
+ **/
1046
+ JS.Singleton = new JS.Class({
1047
+ /**
1048
+ * new JS.Singleton(name, parent, methods)
1049
+ * - name (String): the name of the singleton, used for debugging
1050
+ * - parent (JS.Class): the parent class to inherit from
1051
+ * - methods (Object): list of methods for the singleton
1052
+ *
1053
+ * `Singleton`s are instantiated the same way as instances of `JS.Class`, the only difference
1054
+ * being that `Singleton` returns an instance of the newly created class, rather than the
1055
+ * class itself.
1056
+ **/
1057
+ initialize: function(name, parent, methods) {
1058
+ return new (new JS.Class(name, parent, methods));
1059
+ }
1060
+ });