spontaneous 0.1.0.alpha1

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