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.
- data/Gemfile +49 -0
- data/Gemfile.lock +146 -0
- data/LICENSE +0 -0
- data/README +0 -0
- data/Rakefile +284 -0
- data/Readme.markdown +7 -0
- data/application/css/add_alias_dialogue.scss +27 -0
- data/application/css/definitions.scss +249 -0
- data/application/css/developer.scss +9 -0
- data/application/css/editing.scss +649 -0
- data/application/css/login.scss +91 -0
- data/application/css/min/54ee0ed3c7fac7632bd5c020d69e9a2503e0c88c.css +1 -0
- data/application/css/min/c256adc144e2bdd0b0539356b04eb62db01e1dc3.css +1 -0
- data/application/css/popover.scss +335 -0
- data/application/css/schema_error.scss +90 -0
- data/application/css/spontaneous.scss +111 -0
- data/application/css/unsupported.scss +16 -0
- data/application/css/v2.scss +1606 -0
- data/application/css/variables.scss +80 -0
- data/application/js/add_alias_dialogue.js +59 -0
- data/application/js/add_home_dialogue.js +59 -0
- data/application/js/ajax.js +99 -0
- data/application/js/authentication.js +22 -0
- data/application/js/box.js +104 -0
- data/application/js/box_container.js +82 -0
- data/application/js/compatibility.js +132 -0
- data/application/js/conflicted_field_dialogue.js +92 -0
- data/application/js/content.js +224 -0
- data/application/js/content_area.js +44 -0
- data/application/js/dialogue.js +196 -0
- data/application/js/dom.js +71 -0
- data/application/js/edit_dialogue.js +137 -0
- data/application/js/edit_panel.js +232 -0
- data/application/js/editing.js +42 -0
- data/application/js/entry.js +13 -0
- data/application/js/extensions.js +104 -0
- data/application/js/field.js +4 -0
- data/application/js/field_preview.js +55 -0
- data/application/js/field_types/date_field.js +16 -0
- data/application/js/field_types/file_field.js +71 -0
- data/application/js/field_types/image_field.js +358 -0
- data/application/js/field_types/markdown_field.js +656 -0
- data/application/js/field_types/string_field.js +185 -0
- data/application/js/image.js +72 -0
- data/application/js/init.js +34 -0
- data/application/js/load.js +4 -0
- data/application/js/location.js +157 -0
- data/application/js/login.js +53 -0
- data/application/js/min/492a209de8ee955fa9c729a765377495001e11b1.js +17 -0
- data/application/js/min/80f684d77c940887a1d4a63e3a96102e993baa98.js +88 -0
- data/application/js/min/b8abf302a824c35385ff517b34111e1710ff3b37.js +2 -0
- data/application/js/min/c7140ec9475e5bf868b901e0621338d7d162358b.js +3 -0
- data/application/js/min/f07f2bd6630ee31e1c2288ec223383d8f0658ba6.js +2 -0
- data/application/js/page.js +43 -0
- data/application/js/page_browser.js +147 -0
- data/application/js/page_entry.js +47 -0
- data/application/js/popover.js +99 -0
- data/application/js/popover_view.js +56 -0
- data/application/js/preview.js +64 -0
- data/application/js/progress.js +358 -0
- data/application/js/properties.js +90 -0
- data/application/js/publish.js +187 -0
- data/application/js/require.js +129 -0
- data/application/js/sharded_upload.js +206 -0
- data/application/js/side_bar.js +30 -0
- data/application/js/spontaneous.js +6 -0
- data/application/js/state.js +64 -0
- data/application/js/status_bar.js +47 -0
- data/application/js/top_bar.js +368 -0
- data/application/js/types.js +98 -0
- data/application/js/upload.js +88 -0
- data/application/js/upload_manager.js +319 -0
- data/application/js/user.js +37 -0
- data/application/js/vendor/.DS_Store +0 -0
- data/application/js/vendor/JS.Class-2.1.5/CHANGELOG +283 -0
- data/application/js/vendor/JS.Class-2.1.5/MIT-LICENSE +30 -0
- data/application/js/vendor/JS.Class-2.1.5/README +30 -0
- data/application/js/vendor/JS.Class-2.1.5/min/command.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/comparable.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/constant_scope.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/core.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/decorator.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/enumerable.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/forwardable.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/hash.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/linked_list.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/loader.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/method_chain.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/observable.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/package.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/proxy.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/ruby.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/set.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/stack_trace.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/state.js +1 -0
- data/application/js/vendor/JS.Class-2.1.5/min/stdlib.js +16 -0
- data/application/js/vendor/JS.Class-2.1.5/src/command.js +93 -0
- data/application/js/vendor/JS.Class-2.1.5/src/comparable.js +37 -0
- data/application/js/vendor/JS.Class-2.1.5/src/constant_scope.js +48 -0
- data/application/js/vendor/JS.Class-2.1.5/src/core.js +1060 -0
- data/application/js/vendor/JS.Class-2.1.5/src/decorator.js +50 -0
- data/application/js/vendor/JS.Class-2.1.5/src/enumerable.js +505 -0
- data/application/js/vendor/JS.Class-2.1.5/src/forwardable.js +22 -0
- data/application/js/vendor/JS.Class-2.1.5/src/hash.js +334 -0
- data/application/js/vendor/JS.Class-2.1.5/src/linked_list.js +114 -0
- data/application/js/vendor/JS.Class-2.1.5/src/loader.js +553 -0
- data/application/js/vendor/JS.Class-2.1.5/src/method_chain.js +172 -0
- data/application/js/vendor/JS.Class-2.1.5/src/observable.js +55 -0
- data/application/js/vendor/JS.Class-2.1.5/src/package.js +472 -0
- data/application/js/vendor/JS.Class-2.1.5/src/proxy.js +58 -0
- data/application/js/vendor/JS.Class-2.1.5/src/ruby.js +44 -0
- data/application/js/vendor/JS.Class-2.1.5/src/set.js +332 -0
- data/application/js/vendor/JS.Class-2.1.5/src/stack_trace.js +151 -0
- data/application/js/vendor/JS.Class-2.1.5/src/state.js +95 -0
- data/application/js/vendor/JS.Class-2.1.5/src/stdlib.js +2612 -0
- data/application/js/vendor/crypto-2.3.0-crypto.js +160 -0
- data/application/js/vendor/crypto-2.3.0-sha1.js +91 -0
- data/application/js/vendor/diff_match_patch.js +2153 -0
- data/application/js/vendor/jquery-1.4.2.min.js +154 -0
- data/application/js/vendor/jquery-1.4.3.min.js +166 -0
- data/application/js/vendor/jquery-1.5.1.min.js +16 -0
- data/application/js/vendor/jquery-1.5.1rc1.min.js +24 -0
- data/application/js/vendor/jquery-1.6.2.min.js +18 -0
- data/application/js/vendor/jquery-ui-1.8.6.custom.min.js +265 -0
- data/application/js/vendor/jquery-ui-1.8.9.custom.min.js +415 -0
- data/application/js/vendor/jquery-ui-1.8.custom.min.js +106 -0
- data/application/js/vendor/jquery.hotkeys-0.7.9.js +248 -0
- data/application/js/vendor/jquery.hotkeys-0.7.9.min.js +19 -0
- data/application/js/vendor/jsdiff.js +169 -0
- data/application/js/views/box_view.js +229 -0
- data/application/js/views/page_piece_view.js +45 -0
- data/application/js/views/page_view.js +238 -0
- data/application/js/views/piece_view.js +178 -0
- data/application/js/views.js +110 -0
- data/application/static/editing-0-noise.png +0 -0
- data/application/static/editing-1-noise.png +0 -0
- data/application/static/editing-texture-1.png +0 -0
- data/application/static/editing-texture.png +0 -0
- data/application/static/editing-toolbar-shadow-bottom.png +0 -0
- data/application/static/editing-toolbar-shadow-top.png +0 -0
- data/application/static/favicon.ico +0 -0
- data/application/static/inner-glow.png +0 -0
- data/application/static/item-buttons.png +0 -0
- data/application/static/location-arrow.png +0 -0
- data/application/static/logo-400px-transparent.png +0 -0
- data/application/static/missing.png +0 -0
- data/application/static/orange-down-arrow.png +0 -0
- data/application/static/page-browser-next.png +0 -0
- data/application/static/paper-texture-dark.png +0 -0
- data/application/static/px.gif +0 -0
- data/application/static/select-arrow-root.png +0 -0
- data/application/static/select-arrow.png +0 -0
- data/application/static/slot-down-arrow.png +0 -0
- data/application/static/splash.png +0 -0
- data/application/static/spontaneous.png +0 -0
- data/application/static/spot.png +0 -0
- data/application/static/spot.svg +40 -0
- data/application/static/texture.png +0 -0
- data/application/views/index.erubis +46 -0
- data/application/views/login.erubis +69 -0
- data/application/views/schema_modification_error.html.erb +61 -0
- data/application/views/unsupported.erubis +23 -0
- data/bin/limit-upload +5 -0
- data/bin/spot +10 -0
- data/bin/unlimit-upload +3 -0
- data/config/nginx.conf +60 -0
- data/db/migrations/20100610142136_init.rb +66 -0
- data/db/migrations/20101130104334_timestamps.rb +44 -0
- data/db/migrations/20101202113205_site_publishing_flags.rb +12 -0
- data/db/migrations/20101206124543_aliases.rb +16 -0
- data/db/migrations/20110201133550_visibility.rb +27 -0
- data/db/migrations/20110209152710_users_and_groups.rb +58 -0
- data/db/migrations/20110215133910_boxes.rb +25 -0
- data/db/migrations/20110521114145_remove_slots_and_entries.rb +21 -0
- data/db/migrations/20110604192145_rename_schema_id_columns.rb +22 -0
- data/db/migrations/20110805141925_rename_site_to_state.rb +11 -0
- data/lib/cutaneous/context_helper.rb +82 -0
- data/lib/cutaneous/first_pass_parser.rb +23 -0
- data/lib/cutaneous/first_pass_renderer.rb +18 -0
- data/lib/cutaneous/parser_core.rb +18 -0
- data/lib/cutaneous/preview_context.rb +31 -0
- data/lib/cutaneous/preview_renderer.rb +15 -0
- data/lib/cutaneous/publish_context.rb +9 -0
- data/lib/cutaneous/renderer.rb +122 -0
- data/lib/cutaneous/request_context.rb +8 -0
- data/lib/cutaneous/second_pass_parser.rb +23 -0
- data/lib/cutaneous/second_pass_renderer.rb +18 -0
- data/lib/cutaneous.rb +47 -0
- data/lib/sequel/plugins/content_table_inheritance.rb +196 -0
- data/lib/sequel/plugins/yajl_serialization.rb +154 -0
- data/lib/spontaneous/application/feature.rb +9 -0
- data/lib/spontaneous/application/plugin.rb +13 -0
- data/lib/spontaneous/application.rb +8 -0
- data/lib/spontaneous/box.rb +232 -0
- data/lib/spontaneous/box_style.rb +64 -0
- data/lib/spontaneous/change.rb +107 -0
- data/lib/spontaneous/cli/adapter.rb +13 -0
- data/lib/spontaneous/cli/base.rb +184 -0
- data/lib/spontaneous/cli/console.rb +0 -0
- data/lib/spontaneous/cli/media.rb +13 -0
- data/lib/spontaneous/cli/server.rb +50 -0
- data/lib/spontaneous/cli/site.rb +46 -0
- data/lib/spontaneous/cli/sync.rb +42 -0
- data/lib/spontaneous/cli/tasks.rb +9 -0
- data/lib/spontaneous/cli.rb +83 -0
- data/lib/spontaneous/collections/box_set.rb +56 -0
- data/lib/spontaneous/collections/change_set.rb +43 -0
- data/lib/spontaneous/collections/entry_set.rb +83 -0
- data/lib/spontaneous/collections/field_set.rb +53 -0
- data/lib/spontaneous/collections/prototype_set.rb +131 -0
- data/lib/spontaneous/collections/style_set.rb +13 -0
- data/lib/spontaneous/config.rb +156 -0
- data/lib/spontaneous/constants.rb +24 -0
- data/lib/spontaneous/content.rb +113 -0
- data/lib/spontaneous/content_query.rb +17 -0
- data/lib/spontaneous/errors.rb +48 -0
- data/lib/spontaneous/extensions/array.rb +18 -0
- data/lib/spontaneous/extensions/class.rb +17 -0
- data/lib/spontaneous/extensions/hash.rb +18 -0
- data/lib/spontaneous/extensions/json.rb +26 -0
- data/lib/spontaneous/extensions/kernel.rb +7 -0
- data/lib/spontaneous/extensions/object.rb +30 -0
- data/lib/spontaneous/extensions/object_space.rb +12 -0
- data/lib/spontaneous/extensions/string.rb +44 -0
- data/lib/spontaneous/facet.rb +47 -0
- data/lib/spontaneous/field_types/date_field.rb +12 -0
- data/lib/spontaneous/field_types/field.rb +252 -0
- data/lib/spontaneous/field_types/image_field.rb +329 -0
- data/lib/spontaneous/field_types/markdown_field.rb +37 -0
- data/lib/spontaneous/field_types/string_field.rb +14 -0
- data/lib/spontaneous/field_types.rb +40 -0
- data/lib/spontaneous/generators/page/inline.html.cut +1 -0
- data/lib/spontaneous/generators/page/page.html.cut.tt +4 -0
- data/lib/spontaneous/generators/page/page.rb.tt +9 -0
- data/lib/spontaneous/generators/page.rb +38 -0
- data/lib/spontaneous/generators/site/.gitignore +4 -0
- data/lib/spontaneous/generators/site/Gemfile.tt +31 -0
- data/lib/spontaneous/generators/site/Rakefile.tt +6 -0
- data/lib/spontaneous/generators/site/config/back.ru +7 -0
- data/lib/spontaneous/generators/site/config/boot.rb +19 -0
- data/lib/spontaneous/generators/site/config/database.yml.tt +21 -0
- data/lib/spontaneous/generators/site/config/deploy.rb.tt +0 -0
- data/lib/spontaneous/generators/site/config/environment.rb.tt +8 -0
- data/lib/spontaneous/generators/site/config/environments/development.rb.tt +15 -0
- data/lib/spontaneous/generators/site/config/environments/production.rb.tt +5 -0
- data/lib/spontaneous/generators/site/config/front.ru +8 -0
- data/lib/spontaneous/generators/site/config/user_levels.yml +22 -0
- data/lib/spontaneous/generators/site/lib/site.rb.tt +4 -0
- data/lib/spontaneous/generators/site/lib/tasks/site.rake.tt +8 -0
- data/lib/spontaneous/generators/site/public/css/site.css +0 -0
- data/lib/spontaneous/generators/site/public/favicon.ico +0 -0
- data/lib/spontaneous/generators/site/public/js/.empty_directory +0 -0
- data/lib/spontaneous/generators/site/public/js/site.js +0 -0
- data/lib/spontaneous/generators/site/public/robots.txt +0 -0
- data/lib/spontaneous/generators/site/schema/.map +1 -0
- data/lib/spontaneous/generators/site/schema/page.rb.tt +8 -0
- data/lib/spontaneous/generators/site/schema/piece.rb.tt +4 -0
- data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +13 -0
- data/lib/spontaneous/generators/site.rb +77 -0
- data/lib/spontaneous/generators.rb +23 -0
- data/lib/spontaneous/image_size.rb +117 -0
- data/lib/spontaneous/json.rb +33 -0
- data/lib/spontaneous/layout.rb +15 -0
- data/lib/spontaneous/loader.rb +280 -0
- data/lib/spontaneous/logger.rb +369 -0
- data/lib/spontaneous/media.rb +84 -0
- data/lib/spontaneous/page.rb +92 -0
- data/lib/spontaneous/page_controller.rb +18 -0
- data/lib/spontaneous/page_piece.rb +77 -0
- data/lib/spontaneous/paths.rb +30 -0
- data/lib/spontaneous/permissions/access_group.rb +50 -0
- data/lib/spontaneous/permissions/access_key.rb +35 -0
- data/lib/spontaneous/permissions/user.rb +167 -0
- data/lib/spontaneous/permissions/user_level.rb +177 -0
- data/lib/spontaneous/permissions.rb +55 -0
- data/lib/spontaneous/piece.rb +30 -0
- data/lib/spontaneous/plugins/aliases.rb +128 -0
- data/lib/spontaneous/plugins/allowed_types.rb +173 -0
- data/lib/spontaneous/plugins/application/facets.rb +25 -0
- data/lib/spontaneous/plugins/application/paths.rb +137 -0
- data/lib/spontaneous/plugins/application/render.rb +29 -0
- data/lib/spontaneous/plugins/application/serialisation.rb +16 -0
- data/lib/spontaneous/plugins/application/state.rb +86 -0
- data/lib/spontaneous/plugins/boxes.rb +84 -0
- data/lib/spontaneous/plugins/controllers.rb +52 -0
- data/lib/spontaneous/plugins/entries.rb +193 -0
- data/lib/spontaneous/plugins/entry.rb +51 -0
- data/lib/spontaneous/plugins/fields.rb +103 -0
- data/lib/spontaneous/plugins/instance_code.rb +18 -0
- data/lib/spontaneous/plugins/layouts.rb +87 -0
- data/lib/spontaneous/plugins/media.rb +41 -0
- data/lib/spontaneous/plugins/page/formats.rb +67 -0
- data/lib/spontaneous/plugins/page/request.rb +89 -0
- data/lib/spontaneous/plugins/page_search.rb +64 -0
- data/lib/spontaneous/plugins/page_tree.rb +25 -0
- data/lib/spontaneous/plugins/paths.rb +125 -0
- data/lib/spontaneous/plugins/permissions.rb +63 -0
- data/lib/spontaneous/plugins/prototypes.rb +84 -0
- data/lib/spontaneous/plugins/publishing.rb +255 -0
- data/lib/spontaneous/plugins/render.rb +24 -0
- data/lib/spontaneous/plugins/schema_hierarchy.rb +76 -0
- data/lib/spontaneous/plugins/schema_id.rb +60 -0
- data/lib/spontaneous/plugins/schema_title.rb +33 -0
- data/lib/spontaneous/plugins/serialisation.rb +67 -0
- data/lib/spontaneous/plugins/site/instance.rb +22 -0
- data/lib/spontaneous/plugins/site/map.rb +19 -0
- data/lib/spontaneous/plugins/site/publishing.rb +74 -0
- data/lib/spontaneous/plugins/site/revisions.rb +28 -0
- data/lib/spontaneous/plugins/site/selectors.rb +41 -0
- data/lib/spontaneous/plugins/site_map.rb +34 -0
- data/lib/spontaneous/plugins/styles.rb +119 -0
- data/lib/spontaneous/plugins/supertype.rb +11 -0
- data/lib/spontaneous/plugins/visibility.rb +151 -0
- data/lib/spontaneous/plugins.rb +20 -0
- data/lib/spontaneous/prototypes/box_prototype.rb +168 -0
- data/lib/spontaneous/prototypes/field_prototype.rb +112 -0
- data/lib/spontaneous/prototypes/layout_prototype.rb +17 -0
- data/lib/spontaneous/prototypes/style_prototype.rb +42 -0
- data/lib/spontaneous/proxy_object.rb +12 -0
- data/lib/spontaneous/publishing/fire_and_forget.rb +57 -0
- data/lib/spontaneous/publishing/immediate.rb +197 -0
- data/lib/spontaneous/publishing/threaded.rb +25 -0
- data/lib/spontaneous/publishing.rb +10 -0
- data/lib/spontaneous/rack/around_back.rb +44 -0
- data/lib/spontaneous/rack/around_front.rb +29 -0
- data/lib/spontaneous/rack/around_preview.rb +26 -0
- data/lib/spontaneous/rack/assets.rb +98 -0
- data/lib/spontaneous/rack/back.rb +729 -0
- data/lib/spontaneous/rack/front.rb +41 -0
- data/lib/spontaneous/rack/http.rb +18 -0
- data/lib/spontaneous/rack/media.rb +29 -0
- data/lib/spontaneous/rack/public.rb +232 -0
- data/lib/spontaneous/rack/reloader.rb +42 -0
- data/lib/spontaneous/rack/static.rb +25 -0
- data/lib/spontaneous/rack.rb +55 -0
- data/lib/spontaneous/render/context.rb +100 -0
- data/lib/spontaneous/render/development_renderer.rb +14 -0
- data/lib/spontaneous/render/engine.rb +19 -0
- data/lib/spontaneous/render/format/html.rb +5 -0
- data/lib/spontaneous/render/format.rb +70 -0
- data/lib/spontaneous/render/preview_renderer.rb +18 -0
- data/lib/spontaneous/render/published_renderer.rb +54 -0
- data/lib/spontaneous/render/publishing_renderer.rb +13 -0
- data/lib/spontaneous/render/renderer.rb +46 -0
- data/lib/spontaneous/render.rb +173 -0
- data/lib/spontaneous/revision.rb +7 -0
- data/lib/spontaneous/schema/schema_modification.rb +260 -0
- data/lib/spontaneous/schema/uid.rb +221 -0
- data/lib/spontaneous/schema.rb +295 -0
- data/lib/spontaneous/server.rb +65 -0
- data/lib/spontaneous/site.rb +87 -0
- data/lib/spontaneous/state.rb +53 -0
- data/lib/spontaneous/style.rb +144 -0
- data/lib/spontaneous/tasks/database.rake +9 -0
- data/lib/spontaneous/tasks.rb +5 -0
- data/lib/spontaneous/version.rb +6 -0
- data/lib/spontaneous.rb +179 -0
- data/spontaneous.gemspec.tmpl +66 -0
- data/test/disabled/test_slots.rb +287 -0
- data/test/experimental/test_formats.rb +92 -0
- data/test/experimental/test_plugins.rb +64 -0
- data/test/fixtures/application/css/test.less +5 -0
- data/test/fixtures/application/js/test.js +1 -0
- data/test/fixtures/application/static/favicon.ico +1 -0
- data/test/fixtures/application/static/test.html +1 -0
- data/test/fixtures/application/views/index.erubis +1 -0
- data/test/fixtures/back/public/test.html +1 -0
- data/test/fixtures/back/templates/layouts/standard.html.cut +1 -0
- data/test/fixtures/config/config/environment.rb +4 -0
- data/test/fixtures/config/config/environments/development.rb +13 -0
- data/test/fixtures/config/config/environments/production.rb +22 -0
- data/test/fixtures/config/config/environments/staging.rb +2 -0
- data/test/fixtures/example_application/Gemfile +6 -0
- data/test/fixtures/example_application/Gemfile.lock +76 -0
- data/test/fixtures/example_application/Rakefile +6 -0
- data/test/fixtures/example_application/config/back.rb +15 -0
- data/test/fixtures/example_application/config/back.ru +8 -0
- data/test/fixtures/example_application/config/back.yml +8 -0
- data/test/fixtures/example_application/config/boot.rb +16 -0
- data/test/fixtures/example_application/config/database.yml +24 -0
- data/test/fixtures/example_application/config/environment.rb +4 -0
- data/test/fixtures/example_application/config/environments/development.rb +16 -0
- data/test/fixtures/example_application/config/environments/production.rb +21 -0
- data/test/fixtures/example_application/config/environments/staging.rb +1 -0
- data/test/fixtures/example_application/config/front.rb +8 -0
- data/test/fixtures/example_application/config/front.ru +8 -0
- data/test/fixtures/example_application/config/front.yml +8 -0
- data/test/fixtures/example_application/config/schema.yml +48 -0
- data/test/fixtures/example_application/config/unicorn.rb +1 -0
- data/test/fixtures/example_application/config/user_levels.yml +19 -0
- data/test/fixtures/example_application/public/css/test.css +0 -0
- data/test/fixtures/example_application/public/favicon.ico +1 -0
- data/test/fixtures/example_application/public/js/test.js +0 -0
- data/test/fixtures/example_application/public/test.html +1 -0
- data/test/fixtures/example_application/schema/client_project.rb +18 -0
- data/test/fixtures/example_application/schema/client_projects.rb +8 -0
- data/test/fixtures/example_application/schema/home_page.rb +22 -0
- data/test/fixtures/example_application/schema/info_page.rb +13 -0
- data/test/fixtures/example_application/schema/inline_image.rb +11 -0
- data/test/fixtures/example_application/schema/page.rb +4 -0
- data/test/fixtures/example_application/schema/piece.rb +3 -0
- data/test/fixtures/example_application/schema/project.rb +21 -0
- data/test/fixtures/example_application/schema/project_image.rb +18 -0
- data/test/fixtures/example_application/schema/projects_page.rb +12 -0
- data/test/fixtures/example_application/schema/text.rb +8 -0
- data/test/fixtures/example_application/templates/client_project/images.html.cut +1 -0
- data/test/fixtures/example_application/templates/client_project.html.cut +4 -0
- data/test/fixtures/example_application/templates/client_projects.html.cut +6 -0
- data/test/fixtures/example_application/templates/info_page/inline.html.cut +0 -0
- data/test/fixtures/example_application/templates/inline_image.html.cut +1 -0
- data/test/fixtures/example_application/templates/layouts/home.html.cut +15 -0
- data/test/fixtures/example_application/templates/layouts/info.html.cut +3 -0
- data/test/fixtures/example_application/templates/layouts/project.html.cut +13 -0
- data/test/fixtures/example_application/templates/layouts/projects.html.cut +11 -0
- data/test/fixtures/example_application/templates/layouts/standard.html.cut +0 -0
- data/test/fixtures/example_application/templates/project/inline.html.cut +5 -0
- data/test/fixtures/example_application/templates/project.html.cut +5 -0
- data/test/fixtures/example_application/templates/project_image.html.cut +1 -0
- data/test/fixtures/example_application/templates/text.html.cut +1 -0
- data/test/fixtures/images/rose.greyscale.jpg +0 -0
- data/test/fixtures/images/rose.jpg +0 -0
- data/test/fixtures/images/size.gif +0 -0
- data/test/fixtures/images/size.jpg +0 -0
- data/test/fixtures/images/size.png24 +0 -0
- data/test/fixtures/images/size.png8 +0 -0
- data/test/fixtures/layouts/layouts/custom1.html.cut +1 -0
- data/test/fixtures/layouts/layouts/custom1.pdf.cut +0 -0
- data/test/fixtures/layouts/layouts/custom1.xml.cut +0 -0
- data/test/fixtures/layouts/layouts/custom2.html.cut +1 -0
- data/test/fixtures/layouts/layouts/custom3.html.cut +0 -0
- data/test/fixtures/layouts/layouts/standard.html.cut +1 -0
- data/test/fixtures/media/101/003/rose.jpg +0 -0
- data/test/fixtures/permissions/config/user_levels.yml +9 -0
- data/test/fixtures/permissions/media/image.jpg +0 -0
- data/test/fixtures/plugins/schema_plugin/init.rb +1 -0
- data/test/fixtures/plugins/schema_plugin/schema/external.rb +5 -0
- data/test/fixtures/plugins/schema_plugin/templates/external.html.cut +1 -0
- data/test/fixtures/plugins/schema_plugin/templates/from_plugin.html.cut +0 -0
- data/test/fixtures/plugins/schema_plugin/templates/layouts/from_plugin.html.cut +0 -0
- data/test/fixtures/public/templates/layouts/default.html.cut +1 -0
- data/test/fixtures/public/templates/layouts/default.pdf.cut +1 -0
- data/test/fixtures/public/templates/layouts/default.rss.cut +1 -0
- data/test/fixtures/public/templates/layouts/dynamic.html.cut +1 -0
- data/test/fixtures/public/templates/layouts/standard.html.cut +0 -0
- data/test/fixtures/schema/before.yml +24 -0
- data/test/fixtures/schema/resolvable.yml +12 -0
- data/test/fixtures/schema/schema.yml +7 -0
- data/test/fixtures/serialisation/class_hash.yaml.erb +53 -0
- data/test/fixtures/serialisation/root_hash.yaml.erb +184 -0
- data/test/fixtures/sharding/rose.jpg +0 -0
- data/test/fixtures/sharding/xaa +0 -0
- data/test/fixtures/sharding/xab +0 -0
- data/test/fixtures/sharding/xac +0 -0
- data/test/fixtures/sharding/xad +0 -0
- data/test/fixtures/sharding/xae +0 -0
- data/test/fixtures/sharding/xaf +0 -0
- data/test/fixtures/sharding/xag +0 -0
- data/test/fixtures/styles/box_a/runny.html.cut +0 -0
- data/test/fixtures/styles/box_a.html.cut +1 -0
- data/test/fixtures/styles/named2.html.cut +1 -0
- data/test/fixtures/styles/orange/apple.html.cut +1 -0
- data/test/fixtures/styles/template_class/named1.html.cut +1 -0
- data/test/fixtures/styles/template_class/results.html.cut +1 -0
- data/test/fixtures/styles/template_class/walky.html.cut +0 -0
- data/test/fixtures/styles/template_class.epub.cut +0 -0
- data/test/fixtures/styles/template_class.html.cut +1 -0
- data/test/fixtures/styles/template_class.pdf.cut +0 -0
- data/test/fixtures/styles/template_sub_class1.html.cut +1 -0
- data/test/fixtures/templates/aliases/a/a_style.html.cut +0 -0
- data/test/fixtures/templates/aliases/a/page.html.cut +0 -0
- data/test/fixtures/templates/aliases/a_alias/a_alias_style.html.cut +0 -0
- data/test/fixtures/templates/aliases/layouts/b.html.cut +1 -0
- data/test/fixtures/templates/aliases/layouts/b_alias.html.cut +1 -0
- data/test/fixtures/templates/aliases/layouts/c_alias.html.cut +1 -0
- data/test/fixtures/templates/boxes/blank_content/things.html.cut +1 -0
- data/test/fixtures/templates/boxes/my_box_class/christy.html.cut +1 -0
- data/test/fixtures/templates/boxes/thangs.html.cut +1 -0
- data/test/fixtures/templates/boxes/with_template_box.html.cut +1 -0
- data/test/fixtures/templates/content/include.html.cut +1 -0
- data/test/fixtures/templates/content/include_dir.html.cut +1 -0
- data/test/fixtures/templates/content/included.epub.cut +1 -0
- data/test/fixtures/templates/content/included.html.cut +1 -0
- data/test/fixtures/templates/content/partial/included.html.cut +1 -0
- data/test/fixtures/templates/content/preprocess.html.cut +1 -0
- data/test/fixtures/templates/content/second.html.cut +1 -0
- data/test/fixtures/templates/content/template.epub.cut +1 -0
- data/test/fixtures/templates/content/template.html.cut +1 -0
- data/test/fixtures/templates/default_style_class.html.cut +1 -0
- data/test/fixtures/templates/direct.html.cut +1 -0
- data/test/fixtures/templates/extended/grandparent.html.cut +10 -0
- data/test/fixtures/templates/extended/main.html.cut +6 -0
- data/test/fixtures/templates/extended/parent.html.cut +10 -0
- data/test/fixtures/templates/layouts/entries.html.cut +7 -0
- data/test/fixtures/templates/layouts/page_style.html.cut +1 -0
- data/test/fixtures/templates/layouts/params.html.cut +1 -0
- data/test/fixtures/templates/layouts/preview_render.html.cut +2 -0
- data/test/fixtures/templates/layouts/standard_page.html.cut +1 -0
- data/test/fixtures/templates/layouts/subdir_style.html.cut +1 -0
- data/test/fixtures/templates/layouts/template_params.html.cut +1 -0
- data/test/fixtures/templates/page_class/inline_style.html.cut +1 -0
- data/test/fixtures/templates/preview_render/inline.html.cut +0 -0
- data/test/fixtures/templates/publishing/layouts/dynamic.html.cut +1 -0
- data/test/fixtures/templates/publishing/layouts/static.html.cut +1 -0
- data/test/fixtures/templates/template_class/anonymous_style.html.cut +4 -0
- data/test/fixtures/templates/template_class/another_template.html.cut +0 -0
- data/test/fixtures/templates/template_class/complex_template.html.cut +6 -0
- data/test/fixtures/templates/template_class/complex_template.pdf.cut +6 -0
- data/test/fixtures/templates/template_class/default_template_style.html.cut +4 -0
- data/test/fixtures/templates/template_class/images_with_template.html.cut +5 -0
- data/test/fixtures/templates/template_class/slots_template.html.cut +5 -0
- data/test/fixtures/templates/template_class/slots_template.pdf.cut +5 -0
- data/test/fixtures/templates/template_class/this_template.epub.cut +1 -0
- data/test/fixtures/templates/template_class/this_template.html.cut +1 -0
- data/test/fixtures/templates/template_class/this_template.pdf.cut +1 -0
- data/test/fixtures/templates/with_default_style_class.html.cut +1 -0
- data/test/functional/test_application.rb +176 -0
- data/test/functional/test_back.rb +902 -0
- data/test/functional/test_front.rb +571 -0
- data/test/javascript/test_dom.rb +94 -0
- data/test/javascript/test_markdown.rb +97 -0
- data/test/slow/test_publishing.rb +987 -0
- data/test/slow/test_visibility.rb +250 -0
- data/test/support/custom_matchers.rb +77 -0
- data/test/support/timing.rb +23 -0
- data/test/test_helper.rb +164 -0
- data/test/test_javascript.rb +34 -0
- data/test/ui/test_page_editing.rb +167 -0
- data/test/ui_helper.rb +114 -0
- data/test/unit/test_alias.rb +254 -0
- data/test/unit/test_authentication.rb +510 -0
- data/test/unit/test_boxes.rb +497 -0
- data/test/unit/test_config.rb +156 -0
- data/test/unit/test_content.rb +221 -0
- data/test/unit/test_content_inheritance.rb +103 -0
- data/test/unit/test_extensions.rb +14 -0
- data/test/unit/test_fields.rb +392 -0
- data/test/unit/test_generators.rb +97 -0
- data/test/unit/test_image_size.rb +25 -0
- data/test/unit/test_images.rb +265 -0
- data/test/unit/test_layouts.rb +111 -0
- data/test/unit/test_logger.rb +80 -0
- data/test/unit/test_media.rb +70 -0
- data/test/unit/test_page.rb +244 -0
- data/test/unit/test_permissions.rb +834 -0
- data/test/unit/test_piece.rb +80 -0
- data/test/unit/test_prototype_set.rb +192 -0
- data/test/unit/test_prototypes.rb +102 -0
- data/test/unit/test_render.rb +359 -0
- data/test/unit/test_schema.rb +1009 -0
- data/test/unit/test_serialisation.rb +215 -0
- data/test/unit/test_site.rb +145 -0
- data/test/unit/test_structure.rb +85 -0
- data/test/unit/test_styles.rb +417 -0
- data/test/unit/test_templates.rb +224 -0
- data/test/unit/test_type_hierarchy.rb +28 -0
- 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
|
+
});
|