From 71db4320486acbe2a9eabffaffbe1973674524ea Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Thu, 21 Jun 2012 20:46:01 -0400 Subject: [PATCH] imported futon2 as couchapp work Originally this work was tangled up with the rest of CouchDB at http://github.com/BigBlueHat/couchdb I have untangled it for faster iteration. --- .gitignore | 1 + README | 34 + _attachments/dialog/_admin_party.html | 33 + _attachments/dialog/_change_password.html | 31 + _attachments/dialog/_compact_cleanup.html | 51 + _attachments/dialog/_create_admin.html | 50 + _attachments/dialog/_create_config.html | 42 + _attachments/dialog/_database_security.html | 50 + _attachments/dialog/_delete_database.html | 27 + _attachments/dialog/_delete_document.html | 26 + _attachments/dialog/_login.html | 34 + _attachments/dialog/_save_view_as.html | 35 + _attachments/dialog/_share_test_reports.html | 42 + _attachments/dialog/_signup.html | 35 + _attachments/dialog/_upload_attachment.html | 36 + _attachments/favicon.ico | Bin 0 -> 1406 bytes _attachments/favicon.png | Bin 0 -> 1406 bytes _attachments/image/add.png | Bin 0 -> 709 bytes _attachments/image/apply.gif | Bin 0 -> 652 bytes _attachments/image/bg.png | Bin 0 -> 372 bytes _attachments/image/cancel.gif | Bin 0 -> 659 bytes _attachments/image/compact.png | Bin 0 -> 28735 bytes _attachments/image/delete-mini.png | Bin 0 -> 418 bytes _attachments/image/delete.png | Bin 0 -> 718 bytes _attachments/image/down.png | Bin 0 -> 1337 bytes _attachments/image/grippie.gif | Bin 0 -> 75 bytes _attachments/image/hgrad.gif | Bin 0 -> 118 bytes _attachments/image/key.png | Bin 0 -> 859 bytes _attachments/image/load.png | Bin 0 -> 780 bytes _attachments/image/logo.png | Bin 0 -> 3010 bytes _attachments/image/order-asc.gif | Bin 0 -> 195 bytes _attachments/image/order-desc.gif | Bin 0 -> 187 bytes _attachments/image/path.gif | Bin 0 -> 104 bytes _attachments/image/progress.gif | Bin 0 -> 10819 bytes _attachments/image/rarrow.png | Bin 0 -> 27721 bytes _attachments/image/run-mini.png | Bin 0 -> 478 bytes _attachments/image/run.png | Bin 0 -> 718 bytes _attachments/image/running.png | Bin 0 -> 284 bytes _attachments/image/save.png | Bin 0 -> 843 bytes _attachments/image/sidebar-toggle.png | Bin 0 -> 512 bytes _attachments/image/spinner.gif | Bin 0 -> 1849 bytes _attachments/image/test_failure.gif | Bin 0 -> 114 bytes _attachments/image/test_success.gif | Bin 0 -> 185 bytes _attachments/image/thead-key.gif | Bin 0 -> 77 bytes _attachments/image/thead.gif | Bin 0 -> 51 bytes _attachments/image/toggle-collapse.gif | Bin 0 -> 176 bytes _attachments/image/toggle-expand.gif | Bin 0 -> 181 bytes _attachments/image/twisty.gif | Bin 0 -> 160 bytes _attachments/index.html | 346 + _attachments/old.layout.css | 618 ++ _attachments/script/base64.js | 124 + _attachments/script/couch.js | 473 ++ _attachments/script/couch_test_runner.js | 443 ++ _attachments/script/couch_tests.js | 135 + _attachments/script/futon-dialogs.js | 286 + _attachments/script/futon.js | 849 +++ .../script/jquery-ui-1.8.11.custom.min.js | 81 + _attachments/script/jquery.couch.js | 668 ++ _attachments/script/jquery.js | 6240 +++++++++++++++++ _attachments/script/json2.js | 482 ++ _attachments/script/jspec/jspec.css | 149 + _attachments/script/jspec/jspec.jquery.js | 72 + _attachments/script/jspec/jspec.js | 1756 +++++ _attachments/script/jspec/jspec.xhr.js | 195 + _attachments/script/oauth.js | 511 ++ _attachments/script/old-futon.js | 517 ++ _attachments/script/sammy/LICENSE | 22 + .../script/sammy/plugins/sammy.cache.js | 115 + .../plugins/sammy.data_location_proxy.js | 78 + .../script/sammy/plugins/sammy.ejs.js | 700 ++ .../script/sammy/plugins/sammy.form.js | 274 + .../script/sammy/plugins/sammy.haml.js | 553 ++ .../script/sammy/plugins/sammy.json.js | 362 + .../script/sammy/plugins/sammy.meld.js | 140 + .../script/sammy/plugins/sammy.mustache.js | 444 ++ .../sammy/plugins/sammy.nested_params.js | 118 + .../plugins/sammy.path_location_proxy.js | 29 + .../script/sammy/plugins/sammy.pure.js | 757 ++ .../script/sammy/plugins/sammy.storage.js | 577 ++ .../script/sammy/plugins/sammy.template.js | 117 + .../script/sammy/plugins/sammy.title.js | 59 + _attachments/script/sammy/sammy.js | 1671 +++++ _attachments/script/sha1.js | 202 + _attachments/script/test/all_docs.js | 95 + _attachments/script/test/attachment_names.js | 87 + _attachments/script/test/attachment_paths.js | 153 + _attachments/script/test/attachment_ranges.js | 134 + _attachments/script/test/attachment_views.js | 98 + _attachments/script/test/attachments.js | 275 + .../script/test/attachments_multipart.js | 282 + _attachments/script/test/auth_cache.js | 249 + _attachments/script/test/basics.js | 250 + _attachments/script/test/batch_save.js | 48 + _attachments/script/test/bulk_docs.js | 100 + _attachments/script/test/changes.js | 465 ++ _attachments/script/test/compact.js | 59 + _attachments/script/test/config.js | 163 + _attachments/script/test/conflicts.js | 64 + .../script/test/content_negotiation.js | 39 + _attachments/script/test/cookie_auth.js | 256 + _attachments/script/test/copy_doc.js | 51 + _attachments/script/test/delayed_commits.js | 154 + _attachments/script/test/design_docs.js | 317 + _attachments/script/test/design_options.js | 74 + _attachments/script/test/design_paths.js | 72 + _attachments/script/test/erlang_views.js | 133 + _attachments/script/test/etags_head.js | 78 + _attachments/script/test/etags_views.js | 212 + _attachments/script/test/form_submit.js | 26 + _attachments/script/test/http.js | 54 + _attachments/script/test/invalid_docids.js | 77 + _attachments/script/test/jsonp.js | 82 + _attachments/script/test/large_docs.js | 33 + _attachments/script/test/list_views.js | 450 ++ _attachments/script/test/lorem.txt | 103 + _attachments/script/test/lorem_b64.txt | 1 + _attachments/script/test/lots_of_docs.js | 55 + _attachments/script/test/method_override.js | 40 + _attachments/script/test/multiple_rows.js | 80 + _attachments/script/test/oauth.js | 267 + _attachments/script/test/proxyauth.js | 130 + _attachments/script/test/purge.js | 105 + _attachments/script/test/reader_acl.js | 198 + _attachments/script/test/recreate_doc.js | 80 + _attachments/script/test/reduce.js | 170 + _attachments/script/test/reduce_builtin.js | 179 + _attachments/script/test/reduce_false.js | 44 + _attachments/script/test/reduce_false_temp.js | 37 + _attachments/script/test/replication.js | 731 ++ _attachments/script/test/replicator_db.js | 715 ++ _attachments/script/test/rev_stemming.js | 99 + _attachments/script/test/rewrite.js | 443 ++ .../script/test/security_validation.js | 336 + _attachments/script/test/show_documents.js | 436 ++ _attachments/script/test/stats.js | 330 + _attachments/script/test/update_documents.js | 168 + _attachments/script/test/users_db.js | 103 + _attachments/script/test/utf8.js | 41 + _attachments/script/test/uuids.js | 120 + _attachments/script/test/view_collation.js | 116 + .../script/test/view_collation_raw.js | 123 + _attachments/script/test/view_compaction.js | 104 + _attachments/script/test/view_conflicts.js | 49 + _attachments/script/test/view_errors.js | 189 + _attachments/script/test/view_include_docs.js | 138 + .../script/test/view_multi_key_all_docs.js | 91 + .../script/test/view_multi_key_design.js | 216 + .../script/test/view_multi_key_temp.js | 37 + _attachments/script/test/view_offsets.js | 108 + _attachments/script/test/view_pagination.js | 122 + _attachments/script/test/view_sandboxing.js | 52 + _attachments/script/test/view_update_seq.js | 106 + _attachments/script/test/view_xml.js | 39 + _attachments/session.html | 107 + .../spec/couch_js_class_methods_spec.js | 401 ++ .../spec/couch_js_instance_methods_1_spec.js | 311 + .../spec/couch_js_instance_methods_2_spec.js | 246 + .../spec/couch_js_instance_methods_3_spec.js | 215 + _attachments/spec/custom_helpers.js | 51 + .../jquery_couch_js_class_methods_spec.js | 523 ++ ...jquery_couch_js_instance_methods_1_spec.js | 202 + ...jquery_couch_js_instance_methods_2_spec.js | 433 ++ ...jquery_couch_js_instance_methods_3_spec.js | 540 ++ _attachments/spec/run.html | 46 + _attachments/style/layout.css | 1089 +++ .../ui-lightness/jquery-ui-1.8.11.custom.css | 347 + _attachments/templates/changes.mustache | 39 + _attachments/templates/config.mustache | 124 + _attachments/templates/database.mustache | 65 + _attachments/templates/document.mustache | 533 ++ _attachments/templates/index.mustache | 94 + _attachments/templates/replicator.mustache | 158 + _attachments/templates/stats.mustache | 94 + _attachments/templates/tests.mustache | 83 + _attachments/templates/view.mustache | 140 + _id | 1 + 176 files changed, 37663 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 _attachments/dialog/_admin_party.html create mode 100644 _attachments/dialog/_change_password.html create mode 100644 _attachments/dialog/_compact_cleanup.html create mode 100644 _attachments/dialog/_create_admin.html create mode 100644 _attachments/dialog/_create_config.html create mode 100644 _attachments/dialog/_database_security.html create mode 100644 _attachments/dialog/_delete_database.html create mode 100644 _attachments/dialog/_delete_document.html create mode 100644 _attachments/dialog/_login.html create mode 100644 _attachments/dialog/_save_view_as.html create mode 100644 _attachments/dialog/_share_test_reports.html create mode 100644 _attachments/dialog/_signup.html create mode 100644 _attachments/dialog/_upload_attachment.html create mode 100644 _attachments/favicon.ico create mode 100644 _attachments/favicon.png create mode 100644 _attachments/image/add.png create mode 100644 _attachments/image/apply.gif create mode 100644 _attachments/image/bg.png create mode 100644 _attachments/image/cancel.gif create mode 100644 _attachments/image/compact.png create mode 100644 _attachments/image/delete-mini.png create mode 100644 _attachments/image/delete.png create mode 100644 _attachments/image/down.png create mode 100644 _attachments/image/grippie.gif create mode 100644 _attachments/image/hgrad.gif create mode 100644 _attachments/image/key.png create mode 100644 _attachments/image/load.png create mode 100644 _attachments/image/logo.png create mode 100644 _attachments/image/order-asc.gif create mode 100644 _attachments/image/order-desc.gif create mode 100644 _attachments/image/path.gif create mode 100644 _attachments/image/progress.gif create mode 100644 _attachments/image/rarrow.png create mode 100644 _attachments/image/run-mini.png create mode 100644 _attachments/image/run.png create mode 100644 _attachments/image/running.png create mode 100644 _attachments/image/save.png create mode 100644 _attachments/image/sidebar-toggle.png create mode 100644 _attachments/image/spinner.gif create mode 100644 _attachments/image/test_failure.gif create mode 100644 _attachments/image/test_success.gif create mode 100644 _attachments/image/thead-key.gif create mode 100644 _attachments/image/thead.gif create mode 100644 _attachments/image/toggle-collapse.gif create mode 100644 _attachments/image/toggle-expand.gif create mode 100644 _attachments/image/twisty.gif create mode 100644 _attachments/index.html create mode 100644 _attachments/old.layout.css create mode 100644 _attachments/script/base64.js create mode 100644 _attachments/script/couch.js create mode 100644 _attachments/script/couch_test_runner.js create mode 100644 _attachments/script/couch_tests.js create mode 100644 _attachments/script/futon-dialogs.js create mode 100644 _attachments/script/futon.js create mode 100644 _attachments/script/jquery-ui-1.8.11.custom.min.js create mode 100644 _attachments/script/jquery.couch.js create mode 100644 _attachments/script/jquery.js create mode 100644 _attachments/script/json2.js create mode 100644 _attachments/script/jspec/jspec.css create mode 100644 _attachments/script/jspec/jspec.jquery.js create mode 100644 _attachments/script/jspec/jspec.js create mode 100644 _attachments/script/jspec/jspec.xhr.js create mode 100644 _attachments/script/oauth.js create mode 100644 _attachments/script/old-futon.js create mode 100644 _attachments/script/sammy/LICENSE create mode 100644 _attachments/script/sammy/plugins/sammy.cache.js create mode 100644 _attachments/script/sammy/plugins/sammy.data_location_proxy.js create mode 100644 _attachments/script/sammy/plugins/sammy.ejs.js create mode 100644 _attachments/script/sammy/plugins/sammy.form.js create mode 100644 _attachments/script/sammy/plugins/sammy.haml.js create mode 100644 _attachments/script/sammy/plugins/sammy.json.js create mode 100644 _attachments/script/sammy/plugins/sammy.meld.js create mode 100644 _attachments/script/sammy/plugins/sammy.mustache.js create mode 100644 _attachments/script/sammy/plugins/sammy.nested_params.js create mode 100644 _attachments/script/sammy/plugins/sammy.path_location_proxy.js create mode 100644 _attachments/script/sammy/plugins/sammy.pure.js create mode 100644 _attachments/script/sammy/plugins/sammy.storage.js create mode 100644 _attachments/script/sammy/plugins/sammy.template.js create mode 100644 _attachments/script/sammy/plugins/sammy.title.js create mode 100644 _attachments/script/sammy/sammy.js create mode 100644 _attachments/script/sha1.js create mode 100644 _attachments/script/test/all_docs.js create mode 100644 _attachments/script/test/attachment_names.js create mode 100644 _attachments/script/test/attachment_paths.js create mode 100644 _attachments/script/test/attachment_ranges.js create mode 100644 _attachments/script/test/attachment_views.js create mode 100644 _attachments/script/test/attachments.js create mode 100644 _attachments/script/test/attachments_multipart.js create mode 100644 _attachments/script/test/auth_cache.js create mode 100644 _attachments/script/test/basics.js create mode 100644 _attachments/script/test/batch_save.js create mode 100644 _attachments/script/test/bulk_docs.js create mode 100644 _attachments/script/test/changes.js create mode 100644 _attachments/script/test/compact.js create mode 100644 _attachments/script/test/config.js create mode 100644 _attachments/script/test/conflicts.js create mode 100644 _attachments/script/test/content_negotiation.js create mode 100644 _attachments/script/test/cookie_auth.js create mode 100644 _attachments/script/test/copy_doc.js create mode 100644 _attachments/script/test/delayed_commits.js create mode 100644 _attachments/script/test/design_docs.js create mode 100644 _attachments/script/test/design_options.js create mode 100644 _attachments/script/test/design_paths.js create mode 100644 _attachments/script/test/erlang_views.js create mode 100644 _attachments/script/test/etags_head.js create mode 100644 _attachments/script/test/etags_views.js create mode 100644 _attachments/script/test/form_submit.js create mode 100644 _attachments/script/test/http.js create mode 100644 _attachments/script/test/invalid_docids.js create mode 100644 _attachments/script/test/jsonp.js create mode 100644 _attachments/script/test/large_docs.js create mode 100644 _attachments/script/test/list_views.js create mode 100644 _attachments/script/test/lorem.txt create mode 100644 _attachments/script/test/lorem_b64.txt create mode 100644 _attachments/script/test/lots_of_docs.js create mode 100644 _attachments/script/test/method_override.js create mode 100644 _attachments/script/test/multiple_rows.js create mode 100644 _attachments/script/test/oauth.js create mode 100644 _attachments/script/test/proxyauth.js create mode 100644 _attachments/script/test/purge.js create mode 100644 _attachments/script/test/reader_acl.js create mode 100644 _attachments/script/test/recreate_doc.js create mode 100644 _attachments/script/test/reduce.js create mode 100644 _attachments/script/test/reduce_builtin.js create mode 100644 _attachments/script/test/reduce_false.js create mode 100644 _attachments/script/test/reduce_false_temp.js create mode 100644 _attachments/script/test/replication.js create mode 100644 _attachments/script/test/replicator_db.js create mode 100644 _attachments/script/test/rev_stemming.js create mode 100644 _attachments/script/test/rewrite.js create mode 100644 _attachments/script/test/security_validation.js create mode 100644 _attachments/script/test/show_documents.js create mode 100644 _attachments/script/test/stats.js create mode 100644 _attachments/script/test/update_documents.js create mode 100644 _attachments/script/test/users_db.js create mode 100644 _attachments/script/test/utf8.js create mode 100644 _attachments/script/test/uuids.js create mode 100644 _attachments/script/test/view_collation.js create mode 100644 _attachments/script/test/view_collation_raw.js create mode 100644 _attachments/script/test/view_compaction.js create mode 100644 _attachments/script/test/view_conflicts.js create mode 100644 _attachments/script/test/view_errors.js create mode 100644 _attachments/script/test/view_include_docs.js create mode 100644 _attachments/script/test/view_multi_key_all_docs.js create mode 100644 _attachments/script/test/view_multi_key_design.js create mode 100644 _attachments/script/test/view_multi_key_temp.js create mode 100644 _attachments/script/test/view_offsets.js create mode 100644 _attachments/script/test/view_pagination.js create mode 100644 _attachments/script/test/view_sandboxing.js create mode 100644 _attachments/script/test/view_update_seq.js create mode 100644 _attachments/script/test/view_xml.js create mode 100644 _attachments/session.html create mode 100644 _attachments/spec/couch_js_class_methods_spec.js create mode 100644 _attachments/spec/couch_js_instance_methods_1_spec.js create mode 100644 _attachments/spec/couch_js_instance_methods_2_spec.js create mode 100644 _attachments/spec/couch_js_instance_methods_3_spec.js create mode 100644 _attachments/spec/custom_helpers.js create mode 100644 _attachments/spec/jquery_couch_js_class_methods_spec.js create mode 100644 _attachments/spec/jquery_couch_js_instance_methods_1_spec.js create mode 100644 _attachments/spec/jquery_couch_js_instance_methods_2_spec.js create mode 100644 _attachments/spec/jquery_couch_js_instance_methods_3_spec.js create mode 100644 _attachments/spec/run.html create mode 100644 _attachments/style/layout.css create mode 100644 _attachments/style/ui-lightness/jquery-ui-1.8.11.custom.css create mode 100644 _attachments/templates/changes.mustache create mode 100644 _attachments/templates/config.mustache create mode 100644 _attachments/templates/database.mustache create mode 100644 _attachments/templates/document.mustache create mode 100644 _attachments/templates/index.mustache create mode 100644 _attachments/templates/replicator.mustache create mode 100644 _attachments/templates/stats.mustache create mode 100644 _attachments/templates/tests.mustache create mode 100644 _attachments/templates/view.mustache create mode 100644 _id diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddf4bfb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.couchapprc diff --git a/README b/README new file mode 100644 index 0000000..b84fddc --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +Apache CouchDB Futon (_utils) as a CouchApp +=========================================== + +Futon 2 is a Sammy-based UI for CouchDB. The hope is that +it will eventually replace the current Futon in CouchDB. + +One of the current limitations to Futon is that hacking on +it is slowed by the setup time of compiling CouchDB, or +symlinking Futon 2 into an existing CouchDB repo, or some +other similar setup. + +By providing this CouchApp directory, the hope is that +anyone with client-side web UI experience can contribute +to the further development of Futon 2. + +Installation +------------ + +First you will need to install CouchApp: + + http://couchapp.org/page/installing + +Next, create a DB with any name you like ('futon2' seems +reasonable enough). + +Then, within this directory, type: + + couchapp push . http://localhost:5984/futon2 + +Last, visit your CouchApp Futon 2 at: + + http://localhost:5984/futon2/_design/futon/index.html + +Really last, enjoy! diff --git a/_attachments/dialog/_admin_party.html b/_attachments/dialog/_admin_party.html new file mode 100644 index 0000000..ea9fb15 --- /dev/null +++ b/_attachments/dialog/_admin_party.html @@ -0,0 +1,33 @@ + +
+

Admin Party!

+
+

+ The test suite requires CouchDB to be in Admin Party mode. This + mode give all users admin capabilities. This is the least secure mode of + operation. Do not run the tests on production servers, as you'll impact + both performance and security. +

+

+ Clicking “Remove Admins” will remove all admins from the configuration. You will + have to recreate any admins by hand after the tests have finished. +

+
+
+ + +
+
diff --git a/_attachments/dialog/_change_password.html b/_attachments/dialog/_change_password.html new file mode 100644 index 0000000..40601d9 --- /dev/null +++ b/_attachments/dialog/_change_password.html @@ -0,0 +1,31 @@ + +
+

Change Password

+
+ + + + + + + +
+
+
+ + +
+
diff --git a/_attachments/dialog/_compact_cleanup.html b/_attachments/dialog/_compact_cleanup.html new file mode 100644 index 0000000..506417f --- /dev/null +++ b/_attachments/dialog/_compact_cleanup.html @@ -0,0 +1,51 @@ + +
+

Compact & Cleanup

+
+ +

+ Compacting a database removes deleted documents and previous revisions. + It is an irreversible operation and may take + a while to complete for large databases. +

+
+ +

+ View compaction will affect all views in this design document. This + operation may take some time to complete. Your views will still operate + normally during compaction. +

+
+ +

+ Cleaning up views in a database removes old view files still stored + on the filesystem. It is an irreversible operation. +

+
+
+ + +
+
diff --git a/_attachments/dialog/_create_admin.html b/_attachments/dialog/_create_admin.html new file mode 100644 index 0000000..d4aec95 --- /dev/null +++ b/_attachments/dialog/_create_admin.html @@ -0,0 +1,50 @@ + +
+

Create Server Admin

+
+

+ Before a server admin is configured, all clients have admin privileges. + This is fine when HTTP access is restricted + to trusted users. If end-users will be accessing this CouchDB, you must + create an admin account to prevent accidental (or malicious) data loss. +

+

Server admins can create and destroy databases, install + and update _design documents, run the test suite, and edit all aspects of CouchDB + configuration. +

+ + + + + + + +
+

Non-admin users have read and write access to all databases, which + are controlled by validation functions. CouchDB can be configured to block all + access to anonymous users. +

+

About Authentication

+

+ Couch has a pluggable authentication mechanism. Futon exposes a user friendly cookie-auth which handles login and logout, so app developers can relax. Just use $.couch.session() to load the current user's info. +

+ +
+
+ + +
+
diff --git a/_attachments/dialog/_create_config.html b/_attachments/dialog/_create_config.html new file mode 100644 index 0000000..79e08b0 --- /dev/null +++ b/_attachments/dialog/_create_config.html @@ -0,0 +1,42 @@ + +
+

Create New Config Option

+
+

+ Please enter the section, option, and value. +

+ + + + + + + + + + + + + + + +
+
+
+ + +
+
diff --git a/_attachments/dialog/_database_security.html b/_attachments/dialog/_database_security.html new file mode 100644 index 0000000..d63fa78 --- /dev/null +++ b/_attachments/dialog/_database_security.html @@ -0,0 +1,50 @@ + +
+

Security

+
+

+ Each database contains lists of admins and readers. + Admins and readers are each defined by names and roles, which are lists of strings. +

+ +

Admins

+

Database admins can update design documents and edit the readers list.

+ + + + + + + +
+ +

Readers

+

Database readers can access the database. If no readers are defined, the database is public.

+ + + + + + + +
+ +
+
+ + +
+
diff --git a/_attachments/dialog/_delete_database.html b/_attachments/dialog/_delete_database.html new file mode 100644 index 0000000..039ba39 --- /dev/null +++ b/_attachments/dialog/_delete_database.html @@ -0,0 +1,27 @@ + +
+

Delete Database

+
+

+ Are you sure you want to delete this database? Note that this is an + irreversible operation! +

+
+
+ + +
+
diff --git a/_attachments/dialog/_delete_document.html b/_attachments/dialog/_delete_document.html new file mode 100644 index 0000000..8ae8971 --- /dev/null +++ b/_attachments/dialog/_delete_document.html @@ -0,0 +1,26 @@ + +
+

Delete Document

+
+

+ Are you sure you want to delete this document? +

+
+
+ + +
+
diff --git a/_attachments/dialog/_login.html b/_attachments/dialog/_login.html new file mode 100644 index 0000000..f05a5fd --- /dev/null +++ b/_attachments/dialog/_login.html @@ -0,0 +1,34 @@ + +
+

Login

+
+

+ Login to CouchDB with your name and password. +

+ + + + + + + +
+
+
+ + +
+
diff --git a/_attachments/dialog/_save_view_as.html b/_attachments/dialog/_save_view_as.html new file mode 100644 index 0000000..d59122b --- /dev/null +++ b/_attachments/dialog/_save_view_as.html @@ -0,0 +1,35 @@ + +
+

Save View As…

+
+

+ You can save this function code as a permanent view in the database. Just + enter or select the design document and the name of the view below. Note + that if you choose an existing view, it will be overwritten! +

+ + + + + + +
_design/
+
+
+ + +
+
diff --git a/_attachments/dialog/_share_test_reports.html b/_attachments/dialog/_share_test_reports.html new file mode 100644 index 0000000..82b49a7 --- /dev/null +++ b/_attachments/dialog/_share_test_reports.html @@ -0,0 +1,42 @@ + +
+

Share Test Reports

+
+

+ After each test run, a results summary document is stored in + your local + test_suite_reports database. The data has no personally + identifying information, just details about the test run and your CouchDB + and browser versions. (Click the red link above to see what's stored.) + The data remains private until you click the "share" button below. +

+

+ Test reports are very valuable to the CouchDB community, and are easy to share. + Clicking the "share" button below triggers replication from + your local test_suite_reports database, to a database hosted by the + project. +

+

+ + Browse test reports shared by other users. + Thank you for sharing! +

+
+
+ + +
+
diff --git a/_attachments/dialog/_signup.html b/_attachments/dialog/_signup.html new file mode 100644 index 0000000..7ba3448 --- /dev/null +++ b/_attachments/dialog/_signup.html @@ -0,0 +1,35 @@ + +
+

Create User Account

+
+

+ Create a user document on this CouchDB. You will be logged in as this + user after the document is created. +

+ + + + + + + +
+
+
+ + +
+
diff --git a/_attachments/dialog/_upload_attachment.html b/_attachments/dialog/_upload_attachment.html new file mode 100644 index 0000000..50b7e1f --- /dev/null +++ b/_attachments/dialog/_upload_attachment.html @@ -0,0 +1,36 @@ + +
+

Upload Attachment

+
+

+ Please select the file you want to upload as an attachment to this + document. Please note that this will result in the immediate creation of + a new revision of the document, so it's not necessary to save the + document after the upload. +

+ + + + + +
 
+
+
+ + + +
+
diff --git a/_attachments/favicon.ico b/_attachments/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ed3f7aa7c3485ea32b540bbdfcaf9a22f9eeb47e GIT binary patch literal 1406 zcmeHHJx{`55Pph$9f}rSEd}HStAmE;O5YKb9c{k@7?8c!vP9ERfR8`5kO;O5YKb9c{k@7?8c!vP9ERfR8`5kOu-@d`Q*REYVbLPy^qetO-Yierp^Yeik0|Ej(JUpD7oUE*@fQIPm>SpFl-4Aq+ zPDzkoFoU3yQd&pH%$ciJt=heNH_(tXXYRgx_YP=22DtQXOBPTcXMsm#F#`j)5C}6~ zx?A@LC@5dz8c`CQpH@#-gOP!rfu)|2fq^+|$Ky{742*%EE{-7EkF?`l#IIKn+`=GBWD zjc=4@IlTX0m!7z>cwa^9v*U-(z2n(kC&c7Y6cuXX@8J-~`ecUl)>~7avhn>|H~o0B zWR=kU7~`>Z(rANysVpi zJ3-1;npdn}PG5Ze|D)_dw|s(~FRLyOcKER*V6NLzH;Y;2v9as5zAV{SIicvo^eJop ztkm24en!{>gH?$Y4((D0mMi+{>rX1?ILFSZX(8U|a5A>zcY}}#Yrnfe@4Q~NZukD_ z-Piv=Vvf8RbzAJ_B=Kvvu5&vzR=zHFzfrdG_I2M~hrg!Su^*~XQ_dFo!o)FQ?OtYw z0+T#WmF?O3yVgB!l1`Src=-RNN(&|xx9r2#9k&`%dRrb;UAr~&SNhh8cb2}3w4V{# uktBKd%=w!w7dNKY&H7&S@mKtxhvNND?N4k!=AR9W00vK2KbLh*2~7Ym`%AR| literal 0 HcmV?d00001 diff --git a/_attachments/image/apply.gif b/_attachments/image/apply.gif new file mode 100644 index 0000000000000000000000000000000000000000..63de0d538f63fb7f1153ea70e5dc5b5019fbe475 GIT binary patch literal 652 zcmZ?wbhEHb{_*{%cke#Fd-3tvt5>gHzIyre+0%P3Z$Ene z@X6CB_g>t-`uyCL+m{bL*mdRF4$r| zk5zRZu2{BhVZ*-MnX9HwSl6DnJG^*XT;HnJDN80~ZVzc(Q(Cn?XX>I!6Bc*pZHms? z5?s74Id4PMl=+jo7PU;7J1K2LK*yZswmA)%DT%dR*zl-2T(sv%5OsF3bGhX#+Rb6;=ILg5rTf|Q7cXDEe)IO-`wt&qvMYP|dMLAZ z3%L9J`{%CEV7Y*$%duI!NW-!qsmr21s-(+VMR??CyirfVhI_~M>7*cWT z?YZN;4GugE8y1@few#l{#aQ9vM)iX1|F50qw;8o*o=xSM2N~G^ ae*fBoCM$N8bk6~LiNVv=&t;ucLK6U%KlG^p literal 0 HcmV?d00001 diff --git a/_attachments/image/cancel.gif b/_attachments/image/cancel.gif new file mode 100644 index 0000000000000000000000000000000000000000..4329076e02a0d9a305a8b3dec81b255c6c9b0be5 GIT binary patch literal 659 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 literal 0 HcmV?d00001 diff --git a/_attachments/image/down.png b/_attachments/image/down.png new file mode 100644 index 0000000000000000000000000000000000000000..5e1b6e464ec4e57cdf948d18fc1a0f5ff0906924 GIT binary patch literal 1337 zcmV-91;+Y`P);K~z|U?U+w!TvZgtf9JkTNLz|dg+j$uk=jK;$(s^zT~H|% zEhNw&NTq$#Aljx{1Q#tXbko?WLZS02wl3O+2`MBZDipE^Bs4PMh-r&aYUU>ng`7m?2$$N2`(MgvKw(?0@-pCD|-PswER2%rYQb={9P zAqF^H(;!!_TzT2DtOBro69xbeecykz!?LXYP~6qkMPFYZot>SL3eWS%WHMAL6@cxQ zW%ZlduMI$@QlU^NU|Ck=r%)(_VgN+MHmYhjW|^CtV`XKfE&IyK3UhOFO`NLQ2Cy2* zAPAV6nrbtcnwlaAf>zoxK)#h-TwG*fVWHk$SXf|jak0%5$QxD7MfFTiPZIPE|o)c=h$XU-6h$0Nn1Qi=2D&xcI|ywg-4c%H}T=;#x(kB*Lp*+V1@(A~&n zvsvcn=i9{d^Ydi0*;cyS0Bt+x@bGX`Ef53&!^6W-gIYDU3DSh6r6ndOC+qFW$w`)$ zmfF;+nm5Ln$ogPxY>aZbOu1ZUY-}tl1Y=CD!}t9g#+Y-!3yq!Sa+&e*@%nQF{_%bP zhNvpZWU?2?L}UPns*M2p(&_Yw0Z6CQBf#a>h_J+oz~!2zc{MtY^OlHw2fP@i0{&Lj z6Rzw291?BI`S|hU&&Oi1H;plNSVM|PJ_v$*wOak<`t|EgMdVg}uw}4iuw@XUqm?*# z@ZbSeZ98y?NKQmN~IDt%`u~@i7=p4D&e~B6H~gb8;Su?)r8^kRSy7}Oon2y*d{I( zi)1owyPs_Uzmf5MpP`|)V>ClUL-@YmO8b%Z!OYAI6B85l_Qb>lGcz-7rmC7ZBGP66 zkV>WSJP*(FNTpIyA&5xc+OucRUm|h>h&6Vut*sG@#V8aCO`~bRLsk7yRFy;`@iC9e z+$MB&J(?PgH#ZG9oy+CE4X3i$w!NvVuzWs$L{%SMg{i96h(Ta|1L3-=4u)cg*xq}3 vdiDiD&|j+!>eqoHk~hZOx_9qh({|R{#1=Xm;-vd$G3pe;gBoH2^W88)5(e literal 0 HcmV?d00001 diff --git a/_attachments/image/hgrad.gif b/_attachments/image/hgrad.gif new file mode 100644 index 0000000000000000000000000000000000000000..08aa80ca85d22d14c1af7a22b5a0c2d494640acb GIT binary patch literal 118 zcmZ?wbhEHbWM@cWn8?7Os;ZinmKGKk=H%q0_>+Z!o#8)&4g(N?fmHx4F5wy1Kf!xELtDY}vAzGiOejGNrq_8!ofy3?d7(2u`N!85tQE80h8Y z<>cgKWn~4l@zGjMqE=o--$;{7F2+7P%WiT?(GqBV%GB7Y_?Rfl&fq`+cr;B5VMeo~? zi(!im1eQ8vs7AVmXKY{BHA$o?HmmW>y4L^y_0)we=DoH2=lI@vM{)5^7=UCcBc+bjhvLg6TPA{+S@zUUk_TBzvHP&$B%zMf7+>et>-A5`9p0phvqiAi8mF$6I%9Et>yEs=da@n*yb-~(ER*-R^!WC+l&orFNwU~ zd#%2TdFAf*e`|C2uI^)h{gH8jcIf35T&y4Vs?N*RoH{ABy`+=##fs3t6}PN{`1xP- zM`_k>VVc13H*vdl0Ph#B0~>Xsl{8zEa=zXZj0}nv*<$8WBX#Cmre{K42}5#^_l#+? z5A2Kn-(<`ZXx0}ga;DV8uOp^+`(wxO>Y9|#f2;q#U%!g|6U(*V%X&4rM2;PZj$F8| z>ky~5m&9zYdWEVbBGd1`Nih|e-Li4YVU?8s+%vgdAC@e7eroI1K4HtI%7Ujd7j1rh p_-J@$@#ORcR=@K8a_{@V`OGsibE^HCufQl}@O1TaS?83{1OP*Wqb&dc literal 0 HcmV?d00001 diff --git a/_attachments/image/load.png b/_attachments/image/load.png new file mode 100644 index 0000000000000000000000000000000000000000..07b4f79159af9694629eeccc9362b24a4cf73821 GIT binary patch literal 780 zcmeAS@N?(olHy`uVBq!ia0vp^0zjO=!3-po`I#mGDdu7)&kzm{j@u9Y9{{;?0(?ST z|NsA=1_s8)#>fmHx4OEzxVRW7xNO<7nKNfjnl!1qySt^OWy6LIKtZ5_`}gl(yLRo& znKSR+y?grfDUb^^?fduda4tml=+UEl_wL=gbt_OKno$rda&mHj21Z3i1qTQF`1rWE zxL8_R0?qeTypaiXr%6eWUoeB9l2Ten$IO|lR;}6%0zhl;-n|R73TP(|aQ$iNcA$RF z0*}aI1_o{+5N5n|x9$&6P`<=9q9iy!t)x7$D3!rCF}Wx|H#H?QQNb;bU76yY>Y{gJ@g^L_i%mRzL>n7|HS1ZB;^$6ZJbrr z>~SLe)kO0>KmXL#d!6N~yZx$YXIa47DW)%Pxz9UN7-X_UIs4fK+b(W_n`>Xr;7;sX zc23~bJKbObR=qOczJR`+yMcD~&-8O)z8ZDpk&-8uPSVeW+}O}kYu@*c0>^UI-Q)GP z$NcZ+&sb*Pw{NBIUQ4eTi@q;hCNl5e?>R|6OWm5km7d;on>%FICdro$r%d`TPcle5 zETgf~=!e~>U!F6$cN=`s{d4m!L%B%aPaQ$6YPqw{{{`%f|GMW?ytxs7`O7IzUmmju tyN(?;*>k@6SL3OR(@*PO|DSYB-|e=S=?~jjFJL?|c)I$ztaD0e0sshwaZUgL literal 0 HcmV?d00001 diff --git a/_attachments/image/logo.png b/_attachments/image/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d21ac025b52bb91c5ea388489cfc5570f42131ba GIT binary patch literal 3010 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 Ef=x&Lf&c&j literal 0 HcmV?d00001 diff --git a/_attachments/image/order-asc.gif b/_attachments/image/order-asc.gif new file mode 100644 index 0000000000000000000000000000000000000000..d2a237aecc1e8bee52a684380b4106003ab8a594 GIT binary patch literal 195 zcmZ?wbhEHbFT=rhP?-mW@l%2^-qb4iYl+H-n?!1krQX8&s(y5 z^}3dpmbA3AeTR;hmX-zv1y7tZt)-)T`TA|E)^4n+si|#h&(1GqzyTD0vM{hS{AbVs z$%E`IyXJ@_VHa(7a+pv4aaE{|_dUP}hgeofi@+3?!HqUUCwaVP#EaNV1Tb b&-jqFXlIXTY{Nucl7C7vF<1itXuw1e literal 0 HcmV?d00001 diff --git a/_attachments/image/order-desc.gif b/_attachments/image/order-desc.gif new file mode 100644 index 0000000000000000000000000000000000000000..1043b499ab60426755eade3f71e9d0898ee25847 GIT binary patch literal 187 zcmZ?wbhEHb+3sv;j)&Nmc0Cey$6q`rKPp>^ekV$t#8WA(#o2w?3|w7 z-k_kMl9H0Ca~7@Luyx;|FMbi931T6;E+Z^9Z2f{F#|{~ z1G6s2t~>t>PMzdxO}>-;L91QJksO+b()O%YU3Q9(BX7GztHZs?ta9(o#~_ui}YE>aZ(NQ;0- z4L$TKAR+=54({iD-gD-B-kDi5`LJfznl!Zf0ITjX< zw6M*zDXDPSXu-&SbaR}=R&4ujA5*e1nqVdtemfLlxx z`s#5BI0&c9darz3!be?Y&*jZS&Z2>wmCzZLYxvcVFEuibNYYPB`m&$J-Rx)@D*04o zQ0OwUmSFchNrnlj2T)s)Gr&C2Prg6aM)1H`_mp4%?qu?o`(VE#&GW*GcLAry)Qyy@ zP@T?#XC#pNRmRj>uA#tm{av$OoZPH>>1{b86PN6c>^*y8|LLmhWxpra-8aVrjJsOi z;=jpGH(P!$IOVIf^`^t?M}?r#!R}&JRD0l7dc)_{cGnlrr_9D4Zjv!N&aZpo$*<5CIc zuGfFt7)2?sV;7#@mew$jr3u<{#*I{W=Ed6K_PbGo<4rA zaS?%j>0xM6Vz3_`8{rcifFn3~MMBbJJ&5H|NqP2J80bF>K?wP0C3FfPpb%D&wao_s zroo{en|}Z1p$Qn&)tbgSF*N1Y|6zV`q~CRAbrCVvGq*b8=KpzqapU9H!|BnY9otey z43Xk|Oozzg0PXT%20b<=T~i2dl2r(A zga<9}Cd=GdF0g%EfS08;p%0Hh^^UXelY9ZY>GUc z;>9Z}@7v%w-|d6=QLbXTgo5}g-O^5{yUz6`a9bDrJgBsLD{xM9Usiwr1(Wr2HcZw2 z78s0RvkB%orAC1B#7d&=(+@B$-3c-;qE!!e=J|4EvQYS=uPgAI_p&l6USAd#Qj$DR z-|+kX?fv9^^v17dqtAmIx2#oUe}R8>hzQU4zH6<2;qiN^0CWuGm1hG&c=U8rp>W)o zo}r*cx3L+wiDX|Ozul@q;AqclHK>S(u!zGaK3B6)8H4BPpbO-NIjA7Ki!bM!ETbS*()i|B2;8B#A zLFMhDq018GGB<&lC0)16O}+9$R${{ zs%UMOujW!j!PTq4@S6%+bMe~~%JbF&nyN3!FjQ4p$hY(Q53DXheK(>$4+iJHy({Dz z4q#@M<%d3+=oXKh;bB>dV#q3~?qGMo0gEJAginDTO)-G-A_dj8RM|ZJb5^8AEnBKg z+F@aU^w4^Nk4wRil|I2e7KFJ1r;S?$sie`Z@8KxI(o3HIM(7wR~WfbY#LDA_#?OkBzLVmnToDcbn{FQK`)ak+yj zpUY42b7n*32UxEagSpADk4s@=nhtY^E7?Kkj|5ippL|khFDk4!S}&{LI@+k}I{#y{ zcFg}a)4zv{AVym((-oUN+yaJg;|A(;+oxfxlu_{ub&{4D!;N2Jhc)XAsQ9tq)t zcMHHzkO8uGgRn9U55t-I2BQ0hMn?J&){}!XBQU7VfQ{wSFx|~>b7pL1A{epwm1!l^ z_Hb!?mA;`HnbmM<7oN+^8#LdZrt6pO&*i*chQ%-5Iwnd)hv z8&V^MquCryH5TxvHhN_urMX-sv1$xpusn}UHgJ*D?0JZMMFtRY5bPH#I&o6~|Ab+x%flC2dGyv$@=CI(Y*zhdq3-@sTyeGqp^LxJl^;F z$%PR8Eg3%FHU{b$_cL2kL2=>Id-u~jA72v5)7d-Y&sP39JW>#om zC3F;HjDT!9C?SUkW9wuPXlg{wtIDzJ0k)e&YYir(FwI`(bBJyYecM8g{oFpJiwu(6yO}z8=sHm72iJRfU*oD|9tZeWE;N8&I^)x?f`eKw zL&b8TVx1QTdTr4y`md?Q5_0*O59-Oaqkq4;?~%`24(Qb2v}pkJusza~X25y&l!Be0 zo73&Ttdg*VRs|tB^$?}|2N&CgwwB9aDh}9@sQbKE)tkF{91Mx{3iR@G_jmLO^kqf(+YzwNXa?38k1#US z4{zt`l%9$AXTYFyGoqri3eyvlQ=+ln-bv-S+IS4olGR$)*b@zbcb$Ocge*28Ctw+3 zf)DitMB9EEp6;{pkC~btgCaf#EG*JuCkKZ|nKtLProX_JC+wixiwEvMj@;WBwPTy` z%L6axG%AeLZm~{1Rh7p<@K0DN@;FNM0s+J*7L4?oQzJ$SF&_s%5XM^)VkQ5+t+#{9 zf0(RbeY0qwrVyO2U^`(1;}R#PavO!pu2(M)BuTgmB~V<&hLg_)?pquxmh|SSG@k#K z`c17nfuruC4GxD>KMmo~B+BPFO|!@@bhHbj@W#zpaby|~nmGH;{cC4lB7J9;LM+!; z3|FqEcZ?kAl4ZwJ$u0cSGIfjs7p(*Q?;Hz8u;8tRLKB|-*yP)>K^KcO7^?~Akf@xC zA_MeF0)#o3S4{6NRsg!t6*Km%-&c4KHYlkhS&?Mtw#HKz=i{$(=rZ5OY4owe7wdXz z=GCJ|^cKY7szeL;)w^up0+-Yl*hTV=Mze5g;Jj;^@a*<73G+y z-t08LgRH@hg-lIrngw}&=@wMsKI@r7klNG5LAmHb$onEQ?G#TBe29=abF?kLs}wcP z%Ii}|ZEyMq2l)bDp6<1I1=v(ba!OHgN+B4s`+Yf}drkjTD#q-bbyN+1!7|wUh$fj5 z!m*y4^IQC2l!4t%%9xQQoi=_+#XZ|DWKOP-*IBmMZm#5|Zc?shje)96oWR-anGyxN zm6C{&{0X(ZNkK(gCBu zI2?@0RxQdKZ4im3!y1z>Yb`U0L=~Qm-VXR7ofaN}h)&)J`ngr8+A2;J$gv=O`DWp< z&jFQd_gFh;X>seOtr)cOh&ex8@G0_9m+YX8qcz8i?)fH37I}7K+FEEU&|!i$manv; z^3Z%%-P5%pM#$hR_(qWOR6nbPV>Rf3Xy_jBjsd58pCHSPrH^-+A6{07y?Ck+a%bb^ zgW>Unhy5c<34#@s=p$=2bs3!VAUO9t1)aw9lT=(KJekb$%-Br%Ot*lLo^mc#+2f*kMW(^0Cf5`IlOZW2=Yx)1kLbDF|SFOdGBIN^5tM$IKtUR*=^v;k#u$)%$ z-=T^9#(8*pdb!xa{9z1eVmLX-KMdmy^>qudB$5(X1MEYQsU#O%a7>5@!43ydWa@+a?mu(Co~;R4+P9}u0gSlom}} z9D`O@4-5{^(Cw#oMrS8N_7-Q8w$_-wXRd{f1@$+$^|6VpKf$%*fwD}pXFWt(8BRiu zoskY5M~V^_HyUoJjw693Zyg&rs49pOK_X8=Eyj1n@5M`U8u5*v1fXb~hgq2|hh#V7 zB`t*m7{>}Vku4GQ>y@HyPnxQi^yTrVl8=k--^kq)%}IfR*nOUr38Pv`^%%*Y#Y(5{ zOg3Qt)-JXBC}+|L`#s%}X6kgg1rA-vuz!d*@4zrKdW56&&EAEHyDg*vTrJ+N39pQ7 z>b0AE;)LH4q^sZ!RTpa-X3>OP$>%9>9@+P=RgniyOU`Mc z6=BG0d&@O}7rIOywRXV$D23J9Az?10`p`ux3H)J4>7liD+iNQYhd~%GPKrLN;q%C20LT#c@Gz!u-V45N z5=32*D$a`4kP7}-e#GN^hDRS*Jg4PJ%NxOaT9MjLY{Yym z?&5`hY_Tpo&g+^x+9+<`y?3VIoQ(kh{HR}fnj}4uG6CgVwOt5q=?@0R2yf|~zZb79 zGau6vQ!-X@t3|z}F}N}DypaZUEVbog%UW?rxfV<;l07dC_-;TzxiSw}+Pu}-wdEtl z%%F+Ff&6xVND>_NVKZ3~uhXY`F+#YKc!PZ{n@rT>5FzN53+dvi=r+inxAKn>w2Q_{ zF#aX_HsfB>j!K5jFb`7BGLSIOZ5@-b_H)=dBTzF z?*k*e--=wH!SoBi)uvrV6Ym@^t8C)zETAMGv?~G@5(*>wdP9Rl;v%roq!?EB@Weo0 zhSao#WZ%=-9(Li8`PKp9CCJR=Se!>B*~c~1K?D0Al(-UEEB>IQx0eag6CVPhGL4Q6 zn{6N^x|(bZiwVv_umlKgR( zg%8PRfguiFUzape#qRUmfIStzpY*_TD?f!I(VwTKZtzRWePy8h?>iC`hd9b#D87qD z+*K&TWIg9*Q9FfaYcgT~&B$wn5}xuFquBkrFk2qg;KI&jQb4aVY=zM;62}_kW)ntt zZ@xnEy;5pVXTPtPsDH<@#aaB^6~~WXz^x^frbRkAn|}qIWey|Rn-T)8L7rcY+4M6= z@Hc*(tdUa{flHxp`#dIp$nDuTiHl`brB}UPL;KviP|-T+JUqs1c>UHC+GMC%?&*KSc zlk+V{lhGcwHc^U9e<{y_1>G>hpruyqTxb*?`_N1Dn)O+aFvn;$GQ#Y;Alb=XC(r7J z6+H_MFp(GbXNm64HN=MKY1<5kxQXKb-W89vRZxhdfObPxMw9w3DN01}WTz~v8Xrky zCXQw|;4R`Ms}Y|K&1~&X1^=z9v|@7+-Xwu`?BvNycI;c3Op5K$8;g|!oEw2m zRMX7DSh$i<9>abUYDFR@u2Y3i3~G@OKh2vgc>ssmFVRMpdVVML0 z{i^YlBio{sh}0}}=an@2tKrX*)5qnRN1YBP`Pw)sIDjX6B+6UpkRE-h#VRiteh|D7 zX;_!0=6?4|1KxmStXv6sKM~gIx0<+-Mm|)n=a}sXK|u1xQ_7R2N}L{_jPHw ziid2qwmcl6u8Da!Y*ML$Kd-u$!Nc_>jIK=v0c~9F3D&j-%Go{tFlZd&>TaRv&}YJ< zSqtLhC{n`@Q$yCvJQ_Iat3V7A7y#t+uQZ!b?q0d#8OQ~R!KpWaC5hopzXX+&L+?|R zje?`3W$9kj73_S>N4YAM8TLDR2YpU!H>7=Il`<*s7JH!wU=YjKD;$Q46+gEA;mA|B zzu*5!KYut<`QP-D#RmG<>XblGYrE9-{?m_4+nvx*WUXkXe-$TIWm;H?MaT=`W$Ei{ z?eBplz|cOY$pPW91S@D_bV#f>GSDX2(+lRD=yE#1Gb}eMDX1_s6jPcXoD*A3PAYUl zXjmdmO=K8y(^+v|P*#DH=Jf-jhcNg@PR3|Vuurdh#%YtdK?l(2EYpBT_S(v7PpIS4 z=Sio~k+0COqVFpMNBheyYH4B-v-Ow_+Ba8JAr1&TXtwn2xJ@VP%!SBB*t2DA z71rQf3mxGVYM~M{av&uL_Z|q9HPZ|GQ=V|nQ!{WYX`p({igvfo*p}g2XZOr@=Hv>J zJ)EQd7c<`@Gl&2O=jaEeRs{%?nBwS$vf*N%%c0QSaDAXnBSBF^+t<6Ye}s0Dup)A9 zLts|V+4!o|^3B}1=M9qOrwDZ(90UfFXh7rcymQL+EW`Ax%MS2Z*241{Zp({pD<1DM z`Bc7ql2d)AzPrcwdHuRni^gKrlPB{OFn(+a;_4hT zj#9G{aCg3OU}(h&N^FI)Nz@q-1thBIIR3{HXMH%FqVZ(pd17v;v-^NrAkvK9OMp`} z^&|R~zxIaCeeMt;v#%?Ukzb1Nj6*L9_2dN!pmb1hMw4th0Un;>W8Q<+>NoG-%G;*<5h*M2F>VQZp-jo0&K{~#qEPH=gy*d?+7FwQ<;8=W}kwu>A zvch(Rkos~Ei|WWM9?GF*c4DsNTF%D@A#Hfw>4fy~B5Jp)EBieOJfxUoY+9*MTYkY` za?k*n1JtfJsu;&fnpd)$h&q%AmQ9pYH=j-03@J}#lqsQl2T_iF-xu4Z7SU4d`aw;T7=D)KY4Vk=w7jO{I@&s=uEIVm z=;{QuH2UTAn0(mgz2GWLJ6vM!EqDX5WiB0Q-v;1d!l**9ONOg0DHox}1$pYtfPywl zZvdG=!oW43qs&9x-}>>&BraynNU~SizD||V*IUNc@OkQ$S0?i29$V(FgT0C&A!}N% z`^IQ|bst>)yr<0+HBMN<_Y6Y;UeI{{qJu{5FrwVLf5MV?p>L~Ze1|;U8+ss-Gt&(I z&+&5}l57}K;P+{1C~k7X%{%f8vXQ=D0zFuAd2vJd;u6`ssbrSV>@Pf^wJhR2&;PWq zPS^ibC;tPK|0^c{W_1!sXs7dIkT#bZt8ANKS= zV?KUu^m`cL=*uSP_nn;wlWBNwIA8Ri#>A9^JQt(-ciW#Cj-AmCU9|(ub>Sr5d{I+} z35y>Gzt`jdF~mvAL^||b6z_;v)M|MEtFhLOV1fIp94an+$Po#U zs>x;gZ|xcFg^d!0=WE@?-&lr!uSUT}d7hs}j&CoveRDXtrlb!5|v>#s{L zJ}nZ?*?@x>J+%Do9q&MxIdRNgP*z|Eg@;G%-1#}ro!8Wq=bADq*u7?p!cxdh5pUC( znnv{V{^ly@mHFlimHWT2sNKz^3|7InduFG$hT0|jny z3MjHmV{)-P%6uq7K;K9%`AfN;xg9r13k3&rJpqR7ic@f5_WG*qXtl0^6sj6@%rffoWPf#lXb%!Vhm;VceDW* zPY&j@!nT22YX% zY_jZ3iofYksvWtQTcq^3Bs{ONvbrOK!#}3h2$ekK$e(<&dIvH&sa>mXF(I<(AAvg)^OF~3OLsJ4_*7i920^daYke6O@ zfsxgr&fX9Pi+^we;$S_Uiar4flR#e|0-95c=sAfwOs%P2Bec;8OzrecYd9LVKwFyY zKEcT+@4>aPjiJ@8?WNru(Ba~z+kv@^QL|SGOriXLcmgzqbMZA~YY^07aRREeZf7+- z0}rRsn;lirt_WtKW88Z_mpeUi2!53YO~uyx$qM#nVz#>$)M#1G9TKcnbRhbUX#rS@ zW34als;$8TxVuDWC`XY?xb!m_sy>WKnJZg$8N$NHmLGBMpSncxK}`W&fLw=J2z{gb zJ#F$0dd0vnZ3k`Ah&WsFGK_Zz93ybH;PpPGYUUJ^nBmX>UlqN3?Ak^`c0(oT6Gl=! zvWV{+xRGO%pp7~#pJL{g)?{lYQ1B2>?$T2E*4tm^d1~H%xT{hLVP@u#eWW^tomm|! zJ);zv;q-R7YxeGiUxGVdx7xTh2mFn_mz&_1$~;-7{eJF7=2b=_y;|HGXdPp`ps!bz zTCuTT{>+3qY^s3J{;41}P~_W0fc=N6IyJ0!0?!KzguuDyfBhV}YzmEx2%b}r48@9s z^!>$k7A5N46%ImpsG6Id*}c4A%Bv7(YK8c1Z3IYZ<{uYMQN@2mb9}gd<4F zCkMrjGEs0|Jg8d!fboWJFHonfc+69lAL3#ZEe4mAR4ij%>%P#k3#kGBDBdgx>rgbnil_y5MpW{c# zvDxsm9~QN4a(9fK`-N6z$|lRHjxTaERRKaAr~%&KheCATy4S4srcBo>3&&ZQyqNmqbm@Zm)>gemJL^00wvU9u z+D_NO`Lqfc<&UI5WTI_KKc38qy?W_AIsKI`p^Bx~F7!$jbgdZyL&Etz*1)RF-Lm1=TjQJ@AecnfXzx+p1KB>qB-fe=P zF^0^`I$~M#7P5t;wYe#ohI*mNGOm1mCD3~fRa9un)^oj zK6+_2{p z+Z3P>0P+xKKH)!;;*Ky5p-cZvco-QujC$*;MZ2On{yHwG#j53LMY5=xy;Z=-R8Ps`%uTgRK>oU&lv8J~7# z2+xv%aK)C=+zc$8S@L9Lyf@$w2${8?c3W6aLC=KK(sd-;R)g`u_+mJaf%@L7N!wi^Bh8IXKO6Wm?9Y=MeDxCQ?8Wk#d5Y3W8 zA%bsiQm;FPpEXg=iLmEsH1#U{@?#@r`ATp_rJGijSp?!>eLIR*;f+Kj66CuT&3aO3 zF*Cg1qsW&+3TQ53k`}4Zv+e4}Mlbr)#$4XjvFKS2$%X0@(@$v_-`=M3%w#cY+MG`c zttm^jCFpA*6E(8#Ug~+kU?e_#r8LlDklGX23e-h+4!}_)aE&?rRH4k^xZ`!BcPzbp z1)5y>NkevRoqf|&Zla#J>0y0dzUg&Mw(phxmGznO&ZURaP(0paW5&Mg`*+`N*!lX< zJvHqjNC7m_;x?B~y^+kyhR6=!0!p;H-;x`o{Y1cq%F9icHc@G;4 zFMC&)Cpz}dHbjC#{F3|v67j->57!8+{~&0)+Ism~c-RoBSy*|vI@?&=TRc&;_ptGG z^>FljBGK?^+V2{**3Cmt-!JRvPxt*$fd?dAy}T_PYdUE+KD}Um`|#qV+Xa@H>i?km?;fz` zm}k$P!P|p~%VfnO?JH6fu1Bxz?D$6UYHb96T_?Ft|Es`ND4b73#8TkwsH1(&5z|%A zWyOR+n|b%=AP(a;O4QF^<>lqAt(<+dcdyhQzk4UhE=XcC8tnf0iN%?viXo6)js}l9 zIHnKj`bcymiR3-w_r|a^V%yu`W3-wtglGvg)0*Q=x;&GCPm-d}|U7cY%)-Eb$!_AkCd!y3f z*+N<-I|b8POgnGVUsi#Ae^a70o)R9zeV-Cc=(!o+Q@+k!|f;#)0HxZV2GcIBMK2mCoA^ zu~`C2%wHJG>q%HQP&&^I$(6^k2O2Mo-aOPDcV+#xkd4d_o2{qxe&70|?Dfx@w}X9< z&*M=fVmEhGTfVf{2x$(8D<}gm@7!9ly2+R0;ZJp>DQjhq({%kF8_;wmtON_YVzbnF z8>$yQr5!s(v+hZuT)&Q@&hzf6xr((C9TXsAKJ+EYY3)AEhkv^?P$bVaB4;&?ViW1y zpqrt4!yrHO@eV!t)7~ZwYnqNTV`nqlkc4%U#d3I(CP^;`Q}5GH^Q?KU`X4%#Azo_5 zpB7*JQllsm^B{Y*XGH#f1S~AjU@Iq(>nYImdPA<)?pM%_W~LqSoww)_V$&$j9ooy_ z;w+mlWpK^%{T6%kN_x13@hDxRZOqemK_SWk>c{crhAlOJcw;Qe%C~wJY$$gJ(q7xO zkhcu;(m!sf*wZpjIE24$Whg9ago1Dy3M6%;L!VUgy0X}Vs`>VT)O-u#80<%d_l)- z=tFPD(5qxDDdz+3Ux2!>D-+&*pTE0^;QK`WgyYkU*m$Ng+T3$Wc2H&3gv;{AhWN_n zOPfFR))a%Ptc)$Ni5*gQkY z7oRu9vGv%I&fQ616ju4d|DC7qIuGr60Em^QRah^Qw91!$y>CJXz1yUEYDv;;2zCx3 z4Jj8(7Z~e%$HrJ^LvAg}4otNWNFNx5g zq82ZDD9>?5eAI92LpI2(IUbjyAKD32yrzlyx^|yt>5aU_lIV#yQ{y?vZL+Lxx7~6J zVlv{6g&%yOi|GI0B=mUrZB(8?8&~9Q`S`N5$BHu+5)Skd8KNw=KfxtlC9(}uS5Jsm zyA*yN_IUR3%aoxiSGd6RYxZRGsOU`-qjX3>N;&&|+pJQhrU{Tp_aq%f2-90T1~C+m z9z^W^U#d;8U-L_?guqFaf;*@uCPjSoRqvJdiXIn<-9jpU{9;FWfQd}hd!#N;H>mBV z!SYi5s&cEIX4C67&9F_x=M5ef$JT)ab%xwW4&)gOp~VAvWrCj^B=i0Gbf0!IXqx~x zP0YDL7wl2ds$oBd9|*a=U2NO#FylWpJ2|*_Q|Yd))0bgufpqd0;oaK;X-s8DPzB$_ z6yvZV(ML8?r00(Ei!kVm5q}o#itl@Q8HqAc_eIMR&HaXk?~}Sr7?Hn>q9-_2K9vv(Og!V3os|Ak#@oN+;j;&0=Yj%7Zkun&%@qXtzCUz9{vKBMZq`kaRg#z{GNw=# zOh~Q~*^(mHi3#0zcoXD2X7$!-(`?=1%f65SZ)|ygh&S);iqX&Byi?|b{GsbAP9b$a zPgNd#+^1~TkfI}bsxD1ml^F5t&UEq(u-wEg@PnUZQxFqhwTNW8m1AK#$aeECo>x>K`dn(YBKYf^2k5-w^B&2eY7RQacEaO zM0@*gBF6oN@Ai`z*Vd1krNlS?u(&z=9lTS&|Bg%Y)a{CBhbu_sSNSjOog(d!pbaCf zpzNT%UC)#g{jS^Jxqme{tv`tv16U7*Fu1%vW@jSYflhukB^>{*pCz(->fn}WtYhkxn2bGIV z+U8fF_;UCv);1u2)%MH9@E*kCl@z%$w?(PvjBx8R&+Si-yX=^{e66QcFQ=dH<^9f! z=MP{zTqeWq2N5%wSn`y{oTRglYggyYO`Ar2UWi2INg~GLm)5Hv85mx@ zzt=iy(rX!tlif&{ZR@k!HNo7DT1YSvR+{er(4Ckv!Ds(DqMljkj5*DOde}Wo9RV4Xo7Z0fK^`B(qqvd4=|D1y1t?6BNDh`S#rIF7c$MZWC8zvZ>@?{o8t=G#9 z-og97G&zKXbX2|_+JEZKntWkTF0J~=_(Wl0f3!-n z6s=DzZ>~^H3w=yS_a&;mbG6HBO)X<0Dld?Fq9u2)EbiwhIh?vDvz+CKBd$VNLd#L= zL#Ir z{Y16V3(KV|9z+&oC|Qzt9nJP=Q7SNyakKWXy~@>hrh(VbzB?-DeVndOSrqq~zH9Pz z{rt`s(1!#%{u7dRsZwT|RYV^5_SRFR8PXjlf5nHXg}=z7xd`iMglv zB~rw~S3U)mSS^*5gM@)rl413Q0{ux=ag#VYPv`b%9<<3X`%q{m+QY&u||Xm;Zy2_19E>yRb01&_~m zuRo36v0k*Ub<(Tlc{Vy|lEQ1G+Hoh0AvGUZ|% z#k^O;#^xi^X9NU=-&jb_ur1%yBI<}H^15Yrf7dV!RnHuHWSQPusDhcRm{l~YcWqGf z3=TC!9cvBVB0u>RuS?AI>0_y{r>RNd=g$Gw7cb*(|1G!EC6a{^Sq$Id4O~*`x)8jf zX}ZS08VNOW)~@?oWHe*nr*hxZOzKzE{9GNMv$e^uNZCj1=clb;hxhxFRRTu*#k}g$ z_53tdMum@!)O@#qQ9pmhX59Bq3i~A`C?k0vbJ=a{b z)&e4B%TY*`ru4P8YfpQ8SV8^<$Nj;0$!R4AhFC;m*p}ZHR<1jG_W_c#>t?>7S%(nz z-HL(Pt{0aUKE>JEg zX;+fQwujsB$@#v2f8UT=q?)8x%2G=uB+{@W^ISdJA%l%`V&#}Q@B1>R@|ul& z`CQLX;Lm)g?!zuWff%#+-M3o~>eUympFSwBPx|s~FeSZZH9EbLRIjkmMdM zuj=k{dv8YKM(H}p0vY9C{L7BtH-yyysxPax(8nC07iC+_^J?77z;ZQ3a?p2n$e9Er zy^6f3n2D)yRKK$=yYwfT!htNIXAibUZChCEnR`#^D)n%15Os%{TM6coe984N_F(nQIkj&Om^uz zVmPiIOQMLoTXs9+X}-}Z0}D4H|Igptg=Buq%9C!HQ88x?^g*QIebA1R&_kmbvo?)t z#bxa!Yz9Hc_I>F4gUEZi89%uIJ-)Oar;LAN_HZIJ+oE}2FCc}&jHkxm9?8J1Dyzl? ziFm-zmd|JiYRyd-L+K~G{=ZaqzUE@?BRPGLbwi8YVdb8-{$J-s9&0xj-)!i4{FYxj zZ!qv_k?CD7V1T0QI{NL^L+nnIcI)x8_BU~+XGv=h{E0J?eL5F%*@_ ztFE`ji;z7a5!e&n8!xSH0;XZ5cT3F)+2MGKtdh+!cb58|tSDHTG~L0dFx(Q`gV_8EGQS1shYLfr zFVx#Tc-ys@vb|ntc@g`VZ5?7iBQ9^&%{ygs?9VExy8 zg{ps1ShoBWp;zGtTWwdOmbR;_Gpae4{Rne_nf6cY)SbWdUDLfrN61_5{sWra5k@#% zgP2U+5%Qb=0!@`ixhHj;S6RX$vsoWW>r0=!^f5DywxYhSgjLa9Jr(q7xNhBr(_aN2 zwU_@hp76fK*@?EuI(<9cYRiA9Qp*FLhgJDpcLmfZUk$s`Onnwa+xg*#AOcJ292A|c zOJhtbyYjsuC+S_++@f?rZMtbvC!2xNS;|5=HH>ZI!GH>4MdYrSP(bSTedaXz=r3zK zL0d%f;?y_gtV$t9!JS;Cf!&dJ>~;a)s%F3KzH6*9{N{b%?$eP*sWYUdQDM=%UJ<-? z6()G|FwO4M+F!SGjfU#e!eLJ$KQ|imjkBO-hMIyc)GNrwey3IU znO6BL1!TlRV{S93*ssr|(Il}}zjp;^aeiU@)SJs|Y2(2?z=pY1nRPpeTmrn#Y4QeT zHu)W+n!%LVQ5%t)AmXOUAGsl^l@aot3_L}V^#k{5GK+OR!kyvm3|p0z)OR~JO#b5m zx8STRThdGA|lcx)pW-<>e&2zz;`+?$0ckW}E%P$@xgqFn~_#eb? z-nqKEj}Wb@gmP_Fd@@p1$@zkhvxo)%JQsgvC3-2XX53eA_hF7K9r?3OG+7BmCF?i|%~)?`&@5gsB0?l%JH4jdo#c>DpyExc^@^CXkk5S)&Iw zgUTgwu=jPtTVh;v^-Xv3;JmLnayBGJhZD8X&G%_Ow0Dxw|BQXkMiAPxkp;1MJ4(A zNlZ3XAv*iI-@I&O;o+BJ&0~Pnzb7M0e2{6D#KZfwe5P;@=12yyTPlp+!x0KJT<_%_ zgQ#A+vJ@$PvJQq>y*d$(9DR~vF8tHcG5*e@_dq6C-WwtFP$ug>$7!&T;j1~g+Q8Lw zrh}^v#)E^g*8Gf#B(9iMGEfPn<G{1lvw(eMv;B&1;o;+zqUsDNz(Uy<;Dz=lJ))O7smY@SJ)qNf1F`sL~fYgbnVj z%eq!`4!lhA?^9Q7R(y21%-y6YBxpt!yKxA7^Q$7;r#8j)OB*uJ4ODjc%C%~UG;`W$ zOYyr>dgJA0@JgRh^|zFkV1V_bvI|!cbEOlRv#<471Mh zFV^=H-erUoEVG%?C8}9Ipz;%euWE^GVBDOxOb10@M}Gz0l#M>-Sh<)hf;gQPn4}45F488 z^c9mnx~h=A8kOnyVgPuqYI(k>d)Nuk!tQ^`!J9ALo-hPQyQ_V6C*-)}Z03Yty)l7> z%;~s)dr3zy7CCnsQ+c1`J+s7LY)>^!ze0!>u27k3U|2K+Xbpcq8JQ$DOs?=oM&Fod zpvtJ3V&S%%^WKdG+_3FthRu9tEz~zV(^X)anD+$x`KM>Q>H?C zKN}>%y=Bz7+}0Yvx14Kw6J6y4LNdfmS3kPD!IJx8k9pu(=8YRJ-_@>?)!@tsCOA+5 zA}XK?u`gCoUo-gp=NwU33Mq;Qe)G{!^vYLf(G@NxV}rM33YvBiSh9^{QKp@IK^lVR z)l3#h?128IiyM=hMo{>HGNOzd@p*wN#Af2Qp)BVwDjEgj7b0uvHZGc8FC5b6?Hd$I z?vUec2XF69W$*V*3mLHm9%da`^Y}Ntson9g5h`xSa3s>cdG4#)LYWQY}A&ma?plmuq=bWYYe1)WFNxB@Ho)v1NkG+yuGc+ z@SB&Ty*93m-vJzS?HG87fIOk7LUs$3EJthF(e}kZ;;h8NmEfGZnYiS?W|n_XpC=&j z)&PB!EM-m*zrn@rsE;s>>pUEDn@pINmeiGDr3fQY?suut-McA@`W)U5cGanI>+eA& zf2&;KQFV1cLpkSJfWe~eISq}0GwHC|-NBRE0x8^OW$@6PWH|=*r4aE_S3>u?7!#b}e&4 z2iyRrWd2H0%M7;h-F@DsVuR#kuVk@lT>DT9?Bd?_+Vc}AOl;*%A?G~5_P^lvD(<3W zoivYs%*xdpvSBLZQVyMC)+7Jzftg9a74d(0U`{3kaj|-NauZO>o`=B4!;dJFr1vE@Ey>2&Aw&=S-aR^D*t-|_xc11 zxq8JP7C3pZVHPOm=x5biaFMy;j`Dd4mmb6&82W;~H~n5MP)7xNao~6*vbovuB=?p? z&Zw9St*~E}UIc$B7`Ow-x0Jqo_$P}sog%o0jT+D$zp>6CGkr?MwD%9->hm()&)`#4 z=V(^e<9kW&H-AH8xjYq)t ziaKX25arr(MRcrBxX3e~@ZPBP>~X5vs^F3&WEp1ARLa*C3=}M=oEQrVti--$%}fvc zl+Zo(P_tiBR_i3;u0YX=b4DJB{RS>(MBwspdOJYz<~E1K7NyIj=~c&;kg@a{$09i? z33c8VI_9Pb4yjcuh8KiNryevyNuiBbh}dlbd(3*sI4^uDnmr4AFOyQrQ+D@mp{We( zKPWZf;AV^`Fv-cq30i)GpE8j86(-0djoj%zuAni*|%W>iEwR-*QbM0cr1 zDGX02!wajXyocFNN#lsVQd}B#w|a_H2VgF7=kKH1fhGtVT1?^6Be}iY6aEbymc4zM z>1VF28jLgLarYj{Q&NQd7WFL5C4h|-0M0n=-%s?GcMh2aeQfGTcW}-KKDenf zcl9bo*YV(H+Hi2;ZL66iU5j*v>Nm?ybNfce4u7s}KPb)o@$_YDjvv~Iwf{YunOR&$ zuV?j|S_02CkF`+t)2|ki zD>u5mDcZ0Lpf*z5Ib++IP^%|bBrj2S=RO{)hvvLrxy3+tvr4r4=B5A z5ef{yk7Q=r31FT^@x=I(;nf+Ve`kw{HqHe1uH3^?Dz)u`8=;o+V|$p~v_zED=5|M{ zgn#udj+2JI;Xr0@c+&g_Q0iqCQ`2O&bp5cITz%8fJv`_RlQ&e+#;^R^-`#ob0>5@w zo2rjed=YqXI1W9fA`0!&94WUbzwJQc`pKzBH}QpS9l>R^H`FtIcx=MfI-7xqLGGsp zvs(G9NBo`h*OcUoPhF&u~uEo-?mZgb}BbrWE78i4rlBs$_bpja$k;->5 znh{QY${bRW_EVv;Ipu5pac;8~bR2KfrRWN8nYXf-7t7bIC|vvR{jX=Db2V}8%m+#I z|F1mx|9<$|iJuYSd?4`PmZ>tsyQf^wV+wyfPEgeP4iLMvl5tXAtg*h#ASL(uu~{=J z|EvMZU6iEtp6j0sH;+Q0@COHu@4rDqYgtlNF^~4lwRLk>UBq|J&pioOS680FquwPf zx^uj`ogjaI$pxzeM0F*CfWct+-+NaC016297|1#yT~1Cec!|i9*twvJJKCq)``c{% zCG|vyVTSjZL|y$=owqvuG@wxbgWQPVd(Xm1eS19u3>%L#_f|PoU?oIAn!odVWR0N< zdcK=)X4t7D>zmHk^`>oUy{nvPF;eHd!5+uqvw(io}~V^05XnuI$ojvm^UE(8vqrOe=>%h#sgxe z2|#By@VOEctK#^yA`@}$$T=#xuY}zaYy&5)?FOXG^HgerUmQD)+w1)WN8)N%Fl^>? zGVm;9k8sOGj`MSrnWWwQ)IiA>zkTaov@c7IrXNIj$6eUYWn8steM3pjb*OSX>klI6 zf9RVPu$1%#u%q6kb{rQ84QLe@3DrFdfnp1bB4tx0@drRDO@~3$wp2IhzIl&=s`&Q51-Af4*J+jVZyKh zRj@u6{-&4ne87p5>(ZA3W6$e<$frZ;TN0}3>He6 z%9#(#)#C4=6TgfjVw~xG;OUG4v3h#M!P5w6(6e*S3(P8^PAIqB3J+;vmPQ08c=KMI zaREcY-$!K{buR_m?C2V|aZ)gg0k=&{SiNXq_N6e2GcGw4^ok`b|S$4UXcGjb${?rrKbHeSyS;-LgZc8XZcJG%z>KIgm%L7cmG#p zopNV_b)IOzBu5*3QQ%e$l&k5yC)1#3M)4rzkPgR#`qSY}Gg>QSHYh+2>J0ZfskKT5 z)>-&ej$>pSK(pdte9WFoBPl8v;TFt*74af@l0Ig-+89R~H96Fi0*aXj2OH3%2Q5R6%5OiRvb3 z?x7>`!a)Q)ZfB-M<6q4}L1_366vEdD#2}?lev8|XATUabe=zkq$vGMq;2UC7_$s9N zh31t{a&hAP5fq(msspdsCNX8`Fe@6ZvO8)Ilyh@{l?NOgvhFxSy<6&C16eXC{cruk zSNq#s#A0#*AUrB?mvdA5b>@C$1#P{+%{>Fo zD7*dh7=GWiR9nyYkSRb9IK{Ua%0DSr8vI#C&%>wnmE6cv0E${S8;AY@8HLglP6TP2 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 literal 0 HcmV?d00001 diff --git a/_attachments/image/run-mini.png b/_attachments/image/run-mini.png new file mode 100644 index 0000000000000000000000000000000000000000..b2fcbd820d3d9c13b10b2679e4aabec92d087f98 GIT binary patch literal 478 zcmeAS@N?(olHy`uVBq!ia0vp^azM<_!3-pir1Y-?Qq09po*^6@9Je3(KLBzQ0(?ST z|Ns9#bLPynv@|%mdiCn%%a;SWyLa#2ym>QF>e{tyXU?3dt*tFAECh0&KYxDz{(Xon zQ01galR7#&zJLGz{{4HPI8gDCBS(P7LPUC`!`1`sQYZ=X3uaIM#;quFejlqo=T&r-y!*qjr(*KfS0m*_54`moqGTK*51pZIWK#kFMHSfKCb%n6W!JOPqK%V7G2{%`{@4m jx?KI&(YJ5?od1Jqn;M_VM*SV2cw_K%^>bP0l+XkK!xY}N literal 0 HcmV?d00001 diff --git a/_attachments/image/run.png b/_attachments/image/run.png new file mode 100644 index 0000000000000000000000000000000000000000..a1d79f65161a61f200daef37c5863f09700b7560 GIT binary patch literal 718 zcmeAS@N?(olHy`uVBq!ia0vp^0zjO=!3-po`I#mGDdu7)&kzm{j@u9Y9{{-<1AIbU z|NsA=1_s8)#>fmHx3;#nxVShwI~yn-85y}~(W03%XSTPu1I2;dprD{_+qSJ+w+<)> zQ~*?e?b@|7XU+h*@7}$8_Uzf=!-x0m*#k8H`}gkLr>6&E zII^WcTYP+coSdAjtgL`m=;-JqMmY)s9dBI{dD;^Z)#b9ed5%Pa*kws+2{d9b<}#Ou8~r?0N@P-*VD`CUbY z^@Y>2#2Z}#r}UdgeV)I=j3txt^2<(5$?WFe2_{ytee3KOv`@wZ+X8X6}J2X@tlyCfXMat`kSf}n0k>g(W(^wW?d|6cTmr<@hQ6hBTi^?5I Qz|dsyboFyt=akR{0L`>Z`v3p{ literal 0 HcmV?d00001 diff --git a/_attachments/image/running.png b/_attachments/image/running.png new file mode 100644 index 0000000000000000000000000000000000000000..9b50cd67319c19bccd62c2197f65e0d159e1eaa9 GIT binary patch literal 284 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2ZGmxy8xzq=wn2Vh}LpV4%Za?&Y0OX1W_=LFr z|NsBqy?cB2?wvDdPESuyb#*mR z%y{W;-5;Q!e2HsBNpOBzNqJ&XDuZuga#4P6YD#9Jf?H-$YI%N9cCmuR){ILPK&28O zrOrjEi6xo&c?uz!xv30>MtX(@dPW8Y=8lG1+(0!po-U3d95a(Wg!xQ;l0CS1c|(&0 zN>c-PQw$P01-a5VQg})^QaMsNMFmm~LQ6^u(gb*uI5=BYvAkwth?~fL*`t~B0?-@= MPgg&ebxsLQ07|u2U;qFB literal 0 HcmV?d00001 diff --git a/_attachments/image/save.png b/_attachments/image/save.png new file mode 100644 index 0000000000000000000000000000000000000000..a04e4bcc9b06b5138dc460a988646991f5a29520 GIT binary patch literal 843 zcmeAS@N?(olHy`uVBq!ia0vp^0zjO=!3-po`I#mGDdu7)&kzm{j@u9Y9{{fmHx22_}y1Kf!xHvmI8z>bO6}5Ef(wQ@7_Vo0?r6A%*j~?B>fB)94 zTY(CIB0#O-;BN5u_xJSlbar;Ova$kNq^ql|XkY&n=pDzBAirP+K_#WMj*gi#SAoH9C;-|C z1b0E;9S8s&fCKC@xCaV1&H|6fVg?3oArNM~bhqvgP*A?aHKHUqKdq!Zu_%?nH!-;= zKQ}ccGf}}UvnaJZzbLy{!D4I1r3#=@36N6fqSVBa%=|oskj&gv1|vf~14}(40|RrR z&)+vPFfcZIx;Tbd^u7(qu4*z6U||VS$i14i`chV)H-}P_C`Z_%zy4Qw8P^oCK6kfE zlQ-V%A>4!9CaoQ9QW@rdPd@29f5NmrR_{2+$+HJCHs0#l(bJ}L+2mry zON-#<)e0$g{_T%%-iqYWozc>?F`~9Uch337li`U+=d884AH9FZp$OM$hNT;fjaTI) zWb@v$yVrimN3S{ZujBn)ZTtK0*{!Sn$6Ow;#d2w}@5-HS+9CT({G`w1|Eiq#S#s&K zOp$}DJ?_qGSMkt%+vGHDspL{Y#lTac3&hC?|-KJ zp3L=F@4EVoBdY9y8_rHLxS@W?N3%Kg(UOC2 zXT{H`y}Un0uVz{B=gt0H$E?p(QMjv-SrP=zwmK^Sf%}s!Q@-~hw}$k RuLDLIgQu&X%Q~loCIGJplXn0B literal 0 HcmV?d00001 diff --git a/_attachments/image/sidebar-toggle.png b/_attachments/image/sidebar-toggle.png new file mode 100644 index 0000000000000000000000000000000000000000..3ea32ffeea53f25b8df276d7b931039803a007ba GIT binary patch literal 512 zcmeAS@N?(olHy`uVBq!ia0vp^CP3`K!3-pmM3?XZDdu7)&kzm{j@u9Y9{{phfZ}L4Lsuf`Up)#>Or#p`mGM<>eh6GiR<^wR`uOGk5R4 zd-wnUt7YrVfl4_GJR*x37`TN%nDNrxx<6q3t`Q}{`DrEPiAAXlzKO|2`MIennTZN+ znMJAP`9;~q3Km;4E>!@Pik1MCIv1rTmSpDVDTHL^rZSl88yV{xSe{Lccn4H)*we)^ z#G~`=w3B?z20Si`iT+XFOgH}jZ!Fys!0clC<5Kti%q16Zh_&+s9x?du-n*=?xo=*M zvXk4LCdLfrz84pi${5|h>0M;{^68yQvv)~{^QRle%vUzvW_$eBX`_PKq1?kS%MxE1 zWr^!EzfKK*W>h7f-+eoE`kaF+oV6M(zve|>c;%dRMD?>%UQOMrqSjBZcYHj$ay9R2 z?-SDVv|qOGdDvQ4!`+rB@yvXQ__Qz6CPy`2@!z7hq*QP1UFA#lEQcrjWO!OB{8In< SnIAy!FnGH9xvX@c2>vkx(R>mU)ewQ z_ve{&=6vS+JHI)Zd-oW2SRewCe;`OzRn_z7&+F>yMn^{-4#(o+;^gFHU}b)Oer084 zU|_)K^F4n2cxY(o*|TR$OG`5|Gsyo_(a-6;jD0Vs8S^s|w(GDBfwR&7mZUXUEk~>e zr4HMXLsCn%rK#qy<=}Q(UDaVE`YGU3fnDn0EkPuLJET$+_5wU1=`-L7i|G5p^@FgeRoFWjYX#a|XN zV*KzQb|%OrM==gi@@91+5YiM2Too}W$wSzKpfwrr5(i}T8_6_njSgiw8noCr>yGYp zBROw`zKPDcwOK)!b~Ms#KO`4-rCDM*Dek-d(4KXPB+tXKrl~!s)8F{AHypY*G{v;x zECBxPGfSQmuZCB3hk;zQLWbd#ok}#hi=6-hx}&@~m&carPrcRr=2^q}l+u)tl%^o% zb0$Cm>12kC?3w8FxMvd@)T1v9)#uNJx1+5{xVU=KZ(cP(9`TLH&q2~~ARQI@AE41F@D9csIy{?v=24{+~ zAv9(^m(P;|7*8~rwI=&aCFr>BI3fz zb^BHF&im$te8m|(>3%SDUb4Dh`=gXk(keCZC;_I3)0WF>e%7Wvp)$VScl>lGozw5@ zEH}33^+WoQxzwz;NF2;XM&4fA5}>qirkSZ}1k?Px(hjS7tic20Z`HH-g@zp;s23GD!W0qFyF#n{@Th$VC z%6mrNe>x^4pd|?StvUd3$w>E_WKKn$NM-DW*J4r6Sd5N0eAawXIxE?Bt8!2=9D(&` zb6nJxnHO$e7lli-@i%Q)672Asr;4shyZ|$OIb8&VY~wMHtdbybOP)>35ak4;RCZ|y z63MmJ9On~st?d{B2)5Q*8W)oEiy&z)43PXX@~7YH=T!a?SxUy;Gae_GsZiu@U9{I% zb0ux>d!K$BCS^R|#d22x#2r^klv3!i*I){&r`@@DwVNdCe`r)~j39`@t(82;C(MLU zsZ=loM!a#nBD)uK?5-#i5ZeTqwJD@e9$K?L(CZphr9LTD%&DdoKbn$DMe*Vd6Vi7} zCjPkBawwXYIdIw9Bz|+^QMRtbI-{TUb4AVjM{%&0^zn`K+KIMUx!~@vzOjoLieEUfEqQ-sh($>^uY1e(2EK2vzMYifzwp>eeS+3|bqLZ6?uWOgkO+ z^yG-{0&O{)`sb7P#x}faa2_xxJ**qvLiJ}z?U$CFy`jw-rnAIt97;0wGVC&Sesb&`Sned zO$ciUhP8$obsD0_qQcU`?Ck78j6o%PCF1VlW1(Z8#h-nsegFUe184)^=-=%0>=AGg zr^=_q+{FL?{{R30A^8LW2LK)bEC2ui015yK000FxpeK$VGz_GeNllh!5wK_}f-Riq n6c5BK7={P|I9Mn3 literal 0 HcmV?d00001 diff --git a/_attachments/image/thead-key.gif b/_attachments/image/thead-key.gif new file mode 100644 index 0000000000000000000000000000000000000000..42a43b583824e491f5f36f13f2d339ef6e667d2d GIT binary patch literal 77 zcmZ?wbhEHbs+0{|VP8k7J4 literal 0 HcmV?d00001 diff --git a/_attachments/image/thead.gif b/_attachments/image/thead.gif new file mode 100644 index 0000000000000000000000000000000000000000..1587b1f2fbf13b270280da227d607b0735dd699e GIT binary patch literal 51 zcmZ?wbhEHb E0Gsp=)Bpeg literal 0 HcmV?d00001 diff --git a/_attachments/image/toggle-collapse.gif b/_attachments/image/toggle-collapse.gif new file mode 100644 index 0000000000000000000000000000000000000000..f0979304ac90866070173e72ec45a8fe9f87973a GIT binary patch literal 176 zcmZ?wbhEHb8@b=ZKm(QL(e*E~sqX%~%+`E0}_VpXrE?&NP z=FG{%Cy(yib>#GpgD3az-m+`!+RmBF+Gi|dAPy-0WMN=u_|KpNQVz0{fmJs_xHsjA wi^D3WNn4gVN}t)Bu;tK_*Bo{NElf%mtyDx#urA(O;V{p?bFpi#r4xfS02_Zs&;S4c literal 0 HcmV?d00001 diff --git a/_attachments/image/toggle-expand.gif b/_attachments/image/toggle-expand.gif new file mode 100644 index 0000000000000000000000000000000000000000..03fa8360dd9ef3b5960972dc8840b7bda2f2293d GIT binary patch literal 181 zcmZ?wbhEHb8@b=ZKm(QL(e)Qn(gL}8{+`fL}+QrKk&s;ix z^32J@Cy(yib>#GpgD1BiJh6ZGmfc&|ZQr!2bLO)48Ou00I2iB(#h)w;>;= zb~3OUB^dWUdE(=+ifM5a>jw_@X$m5X10wgdK4ETJTJXb{r=!JnV?uxiw}*>UZ-C!< Kou`6K4AuZSJ4_Y; literal 0 HcmV?d00001 diff --git a/_attachments/image/twisty.gif b/_attachments/image/twisty.gif new file mode 100644 index 0000000000000000000000000000000000000000..5ba57a1ad89c991ba8feddd495472963cf4a458f GIT binary patch literal 160 zcmV;R0AK${Nk%w1VG96n0HOc@`}_Oq>gv|k*15U4rKP2ml$3;ogv`v$A^8LW2LJ~E zEC2ui01E(b000AJcyioMBsgbG8DO4Ti^vVfd7vl~jA?!8$c7fCL6WA}c&_h!@BhG{ za7Zi~kI1BQ$!t2G(5Q4uty-_xtai(F3J59#VF6a54uOKHP&*7v0|!9e0W}1i#bj== OJMelM00e$J2>?4CP(yP7 literal 0 HcmV?d00001 diff --git a/_attachments/index.html b/_attachments/index.html new file mode 100644 index 0000000..9c4d836 --- /dev/null +++ b/_attachments/index.html @@ -0,0 +1,346 @@ + + + + + Overview + + + + + + + +
+

+   +   +

+
+
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/_attachments/old.layout.css b/_attachments/old.layout.css new file mode 100644 index 0000000..4fff498 --- /dev/null +++ b/_attachments/old.layout.css @@ -0,0 +1,618 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + +*/ + +/* General styles */ + +html, body { color: #000; font: normal 90% Arial,Helvetica,sans-serif; + height: 100%; margin: 0; padding: 0; overflow: hidden; +} +:link, :visited { color: #ba1e16; text-decoration: none; } +:link img, :visited img { border: none; } + +h1 { background: #333; border-right: 2px solid #111; + border-bottom: 1px solid #333; color: #999; + font: normal 125% Arial,Helvetica,sans-serif; height: 32px; + line-height: 32px; margin: 0; padding: 0 0 0 .5em; position: relative; +} +h1 :link, h1 :visited, h1 strong { padding: .4em .5em; } +h1 :link, h1 :visited { + background: url(../image/path.gif) 100% 50% no-repeat; + color: #bbb; cursor: pointer; padding-right: 2.2em; + text-shadow: #333 2px 2px 1px; +} +h1 strong { color: #fff; font-weight: normal; padding-right: 25px; } +h1 strong a {color:#fff !important;background:none !important;} +h1 :link.raw, h1 :visited.raw { + background: url(../image/rarrow.png) 100% 50% no-repeat; position: absolute; + right: 20px; width: 35px; height: 100%; padding: 0; margin: 0; +} +body.loading h1 strong { + background: url(../image/spinner.gif) right center no-repeat; +} + +hr { border: 1px solid #999; border-width: 1px 0 0; } +dl dt { font-weight: bold; } +code, tt, pre { + font-family: "DejaVu Sans Mono",Menlo,Courier,monospace; +} +code.key { color: #333; font-weight: bold; } +code.string { color: #393; } +code.number, code.boolean { color: #339; } +code.null { color: #666; } + +button { font-size: 100%; -webkit-appearance: square-button; } +button[disabled] { color: #999; } +input, select, textarea { background: #fff; border: 1px solid; + border-color: #999 #ddd #ddd #999; color: #000; margin: 0; padding: 1px; +} +input.placeholder { color: #999; } +textarea { + font-family: "DejaVu Sans Mono",Menlo,Courier,monospace; + font-size: 100%; +} +fieldset { border: none; font-size: 95%; margin: 0; padding: .2em 0 0; } +fieldset legend { color: #666; font-weight: bold; padding: 0; } +fieldset input, fieldset select { font-size: 95%; } +fieldset p { margin: .4em; } + +p.help { color: #999; font-size: 90%; margin: 0 2em 1em; } + +/* Tabular listings */ + +table.listing { border-collapse: separate; border-spacing: 0; + border: 1px solid #a7a7a7; clear: both; width: 100%; +} +table.listing caption { display: none; } +table.listing th, table.listing td { padding: .2em .5em; } +table.listing thead th { background: #dadada url(../image/thead.gif) repeat-x; + border: 1px solid #a7a7a7; border-width: 0 0 1px 1px; color: #333; + font-size: 95%; font-weight: normal; text-align: left; + text-shadow: #999 2px 1px 2px; white-space: nowrap; +} +table.listing thead th:first-child { border-left: none; } +table.listing thead th.key { + background: #a7afb6 url(../image/thead-key.gif) 0 0 repeat-x; + padding-top: 2px; +} +table.listing thead th.key span { + background: url(../image/order-asc.gif) 100% 3px no-repeat; cursor: pointer; + padding-right: 20px; +} +table.listing thead th.desc span { + background-image: url(../image/order-desc.gif); +} +table.listing tbody tr th, table.listing tbody tr td { background: #feffea; } +table.listing tbody tr.odd th, table.listing tbody tr.odd td, +table.listing tbody.odd tr th, table.listing tbody.odd tr td { + background: #fff; +} +table.listing tbody th, table.listing tbody td { + border-left: 1px solid #d9d9d9; padding: .4em .5em; vertical-align: top; +} +table.listing tbody th:first-child, table.listing tbody td:first-child { + border-left: none; +} +table.listing tbody th { text-align: left; } +table.listing tbody th :link, table.listing tbody th :visited { + display: block; +} +table.listing tbody.footer tr td { background: #e9e9e9; + border-top: 1px solid #a7a7a7; color: #999; font-size: 90%; + line-height: 1.8em; +} +table.listing tbody.footer #paging { float: right; } +table.listing tbody.footer #paging a, +table.listing tbody.footer #paging label { + padding: 0 .5em; +} +table.listing tbody.footer #paging label { color: #666; } +table.listing tbody.footer #paging select { font-size: 90%; padding: 0; } + +/* Inline editing */ + +span.editinline-tools { margin: 2px 2px 0; float: right; margin-right: -45px; } +span.editinline-tools button { background: transparent 0 0 no-repeat; + border: none; cursor: pointer; display: block; float: left; margin: 0 .2em; + width: 11px; height: 11px; +} +span.editinline-tools button:hover { background-position: 0 -22px; } +span.editinline-tools button:active { background-position: 0 -44px; } +span.editinline-tools button.apply { + background-image: url(../image/apply.gif); +} +span.editinline-tools button.cancel { + background-image: url(../image/cancel.gif); +} + +/* Resizer grippies */ + +div.grippie { background: #e9e9e9 url(../image/grippie.gif) 50% 50% no-repeat; + border: 1px solid #aaa; border-top: none; min-height: 10px; +} + +/* Suggest results */ + +ul.suggest-dropdown { border: 1px solid #999; background-color: #eee; + padding: 0; margin: 0; list-style: none; opacity: .85; + -moz-box-shadow: 2px 2px 10px #333; -webkit-box-shadow: 2px 2px 10px #333; +} +ul.suggest-dropdown li { padding: 2px 5px; white-space: nowrap; color: #101010; + text-align: left; +} +ul.suggest-dropdown li.selected { cursor: pointer; background: Highlight; + color: HighlightText; +} + +/* Logo & Navigation */ + +#sidebar { background: #fff; position: absolute; top: 0; right: -210px; + width: 210px; height: 100%; +} +body.fullwidth #sidebar { border-bottom: 1px solid #333; right: 0; + width: 26px; +} +#sidebar-toggle { background: url(../image/sidebar-toggle.png) 0 0 no-repeat; + color: #999; cursor: pointer; display: block; position: absolute; right: 0; + top: 0; font-size: 110%; width: 26px; height: 32px; text-indent: -9999px; +} +#sidebar-toggle:hover { background-position: -26px 0; } +#sidebar-toggle:focus { outline: none; } +#sidebar.hidden #sidebar-toggle { background-position: 0 -32px; } +#sidebar.hidden #sidebar-toggle:hover { background-position: -26px -32px; } + +#logo { margin: 30px 0 0; padding: 0 18px 10px; } + +#nav { color: #333; font-size: 110%; font-weight: bold; list-style: none; + margin: 0; overflow: auto; overflow-x: hidden; padding: 0; width: 210px; +} +#nav ul { list-style: none; margin: 0; padding: 0; } +#nav li { color: #999; margin: 5px 0 0; padding: 3px 0; } +#nav li span { padding: 0 20px; } +#nav li.selected { background: #e9e9e9; } +#nav li li { font-size: 90%; font-weight: normal; margin: 0; + padding: 2px 20px 2px 40px; +} +#nav li li:hover { background: #e4e4e4; } +#nav li.selected li:hover { background: #d7d7d7; } +#nav li li :link, #nav li li :visited { color: #333; display: block; + overflow: hidden; text-decoration: none; text-overflow: ellipsis; +} +#nav li li :link:hover, #nav li li :visited:hover { color: #000; } +#nav li li :link:focus, #nav li li :visited:focus { outline: none; } +#nav li li.selected { background: #aaa !important; border-top: 1px solid #999; + color: #fff; padding-top: 1px; +} +#nav li li.selected :link, #nav li li.selected :visited { color: #fff; } +#nav li li.selected :link:hover, #nav li li.selected :visited:hover { + color: #fff; +} +#nav li button { background: transparent 0 0 no-repeat; border: none; + cursor: pointer; width: 15px; height: 15px; margin-left: -20px; + position: absolute; vertical-align: top; +} +#nav li li:hover button.remove { + background-image: url(../image/delete-mini.png); +} +#nav li button.remove:hover { background-position: -15px 0; } + +#footer { background: #ddd; border-top: 1px solid #bbb; color: #000; + font-size: 80%; opacity: .7; padding: 5px 10px; position: absolute; right: 0; + bottom: 0; min-height: 1.3em; width: 190px; text-align: right; +} +#footer .couch :link, #footer .couch :visited { color: #000; } + +#userCtx span { display:none; } + +#wrap { background: #fff url(../image/bg.png) 100% 0 repeat-y; + height: 100%; margin-right: 210px; position: relative; +} +body.fullwidth #wrap { margin-right: 0; } +#content { padding: 1em 16px 3em 10px; overflow: auto; overflow-y: scroll; + position: absolute; top: 33px; bottom: 0; left: 0; right: 0; +} + +/* Toolbar */ + +#toolbar { font-size: 90%; line-height: 16px; list-style: none; + margin: 0 0 .5em; padding: 5px 5px 5px 3px; +} +#toolbar li { display: inline; } +#toolbar li.current {float:right;} +#toolbar button { background: transparent 2px 2px no-repeat; border: none; + color: #666; margin: 0; padding: 2px 1em 2px 22px; cursor: pointer; + font-size: 95%; line-height: 16px; +} +#toolbar button:hover { background-position: 2px -30px; color: #000; } +#toolbar button:active { background-position: 2px -62px; color: #000; } +#toolbar button.add { background-image: url(../image/add.png); } +#toolbar button.security { background-image: url(../image/key.png); } +#toolbar button.compact { background-image: url(../image/compact.png); } +#toolbar button.delete { background-image: url(../image/delete.png); } +#toolbar button.load { background-image: url(../image/load.png); } +#toolbar button.run { background-image: url(../image/run.png); } +#toolbar button.save { background-image: url(../image/save.png); } +#toolbar button.share { background-image: url(../image/compact.png); } + +/* Dialogs */ + +#overlay { background: #bbb; cursor: wait; position: fixed; width: 100%; + height: 100%; top: 0; left: 0; +} +*html #overlay { position: absolute; + width: expression(document.body.clientWidth + 'px'); + height: expression(document.body.clientHeight + 'px'); +} +#dialog { background: #333 url(../image/progress.gif) 50% 50% no-repeat; + color: #f4f4f4; overflow: hidden; opacity: .95; max-width: 33em; + padding: 1em 1em 0; -moz-border-radius: 7px; + -moz-box-shadow: 4px 4px 6px #333; -webkit-border-radius: 7px; + -webkit-box-shadow: 4px 4px 6px #333; +} +*html #dialog { width: 33em; } +#dialog.loading { width: 220px; height: 80px; } +#dialog.loaded { background-image: none; } +#dialog h2 { background: #666 98% 50% no-repeat; + border-top: 1px solid #555; border-bottom: 1px solid #777; color: #ccc; + font-size: 110%; font-weight: bold; margin: 0 -1em; padding: .35em 1em; +} +body.loading #dialog h2 { + background-image: url(../image/spinner.gif); +} +#dialog h3 { color: #ccc; font-size: 100%; font-weight: bold; margin: 0 -2em; + padding: .35em 2em 0; +} +#dialog fieldset { background: #222; border-top: 1px solid #111; + margin: 0 0 1em; padding: .5em 1em 1em; + -moz-border-radius-bottomleft: 7px; -moz-border-radius-bottomright: 7px; + -webkit-border-bottom-left-radius: 7px; + -webkit-border-bottom-right-radius: 7px; +} +#dialog p.help { color: #bbb; font-size: 95%; margin: 0 0 1em; } +#dialog fieldset table { margin-top: 1em; } +#dialog fieldset th, #dialog fieldset td { padding: .5em; + vertical-align: top; +} +#dialog fieldset th { color: #999; font-weight: bold; + text-align: right; +} +#dialog fieldset input { background-color: #e9e9e9; vertical-align: middle; } +#dialog fieldset input.error { background-color: #f9e4e4; } +#dialog fieldset div.error { padding-top: .3em; color: #b33; } +#dialog fieldset.radiogroup { padding-top: 1em; } +#dialog fieldset.radiogroup label { position: relative; padding-left: 25px; } +#dialog fieldset.radiogroup input { position: absolute; left: 5px; top: 2px; } +#dialog fieldset.radiogroup p.help { margin-top: .5em; margin-left: 25px; } +#dialog fieldset.radiogroup hr { border-color: #333; margin-left: 25px; } +#dialog .buttons { padding: 0 .5em .5em; text-align: right; } +#dialog .buttons button { background: #444; border: 1px solid #aaa; + color: #ddd; cursor: pointer; font-size: 90%; font-weight: normal; + margin: 0 0 0 5px; padding: .2em 2em; -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} +#dialog .buttons button[type=submit] { font-weight: bold; } +#dialog .buttons button:hover { background: #555; } +#dialog .buttons button:active { background: #333; color: #fff; } + +#dialog fieldset td#progress { + background: url(../image/progress.gif) 50% 50% no-repeat; + visibility: hidden; +} + +/* Document quick jump */ + +#jumpto { float: right; padding: 5px 10px 5px 5px; line-height: 16px; + font-weight: bold; color: #666; font-size: 90%; } + +#jumpto input { font-size: 90%; } + +/* View selector */ + +#switch { color: #666; float: right; font-size: 90%; font-weight: bold; + line-height: 16px; padding: 5px; +} +#switch select { font-size: 90%; } + +/* Stale views checkbox */ + +#staleviews { + color: #666; float: right; font-size: 90%; + font-weight: bold; line-height: 16px; padding: 5px; +} + +/* View function editing */ + +#viewcode { background: #fff; border: 1px solid; + border-color: #999 #ddd #ddd #999; margin: 0 0 1em; overflow: hidden; +} +#viewcode .top, #viewcode .bottom { background-color: #e9e9e9; + border: 1px solid; border-color: #ddd #ddd #e9e9e9 #ddd; color: #333; + padding: 0 .5em 2px; +} +#viewcode .top { border-bottom: 1px solid #ddd; color: #aaa; font-size: 95%; } +#viewcode .top span { border: none; color: #666; cursor: pointer; + display: block; font-size: 90%; margin: 0; padding: 2px 0 0; +} +#viewcode .top span#view-toggle { + background: url(../image/twisty.gif) 0 -96px no-repeat; padding-left: 15px; +} +#viewcode.collapsed .top span#view-toggle { background-position: 0 4px; } +#viewcode .top a { float: right; font-size: 90%; line-height: 1.4em; + padding: 2px 2px 0 0; +} +#viewcode .top a:link, #viewcode .top a:visited { color: #999; } +#viewcode table { border: none; border-collapse: separate; border-spacing: 0; + margin: 0; table-layout: fixed; width: 100%; max-width: 100%; +} +#viewcode table td { border: none; padding: 0; } +#viewcode table td.splitter { background: #e9e9e9; width: 4px; } +#viewcode table td.map { border-right: 1px solid #ccc; } +#viewcode table td.reduce { border-left: 1px solid #ccc; } +#viewcode .code label { font-size: 90%; color: #999; padding: 0 .5em; + white-space: nowrap; +} +#viewcode .code textarea { border: none; border-top: 1px solid #ccc; + color: #333; font-size: 11px; margin: 0; min-height: 50px; overflow: auto; + padding: .4em 0 0; resize: none; width: 100%; +} +#viewcode .code textarea:focus { background: #e9f4ff; } +#viewcode .bottom { border-bottom: none; clear: left; padding: 1px 3px; } +#viewcode .bottom button { font-size: 90%; margin: 0 1em 0 0; + padding-left: 2em; padding-right: 2em; +} +*html #viewcode .bottom button { padding: 0 .5em; } +*+html #viewcode .bottom button { padding: 0 .5em; } +#viewcode .bottom button.revert, #viewcode .bottom button.save, +#viewcode .bottom button.saveas { + float: right; margin: 0 0 0 1em; +} +#viewcode .bottom button.save { font-weight: bold; } +#viewcode .bottom label { color: #666; font-size: 90%; } +#viewcode .grippie { background-position: 50% 50%; } +#viewcode.collapsed { background: #e9e9e9; } +#viewcode.collapsed .top { border-bottom: none; } +#viewcode.collapsed .top span { background-position: 0 3px; } +#viewcode.collapsed table, #viewcode.collapsed .bottom { display: none; } + +#tempwarn { display: none; font-size: 90%; margin: 0 2em 1.5em; } +#grouptruenotice { display: none; font-size: 90%; margin: 1ex 2em 1.5em; } + +/* Database table */ + +#databases thead th.size, #databases thead th.count, #databases thead th.seq, +#databases tbody td.size, #databases tbody td.count, #databases tbody td.seq { + text-align: right; +} + +/* Documents table */ + +#documents thead th { line-height: 150%; width: 50%; } +#documents thead th label { color: #333; float: right; font-size: 90%; + text-shadow: none; +} +#documents thead th label.disabled { color: #777; } +#documents thead th label input { vertical-align: middle; } +#documents thead th label input[type=range] { width: 7em; } +#documents thead th label output { width: 4em; display: inline-block; } +#documents tbody.content td { color: #999; + font: normal 11px "DejaVu Sans Mono",Menlo,Courier,monospace; +} +#documents tbody.content td.key { color: #333; } +#documents tbody.content td.key a { display: block; } +#documents tbody.content td.key a strong { font-weight: normal; } +#documents tbody.content td.key span.docid { color: #999; + font: normal 10px Arial,Helvetica,sans-serif; +} +#documents tbody.content td.value { font-size: 10px; } + +/* Document display tabs */ + +#tabs { float: right; list-style: none; margin: -1.4em 0 0; } +#tabs li { display: inline; font-size: 95%; padding: 0; } +#tabs li.active { font-weight: bold; } +#tabs :link, #tabs :visited { background: #dadada; color: #666; + border: 1px solid #a7a7a7; float: left; margin: 0 0 0 .5em; + padding: .5em 2em .3em; position: relative; top: 1px; +} +#tabs .active :link, #tabs .active :visited { background: #e9e9e9; + border-bottom-color: #e9e9e9; color: #333; +} +#tabs :link:focus, #tabs :visited:focus { outline: none; } + +/* Document fields table */ + +#fields { clear: right; table-layout: fixed; } +#fields col.field { width: 33%; } +#fields tbody.content th { padding-left: 25px; padding-right: 48px; } +#fields tbody.content th button.delete { + background: url(../image/delete-mini.png) no-repeat; border: none; + cursor: pointer; float: left; margin: .2em 5px 0 -20px; padding: 0; + width: 15px; height: 15px; +} +#fields tbody.content th button.delete:hover { background-position: -15px 0; } +#fields tbody.content th b { display: block; padding: 2px 2px 2px 3px; } +#fields tbody.content th b.editinline-container { padding: 0; } +#fields tbody.content td { color: #999; padding-left: 14px; + padding-right: 48px; +} +#fields tbody.content td code { display: block; font-size: 11px; + padding: 2px 2px 2px 3px; position: relative; +} +#fields tbody.content td code.string { white-space: pre-wrap; } +#fields tbody.content td code.string:before { color: #ccc; content: "“"; + position: absolute; left: -4px; +} +#fields tbody.content td code.string:after { color: #ccc; content: "”"; } + +#fields tbody.content td dl { margin: 0; padding: 0; } +#fields tbody.content td dt { + background: transparent url(../image/toggle-collapse.gif) 0 3px no-repeat; + clear: left; color: #333; cursor: pointer; line-height: 1em; + margin-left: -12px; padding-left: 14px; +} +#fields tbody.content td dd { line-height: 1em; margin: 0; + padding: 0 0 0 1em; +} +#fields tbody.content td dt.collapsed { + background-image: url(../image/toggle-expand.gif); +} +#fields tbody.content td dt.inline { background-image: none; cursor: default; + float: left; margin-left: 0; padding-left: 2px; padding-right: .5em; + padding-top: 2px; +} +#fields tbody.content td dd code.string { left: 4px; text-indent: -6px; } +#fields tbody.content td dd code.string:before { position: static; } +#fields tbody.content input, #fields tbody.content textarea, +#fields tbody.source textarea { + background: #fff; border: 1px solid; border-color: #999 #ddd #ddd #999; + margin: 0; padding: 1px; width: 100%; +} +#fields tbody.content th input { font-family: inherit; font-size: inherit; + font-weight: bold; +} +#fields tbody.content td input, #fields tbody.content td textarea, +#fields tbody.source textarea { + font: normal 11px "DejaVu Sans Mono",Menlo,Courier,monospace; +} +#fields tbody.content input.invalid, +#fields tbody.content textarea.invalid, +#fields tbody.source textarea.invalid { + background: #f9f4f4; border-color: #b66 #ebb #ebb #b66; +} +#fields tbody.content div.grippie, #fields tbody.source div.grippie { + padding: 0 1px; width: 100%; +} +#fields tbody.content div.error, #fields tbody.source div.error { + color: #d33; +} + +#fields tbody.content td ul.attachments { list-style: none; margin: 0; + padding: 0; +} +#fields tbody.content td ul.attachments li { + margin-bottom: .3em; min-height: 20px; padding-left: 20px; +} +#fields tbody.content td ul.attachments tt { font-size: 11px; } +#fields tbody.content td ul.attachments li span.info { color: #666; + display: block; font-size: 95%; +} +#fields tbody.content td ul.attachments li button { + background: transparent no-repeat; border: none; cursor: pointer; + float: left; margin: 0 2px 0 -20px; padding: 0; width: 15px; height: 15px; + vertical-align: middle; +} +#fields tbody.content td ul.attachments li button:hover { + background-position: -15px 0; +} +#fields tbody.content td ul.attachments li button.delete { + background-image: url(../image/delete-mini.png); +} +#fields tbody.source td pre { color: #999; font-size: 11px; line-height: 1.6em; + margin: 0; overflow: auto; white-space: pre-wrap; width: 100%; +} +#fields tbody.source td.editinline-container { padding-left: 14px; padding-right: 48px; } + +/* Test suite */ + +#tests { table-layout: fixed; } +#tests thead th.name { width: 20%; } +#tests thead th.status { padding-left: 20px; width: 10em; } +#tests thead th.duration { text-align: right; width: 7em; } +#tests tbody.content th { cursor: help; padding-left: 25px; + white-space: nowrap; +} +#tests tbody.content th button.run { + background: url(../image/run-mini.png) no-repeat; border: none; + cursor: pointer; float: left; margin: .2em 5px 0 -20px; padding: 0; + width: 15px; height: 15px; +} +#tests tbody.content th button.run:hover { background-position: -15px 0; } +#tests tbody.content td.duration { text-align: right; width: 6em; } +#tests tbody.content td.status { background-position: 5px 8px; + background-repeat: no-repeat; color: #999; padding-left: 20px; +} +#tests tbody.content td.details { width: 50%; } +#tests tbody.content td.details a { border-bottom: 1px dashed #ccc; + color: #999; float: right; font-size: 85%; +} +#tests tbody.content td.details ol { color: #999; margin: 0; + padding: 0 0 0 1.5em; +} +#tests tbody.content td.details ol b { color: #333; font-weight: normal; } +#tests tbody.content td.details ol code { color: #c00; font-size: 100%; } +#tests tbody.content td.details ol code.error { white-space: pre; } +#tests tbody.content td.running { + background-image: url(../image/running.png); color: #333; +} +#tests tbody.content td.success, span.success { + background-image: url(../image/test_success.gif) no-repeat; color: #060; +} +#tests tbody.content td.error, #tests tbody.content td.failure, span.failure { + background-image: url(../image/test_failure.gif) no-repeat; color: #c00; +} + +/* Configuration */ + +#config tbody th { background: #e6e6e6; border-right: none; + border-top: 1px solid #d9d9d9; +} +#config tbody td.name { border-left: 1px solid #d9d9d9; color: #333; + font-weight: bold; +} +#config tbody td.value { padding: 1px 48px 1px 1px; } +#config tbody td.value code { display: block; font-size: 11px; + padding: 2px 2px 2px 3px; +} +#config tbody td.value code.editinline-container { padding: 0; } +#config tbody td input { + background: #fff; border: 1px solid; border-color: #999 #ddd #ddd #999; + font: normal 11px "DejaVu Sans Mono",Menlo,Courier,monospace; + margin: 0; padding: 1px; width: 100%; +} + +/* Replication */ + +form#replicator { background: #f4f4f4; border: 1px solid; + border-color: #999 #ccc #ccc #999; margin: .5em 1em 1.5em; padding: .5em; + -moz-border-radius: 7px; -webkit-border-radius: 7px; +} +form#replicator fieldset { float: left; padding: 1px; } +form#replicator p.swap { float: left; margin: 2em 0 0; padding: 1px 1em; } +form#replicator p.swap button { background: transparent; border: none; + color: #666; cursor: pointer; font-size: 150%; +} +form#replicator p.swap button:hover { color: #000; } +form#replicator p.actions { padding: 1px; clear: left; margin: 0; + text-align: right; +} + +/* Active tasks */ + +#interval { color: #666; float: right; font-size: 90%; font-weight: bold; + line-height: 16px; padding: 5px; +} +#interval input { vertical-align: top; } +#interval .secs { display: inline-block; width: 2em; text-align: right; } + +#status tr.none th { color: #666; font-weight: normal; } +#status td.object, #status td.pid { + font-family: "DejaVu Sans Mono",Menlo,Courier,monospace; + font-size: 11px; +} + + +/* Session */ +#loginSignup { + font-size:200%; +} \ No newline at end of file diff --git a/_attachments/script/base64.js b/_attachments/script/base64.js new file mode 100644 index 0000000..e0aab30 --- /dev/null +++ b/_attachments/script/base64.js @@ -0,0 +1,124 @@ +/* Copyright (C) 1999 Masanao Izumo + * Version: 1.0 + * LastModified: Dec 25 1999 + * This library is free. You can redistribute it and/or modify it. + */ + /* Modified by Chris Anderson to not use CommonJS */ + /* Modified by Dan Webb not to require Narwhal's binary library */ + +var Base64 = {}; +(function(exports) { + + var encodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var decodeChars = [ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 + ]; + + exports.encode = function (str) { + var out, i, length; + var c1, c2, c3; + + length = len(str); + i = 0; + out = []; + while(i < length) { + c1 = str.charCodeAt(i++) & 0xff; + if(i == length) + { + out.push(encodeChars.charCodeAt(c1 >> 2)); + out.push(encodeChars.charCodeAt((c1 & 0x3) << 4)); + out.push("=".charCodeAt(0)); + out.push("=".charCodeAt(0)); + break; + } + c2 = str.charCodeAt(i++); + if(i == length) + { + out.push(encodeChars.charCodeAt(c1 >> 2)); + out.push(encodeChars.charCodeAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4))); + out.push(encodeChars.charCodeAt((c2 & 0xF) << 2)); + out.push("=".charCodeAt(0)); + break; + } + c3 = str.charCodeAt(i++); + out.push(encodeChars.charCodeAt(c1 >> 2)); + out.push(encodeChars.charCodeAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4))); + out.push(encodeChars.charCodeAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6))); + out.push(encodeChars.charCodeAt(c3 & 0x3F)); + } + + var str = ""; + out.forEach(function(chr) { str += String.fromCharCode(chr) }); + return str; + }; + + exports.decode = function (str) { + var c1, c2, c3, c4; + var i, length, out; + + length = len(str); + i = 0; + out = []; + while(i < length) { + /* c1 */ + do { + c1 = decodeChars[str.charCodeAt(i++) & 0xff]; + } while(i < length && c1 == -1); + if(c1 == -1) + break; + + /* c2 */ + do { + c2 = decodeChars[str.charCodeAt(i++) & 0xff]; + } while(i < length && c2 == -1); + if(c2 == -1) + break; + + out.push(String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4))); + + /* c3 */ + do { + c3 = str.charCodeAt(i++) & 0xff; + if(c3 == 61) + return out.join(''); + c3 = decodeChars[c3]; + } while(i < length && c3 == -1); + if(c3 == -1) + break; + + out.push(String.fromCharCode(((c2 & 0xF) << 4) | ((c3 & 0x3C) >> 2))); + + /* c4 */ + do { + c4 = str.charCodeAt(i++) & 0xff; + if(c4 == 61) + return out.join(''); + c4 = decodeChars[c4]; + } while(i < length && c4 == -1); + + if(c4 == -1) + break; + + out.push(String.fromCharCode(((c3 & 0x03) << 6) | c4)); + } + + return out.join(''); + }; + + var len = function (object) { + if (object.length !== undefined) { + return object.length; + } else if (object.getLength !== undefined) { + return object.getLength(); + } else { + return undefined; + } + }; +})(Base64); diff --git a/_attachments/script/couch.js b/_attachments/script/couch.js new file mode 100644 index 0000000..bcc1965 --- /dev/null +++ b/_attachments/script/couch.js @@ -0,0 +1,473 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// A simple class to represent a database. Uses XMLHttpRequest to interface with +// the CouchDB server. + +function CouchDB(name, httpHeaders) { + this.name = name; + this.uri = "/" + encodeURIComponent(name) + "/"; + + // The XMLHttpRequest object from the most recent request. Callers can + // use this to check result http status and headers. + this.last_req = null; + + this.request = function(method, uri, requestOptions) { + requestOptions = requestOptions || {}; + requestOptions.headers = combine(requestOptions.headers, httpHeaders); + return CouchDB.request(method, uri, requestOptions); + }; + + // Creates the database on the server + this.createDb = function() { + this.last_req = this.request("PUT", this.uri); + CouchDB.maybeThrowError(this.last_req); + return JSON.parse(this.last_req.responseText); + }; + + // Deletes the database on the server + this.deleteDb = function() { + this.last_req = this.request("DELETE", this.uri); + if (this.last_req.status == 404) { + return false; + } + CouchDB.maybeThrowError(this.last_req); + return JSON.parse(this.last_req.responseText); + }; + + // Save a document to the database + this.save = function(doc, options) { + if (doc._id == undefined) { + doc._id = CouchDB.newUuids(1)[0]; + } + + this.last_req = this.request("PUT", this.uri + + encodeURIComponent(doc._id) + encodeOptions(options), + {body: JSON.stringify(doc)}); + CouchDB.maybeThrowError(this.last_req); + var result = JSON.parse(this.last_req.responseText); + doc._rev = result.rev; + return result; + }; + + // Open a document from the database + this.open = function(docId, options) { + this.last_req = this.request("GET", this.uri + encodeURIComponent(docId) + + encodeOptions(options)); + if (this.last_req.status == 404) { + return null; + } + CouchDB.maybeThrowError(this.last_req); + return JSON.parse(this.last_req.responseText); + }; + + // Deletes a document from the database + this.deleteDoc = function(doc) { + this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id) + + "?rev=" + doc._rev); + CouchDB.maybeThrowError(this.last_req); + var result = JSON.parse(this.last_req.responseText); + doc._rev = result.rev; //record rev in input document + doc._deleted = true; + return result; + }; + + // Deletes an attachment from a document + this.deleteDocAttachment = function(doc, attachment_name) { + this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id) + + "/" + attachment_name + "?rev=" + doc._rev); + CouchDB.maybeThrowError(this.last_req); + var result = JSON.parse(this.last_req.responseText); + doc._rev = result.rev; //record rev in input document + return result; + }; + + this.bulkSave = function(docs, options) { + // first prepoulate the UUIDs for new documents + var newCount = 0; + for (var i=0; i= n) { + var uuids = CouchDB.uuids_cache.slice(CouchDB.uuids_cache.length - n); + if(CouchDB.uuids_cache.length - n == 0) { + CouchDB.uuids_cache = []; + } else { + CouchDB.uuids_cache = + CouchDB.uuids_cache.slice(0, CouchDB.uuids_cache.length - n); + } + return uuids; + } else { + CouchDB.last_req = CouchDB.request("GET", "/_uuids?count=" + (buf + n)); + CouchDB.maybeThrowError(CouchDB.last_req); + var result = JSON.parse(CouchDB.last_req.responseText); + CouchDB.uuids_cache = + CouchDB.uuids_cache.concat(result.uuids.slice(0, buf)); + return result.uuids.slice(buf); + } +}; + +CouchDB.maybeThrowError = function(req) { + if (req.status >= 400) { + try { + var result = JSON.parse(req.responseText); + } catch (ParseError) { + var result = {error:"unknown", reason:req.responseText}; + } + throw result; + } +} + +CouchDB.params = function(options) { + options = options || {}; + var returnArray = []; + for(var key in options) { + var value = options[key]; + returnArray.push(key + "=" + value); + } + return returnArray.join("&"); +}; diff --git a/_attachments/script/couch_test_runner.js b/_attachments/script/couch_test_runner.js new file mode 100644 index 0000000..d159902 --- /dev/null +++ b/_attachments/script/couch_test_runner.js @@ -0,0 +1,443 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// *********************** Test Framework of Sorts ************************* // + + + +function loadScript(url) { + // if (typeof document != "undefined") document.write(''); + var head= document.getElementsByTagName('head')[0]; + var script= document.createElement('script'); + script.type= 'text/javascript'; + script.src= url; + head.appendChild(script); +}; + +function patchTest(fun) { + var source = fun.toString(); + var output = ""; + var i = 0; + var testMarker = "T("; + while (i < source.length) { + var testStart = source.indexOf(testMarker, i); + if (testStart == -1) { + output = output + source.substring(i, source.length); + break; + } + var testEnd = source.indexOf(");", testStart); + var testCode = source.substring(testStart + testMarker.length, testEnd); + output += source.substring(i, testStart) + "T(" + testCode + "," + JSON.stringify(testCode); + i = testEnd; + } + try { + return eval("(" + output + ")"); + } catch (e) { + return null; + } +} + +function runAllTests() { + var rows = $("#tests tbody.content tr"); + $("td", rows).text(""); + $("td.status", rows).removeClass("error").removeClass("failure").removeClass("success").text("not run"); + var offset = 0; + function runNext() { + if (offset < rows.length) { + var row = rows.get(offset); + runTest($("th button", row).get(0), function() { + offset += 1; + setTimeout(runNext, 100); + }, false, true); + } else { + saveTestReport(); + } + } + runNext(); +} + +var numFailures = 0; +var currentRow = null; + +function runTest(button, callback, debug, noSave) { + + // offer to save admins + if (currentRow != null) { + alert("Can not run multiple tests simultaneously."); + return; + } + var row = currentRow = $(button).parents("tr").get(0); + $("td.status", row).removeClass("error").removeClass("failure").removeClass("success"); + $("td", row).text(""); + var id = row.id.replace('test-', '') + $("#toolbar li.current").text("Running: "+id); + var testFun = couchTests[id]; + function run() { + numFailures = 0; + var start = new Date().getTime(); + try { + if (debug == undefined || !debug) { + testFun = patchTest(testFun) || testFun; + } + testFun(debug); + var status = numFailures > 0 ? "failure" : "success"; + } catch (e) { + var status = "error"; + if ($("td.details ol", row).length == 0) { + $("
    ").appendTo($("td.details", row)); + } + $("
  1. Exception raised:
  2. ") + .find("code").text(JSON.stringify(e)).end() + .appendTo($("td.details ol", row)); + if (debug) { + currentRow = null; + throw e; + } + } + if ($("td.details ol", row).length) { + $("Run with debugger").click(function() { + runTest(this, undefined, true); + }).prependTo($("td.details ol", row)); + } + var duration = new Date().getTime() - start; + $("td.status", row).removeClass("running").addClass(status).text(status); + $("td.duration", row).text(duration + "ms"); + $("#toolbar li.current").text("Finished: "+id); + updateTestsFooter(); + currentRow = null; + if (callback) callback(); + if (!noSave) saveTestReport(); + } + $("td.status", row).addClass("running").text("running…"); + setTimeout(run, 100); +} + +function showSource(cell) { + var name = $(cell).text(); + var win = window.open("", name, "width=700,height=500,resizable=yes,scrollbars=yes"); + win.document.location = "script/test/" + name + ".js"; +} + +var readyToRun; +function setupAdminParty(fun) { + if (readyToRun) { + fun(); + } else { + function removeAdmins(confs, doneFun) { + // iterate through the config and remove current user last + // current user is at front of list + var remove = confs.pop(); + if (remove) { + $.couch.config({ + success : function() { + removeAdmins(confs, doneFun); + } + }, "admins", remove[0], null); + } else { + doneFun(); + } + }; + $.couch.session({ + success : function(resp) { + var userCtx = resp.userCtx; + if (userCtx.name && userCtx.roles.indexOf("_admin") != -1) { + // admin but not admin party. dialog offering to make admin party + $.showDialog("dialog/_admin_party.html", { + submit: function(data, callback) { + $.couch.config({ + success : function(conf) { + var meAdmin, adminConfs = []; + for (var name in conf) { + if (name == userCtx.name) { + meAdmin = [name, conf[name]]; + } else { + adminConfs.push([name, conf[name]]); + } + } + adminConfs.unshift(meAdmin); + removeAdmins(adminConfs, function() { + callback(); + $.futonSidebar(); + readyToRun = true; + setTimeout(fun, 500); + }); + } + }, "admins"); + } + }); + } else if (userCtx.roles.indexOf("_admin") != -1) { + // admin party! + readyToRun = true; + fun(); + } else { + // not an admin + alert("Error: You need to be an admin to run the tests."); + }; + } + }); + } +}; + +function updateTestsListing() { + var names = []; + for (name in couchTests) names.push(name) + names.sort(); + names.splice(names.indexOf('basics'), 1); + names.unshift('basics'); + + for (var i=0;i") + .find("th").text(names[i]).attr("title", "Show source").click(function() { + showSource(this); + }).end() + .find("td:nth(0)").addClass("status").text("not run").end() + .find("td:nth(1)").addClass("duration").end() + .find("td:nth(2)").addClass("details").end(); + $("").click(function() { + this.blur(); + var self = this; + // check for admin party + setupAdminParty(function() { + runTest(self); + }); + return false; + }).prependTo(row.find("th")); + row.attr("id", 'test-'+names[i]).appendTo("#tests tbody.content"); + } + $("#tests tr").removeClass("odd").filter(":odd").addClass("odd"); + updateTestsFooter(); +} + +function updateTestsFooter() { + var tests = $("#tests tbody.content tr td.status"); + var testsRun = tests.filter(".success, .error, .failure"); + var testsFailed = testsRun.not(".success"); + var totalDuration = 0; + $("#tests tbody.content tr td.duration:contains('ms')").each(function() { + var text = $(this).text(); + totalDuration += parseInt(text.substr(0, text.length - 2), 10); + }); + $("#tests tbody.footer td").html(""+testsRun.length + " of " + tests.length + + " test(s) run, " + testsFailed.length + " failures (" + + totalDuration + " ms) "); +} + +// make report and save to local db +// display how many reports need replicating to the mothership +// have button to replicate them + +function saveTestReport(report) { + var report = makeTestReport(); + if (report) { + var db = $.couch.db("test_suite_reports"); + var saveReport = function(db_info) { + report.db = db_info; + $.couch.info({success : function(node_info) { + report.node = node_info; + db.saveDoc(report); + }}); + }; + var createDb = function() { + db.create({success: function() { + db.info({success:saveReport}); + }}); + }; + db.info({error: createDb, success:saveReport}); + } +}; + +function makeTestReport() { + var report = {}; + report.summary = $("#tests tbody.footer td").text(); + report.platform = testPlatform(); + var date = new Date(); + report.timestamp = date.getTime(); + report.timezone = date.getTimezoneOffset(); + report.tests = []; + $("#tests tbody.content tr").each(function() { + var status = $("td.status", this).text(); + if (status != "not run") { + var test = {}; + test.name = this.id; + test.status = status; + test.duration = parseInt($("td.duration", this).text()); + test.details = []; + $("td.details li", this).each(function() { + test.details.push($(this).text()); + }); + if (test.details.length == 0) { + delete test.details; + } + report.tests.push(test); + } + }); + if (report.tests.length > 0) return report; +}; + +function testPlatform() { + var b = $.browser; + var bs = ["mozilla", "msie", "opera", "safari"]; + for (var i=0; i < bs.length; i++) { + if (b[bs[i]]) { + return {"browser" : bs[i], "version" : b.version}; + } + }; + return {"browser" : "undetected"}; +} + + +function reportTests() { + // replicate the database to couchdb.couchdb.org +} + +// Use T to perform a test that returns false on failure and if the test fails, +// display the line that failed. +// Example: +// T(MyValue==1); +function T(arg1, arg2, testName) { + if (!arg1) { + if (currentRow) { + if ($("td.details ol", currentRow).length == 0) { + $("
      ").appendTo($("td.details", currentRow)); + } + var message = (arg2 != null ? arg2 : arg1).toString(); + $("
    1. Assertion " + (testName ? "'" + testName + "'" : "") + " failed:
    2. ") + .find("code").text(message).end() + .appendTo($("td.details ol", currentRow)); + } + numFailures += 1; + } +} + +function TEquals(expected, actual, testName) { + T(equals(expected, actual), "expected '" + repr(expected) + + "', got '" + repr(actual) + "'", testName); +} + +function TEqualsIgnoreCase(expected, actual, testName) { + T(equals(expected.toUpperCase(), actual.toUpperCase()), "expected '" + repr(expected) + + "', got '" + repr(actual) + "'", testName); +} + +function equals(a,b) { + if (a === b) return true; + try { + return repr(a) === repr(b); + } catch (e) { + return false; + } +} + +function repr(val) { + if (val === undefined) { + return null; + } else if (val === null) { + return "null"; + } else { + return JSON.stringify(val); + } +} + +function makeDocs(start, end, templateDoc) { + var templateDocSrc = templateDoc ? JSON.stringify(templateDoc) : "{}"; + if (end === undefined) { + end = start; + start = 0; + } + var docs = []; + for (var i = start; i < end; i++) { + var newDoc = eval("(" + templateDocSrc + ")"); + newDoc._id = (i).toString(); + newDoc.integer = i; + newDoc.string = (i).toString(); + docs.push(newDoc); + } + return docs; +} + +function run_on_modified_server(settings, fun) { + try { + // set the settings + for(var i=0; i < settings.length; i++) { + var s = settings[i]; + var xhr = CouchDB.request("PUT", "/_config/" + s.section + "/" + s.key, { + body: JSON.stringify(s.value), + headers: {"X-Couch-Persist": "false"} + }); + CouchDB.maybeThrowError(xhr); + s.oldValue = xhr.responseText; + } + // run the thing + fun(); + } finally { + // unset the settings + for(var j=0; j < i; j++) { + var s = settings[j]; + if(s.oldValue == "\"\"\n") { // unset value + CouchDB.request("DELETE", "/_config/" + s.section + "/" + s.key, { + headers: {"X-Couch-Persist": "false"} + }); + } else { + CouchDB.request("PUT", "/_config/" + s.section + "/" + s.key, { + body: s.oldValue, + headers: {"X-Couch-Persist": "false"} + }); + } + } + } +} + +function stringFun(fun) { + var string = fun.toSource ? fun.toSource() : "(" + fun.toString() + ")"; + return string; +} + +function waitForSuccess(fun, tag) { + var start = new Date(); + while(true) { + if (new Date() - start > 5000) { + throw("timeout: "+tag); + } else { + try { + fun(); + break; + } catch (e) {} + // sync http req allow async req to happen + CouchDB.request("GET", "/test_suite_db/?tag="+encodeURIComponent(tag)); + } + } +} + +function waitForRestart() { + var waiting = true; + while (waiting) { + try { + CouchDB.request("GET", "/"); + CouchDB.request("GET", "/"); + waiting = false; + } catch(e) { + // the request will fail until restart completes + } + } +}; + +function restartServer() { + var xhr; + try { + CouchDB.request("POST", "/_restart"); + } catch(e) { + // this request may sometimes fail + } + waitForRestart(); +} + diff --git a/_attachments/script/couch_tests.js b/_attachments/script/couch_tests.js new file mode 100644 index 0000000..b0cd68f --- /dev/null +++ b/_attachments/script/couch_tests.js @@ -0,0 +1,135 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + + + +// Used by replication test +if (typeof window == 'undefined' || !window) { + CouchDB.host = "127.0.0.1:5984"; + CouchDB.protocol = "http://"; + CouchDB.inBrowser = false; +} else { + CouchDB.host = window.location.host; + CouchDB.inBrowser = true; + CouchDB.protocol = window.location.protocol + "//"; +} + +CouchDB.urlPrefix = ".."; +var couchTests = {}; +var allCouchTests = [ + "basics.js" + , "all_docs.js" + , "attachments.js" + , "attachments_multipart.js" + , "attachment_names.js" + , "attachment_paths.js" + , "attachment_views.js" + , "auth_cache.js" + , "batch_save.js" + , "bulk_docs.js" + , "changes.js" + , "compact.js" + , "config.js" + , "conflicts.js" + , "content_negotiation.js" + , "cookie_auth.js" + , "copy_doc.js" + , "delayed_commits.js" + , "design_docs.js" + , "design_options.js" + , "design_paths.js" + , "erlang_views.js" + , "etags_head.js" + , "etags_views.js" + , "form_submit.js" + , "http.js" + , "invalid_docids.js" + , "jsonp.js" + , "large_docs.js" + , "list_views.js" + , "lots_of_docs.js" + , "method_override.js" + , "multiple_rows.js" + , "oauth.js" + , "proxyauth.js" + , "purge.js" + , "reader_acl.js" + , "recreate_doc.js" + , "reduce.js" + , "reduce_builtin.js" + , "reduce_false.js" + , "reduce_false_temp.js" + , "replication.js" + , "replicator_db.js" + , "rev_stemming.js" + , "rewrite.js" + , "security_validation.js" + , "show_documents.js" + , "stats.js" + , "update_documents.js" + , "users_db.js" + , "utf8.js" + , "uuids.js" + , "view_collation.js" + , "view_collation_raw.js" + , "view_conflicts.js" + , "view_compaction.js" + , "view_errors.js" + , "view_include_docs.js" + , "view_multi_key_all_docs.js" + , "view_multi_key_design.js" + , "view_multi_key_temp.js" + , "view_offsets.js" + , "view_pagination.js" + , "view_sandboxing.js" + , "view_update_seq.js" + , "view_xml.js" +] + +function loadTests (tests, callback) { + // Load a list of tests + + var loadScript = function (url, callback){ + // Load a single script, fire callback when finished. + var script = document.createElement("script") + script.type = "text/javascript"; + + if (script.readyState){ //IE + script.onreadystatechange = function(){ + if (script.readyState == "loaded" || + script.readyState == "complete"){ + script.onreadystatechange = null; + callback(); + } + }; + } else { //Others + script.onload = function(){ + callback(); + }; + } + + script.src = url; + document.body.appendChild(script); + } + + var l = tests.length + , i = 0 + , failed = [] + ; + + tests.forEach(function (test) { + loadScript('script/test/'+test, function () { + i += 1; + if (i === l) callback() + }); + }) +} \ No newline at end of file diff --git a/_attachments/script/futon-dialogs.js b/_attachments/script/futon-dialogs.js new file mode 100644 index 0000000..2125071 --- /dev/null +++ b/_attachments/script/futon-dialogs.js @@ -0,0 +1,286 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +(function($) { + + $.fn.centerBox = function() { + return this.each(function() { + var s = this.style; + s.left = (($(window).width() - $(this).width()) / 2) + "px"; + s.top = (($(window).height() - $(this).height()) / 2) + "px"; + }); + } + + $.showDialog = function(url, options) { + options = options || {}; + options.load = options.load || function() {}; + options.cancel = options.cancel || function() {}; + options.validate = options.validate || function() { return true }; + options.submit = options.submit || function() {}; + + var overlay = $('
      ') + .css("opacity", "0"); + var dialog = $(''); + if ($.browser.msie) { + var frame = $('') + .css("opacity", "0").appendTo(document.body); + if (parseInt($.browser.version)<7) { + dialog.css("position", "absolute"); + overlay.css("position", "absolute"); + $("html,body").css({width: "100%", height: "100%"}); + } + } + overlay.appendTo(document.body).fadeTo(100, 0.6); + dialog.appendTo(document.body).centerBox().fadeIn(400); + + $(document).keydown(function(e) { + if (e.keyCode == 27) dismiss(); // dismiss on escape key + }); + function dismiss() { + dialog.fadeOut("fast", function() { + $("#dialog, #overlay, #overlay-frame").remove(); + }); + $(document).unbind("keydown"); + } + overlay.click(function() { dismiss(); }); + + function showError(name, message) { + var input = dialog.find(":input[name=" + name + "]"); + input.addClass("error").next("div.error").remove(); + $('
      ').text(message).insertAfter(input); + } + + $.get(url, function(html) { + $(html).appendTo(dialog); + dialog.centerBox().each(function() { + options.load(dialog.children()[0]); + $(":input:first", dialog).each(function() { this.focus() }); + $("button.cancel", dialog).click(function() { // dismiss on cancel + dismiss(); + options.cancel(); + }); + $("form", dialog).submit(function(e) { // invoke callback on submit + e.preventDefault(); + dialog.find("div.error").remove().end().find(".error").removeClass("error"); + var data = {}; + $.each($("form :input", dialog).serializeArray(), function(i, field) { + data[field.name] = field.value; + }); + $("form :file", dialog).each(function() { + data[this.name] = this.value; // file inputs need special handling + }); + options.submit(data, function callback(errors) { + if ($.isEmptyObject(errors)) { + dismiss(); + } else { + for (var name in errors) { + showError(name, errors[name]); + } + } + }); + return false; + }); + }); + }); + } + + $.futonDialogs = { + createDatabase : function() { + $.showDialog("dialog/_create_database.html", { + submit: function(data, callback) { + if (!data.name || data.name.length == 0) { + callback({name: "Please enter a name."}); + return; + } + $.couch.db(data.name).create({ + error: function(status, id, reason) { callback({name: reason}) }, + success: function(resp) { + location.hash = "#/" + encodeURIComponent(data.name); + callback(); + } + }); + } + }); + return false; + } + + , deleteDatabase : function(dbName) { + $.showDialog("dialog/_delete_database.html", { + submit: function(data, callback) { + $.couch.db(dbName).drop({ + success: function(resp) { + callback(); + location.href = "#/"; + if (window !== null) { + $("#dbs li").filter(function(index) { + return $("a", this).text() == dbName; + }).remove(); + // $.futon.navigation.removeDatabase(dbName); + } + } + }); + } + }); + } + + , compactAndCleanup : function(dbName) { + var db = $.couch.db(dbName); + $.showDialog("dialog/_compact_cleanup.html", { + submit: function(data, callback) { + switch (data.action) { + case "compact_database": + db.compact({success: function(resp) { callback() }}); + break; + case "compact_views": + var groupname = page.viewName.substring(8, + page.viewName.indexOf("/_view")); + db.compactView(groupname, {success: function(resp) { callback() }}); + break; + case "view_cleanup": + db.viewCleanup({success: function(resp) { callback() }}); + break; + } + } + }); + } + } + +})(jQuery); + +// Also add pretty JSON + +(function($) { + var _escape = function(string) { + return string.replace(/&/g, "&") + .replace(//g, ">"); + }; + + // JSON pretty printing + $.formatJSON = function (val, options) { + options = $.extend({ + escapeStrings: true, + indent: 4, + linesep: "\n", + quoteKeys: true + }, options || {}); + var itemsep = options.linesep.length ? "," + options.linesep : ", "; + + function format(val, depth) { + var tab = []; + for (var i = 0; i < options.indent * depth; i++) tab.push(""); + tab = tab.join(" "); + + var type = typeof val; + switch (type) { + case "boolean": + case "number": + case "string": + var retval = val; + if (type == "string" && !options.escapeStrings) { + retval = indentLines(retval.replace(/\r\n/g, "\n"), tab.substr(options.indent)); + } else { + if (options.html) { + retval = escape(JSON.stringify(val)); + } else { + retval = JSON.stringify(val); + } + } + if (options.html) { + retval = "" + retval + ""; + } + return retval; + + case "object": { + if (val === null) { + if (options.html) { + return "null"; + } + return "null"; + } + if (val.constructor == Date) { + return JSON.stringify(val); + } + + var buf = []; + + if (val.constructor == Array) { + buf.push("["); + for (var index = 0; index < val.length; index++) { + buf.push(index > 0 ? itemsep : options.linesep); + buf.push(tab, format(val[index], depth + 1)); + } + if (index >= 0) { + buf.push(options.linesep, tab.substr(options.indent)); + } + buf.push("]"); + if (options.html) { + return "" + buf.join("") + ""; + } + + } else { + buf.push("{"); + var index = 0; + for (var key in val) { + buf.push(index > 0 ? itemsep : options.linesep); + var keyDisplay = options.quoteKeys ? JSON.stringify(key) : key; + if (options.html) { + if (options.quoteKeys) { + keyDisplay = keyDisplay.substr(1, keyDisplay.length - 2); + } + keyDisplay = "" + _escape(keyDisplay) + ""; + if (options.quoteKeys) { + keyDisplay = '"' + keyDisplay + '"'; + } + } + buf.push(tab, keyDisplay, + ": ", format(val[key], depth + 1)); + index++; + } + if (index >= 0) { + buf.push(options.linesep, tab.substr(options.indent)); + } + buf.push("}"); + if (options.html) { + return "" + buf.join("") + ""; + } + } + + return buf.join(""); + } + } + } + + function indentLines(text, tab) { + var lines = text.split("\n"); + for (var i in lines) { + lines[i] = (i > 0 ? tab : "") + _escape(lines[i]); + } + return lines.join("
      "); + } + + return format(val, 1); + }; + + // File size pretty printing + var formatSize = function(size) { + var jump = 512; + if (size < jump) return size + " bytes"; + var units = ["KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + var i = 0; + while (size >= jump && i < units.length) { + i += 1; + size /= 1024 + } + return size.toFixed(1) + ' ' + units[i - 1]; + } +})(jQuery); diff --git a/_attachments/script/futon.js b/_attachments/script/futon.js new file mode 100644 index 0000000..e0f3a84 --- /dev/null +++ b/_attachments/script/futon.js @@ -0,0 +1,849 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// A simple class to represent a database. Uses XMLHttpRequest to interface with +// the CouchDB server. + +var app = {}; +window.app = app; + +var isEven = function (someNumber) { + return (someNumber%2 === 0) ? true : false; +}; +var sum = function (arr) { + var s = 0; + for (var i=0;i= jump && i < units.length) { + i += 1; + size /= 1024; + } + return size.toFixed(1) + ' ' + units[i - 1]; +}; +var getQuery = function () { + if (window.location.hash.indexOf('?') !== -1) { + query = window.location.hash.slice(window.location.hash.indexOf('?') + 1); + } else { + return; + } + var s = query.split('&'); + var r = {}; + for (var i=0;i min_width) { + min_width = this_width; + } + }); + return min_width; +} + +var param = function( a ) { + // Query param builder from jQuery, had to copy out to remove conversion of spaces to + + var s = []; + if ( jQuery.isArray(a) || a.jquery ) { + jQuery.each( a, function() { + add( this.name, this.value ); + }); + } else { + for ( var prefix in a ) { + buildParams( prefix, a[prefix] ); + } + } + return s.join("&"); + + function buildParams( prefix, obj ) { + if ( jQuery.isArray(obj) ) { + jQuery.each( obj, function( i, v ) { + if ( /\[\]$/.test( prefix ) ) { + add( prefix, v ); + } else { + buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v ); + } + }); + } else if ( obj != null && typeof obj === "object" ) { + jQuery.each( obj, function( k, v ) { + buildParams( prefix + "[" + k + "]", v ); + }); + } else { + add( prefix, obj ); + } + } + + function add( key, value ) { + value = jQuery.isFunction(value) ? value() : value; + s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value); + } +}; + +var request = function (options, callback) { + options.success = function (obj, t, xhr) { + if (xhr.status === 0) callback(xhr); + else callback(null, obj); + }; + options.error = function (err) { + if (err) callback(err); + else callback(true); + }; + if (options.data && typeof options.data == 'object') { + options.data = JSON.stringify(options.data); + } + options.processData = false; + options.dataType = 'json'; + options.contentType = 'application/json'; + $.ajax(options); +}; + +var handleError = function (err, resp) { + if (!resp) { + resp = err.responseText; + } + try { + resp = JSON.parse(resp); + } catch(error) {} + var e = $('
      '); + if (err.status) { + e.append(''+err.status+''); + } + else if (err.status === 0) { + e.append('Lost Connection'); + } + + e.append(''+resp.error || err.statusText || resp +''); + if (e.find('span.error-title').text() == "undefined") { + e.find('span.error-title').text(''); + } + if (resp.error) { + e.append('
      ').append(''+resp.reason+''); + } + + // Because of futon's crazy scroll constraints we can't leave the error + // container in the default html and have to append it to content when it's not there + + if (!$('div#error-container').length) { + $('div#content').prepend('
      '); + } + e.appendTo('div#error-container'); + var r = $('') + .click(function () { + $(this).remove(); + e.remove(); + }); + e.parent().append(r); + var p = e.position(); + r.css({ + left:p.left+e.outerWidth()+5, + top:p.top + (e.outerHeight() / 2) - (r.outerHeight()/2) + }); + if (console) { + console.log(err); + } + throw {err:err, resp:resp, e:e}; +}; + +$.expr[":"].exactly = function(obj, index, meta, stack){ + return ($(obj).text() == meta[3]); +}; + +app.showIndex = function () { + this.title('Overview'); + var t = this, + a = arguments; + this.render('templates/index.mustache').replace('#content').then(function () { + app.loadIndex.apply(t, a); + }); +}; + +app.showDocument = function () { + this.title('/'+this.params.db + '/'+this.params.docid); + var t = this, + a = arguments; + this.render('templates/document.mustache', this.params).replace('#content').then(function () { + app.loadDocument.apply(t, a); + }); +}; + +app.showChanges = function () { + var db = this.params.db, + _this = this, + a = arguments, + query = getQuery(), + url = '/'+encodeURIComponent(db)+'/_changes'; + + if (query) { + url += ('?' + param(query)); + } + + this.title('/'+db+'/_changes'); + this.addDBTopBar(db); + this.setCurrentTab(); + + this.load(url).then(function(content) { + if (typeof content != 'object') { + content = JSON.parse(content); + } + $(content.results).each(function(idx, el) { + el.baseUrl = '#/'+db+'/'; + el.prior_seq = el.seq-1; + }); + this.render('templates/changes.mustache', content).replace('#content'); + }); +}; + +app.showConfig = function () { + this.title('Configuration'); + $('span#topbar').html('Configuration'); + this.render('templates/config.mustache').replace('#content').then(function () { + + }); +}; + +app.showStats = function () { + this.title('Status'); + $('span#topbar').html('Status'); + this.render('templates/stats.mustache').replace('#content').then(function () { + + }); +}; + +app.showTests = function () { + this.title('Test Suite'); + $('span#topbar').html('Test Suite'); + this.render('templates/tests.mustache').replace('#content').then(function () { + + }); +}; +app.showReplicator = function () { + this.title('Replicator'); + $('span#topbar').html('Replicator'); + this.render('templates/replicator.mustache').replace('#content').then(function () { + + }); +}; +var ddoc_; + +app.showView = function () { + var db = this.params.db, + ddoc = this.params.ddoc, + view = this.params.view, + _this = this, + _args = arguments; + + this.title('/'+db+'/_design'+(ddoc ? '/'+ddoc+(view ? '/_view/'+view : '') : '')); + + var refresh = function () { + var h = '#/' + encodeURIComponent(db) + '/_design/' + ddoc + '/_view/' + view, + query = {}; + $('input.qinput').each(function (i, n) { + n = $(n); + var name = n.attr('name'), + type = n.attr('type'), + val = n.val(); + if (type == "text") { + if (val.length > 0) { + if (name == "skip" || name == "limit" || name == "group_level") { + query[name] = parseInt(n.val(), 10); + } else if (name == "startkey_docid" || name == "endkey_docid" ) { + if (val[0] == '"') { + query[name] = val; + } else { + query[name] = JSON.stringify(val); + } + } else if (name == "startkey" || name == "endkey" || name == "key") { + if (val[0] == '"' || val[0] == '[' || val[0] == '{') { + query[name] = val; + } else { + query[name] = JSON.stringify(val); + } + } + } + } else if (type == "checkbox" && n.attr("checked")) { + if (name == "stale") { + query[name] = 'ok'; + } else { + query[name] = 'true'; + } + } + }); + + window.location.hash = h + '?' + param(query); + }; + + var setupViews = function () { + + var updateResults = function () { + var c = $('tbody.content'), + url = window.location.hash.replace('#',''); + + c.html(''); + request({url:url}, function (err, resp) { + $('td#viewfoot').html(''); + if (!resp) { + err = JSON.parse(err.responseText); + $('td#viewfoot').append($( + '
      Error : ' + err.error + '
      ' + + '
      Reason : '+ err.reason + '
      ' + )); + } else { + $('th.doc').remove(); + if (getQuery() && getQuery().include_docs) { + $('tr.viewhead').append('doc').find('span') + .click(function () {$('span.expand-doc').click(); }); + } + var odd = 'even'; + resp.rows.forEach(function (row) { + var tr = $('' + + '' + + '
      ' + + ''+JSON.stringify(row.key)+'' + + '^' + + '\>' + + '\<' + + '
      ' + + '
      ' + + 'ID: ' + row.id + '' + + '\>' + + '\<' + + '
      ' + + '' + + '' + $.formatJSON(row.value) + '' + + ''); + if (row.doc) { + var expand = function () { + var d = $('' + + $.formatJSON(row.doc) + '' + ); + var collapse = function () { + d.remove(); + $(this).text('⟱').css('cursor', 'pointer').unbind('click', collapse); + $(this).click(expand); + }; + $(this).text('⟰').css('cursor', 'pointer').unbind('click', expand); + $(this).click(collapse); + d.insertAfter(tr); + }; + + $('' + '' + '') + .click(expand) + .appendTo(tr) + ; + } + if (odd == 'odd') { + odd = 'even'; + } else { + odd = 'odd'; + } + c.append(tr); + }); + + // Add quick links for setting key, startkey, endkey, startkey_docid & endkey_docid + $("span.viewstart").click(function () { + var c = $(this).parent(); + if (c.attr('class') == 'viewkey') { + $("input[name='startkey']").val(c.text().slice(0, c.text().length - 3)).change(); + } else if (c.attr('class') == 'docid') { + $("input[name='startkey_docid']").val(c.text().slice(4, c.text().length - 2)).change(); + } + }); + $("span.viewend").click(function () { + var c = $(this).parent(); + if (c.attr('class') == 'viewkey') { + $("input[name='endkey']").val(c.text().slice(0, c.text().length - 3)).change(); + } else if (c.attr('class') == 'docid') { + $("input[name='endkey_docid']").val(c.text().slice(4, c.text().length - 2)).change(); + } + }); + $("span.viewkey").click(function () { + var c = $(this).parent(); + $("input[name='key']").val(c.text().slice(0, c.text().length - 3)).change(); + }); + + // Add view result info + $('td#viewfoot').append( + '
      total_rows' + + resp.total_rows + '
      '+ + '
      offset' + + resp.offset + '
      ' + + '
      rows' + + resp.rows.length + '
      ' + ); + } + }); + }; + + var release = function () { + // Clear all fields + $("input.qinput[type='text']").val(''); + $("input.qinput[type='checkbox']").attr('checked', false); + // Repopulate all the fields from the url + var query = getQuery(); + for (i in query) { + var n = $('input[name='+i+']'), + type = n.attr("type"); + if (type == "text") { + n.val(query[i]); + } else if (type == 'checkbox' && (query[i] == 'true' || query[i] == 'ok')) { + n.attr('checked', 'true'); + } + } + + if (!$('input.quinput[name=limit]').attr('released')) { + $('*.qinput').css('color', '#1A1A1A'); + $('*.qinput').attr('disabled', false); + + if (!ddoc_.views[view] && !ddoc_.views[view].reduce) { + $('input.reduce').attr('disabled', true); + $('span.reduce').css('color', '#A1A1A1'); + } + + $("input.qinput[type='checkbox']").click(refresh); + $("input[type='text']").change(refresh); + $("input.qinput[name='limit']").attr('released', true); + } + + // refresh(); + updateResults(); + }; + + if (!$("div.view-ddoc").length) { + // No views in the list, populat + request({url: '/' + encodeURIComponent(db) + + '/_all_docs?startkey="_design/"&endkey="_design0"&include_docs=true'}, + function (err, docs) { + if (err) handleError(err, docs); + $("div#view-selection").attr('loaded', true); + var s = $('div#ddoc-selection'); + var getAddView = function () { + var addView = $('
      new
      '); + addView.click(function () { + var self = $(this); + $('') + .change(function () { + view = $(this).val(); + $("span.add-view").parent().before('
      '+view+'
      '); + $('div#view-editor').show(); + $('textarea#view-editor-reduce').val(''); + $('textarea#view-editor-map').val('').focus(); + $(this).remove(); + self.show(); + }) + .appendTo(self.parent()) + .focus(); + self.hide(); + }); + return addView; + }; + + docs.rows.forEach(function (row) { + var populate = function () { + var v = $("div#ddoc-view-selection"); + v.html(''); + if (row.doc.views) { + for (viewName in row.doc.views) { + $('
      '+viewName+'edit
      ') + .appendTo(v) + .click(function () { + window.location.hash = "#/"+encodeURIComponent(db)+'/'+row.id+'/_view/'+encodeURIComponent(viewName)+'?limit=10'; + }); + } + } + v.append(getAddView()); + $("span.edit-view") + .click(function () { + var v = $(this).parent().text(); + v = v.slice(0, v.length -4); + $('div#view-editor').show(); + $('textarea#view-editor-map').val(ddoc_.views[v].map); + $('textarea#view-editor-reduce').val(ddoc_.views[v].reduce); + }); + ddoc_ = row.doc; + if (view) { + release(); + } + }; + + $('
      '+row.id+'
      ') + .click(function () { + populate(); + $("div.view-ddoc-selected").removeClass("view-ddoc-selected"); + $(this).addClass("view-ddoc-selected"); + $("*.qinput").attr('disabled', true).css('color', '#A1A1A1'); + $("tbody.content").html(''); + $('td#viewfoot').html(''); + $('div#view-editor').hide(); + window.location.hash = "#/"+encodeURIComponent(db)+'/'+row.id+'/_view/'; + }) + .appendTo(s); + if ('_design/'+ddoc == row.id) { + populate(); + } + }); + $('
      new
      ') + .click(function () { + $('') + .appendTo($(this).parent()) + .change(function () { + var id = $(this).val(); + if (id.slice(0, '_design/'.length) !== '_design/') { + id = '_design/'+id; + } + $(this).parent().append('
      '+id+'
      '); + $(this).remove(); + ddoc = id.replace('_design/', ''); + ddoc_ = {_id:id, views:{}}; + var av = getAddView(); + $("div#ddoc-view-selection") + .html('') + .append(av) + ; + av.click(); + }) + .focus() + ; + $(this).remove(); + }) + .appendTo(s) + ; + }); + $('span.save-view-button') + .unbind('click') + .click(function () { + var m = $('textarea#view-editor-map').val(), + r = $('textarea#view-editor-reduce').val(); + if (!ddoc_.views) { + ddoc_.views = {}; + } + ddoc_.views[view] = {}; + if (m.length) ddoc_.views[view].map = m; + if (r.length) ddoc_.views[view].reduce = r; + request({url:'/'+encodeURIComponent(db), type:'POST', data:ddoc_}, function (err, resp) { + if (err) handleError(err, resp); + $('div#content').html(''); + var oldHash = window.location.hash, + h = '#/' + encodeURIComponent(db) + '/_design/' + ddoc + + '/_view/' + view + "?limit=10"; + if (oldHash !== h) { + window.location.hash = h; + } else { + app.showView.apply(_this, _args); + } + }); + }) + ; + + } else if (view) { + release(); + } + }; + + $('span#topbar').html('Overview'+db+'_view'); + if ($('div#query-options').length === 0) { + this.render('templates/view.mustache').replace('#content').then(setupViews); + } else {setupViews();} +}; + +app.showDatabase = function (context) { + var db = this.params.db, + query = getQuery(); + + this.title('/'+db); + + var init = function (context) { + context.addDBTopBar(db); + context.setCurrentTab(); + + var dbs = []; + $(context.cache('dbs')).each(function (i, el) { + dbs.push({'db':el, 'url':encodeURIComponent(el)}); + }); + context.render($('#all-dbs-template'), {dbs:dbs, current: function () { return this.db == db; }}) + .appendTo('#current-db'); + $("#topbar button.down").click(function () { + $('#all-dbs').slideToggle(); + }); + $("#toolbar button.add").click( function () { + $("div#content").html(''); + request({url:'/_uuids'}, function (err, resp) { + if (err) handleError(err, resp); + location.hash = '#/' + db + '/' + resp.uuids[0]; + }); + // location.hash = "#/" + db + '/_new'; + }); + $("#toolbar button.compact").click(function () { + $.futonDialogs.compactAndCleanup(db); + }); + + $("#toolbar button.delete").click(function (){ + $.futonDialogs.deleteDatabase(db); + }); + // $("#toolbar button.security").click(page.databaseSecurity); TODO : New security UI + + // JumpToDoc + $('input#jumptodoc').change(function () { + window.location.hash = '#/' + db + '/' + $(this).val(); + }); + + var addquery = function () { + // This function adds the _all_docs startkey/endkey query options + $('select.dbquery-select').before( + '
      ' + + 'end' + + 'start' + + '
      ' + ); + $('input.query-option').change(function () { + var startkey = $('input#start').val(), + endkey = $('input#end').val(); + // Check if the keys are properly json encoded as strings, if not do it + if (startkey[0] !== '"' && startkey.length !== 0) { + startkey = '"'+startkey+'"'; + } + if (endkey[0] !== '"' && endkey.length !== 0) { + endkey = '"'+endkey+'"'; + } + // Craft query + h = '#/'+db+'/_all_docs?'; + if (startkey.length > 0) h += ('startkey='+escape(startkey) + '&'); + if (endkey.length > 0) h += ( 'endkey='+escape(endkey) + '&'); + window.location.hash = h; + }); + }; + + request({url: '/'+encodeURIComponent(db)}, function (err, info) { + context.futonUpdateRecentDatabases(db); + + if (err) handleError(err, info); + // Fill out all info from the db query. + for (i in info) { + $('#'+i).text(info[i]); + } + var disk_size = info.disk_size; + $('#disk_size').text(formatSize(info.disk_size)); + + // Query for ddocs to calculate size + request({url:'/'+encodeURIComponent(db)+'/_all_docs?startkey="_design/"&endkey="_design0"'}, function (err, docs) { + if (err) { + handleError(err, docs); + } + var sizes = []; + for (var i=0;i'+resp.rows[i].key+'' + + resp.rows[i].value.rev+'' + ) + // rowCount currently breaks on odd pagination values + .addClass(isEven(rowCount) ? "even" : "odd") + .appendTo('tbody.content') + ; + rowCount += 1; + } + if (!$('span.more').length && (resp.rows.length == limit) ) { + // The number of rows is less than the limit and we haven't added the pagination element yet + $('td.more').append(''); + } else if ( resp.rows.length < limit ) { + // If the return rows are less than the limit we can remove pagination + $('div#pagination').remove(); + } + // Remove the previous pagination handler and add a new one with the new closure values + $('span.more').unbind('click'); + $('span.more').click(function ( ) { + moreRows(start + limit, parseInt($('#pages-input').val(), 10)); + }); + }); + }; + + this.render('templates/database.mustache', {db:encodeURIComponent(db)}) + .replace('#content') + .then(function () {init.call(this, context); moreRows(0,20);}); +}; + +app.wildcard = function () { + var args = this.path.split('/'); + args.splice(0,1); + this.params.db = args.splice(0,1); + this.params.docid = args.join('/'); + app.showDocument.call(this, arguments); +}; + +var futonApp = $.sammy(function () { + this.use('Mustache'); + this.use('Title'); + this.use('Cache'); + + this.template_engine = 'mustache'; + + this.setTitle('CouchDB: '); + + // Recent databases + var recents = this.cache('recentDatabases') || []; + + this.helpers({ + futonUpdateRecentDatabases: function (dbname) { + if (dbname) { + if (recents.indexOf(dbname) !== -1) { + recents.splice(recents.indexOf(dbname), 1); + } + recents.push(dbname); + } + + var c = $("ul#dbs"); + c.html(''); + for (var i=recents.length;i>0;i-=1) { + c.append('
    3. '+recents[i - 1]+'
    4. '); + } + while(recents.length > 10) recents.shift(); + this.cache("recentDatabases", JSON.stringify(recents)); + }, + addDBTopBar: function(db) { + $('#topbar').html( + 'Overview' + + db +' ' + + ''); + }, + setCurrentTab: function() { + // check for design doc list first, then _all_docs, then _changes + if (location.hash.search(/_all_docs\?startkey\=(\%22_design\/\%22|\"_design\/\")/g) > -1) { + $('#topbar .tabs .design-docs').addClass('current'); + } else if (location.hash.search(/_all_docs/g) > -1) { + $('#topbar .tabs .all-docs').addClass('current'); + } else if (location.hash.search(/_changes/g) > -1) { + $('#topbar .tabs .changes').addClass('current'); + } + } + }); + + this.bind('run', function() { + this.futonUpdateRecentDatabases(); + }); + + // Index of all databases + this.get('', app.showIndex); + this.get("#/", app.showIndex); + + this.get('#/_config', app.showConfig); + this.get('#/_stats', app.showStats); + this.get('#/_tests', app.showTests); + this.get('#/_replicate', app.showReplicator); + + this.get('#/:db/_views', app.showView); // TODO: see below...duplicate route? + this.get('#/:db/_design/:ddoc/_view/', app.showView); + this.get('#/:db/_design/:ddoc/_view/:view', app.showView); + + + // Database view + this.get('#/:db', app.showDatabase); // TODO: redirect to _all_docs + this.get('#/:db/_all_docs', app.showDatabase); + // Database _changes feed + this.get('#/:db/_changes', app.showChanges); + + // Database views viewer + this.get('#/:db/_views', app.showViews); // TODO: not a real CouchDB URL...replace? + // Document editor/viewer + this.get('#/:db/:docid', app.showDocument); + + this.get(/\#\/(.*)/, app.wildcard); +}); \ No newline at end of file diff --git a/_attachments/script/jquery-ui-1.8.11.custom.min.js b/_attachments/script/jquery-ui-1.8.11.custom.min.js new file mode 100644 index 0000000..45b927e --- /dev/null +++ b/_attachments/script/jquery-ui-1.8.11.custom.min.js @@ -0,0 +1,81 @@ +/*! + * jQuery UI 1.8.11 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.11",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106, +NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this, +"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position"); +if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f, +"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h, +d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}}); +c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+= +a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b), +g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery); +;/* + * jQuery UI Autocomplete 1.8.11 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.position.js + */ +(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.attr("readonly"))){g= +false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!= +a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)}; +this.menu=d("
        ").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&& +a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"); +d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&& +b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source= +this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length").data("item.autocomplete",b).append(d("").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, +"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery); +(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", +-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.attr("scrollTop"),c=this.element.height();if(b<0)this.element.attr("scrollTop",g+b);else b>=c&&this.element.attr("scrollTop",g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})}, +deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0); +e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b,this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e, +g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first")); +this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()= 0) + continue; + var value = options[name]; + if ($.inArray(name, ["key", "startkey", "endkey"]) >= 0) { + value = toJSON(value); + } + buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value)); + } + } + return buf.length ? "?" + buf.join("&") : ""; + } + + function toJSON(obj) { + return obj !== null ? JSON.stringify(obj) : null; + } + +})(jQuery); diff --git a/_attachments/script/jquery.js b/_attachments/script/jquery.js new file mode 100644 index 0000000..fff6776 --- /dev/null +++ b/_attachments/script/jquery.js @@ -0,0 +1,6240 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function( window, undefined ) { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, + + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // Has the ready events already been bound? + readyBound = false, + + // The functions to execute on DOM ready + readyList = [], + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwnProperty = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + indexOf = Array.prototype.indexOf; + +jQuery.fn = jQuery.prototype = { + init: function( selector, context ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context ) { + this.context = document; + this[0] = document.body; + this.selector = "body"; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + if ( elem ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $("TAG") + } else if ( !context && /^\w+$/.test( selector ) ) { + this.selector = selector; + this.context = document; + selector = document.getElementsByTagName( selector ); + return jQuery.merge( this, selector ); + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return jQuery( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.4.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = jQuery(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // If the DOM is already ready + if ( jQuery.isReady ) { + // Execute the function immediately + fn.call( document, jQuery ); + + // Otherwise, remember the function for later + } else if ( readyList ) { + // Add the function to the wait list + readyList.push( fn ); + } + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || jQuery(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging object literal values or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) { + var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src + : jQuery.isArray(copy) ? [] : {}; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + window.$ = _$; + + if ( deep ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // Handle when the DOM is ready + ready: function() { + // Make sure that the DOM is not already loaded + if ( !jQuery.isReady ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 13 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If there are functions bound, to execute + if ( readyList ) { + // Execute all of them + var fn, i = 0; + while ( (fn = readyList[ i++ ]) ) { + fn.call( document, jQuery ); + } + + // Reset the list of functions + readyList = null; + } + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyBound ) { + return; + } + + readyBound = true; + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + return jQuery.ready(); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent("onreadystatechange", DOMContentLoaded); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, + + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor + && !hasOwnProperty.call(obj, "constructor") + && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwnProperty.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@") + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]") + .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) { + + // Try to use the native JSON parser first + return window.JSON && window.JSON.parse ? + window.JSON.parse( data ) : + (new Function("return " + data))(); + + } else { + jQuery.error( "Invalid JSON: " + data ); + } + }, + + noop: function() {}, + + // Evalulates a script in a global context + globalEval: function( data ) { + if ( data && rnotwhite.test(data) ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.getElementsByTagName("head")[0] || document.documentElement, + script = document.createElement("script"); + + script.type = "text/javascript"; + + if ( jQuery.support.scriptEval ) { + script.appendChild( document.createTextNode( data ) ); + } else { + script.text = data; + } + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction(object); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} + } + } + + return object; + }, + + trim: function( text ) { + return (text || "").replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = []; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + if ( !inv !== !callback( elems[ i ], i ) ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var ret = [], value; + + // Go through the array, translating each of the items to their + // new value (or values). + for ( var i = 0, length = elems.length; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + proxy: function( fn, proxy, thisObject ) { + if ( arguments.length === 2 ) { + if ( typeof proxy === "string" ) { + thisObject = fn; + fn = thisObject[ proxy ]; + proxy = undefined; + + } else if ( proxy && !jQuery.isFunction( proxy ) ) { + thisObject = proxy; + proxy = undefined; + } + } + + if ( !proxy && fn ) { + proxy = function() { + return fn.apply( thisObject || this, arguments ); + }; + } + + // Set the guid of unique handler to the same of original handler, so it can be removed + if ( fn ) { + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + } + + // So proxy can be declared as an argument + return proxy; + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + browser: {} +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +if ( indexOf ) { + jQuery.inArray = function( elem, array ) { + return indexOf.call( array, elem ); + }; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch( error ) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +function evalScript( i, elem ) { + if ( elem.src ) { + jQuery.ajax({ + url: elem.src, + async: false, + dataType: "script" + }); + } else { + jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } +} + +// Mutifunctional method to get and set values to a collection +// The value/s can be optionally by executed if its a function +function access( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; +} + +function now() { + return (new Date).getTime(); +} +(function() { + + jQuery.support = {}; + + var root = document.documentElement, + script = document.createElement("script"), + div = document.createElement("div"), + id = "script" + now(); + + div.style.display = "none"; + div.innerHTML = "
        a"; + + var all = div.getElementsByTagName("*"), + a = div.getElementsByTagName("a")[0]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return; + } + + jQuery.support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: div.firstChild.nodeType === 3, + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText insted) + style: /red/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: a.getAttribute("href") === "/a", + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55$/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: div.getElementsByTagName("input")[0].value === "on", + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected, + + parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null, + + // Will be defined later + deleteExpando: true, + checkClone: false, + scriptEval: false, + noCloneEvent: true, + boxModel: null + }; + + script.type = "text/javascript"; + try { + script.appendChild( document.createTextNode( "window." + id + "=1;" ) ); + } catch(e) {} + + root.insertBefore( script, root.firstChild ); + + // Make sure that the execution of code works by injecting a script + // tag with appendChild/createTextNode + // (IE doesn't support this, fails, and uses .text instead) + if ( window[ id ] ) { + jQuery.support.scriptEval = true; + delete window[ id ]; + } + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete script.test; + + } catch(e) { + jQuery.support.deleteExpando = false; + } + + root.removeChild( script ); + + if ( div.attachEvent && div.fireEvent ) { + div.attachEvent("onclick", function click() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + jQuery.support.noCloneEvent = false; + div.detachEvent("onclick", click); + }); + div.cloneNode(true).fireEvent("onclick"); + } + + div = document.createElement("div"); + div.innerHTML = ""; + + var fragment = document.createDocumentFragment(); + fragment.appendChild( div.firstChild ); + + // WebKit doesn't clone checked state correctly in fragments + jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked; + + // Figure out if the W3C box model works as expected + // document.body must exist before we can do this + jQuery(function() { + var div = document.createElement("div"); + div.style.width = div.style.paddingLeft = "1px"; + + document.body.appendChild( div ); + jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; + document.body.removeChild( div ).style.display = 'none'; + + div = null; + }); + + // Technique from Juriy Zaytsev + // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + var eventSupported = function( eventName ) { + var el = document.createElement("div"); + eventName = "on" + eventName; + + var isSupported = (eventName in el); + if ( !isSupported ) { + el.setAttribute(eventName, "return;"); + isSupported = typeof el[eventName] === "function"; + } + el = null; + + return isSupported; + }; + + jQuery.support.submitBubbles = eventSupported("submit"); + jQuery.support.changeBubbles = eventSupported("change"); + + // release memory in IE + root = script = div = all = a = null; +})(); + +jQuery.props = { + "for": "htmlFor", + "class": "className", + readonly: "readOnly", + maxlength: "maxLength", + cellspacing: "cellSpacing", + rowspan: "rowSpan", + colspan: "colSpan", + tabindex: "tabIndex", + usemap: "useMap", + frameborder: "frameBorder" +}; +var expando = "jQuery" + now(), uuid = 0, windowData = {}; + +jQuery.extend({ + cache: {}, + + expando:expando, + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + "object": true, + "applet": true + }, + + data: function( elem, name, data ) { + if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { + return; + } + + elem = elem == window ? + windowData : + elem; + + var id = elem[ expando ], cache = jQuery.cache, thisCache; + + if ( !id && typeof name === "string" && data === undefined ) { + return null; + } + + // Compute a unique ID for the element + if ( !id ) { + id = ++uuid; + } + + // Avoid generating a new cache unless none exists and we + // want to manipulate it. + if ( typeof name === "object" ) { + elem[ expando ] = id; + thisCache = cache[ id ] = jQuery.extend(true, {}, name); + + } else if ( !cache[ id ] ) { + elem[ expando ] = id; + cache[ id ] = {}; + } + + thisCache = cache[ id ]; + + // Prevent overriding the named cache with undefined values + if ( data !== undefined ) { + thisCache[ name ] = data; + } + + return typeof name === "string" ? thisCache[ name ] : thisCache; + }, + + removeData: function( elem, name ) { + if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { + return; + } + + elem = elem == window ? + windowData : + elem; + + var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ]; + + // If we want to remove a specific section of the element's data + if ( name ) { + if ( thisCache ) { + // Remove the section of cache data + delete thisCache[ name ]; + + // If we've removed all the data, remove the element's cache + if ( jQuery.isEmptyObject(thisCache) ) { + jQuery.removeData( elem ); + } + } + + // Otherwise, we want to remove all of the element's data + } else { + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } + + // Completely remove the data cache + delete cache[ id ]; + } + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + if ( typeof key === "undefined" && this.length ) { + return jQuery.data( this[0] ); + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + } + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } else { + return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() { + jQuery.data( this, key, value ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); +jQuery.extend({ + queue: function( elem, type, data ) { + if ( !elem ) { + return; + } + + type = (type || "fx") + "queue"; + var q = jQuery.data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( !data ) { + return q || []; + } + + if ( !q || jQuery.isArray(data) ) { + q = jQuery.data( elem, type, jQuery.makeArray(data) ); + + } else { + q.push( data ); + } + + return q; + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), fn = queue.shift(); + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift("inprogress"); + } + + fn.call(elem, function() { + jQuery.dequeue(elem, type); + }); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function( i, elem ) { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + + return this.queue( type, function() { + var elem = this; + setTimeout(function() { + jQuery.dequeue( elem, type ); + }, time ); + }); + }, + + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + } +}); +var rclass = /[\n\t]/g, + rspace = /\s+/, + rreturn = /\r/g, + rspecialurl = /href|src|style/, + rtype = /(button|input)/i, + rfocusable = /(button|input|object|select|textarea)/i, + rclickable = /^(a|area)$/i, + rradiocheck = /radio|checkbox/; + +jQuery.fn.extend({ + attr: function( name, value ) { + return access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name, fn ) { + return this.each(function(){ + jQuery.attr( this, name, "" ); + if ( this.nodeType === 1 ) { + this.removeAttribute( name ); + } + }); + }, + + addClass: function( value ) { + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + self.addClass( value.call(this, i, self.attr("class")) ); + }); + } + + if ( value && typeof value === "string" ) { + var classNames = (value || "").split( rspace ); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className ) { + elem.className = value; + + } else { + var className = " " + elem.className + " ", setClass = elem.className; + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { + setClass += " " + classNames[c]; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + self.removeClass( value.call(this, i, self.attr("class")) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + var classNames = (value || "").split(rspace); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + var className = (" " + elem.className + " ").replace(rclass, " "); + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[c] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this); + self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, i = 0, self = jQuery(this), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery.data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + if ( value === undefined ) { + var elem = this[0]; + + if ( elem ) { + if ( jQuery.nodeName( elem, "option" ) ) { + return (elem.attributes.value || {}).specified ? elem.value : elem.text; + } + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + if ( option.selected ) { + // Get the specifc value for the option + value = jQuery(option).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + } + + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) { + return elem.getAttribute("value") === null ? "on" : elem.value; + } + + + // Everything else, we just grab the value + return (elem.value || "").replace(rreturn, ""); + + } + + return undefined; + } + + var isFunction = jQuery.isFunction(value); + + return this.each(function(i) { + var self = jQuery(this), val = value; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call(this, i, self.val()); + } + + // Typecast each time if the value is a Function and the appended + // value is therefore different each time. + if ( typeof val === "number" ) { + val += ""; + } + + if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) { + this.checked = jQuery.inArray( self.val(), val ) >= 0; + + } else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(val); + + jQuery( "option", this ).each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + this.selectedIndex = -1; + } + + } else { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + // don't set attributes on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery(elem)[name](value); + } + + var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ), + // Whether we are setting (or getting) + set = value !== undefined; + + // Try to normalize/fix the name + name = notxml && jQuery.props[ name ] || name; + + // Only do all the following if this is a node (faster for style) + if ( elem.nodeType === 1 ) { + // These attributes require special treatment + var special = rspecialurl.test( name ); + + // Safari mis-reports the default selected property of an option + // Accessing the parent's selectedIndex property fixes it + if ( name === "selected" && !jQuery.support.optSelected ) { + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + + // If applicable, access the attribute via the DOM 0 way + if ( name in elem && notxml && !special ) { + if ( set ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } + + elem[ name ] = value; + } + + // browsers index elements by id/name on forms, give priority to attributes. + if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) { + return elem.getAttributeNode( name ).nodeValue; + } + + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + if ( name === "tabIndex" ) { + var attributeNode = elem.getAttributeNode( "tabIndex" ); + + return attributeNode && attributeNode.specified ? + attributeNode.value : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + + return elem[ name ]; + } + + if ( !jQuery.support.style && notxml && name === "style" ) { + if ( set ) { + elem.style.cssText = "" + value; + } + + return elem.style.cssText; + } + + if ( set ) { + // convert the value to a string (all browsers do this but IE) see #1070 + elem.setAttribute( name, "" + value ); + } + + var attr = !jQuery.support.hrefNormalized && notxml && special ? + // Some attributes require a special call on IE + elem.getAttribute( name, 2 ) : + elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return attr === null ? undefined : attr; + } + + // elem is actually elem.style ... set the style + // Using attr for specific style information is now deprecated. Use style instead. + return jQuery.style( elem, name, value ); + } +}); +var rnamespaces = /\.(.*)$/, + fcleanup = function( nm ) { + return nm.replace(/[^\w\s\.\|`]/g, function( ch ) { + return "\\" + ch; + }); + }; + +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + + // Bind an event to an element + // Original by Dean Edwards + add: function( elem, types, handler, data ) { + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // For whatever reason, IE has trouble passing the window object + // around, causing it to be cloned in the process + if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) { + elem = window; + } + + var handleObjIn, handleObj; + + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the function being executed has a unique ID + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure + var elemData = jQuery.data( elem ); + + // If no elemData is found then we must be trying to bind to one of the + // banned noData elements + if ( !elemData ) { + return; + } + + var events = elemData.events = elemData.events || {}, + eventHandle = elemData.handle, eventHandle; + + if ( !eventHandle ) { + elemData.handle = eventHandle = function() { + // Handle the second event of a trigger and when + // an event is called after a page has unloaded + return typeof jQuery !== "undefined" && !jQuery.event.triggered ? + jQuery.event.handle.apply( eventHandle.elem, arguments ) : + undefined; + }; + } + + // Add elem as a property of the handle function + // This is to prevent a memory leak with non-native events in IE. + eventHandle.elem = elem; + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = types.split(" "); + + var type, i = 0, namespaces; + + while ( (type = types[ i++ ]) ) { + handleObj = handleObjIn ? + jQuery.extend({}, handleObjIn) : + { handler: handler, data: data }; + + // Namespaced event handlers + if ( type.indexOf(".") > -1 ) { + namespaces = type.split("."); + type = namespaces.shift(); + handleObj.namespace = namespaces.slice(0).sort().join("."); + + } else { + namespaces = []; + handleObj.namespace = ""; + } + + handleObj.type = type; + handleObj.guid = handler.guid; + + // Get the current list of functions bound to this event + var handlers = events[ type ], + special = jQuery.event.special[ type ] || {}; + + // Init the event handler queue + if ( !handlers ) { + handlers = events[ type ] = []; + + // Check for a special event handler + // Only use addEventListener/attachEvent if the special + // events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add the function to the element's handler list + handlers.push( handleObj ); + + // Keep track of which events have been used, for global triggering + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, pos ) { + // don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, + elemData = jQuery.data( elem ), + events = elemData && elemData.events; + + if ( !elemData || !events ) { + return; + } + + // types is actually an event object here + if ( types && types.type ) { + handler = types.handler; + types = types.type; + } + + // Unbind all events for the element + if ( !types || typeof types === "string" && types.charAt(0) === "." ) { + types = types || ""; + + for ( type in events ) { + jQuery.event.remove( elem, type + types ); + } + + return; + } + + // Handle multiple events separated by a space + // jQuery(...).unbind("mouseover mouseout", fn); + types = types.split(" "); + + while ( (type = types[ i++ ]) ) { + origType = type; + handleObj = null; + all = type.indexOf(".") < 0; + namespaces = []; + + if ( !all ) { + // Namespaced event handlers + namespaces = type.split("."); + type = namespaces.shift(); + + namespace = new RegExp("(^|\\.)" + + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)") + } + + eventType = events[ type ]; + + if ( !eventType ) { + continue; + } + + if ( !handler ) { + for ( var j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( all || namespace.test( handleObj.namespace ) ) { + jQuery.event.remove( elem, origType, handleObj.handler, j ); + eventType.splice( j--, 1 ); + } + } + + continue; + } + + special = jQuery.event.special[ type ] || {}; + + for ( var j = pos || 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( handler.guid === handleObj.guid ) { + // remove the given handler for the given type + if ( all || namespace.test( handleObj.namespace ) ) { + if ( pos == null ) { + eventType.splice( j--, 1 ); + } + + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + + if ( pos != null ) { + break; + } + } + } + + // remove generic event handler if no more handlers exist + if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + removeEvent( elem, type, elemData.handle ); + } + + ret = null; + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + var handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + delete elemData.events; + delete elemData.handle; + + if ( jQuery.isEmptyObject( elemData ) ) { + jQuery.removeData( elem ); + } + } + }, + + // bubbling is internal + trigger: function( event, data, elem /*, bubbling */ ) { + // Event object or event type + var type = event.type || event, + bubbling = arguments[3]; + + if ( !bubbling ) { + event = typeof event === "object" ? + // jQuery.Event object + event[expando] ? event : + // Object literal + jQuery.extend( jQuery.Event(type), event ) : + // Just the event type (string) + jQuery.Event(type); + + if ( type.indexOf("!") >= 0 ) { + event.type = type = type.slice(0, -1); + event.exclusive = true; + } + + // Handle a global trigger + if ( !elem ) { + // Don't bubble custom events when global (to avoid too much overhead) + event.stopPropagation(); + + // Only trigger if we've ever bound an event for it + if ( jQuery.event.global[ type ] ) { + jQuery.each( jQuery.cache, function() { + if ( this.events && this.events[type] ) { + jQuery.event.trigger( event, data, this.handle.elem ); + } + }); + } + } + + // Handle triggering a single element + + // don't do events on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { + return undefined; + } + + // Clean up in case it is reused + event.result = undefined; + event.target = elem; + + // Clone the incoming data, if any + data = jQuery.makeArray( data ); + data.unshift( event ); + } + + event.currentTarget = elem; + + // Trigger the event, it is assumed that "handle" is a function + var handle = jQuery.data( elem, "handle" ); + if ( handle ) { + handle.apply( elem, data ); + } + + var parent = elem.parentNode || elem.ownerDocument; + + // Trigger an inline bound script + try { + if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) { + if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) { + event.result = false; + } + } + + // prevent IE from throwing an error for some elements with some event types, see #3533 + } catch (e) {} + + if ( !event.isPropagationStopped() && parent ) { + jQuery.event.trigger( event, data, parent, true ); + + } else if ( !event.isDefaultPrevented() ) { + var target = event.target, old, + isClick = jQuery.nodeName(target, "a") && type === "click", + special = jQuery.event.special[ type ] || {}; + + if ( (!special._default || special._default.call( elem, event ) === false) && + !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { + + try { + if ( target[ type ] ) { + // Make sure that we don't accidentally re-trigger the onFOO events + old = target[ "on" + type ]; + + if ( old ) { + target[ "on" + type ] = null; + } + + jQuery.event.triggered = true; + target[ type ](); + } + + // prevent IE from throwing an error for some elements with some event types, see #3533 + } catch (e) {} + + if ( old ) { + target[ "on" + type ] = old; + } + + jQuery.event.triggered = false; + } + } + }, + + handle: function( event ) { + var all, handlers, namespaces, namespace, events; + + event = arguments[0] = jQuery.event.fix( event || window.event ); + event.currentTarget = this; + + // Namespaced event handlers + all = event.type.indexOf(".") < 0 && !event.exclusive; + + if ( !all ) { + namespaces = event.type.split("."); + event.type = namespaces.shift(); + namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + var events = jQuery.data(this, "events"), handlers = events[ event.type ]; + + if ( events && handlers ) { + // Clone the handlers to prevent manipulation + handlers = handlers.slice(0); + + for ( var j = 0, l = handlers.length; j < l; j++ ) { + var handleObj = handlers[ j ]; + + // Filter the functions by class + if ( all || namespace.test( handleObj.namespace ) ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + event.handler = handleObj.handler; + event.data = handleObj.data; + event.handleObj = handleObj; + + var ret = handleObj.handler.apply( this, arguments ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + } + + return event.result; + }, + + props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + + fix: function( event ) { + if ( event[ expando ] ) { + return event; + } + + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = jQuery.Event( originalEvent ); + + for ( var i = this.props.length, prop; i; ) { + prop = this.props[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary + if ( !event.target ) { + event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either + } + + // check if target is a textnode (safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && event.fromElement ) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var doc = document.documentElement, body = document.body; + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) { + event.which = event.charCode || event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + // Deprecated, use jQuery.guid instead + guid: 1E8, + + // Deprecated, use jQuery.proxy instead + proxy: jQuery.proxy, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady, + teardown: jQuery.noop + }, + + live: { + add: function( handleObj ) { + jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); + }, + + remove: function( handleObj ) { + var remove = true, + type = handleObj.origType.replace(rnamespaces, ""); + + jQuery.each( jQuery.data(this, "events").live || [], function() { + if ( type === this.origType.replace(rnamespaces, "") ) { + remove = false; + return false; + } + }); + + if ( remove ) { + jQuery.event.remove( this, handleObj.origType, liveHandler ); + } + } + + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( this.setInterval ) { + this.onbeforeunload = eventHandle; + } + + return false; + }, + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + } +}; + +var removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + elem.removeEventListener( type, handle, false ); + } : + function( elem, type, handle ) { + elem.detachEvent( "on" + type, handle ); + }; + +jQuery.Event = function( src ) { + // Allow instantiation without the 'new' keyword + if ( !this.preventDefault ) { + return new jQuery.Event( src ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + // Event type + } else { + this.type = src; + } + + // timeStamp is buggy for some events on Firefox(#3843) + // So we won't rely on the native value + this.timeStamp = now(); + + // Mark it as fixed + this[ expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + } + // otherwise set the returnValue property of the original event to false (IE) + e.returnValue = false; + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function( event ) { + // Check if mouse(over|out) are still within the same parent element + var parent = event.relatedTarget; + + // Firefox sometimes assigns relatedTarget a XUL element + // which we cannot access the parentNode property of + try { + // Traverse up the tree + while ( parent && parent !== this ) { + parent = parent.parentNode; + } + + if ( parent !== this ) { + // set the correct event type + event.type = event.data; + + // handle event if we actually just moused on to a non sub-element + jQuery.event.handle.apply( this, arguments ); + } + + // assuming we've left the element since we most likely mousedover a xul element + } catch(e) { } +}, + +// In case of event delegation, we only need to rename the event.type, +// liveHandler will take care of the rest. +delegate = function( event ) { + event.type = event.data; + jQuery.event.handle.apply( this, arguments ); +}; + +// Create mouseenter and mouseleave events +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + setup: function( data ) { + jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); + }, + teardown: function( data ) { + jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + } + }; +}); + +// submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function( data, namespaces ) { + if ( this.nodeName.toLowerCase() !== "form" ) { + jQuery.event.add(this, "click.specialSubmit", function( e ) { + var elem = e.target, type = elem.type; + + if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { + return trigger( "submit", this, arguments ); + } + }); + + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { + var elem = e.target, type = elem.type; + + if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { + return trigger( "submit", this, arguments ); + } + }); + + } else { + return false; + } + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialSubmit" ); + } + }; + +} + +// change delegation, happens here so we have bind. +if ( !jQuery.support.changeBubbles ) { + + var formElems = /textarea|input|select/i, + + changeFilters, + + getVal = function( elem ) { + var type = elem.type, val = elem.value; + + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; + + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; + + } else if ( elem.nodeName.toLowerCase() === "select" ) { + val = elem.selectedIndex; + } + + return val; + }, + + testChange = function testChange( e ) { + var elem = e.target, data, val; + + if ( !formElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery.data( elem, "_change_data" ); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery.data( elem, "_change_data", val ); + } + + if ( data === undefined || val === data ) { + return; + } + + if ( data != null || val ) { + e.type = "change"; + return jQuery.event.trigger( e, arguments[1], elem ); + } + }; + + jQuery.event.special.change = { + filters: { + focusout: testChange, + + click: function( e ) { + var elem = e.target, type = elem.type; + + if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) { + return testChange.call( this, e ); + } + }, + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = elem.type; + + if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + return testChange.call( this, e ); + } + }, + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information/focus[in] is not needed anymore + beforeactivate: function( e ) { + var elem = e.target; + jQuery.data( elem, "_change_data", getVal(elem) ); + } + }, + + setup: function( data, namespaces ) { + if ( this.type === "file" ) { + return false; + } + + for ( var type in changeFilters ) { + jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + } + + return formElems.test( this.nodeName ); + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialChange" ); + + return formElems.test( this.nodeName ); + } + }; + + changeFilters = jQuery.event.special.change.filters; +} + +function trigger( type, elem, args ) { + args[0].type = type; + return jQuery.event.handle.apply( elem, args ); +} + +// Create "bubbling" focus and blur events +if ( document.addEventListener ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + jQuery.event.special[ fix ] = { + setup: function() { + this.addEventListener( orig, handler, true ); + }, + teardown: function() { + this.removeEventListener( orig, handler, true ); + } + }; + + function handler( e ) { + e = jQuery.event.fix( e ); + e.type = fix; + return jQuery.event.handle.call( this, e ); + } + }); +} + +jQuery.each(["bind", "one"], function( i, name ) { + jQuery.fn[ name ] = function( type, data, fn ) { + // Handle object literals + if ( typeof type === "object" ) { + for ( var key in type ) { + this[ name ](key, data, type[key], fn); + } + return this; + } + + if ( jQuery.isFunction( data ) ) { + fn = data; + data = undefined; + } + + var handler = name === "one" ? jQuery.proxy( fn, function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }) : fn; + + if ( type === "unload" && name !== "one" ) { + this.one( type, data, fn ); + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.add( this[i], type, handler, data ); + } + } + + return this; + }; +}); + +jQuery.fn.extend({ + unbind: function( type, fn ) { + // Handle object literals + if ( typeof type === "object" && !type.preventDefault ) { + for ( var key in type ) { + this.unbind(key, type[key]); + } + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.remove( this[i], type, fn ); + } + } + + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.live( types, data, fn, selector ); + }, + + undelegate: function( selector, types, fn ) { + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + + triggerHandler: function( type, data ) { + if ( this[0] ) { + var event = jQuery.Event( type ); + event.preventDefault(); + event.stopPropagation(); + jQuery.event.trigger( event, data, this[0] ); + return event.result; + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, i = 1; + + // link all the functions, so any of them can unbind this click handler + while ( i < args.length ) { + jQuery.proxy( fn, args[ i++ ] ); + } + + return this.click( jQuery.proxy( fn, function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + })); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +var liveMap = { + focus: "focusin", + blur: "focusout", + mouseenter: "mouseover", + mouseleave: "mouseout" +}; + +jQuery.each(["live", "die"], function( i, name ) { + jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { + var type, i = 0, match, namespaces, preType, + selector = origSelector || this.selector, + context = origSelector ? this : jQuery( this.context ); + + if ( jQuery.isFunction( data ) ) { + fn = data; + data = undefined; + } + + types = (types || "").split(" "); + + while ( (type = types[ i++ ]) != null ) { + match = rnamespaces.exec( type ); + namespaces = ""; + + if ( match ) { + namespaces = match[0]; + type = type.replace( rnamespaces, "" ); + } + + if ( type === "hover" ) { + types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); + continue; + } + + preType = type; + + if ( type === "focus" || type === "blur" ) { + types.push( liveMap[ type ] + namespaces ); + type = type + namespaces; + + } else { + type = (liveMap[ type ] || type) + namespaces; + } + + if ( name === "live" ) { + // bind live handler + context.each(function(){ + jQuery.event.add( this, liveConvert( type, selector ), + { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); + }); + + } else { + // unbind live handler + context.unbind( liveConvert( type, selector ), fn ); + } + } + + return this; + } +}); + +function liveHandler( event ) { + var stop, elems = [], selectors = [], args = arguments, + related, match, handleObj, elem, j, i, l, data, + events = jQuery.data( this, "events" ); + + // Make sure we avoid non-left-click bubbling in Firefox (#3861) + if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) { + return; + } + + event.liveFired = this; + + var live = events.live.slice(0); + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { + selectors.push( handleObj.selector ); + + } else { + live.splice( j--, 1 ); + } + } + + match = jQuery( event.target ).closest( selectors, event.currentTarget ); + + for ( i = 0, l = match.length; i < l; i++ ) { + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( match[i].selector === handleObj.selector ) { + elem = match[i].elem; + related = null; + + // Those two events require additional checking + if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { + related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; + } + + if ( !related || related !== elem ) { + elems.push({ elem: elem, handleObj: handleObj }); + } + } + } + } + + for ( i = 0, l = elems.length; i < l; i++ ) { + match = elems[i]; + event.currentTarget = match.elem; + event.data = match.handleObj.data; + event.handleObj = match.handleObj; + + if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) { + stop = false; + break; + } + } + + return stop; +} + +function liveConvert( type, selector ) { + return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&"); +} + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( fn ) { + return fn ? this.bind( name, fn ) : this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } +}); + +// Prevent memory leaks in IE +// Window isn't included so as not to unbind existing unload events +// More info: +// - http://isaacschlueter.com/2006/10/msie-memory-leaks/ +if ( window.attachEvent && !window.addEventListener ) { + window.attachEvent("onunload", function() { + for ( var id in jQuery.cache ) { + if ( jQuery.cache[ id ].handle ) { + // Try/Catch is to handle iframes being unloaded, see #4280 + try { + jQuery.event.remove( jQuery.cache[ id ].handle.elem ); + } catch(e) {} + } + } + }); +} +/*! + * Sizzle CSS Selector Engine - v1.0 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function(){ + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function(selector, context, results, seed) { + results = results || []; + var origContext = context = context || document; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context), + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + var ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; + } + + if ( context ) { + var ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray(set); + } else { + prune = false; + } + + while ( parts.length ) { + var cur = parts.pop(), pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + } else if ( context && context.nodeType === 1 ) { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + } else { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function(results){ + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort(sortOrder); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[i-1] ) { + results.splice(i--, 1); + } + } + } + } + + return results; +}; + +Sizzle.matches = function(expr, set){ + return Sizzle(expr, null, null, set); +}; + +Sizzle.find = function(expr, context, isXML){ + var set, match; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var type = Expr.order[i], match; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice(1,1); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace(/\\/g, ""); + set = Expr.find[ type ]( match, context, isXML ); + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = context.getElementsByTagName("*"); + } + + return {set: set, expr: expr}; +}; + +Sizzle.filter = function(expr, set, inplace, not){ + var old = expr, result = [], curLoop = set, match, anyFound, + isXMLFilter = set && set[0] && isXML(set[0]); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var filter = Expr.filter[ type ], found, item, left = match[1]; + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + } else { + curLoop[i] = false; + } + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + match: { + ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + leftMatch: {}, + attrMap: { + "class": "className", + "for": "htmlFor" + }, + attrHandle: { + href: function(elem){ + return elem.getAttribute("href"); + } + }, + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !/\W/.test(part), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + ">": function(checkSet, part){ + var isPartStr = typeof part === "string"; + + if ( isPartStr && !/\W/.test(part) ) { + part = part.toLowerCase(); + + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + } else { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + "": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( typeof part === "string" && !/\W/.test(part) ) { + var nodeCheck = part = part.toLowerCase(); + checkFn = dirNodeCheck; + } + + checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); + }, + "~": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( typeof part === "string" && !/\W/.test(part) ) { + var nodeCheck = part = part.toLowerCase(); + checkFn = dirNodeCheck; + } + + checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); + } + }, + find: { + ID: function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? [m] : []; + } + }, + NAME: function(match, context){ + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], results = context.getElementsByName(match[1]); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + TAG: function(match, context){ + return context.getElementsByTagName(match[1]); + } + }, + preFilter: { + CLASS: function(match, curLoop, inplace, result, not, isXML){ + match = " " + match[1].replace(/\\/g, "") + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + ID: function(match){ + return match[1].replace(/\\/g, ""); + }, + TAG: function(match, curLoop){ + return match[1].toLowerCase(); + }, + CHILD: function(match){ + if ( match[1] === "nth" ) { + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + ATTR: function(match, curLoop, inplace, result, not, isXML){ + var name = match[1].replace(/\\/g, ""); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + PSEUDO: function(match, curLoop, inplace, result, not){ + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + if ( !inplace ) { + result.push.apply( result, ret ); + } + return false; + } + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + POS: function(match){ + match.unshift( true ); + return match; + } + }, + filters: { + enabled: function(elem){ + return elem.disabled === false && elem.type !== "hidden"; + }, + disabled: function(elem){ + return elem.disabled === true; + }, + checked: function(elem){ + return elem.checked === true; + }, + selected: function(elem){ + // Accessing this property makes selected-by-default + // options in Safari work properly + elem.parentNode.selectedIndex; + return elem.selected === true; + }, + parent: function(elem){ + return !!elem.firstChild; + }, + empty: function(elem){ + return !elem.firstChild; + }, + has: function(elem, i, match){ + return !!Sizzle( match[3], elem ).length; + }, + header: function(elem){ + return /h\d/i.test( elem.nodeName ); + }, + text: function(elem){ + return "text" === elem.type; + }, + radio: function(elem){ + return "radio" === elem.type; + }, + checkbox: function(elem){ + return "checkbox" === elem.type; + }, + file: function(elem){ + return "file" === elem.type; + }, + password: function(elem){ + return "password" === elem.type; + }, + submit: function(elem){ + return "submit" === elem.type; + }, + image: function(elem){ + return "image" === elem.type; + }, + reset: function(elem){ + return "reset" === elem.type; + }, + button: function(elem){ + return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; + }, + input: function(elem){ + return /input|select|textarea|button/i.test(elem.nodeName); + } + }, + setFilters: { + first: function(elem, i){ + return i === 0; + }, + last: function(elem, i, match, array){ + return i === array.length - 1; + }, + even: function(elem, i){ + return i % 2 === 0; + }, + odd: function(elem, i){ + return i % 2 === 1; + }, + lt: function(elem, i, match){ + return i < match[3] - 0; + }, + gt: function(elem, i, match){ + return i > match[3] - 0; + }, + nth: function(elem, i, match){ + return match[3] - 0 === i; + }, + eq: function(elem, i, match){ + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function(elem, match, i, array){ + var name = match[1], filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + } else if ( name === "not" ) { + var not = match[3]; + + for ( var i = 0, l = not.length; i < l; i++ ) { + if ( not[i] === elem ) { + return false; + } + } + + return true; + } else { + Sizzle.error( "Syntax error, unrecognized expression: " + name ); + } + }, + CHILD: function(elem, match){ + var type = match[1], node = elem; + switch (type) { + case 'only': + case 'first': + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + if ( type === "first" ) { + return true; + } + node = elem; + case 'last': + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + return true; + case 'nth': + var first = match[2], last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + if ( first === 0 ) { + return diff === 0; + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + ID: function(elem, match){ + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + TAG: function(elem, match){ + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + CLASS: function(elem, match){ + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + ATTR: function(elem, match){ + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + POS: function(elem, match, i, array){ + var name = match[2], filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){ + return "\\" + (num - 0 + 1); + })); +} + +var makeArray = function(array, results) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch(e){ + makeArray = function(array, results) { + var ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + } else { + if ( typeof array.length === "number" ) { + for ( var i = 0, l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + } else { + for ( var i = 0; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.compareDocumentPosition ? -1 : 1; + } + + var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( "sourceIndex" in document.documentElement ) { + sortOrder = function( a, b ) { + if ( !a.sourceIndex || !b.sourceIndex ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.sourceIndex ? -1 : 1; + } + + var ret = a.sourceIndex - b.sourceIndex; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( document.createRange ) { + sortOrder = function( a, b ) { + if ( !a.ownerDocument || !b.ownerDocument ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.ownerDocument ? -1 : 1; + } + + var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); + aRange.setStart(a, 0); + aRange.setEnd(a, 0); + bRange.setStart(b, 0); + bRange.setEnd(b, 0); + var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +function getText( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += getText( elem.childNodes ); + } + } + + return ret; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date).getTime(); + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + var root = document.documentElement; + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; + } + }; + + Expr.filter.ID = function(elem, match){ + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + root = form = null; // release memory in IE +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function(match, context){ + var results = context.getElementsByTagName(match[1]); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + Expr.attrHandle.href = function(elem){ + return elem.getAttribute("href", 2); + }; + } + + div = null; // release memory in IE +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, div = document.createElement("div"); + div.innerHTML = "

        "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function(query, context, extra, seed){ + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && context.nodeType === 9 && !isXML(context) ) { + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(e){} + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + div = null; // release memory in IE + })(); +} + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
        "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function(match, context, isXML) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + div = null; // release memory in IE +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +var contains = document.compareDocumentPosition ? function(a, b){ + return !!(a.compareDocumentPosition(b) & 16); +} : function(a, b){ + return a !== b && (a.contains ? a.contains(b) : true); +}; + +var isXML = function(elem){ + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function(selector, context){ + var tmpSet = [], later = "", match, + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = getText; +jQuery.isXMLDoc = isXML; +jQuery.contains = contains; + +return; + +window.Sizzle = Sizzle; + +})(); +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + slice = Array.prototype.slice; + +// Implement the identical functionality for filter and not +var winnow = function( elements, qualifier, keep ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return (elem === qualifier) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + }); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var ret = this.pushStack( "", "find", selector ), length = 0; + + for ( var i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( var n = length; n < ret.length; n++ ) { + for ( var r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && jQuery.filter( selector, this ).length > 0; + }, + + closest: function( selectors, context ) { + if ( jQuery.isArray( selectors ) ) { + var ret = [], cur = this[0], match, matches = {}, selector; + + if ( cur && selectors.length ) { + for ( var i = 0, l = selectors.length; i < l; i++ ) { + selector = selectors[i]; + + if ( !matches[selector] ) { + matches[selector] = jQuery.expr.match.POS.test( selector ) ? + jQuery( selector, context || this.context ) : + selector; + } + } + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( selector in matches ) { + match = matches[selector]; + + if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) { + ret.push({ selector: selector, elem: cur }); + delete matches[selector]; + } + } + cur = cur.parentNode; + } + } + + return ret; + } + + var pos = jQuery.expr.match.POS.test( selectors ) ? + jQuery( selectors, context || this.context ) : null; + + return this.map(function( i, cur ) { + while ( cur && cur.ownerDocument && cur !== context ) { + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) { + return cur; + } + cur = cur.parentNode; + } + return null; + }); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context || this.context ) : + jQuery.makeArray( selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, slice.call(arguments).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], cur = elem[dir]; + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); +var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g, + rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i, + rtagName = /<([\w:]+)/, + rtbody = /"; + }, + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
        ", "
        " ], + thead: [ 1, "", "
        " ], + tr: [ 2, "", "
        " ], + td: [ 3, "", "
        " ], + col: [ 2, "", "
        " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and \n"); + } + }, + + // css blocks + { + regexp: /^(\s*):css\s*$/, + process: function () { + return JSON.stringify('\n\n"); + } + }, + + ]; + + function compile(lines) { + var block = false, + output = []; + + // If lines is a string, turn it into an array + if (typeof lines === 'string') { + lines = lines.trim().split("\n"); + } + + lines.forEach(function(line) { + var match, found = false; + + // Collect all text as raw until outdent + if (block) { + match = block.check_indent(line); + if (match) { + block.contents.push(match[1] || ""); + return; + } else { + output.push(block.process()); + block = false; + } + } + + matchers.forEach(function (matcher) { + if (!found) { + match = matcher.regexp(line); + if (match) { + block = { + contents: [], + matches: match, + check_indent: new RegExp("^(?:\\s*|" + match[1] + " (.*))$"), + process: matcher.process, + render_contents: function () { + return compile(this. contents); + } + }; + found = true; + } + } + }); + + // Match plain text + if (!found) { + output.push(function () { + // Escaped plain text + if (line[0] === '\\') { + return parse_interpol(line.substr(1, line.length)); + } + + // Plain variable data + if (line[0] === '=') { + line = line.substr(1, line.length).trim(); + try { + return parse_interpol(JSON.parse(line)); + } catch (e) { + return line; + } + } + + // HTML escape variable data + if (line.substr(0, 2) === "&=") { + line = line.substr(2, line.length).trim(); + try { + return JSON.stringify(html_escape(JSON.parse(line))); + } catch (e2) { + return 'html_escape(' + line + ')'; + } + } + + // Plain text + return parse_interpol(line); + }()); + } + + }); + if (block) { + output.push(block.process()); + } + return output.filter(function (part) { return part && part.length > 0}).join(" +\n"); + }; + + function optimize(js) { + var new_js = [], buffer = [], part, end; + + function flush() { + if (buffer.length > 0) { + new_js.push(JSON.stringify(buffer.join("")) + end); + buffer = []; + } + } + js.split("\n").forEach(function (line) { + part = line.match(/^(\".*\")(\s*\+\s*)?$/); + if (!part) { + flush(); + new_js.push(line); + return; + } + end = part[2] || ""; + part = part[1]; + try { + buffer.push(JSON.parse(part)); + } catch (e) { + flush(); + new_js.push(line); + } + }); + flush(); + return new_js.join("\n"); + }; + + function Haml(haml) { + var js = optimize(compile(haml)); + return new Function("locals", + html_escape + "\n" + + "with(locals || {}) {\n" + + " try {\n" + + " return (" + js + ");\n" + + " } catch (e) {\n" + + " return \"\\n
        \" + html_escape(e.stack) + \"
        \\n\";\n" + + " }\n" + + "}"); + } + + + Sammy = Sammy || {}; + + // Sammy.Haml provides a quick way of using haml style templates in your app. + // The plugin itself includes the haml-js library created by Tim Caswell at + // at http://github.com/creationix/haml-js + // + // Haml is an alternative HTML syntax that is really great for describing + // the structure of HTML documents. + // + // By default using Sammy.Haml in your app adds the haml() method to the EventContext + // prototype. However, just like Sammy.Template you can change the default name of the method + // by passing a second argument (e.g. you could use the hml() as the method alias so that all the template + // files could be in the form file.hml instead of file.haml) + // + // ### Example + // + // The template (mytemplate.haml): + // + // %h1&= title + // + // Hey, #{name}! Welcome to Haml! + // + // The app: + // + // var $.app = $.sammy(function() { + // // include the plugin + // this.use(Sammy.Haml); + // + // this.get('#/hello/:name', function() { + // // set local vars + // this.title = 'Hello!' + // this.name = this.params.name; + // // render the template and pass it through haml + // this.partial('mytemplate.haml'); + // }); + // + // }); + // + // If I go to #/hello/AQ in the browser, Sammy will render this to the body: + // + //

        Hello!

        + // + // Hey, AQ! Welcome to HAML! + // + // Note: You dont have to include the haml.js file on top of the plugin as the plugin + // includes the full source. + // + Sammy.Haml = function(app, method_alias) { + app.use(Sammy.JSON); + + var haml_cache = {}; + // *Helper* Uses haml-js to parse a template and interpolate and work with the passed data + // + // ### Arguments + // + // * `template` A String template. + // * `data` An Object containing the replacement values for the template. + // data is extended with the EventContext allowing you to call its methods within the template. + // + var haml = function(template, data, name) { + // use name for caching + if (typeof name == 'undefined') name = template; + var fn = haml_cache[name]; + if (!fn) { + fn = haml_cache[name] = Haml(template); + } + return fn($.extend({}, this, data)); + }; + + // set the default method name/extension + if (!method_alias) method_alias = 'haml'; + app.helper(method_alias, haml); + + }; + +})(jQuery); diff --git a/_attachments/script/sammy/plugins/sammy.json.js b/_attachments/script/sammy/plugins/sammy.json.js new file mode 100644 index 0000000..83b9dd0 --- /dev/null +++ b/_attachments/script/sammy/plugins/sammy.json.js @@ -0,0 +1,362 @@ +(function($) { + + // json2.js - only included if native json does not exist + // http://www.json.org/js.html + if (!window.JSON) { + window.JSON = {}; + } + (function () { + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + + // If the string contains no control characters, no quote characters, and no + // backslash characters, then we can safely slap some quotes around it. + // Otherwise we must also replace the offending characters with safe escape + // sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? + '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : + '"' + string + '"'; + } + + + function str(key, holder) { + + // Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + + // If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + + // If we were called with a replacer function, then call the replacer to + // obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + + // What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + + // JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + + // If the value is a boolean or null, convert it to a string. Note: + // typeof null does not produce 'null'. The case is included here in + // the remote chance that this gets fixed someday. + + return String(value); + + // If the type is 'object', we might be dealing with an object or an array or + // null. + + case 'object': + + // Due to a specification blunder in ECMAScript, typeof null is 'object', + // so watch out for that case. + + if (!value) { + return 'null'; + } + + // Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + + // Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + + // The value is an array. Stringify every element. Use null as a placeholder + // for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + + // Join all of the elements together, separated with commas, and wrap them in + // brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + + // If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + + // Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + + // Join all of the member texts together, separated with commas, + // and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + + // If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + + // The stringify method takes a value and an optional replacer, and an optional + // space parameter, and returns a JSON text. The replacer can be a function + // that can replace values, or an array of strings that will select the keys. + // A default replacer method can be provided. Use of the space parameter can + // produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + + // If the space parameter is a number, make an indent string containing that + // many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + + // If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + + // If there is a replacer, it must be a function or an array. + // Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + + // Make a fake root object containing our value under the key of ''. + // Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + + // If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + + // The parse method takes a text and an optional reviver function, and returns + // a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + + // The walk method is used to recursively walk the resulting structure so + // that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + + // Parsing happens in four stages. In the first stage, we replace certain + // Unicode characters with escape sequences. JavaScript handles many characters + // incorrectly, either silently deleting them, or treating them as line endings. + + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + + // In the second stage, we run the text against regular expressions that look + // for non-JSON patterns. We are especially concerned with '()' and 'new' + // because they can cause invocation, and '=' because it can cause mutation. + // But just to be safe, we want to reject all unexpected forms. + + // We split the second stage into 4 regexp operations in order to work around + // crippling inefficiencies in IE's and Safari's regexp engines. First we + // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we + // replace all simple value tokens with ']' characters. Third, we delete all + // open brackets that follow a colon or comma or that begin the text. Finally, + // we look to see that the remaining characters are only whitespace or ']' or + // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/. + test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). + replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). + replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + // In the third stage we use the eval function to compile the text into a + // JavaScript structure. The '{' operator is subject to a syntactic ambiguity + // in JavaScript: it can begin a block or an object literal. We wrap the text + // in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + + // In the optional fourth stage, we recursively walk the new structure, passing + // each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + + // If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } + }()); + + Sammy = Sammy || {}; + + // Sammy.JSON is a simple wrapper around Douglas Crockford's ever-useful json2.js + // (http://www.json.org/js.html]) Sammy.JSON includes the top level JSON object if + // it doesn't already exist (a.k.a. does not override the native implementation that + // some browsers include). It also adds a json() helper to a Sammy app when + // included. + Sammy.JSON = function(app) { + + app.helpers({ + // json is a polymorphic function that translates objects aback and forth + // from JSON to JS. If given a string, it will parse into JS, if given a JS + // object it will stringify into JSON. + // + // ### Example + // + // var app = $.sammy(function() { + // this.use(Sammy.JSON); + // + // this.get('#/', function() { + // this.json({user_id: 123}); //=> "{\"user_id\":\"123\"}" + // this.json("{\"user_id\":\"123\"}"); //=> [object Object] + // this.json("{\"user_id\":\"123\"}").user_id; //=> "123" + // }); + // }) + // + // + json: function(object) { + if (typeof object == 'string') { + return JSON.parse(object); + } else { + return JSON.stringify(object); + } + } + }); + + } + +})(jQuery); diff --git a/_attachments/script/sammy/plugins/sammy.meld.js b/_attachments/script/sammy/plugins/sammy.meld.js new file mode 100644 index 0000000..9753969 --- /dev/null +++ b/_attachments/script/sammy/plugins/sammy.meld.js @@ -0,0 +1,140 @@ +(function($) { + + Sammy = Sammy || {}; + + // `Sammy.Meld` is a simple templating engine that uses the power of jQuery's + // DOM manipulation to easily meld JSON data and HTML templates very quickly. + // + // The template can either be a string (i.e. loaded from a remote template) + // or a DOM Element/jQuery object. This allows you to have templates be DOM + // elements as the initial document load. + // + // ### Example + // + // The simplest case is a nested `
        ` whose class name is tied to a + // property of a JS object. + // + // Template: + // + //
        + //
        + //
        + //
        + // + //
        + //
        + // + // Data: + // + // { + // "post": { + // "title": "My Post", + // "entry": "My Entry", + // "author": { + // "name": "@aq" + // } + // } + // } + // + // Result: + // + //
        + //
        My Post
        + //
        My Entry
        + //
        + // @aq + //
        + //
        + // + // Templates can be much more complex, and more deeply nested. + // More examples can be found in `test/fixtures/meld/` + // + // If you don't think the lookup by classes is semantic for you, you can easily + // switch the method of lookup by defining a selector function in the options + // + // For example: + // + // meld($('.post'), post_data, { + // selector: function(k) { + // return '[data-key=' + k + ']'; + // } + // }); + // + // Would look for template nodes like `
        ` + // + Sammy.Meld = function(app, method_alias) { + var default_options = { + selector: function(k) { return '.' + k; }, + remove_false: true + }; + + var meld = function(template, data, options) { + var $template = $(template); + + options = $.extend(default_options, options || {}); + + if (typeof data === 'string') { + $template.html(data); + } else { + $.each(data, function(key, value) { + var selector = options.selector(key), + $sub = $template.filter(selector), + $container, + $item, + is_list = false, + subindex = $template.index($sub); + + if ($sub.length === 0) { $sub = $template.find(selector); } + if ($sub.length > 0) { + if ($.isArray(value)) { + $container = $('
        '); + if ($sub.is('ol, ul')) { + is_list = true; + $item = $sub.children('li:first'); + if ($item.length == 0) { $item = $('
      • '); } + } else if ($sub.children().length == 1) { + is_list = true; + $item = $sub.children(':first').clone(); + } else { + $item = $sub.clone(); + } + for (var i = 0; i < value.length; i++) { + $container.append(meld($item.clone(), value[i], options)); + } + if (is_list) { + $sub.html($container.html()); + } else if ($sub[0] == $template[0]) { + $template = $($container.html()); + } else if (subindex >= 0) { + var args = [subindex, 1]; + args = args.concat($container.children().get()); + $template.splice.apply($template, args); + } + } else if (options.remove_false && value === false) { + $template.splice(subindex, 1); + } else if (typeof value === 'object') { + if ($sub.is(':empty')) { + $sub.attr(value, true); + } else { + $sub.html(meld($sub.html(), value, options)); + } + } else { + $sub.html(value.toString()); + } + } else { + $template.attr({key: value}, true); + } + }); + } + var dom = $template; + return dom; + }; + + // set the default method name/extension + if (!method_alias) method_alias = 'meld'; + // create the helper at the method alias + app.helper(method_alias, meld); + + }; + +})(jQuery); diff --git a/_attachments/script/sammy/plugins/sammy.mustache.js b/_attachments/script/sammy/plugins/sammy.mustache.js new file mode 100644 index 0000000..169f0c7 --- /dev/null +++ b/_attachments/script/sammy/plugins/sammy.mustache.js @@ -0,0 +1,444 @@ +(function($) { + +if (!window.Mustache) { + + /* + mustache.js — Logic-less templates in JavaScript + + See http://mustache.github.com/ for more info. + */ + + var Mustache = function() { + var Renderer = function() {}; + + Renderer.prototype = { + otag: "{{", + ctag: "}}", + pragmas: {}, + buffer: [], + pragmas_implemented: { + "IMPLICIT-ITERATOR": true + }, + context: {}, + + render: function(template, context, partials, in_recursion) { + // reset buffer & set context + if(!in_recursion) { + this.context = context; + this.buffer = []; // TODO: make this non-lazy + } + + // fail fast + if(!this.includes("", template)) { + if(in_recursion) { + return template; + } else { + this.send(template); + return; + } + } + + template = this.render_pragmas(template); + var html = this.render_section(template, context, partials); + if(in_recursion) { + return this.render_tags(html, context, partials, in_recursion); + } + + this.render_tags(html, context, partials, in_recursion); + }, + + /* + Sends parsed lines + */ + send: function(line) { + if(line != "") { + this.buffer.push(line); + } + }, + + /* + Looks for %PRAGMAS + */ + render_pragmas: function(template) { + // no pragmas + if(!this.includes("%", template)) { + return template; + } + + var that = this; + var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + + this.ctag); + return template.replace(regex, function(match, pragma, options) { + if(!that.pragmas_implemented[pragma]) { + throw({message: + "This implementation of mustache doesn't understand the '" + + pragma + "' pragma"}); + } + that.pragmas[pragma] = {}; + if(options) { + var opts = options.split("="); + that.pragmas[pragma][opts[0]] = opts[1]; + } + return ""; + // ignore unknown pragmas silently + }); + }, + + /* + Tries to find a partial in the curent scope and render it + */ + render_partial: function(name, context, partials) { + name = this.trim(name); + if(!partials || partials[name] === undefined) { + throw({message: "unknown_partial '" + name + "'"}); + } + if(typeof(context[name]) != "object") { + return this.render(partials[name], context, partials, true); + } + return this.render(partials[name], context[name], partials, true); + }, + + /* + Renders inverted (^) and normal (#) sections + */ + render_section: function(template, context, partials) { + if(!this.includes("#", template) && !this.includes("^", template)) { + return template; + } + + var that = this; + // CSW - Added "+?" so it finds the tighest bound, not the widest + var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag + + "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag + + "\\s*", "mg"); + + // for each {{#foo}}{{/foo}} section do... + return template.replace(regex, function(match, type, name, content) { + var value = that.find(name, context); + if(type == "^") { // inverted section + if(!value || that.is_array(value) && value.length === 0) { + // false or empty list, render it + return that.render(content, context, partials, true); + } else { + return ""; + } + } else if(type == "#") { // normal section + if(that.is_array(value)) { // Enumerable, Let's loop! + return that.map(value, function(row) { + return that.render(content, that.create_context(row), + partials, true); + }).join(""); + } else if(that.is_object(value)) { // Object, Use it as subcontext! + return that.render(content, that.create_context(value), + partials, true); + } else if(typeof value === "function") { + // higher order section + return value.call(context, content, function(text) { + return that.render(text, context, partials, true); + }); + } else if(value) { // boolean section + return that.render(content, context, partials, true); + } else { + return ""; + } + } + }); + }, + + /* + Replace {{foo}} and friends with values from our view + */ + render_tags: function(template, context, partials, in_recursion) { + // tit for tat + var that = this; + + var new_regex = function() { + return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + + that.ctag + "+", "g"); + }; + + var regex = new_regex(); + var tag_replace_callback = function(match, operator, name) { + switch(operator) { + case "!": // ignore comments + return ""; + case "=": // set new delimiters, rebuild the replace regexp + that.set_delimiters(name); + regex = new_regex(); + return ""; + case ">": // render partial + return that.render_partial(name, context, partials); + case "{": // the triple mustache is unescaped + return that.find(name, context); + default: // escape the value + return that.escape(that.find(name, context)); + } + }; + var lines = template.split("\n"); + for(var i = 0; i < lines.length; i++) { + lines[i] = lines[i].replace(regex, tag_replace_callback, this); + if(!in_recursion) { + this.send(lines[i]); + } + } + + if(in_recursion) { + return lines.join("\n"); + } + }, + + set_delimiters: function(delimiters) { + var dels = delimiters.split(" "); + this.otag = this.escape_regex(dels[0]); + this.ctag = this.escape_regex(dels[1]); + }, + + escape_regex: function(text) { + // thank you Simon Willison + if(!arguments.callee.sRE) { + var specials = [ + '/', '.', '*', '+', '?', '|', + '(', ')', '[', ']', '{', '}', '\\' + ]; + arguments.callee.sRE = new RegExp( + '(\\' + specials.join('|\\') + ')', 'g' + ); + } + return text.replace(arguments.callee.sRE, '\\$1'); + }, + + /* + find `name` in current `context`. That is find me a value + from the view object + */ + find: function(name, context) { + name = this.trim(name); + + // Checks whether a value is thruthy or false or 0 + function is_kinda_truthy(bool) { + return bool === false || bool === 0 || bool; + } + + var value; + if(is_kinda_truthy(context[name])) { + value = context[name]; + } else if(is_kinda_truthy(this.context[name])) { + value = this.context[name]; + } + + if(typeof value === "function") { + return value.apply(context); + } + if(value !== undefined) { + return value; + } + // silently ignore unkown variables + return ""; + }, + + // Utility methods + + /* includes tag */ + includes: function(needle, haystack) { + return haystack.indexOf(this.otag + needle) != -1; + }, + + /* + Does away with nasty characters + */ + escape: function(s) { + s = String(s === null ? "" : s); + return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) { + switch(s) { + case "&": return "&"; + case "\\": return "\\\\"; + case '"': return '\"'; + case "<": return "<"; + case ">": return ">"; + default: return s; + } + }); + }, + + // by @langalex, support for arrays of strings + create_context: function(_context) { + if(this.is_object(_context)) { + return _context; + } else { + var iterator = "."; + if(this.pragmas["IMPLICIT-ITERATOR"]) { + iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; + } + var ctx = {}; + ctx[iterator] = _context; + return ctx; + } + }, + + is_object: function(a) { + return a && typeof a == "object"; + }, + + is_array: function(a) { + return Object.prototype.toString.call(a) === '[object Array]'; + }, + + /* + Gets rid of leading and trailing whitespace + */ + trim: function(s) { + return s.replace(/^\s*|\s*$/g, ""); + }, + + /* + Why, why, why? Because IE. Cry, cry cry. + */ + map: function(array, fn) { + if (typeof array.map == "function") { + return array.map(fn); + } else { + var r = []; + var l = array.length; + for(var i = 0; i < l; i++) { + r.push(fn(array[i])); + } + return r; + } + } + }; + + return({ + name: "mustache.js", + version: "0.3.1-dev", + + /* + Turns a template and view into HTML + */ + to_html: function(template, view, partials, send_fun) { + var renderer = new Renderer(); + if(send_fun) { + renderer.send = send_fun; + } + renderer.render(template, view, partials); + if(!send_fun) { + return renderer.buffer.join("\n"); + } + } + }); + }(); + +} // Ensure Mustache + + Sammy = Sammy || {}; + + // Sammy.Mustache provides a quick way of using mustache style templates in your app. + // The plugin itself includes the awesome mustache.js lib created and maintained by Jan Lehnardt + // at http://github.com/janl/mustache.js + // + // Mustache is a clever templating system that relys on double brackets {{}} for interpolation. + // For full details on syntax check out the original Ruby implementation created by Chris Wanstrath at + // http://github.com/defunkt/mustache + // + // By default using Sammy.Mustache in your app adds the mustache() method to the EventContext + // prototype. However, just like Sammy.Template you can change the default name of the method + // by passing a second argument (e.g. you could use the ms() as the method alias so that all the template + // files could be in the form file.ms instead of file.mustache) + // + // ### Example #1 + // + // The template (mytemplate.ms): + // + //

        \{\{title\}\}

        + // + // Hey, {{name}}! Welcome to Mustache! + // + // The app: + // + // var $.app = $.sammy(function() { + // // include the plugin and alias mustache() to ms() + // this.use(Sammy.Mustache, 'ms'); + // + // this.get('#/hello/:name', function() { + // // set local vars + // this.title = 'Hello!' + // this.name = this.params.name; + // // render the template and pass it through mustache + // this.partial('mytemplate.ms'); + // }); + // + // }); + // + // If I go to #/hello/AQ in the browser, Sammy will render this to the body: + // + //

        Hello!

        + // + // Hey, AQ! Welcome to Mustache! + // + // + // ### Example #2 - Mustache partials + // + // The template (mytemplate.ms) + // + // Hey, {{name}}! {{>hello_friend}} + // + // + // The partial (mypartial.ms) + // + // Say hello to your friend {{friend}}! + // + // The app: + // + // var $.app = $.sammy(function() { + // // include the plugin and alias mustache() to ms() + // this.use(Sammy.Mustache, 'ms'); + // + // this.get('#/hello/:name/to/:friend', function() { + // var context = this; + // + // // fetch mustache-partial first + // $.get('mypartial.ms', function(response){ + // context.partials = response; + // + // // set local vars + // context.name = this.params.name; + // context.hello_friend = {name: this.params.friend}; + // + // // render the template and pass it through mustache + // context.partial('mytemplate.ms'); + // }); + // }); + // + // }); + // + // If I go to #/hello/AQ/to/dP in the browser, Sammy will render this to the body: + // + // Hey, AQ! Say hello to your friend dP! + // + // Note: You dont have to include the mustache.js file on top of the plugin as the plugin + // includes the full source. + // + Sammy.Mustache = function(app, method_alias) { + + // *Helper* Uses Mustache.js to parse a template and interpolate and work with the passed data + // + // ### Arguments + // + // * `template` A String template. {{}} Tags are evaluated and interpolated by Mustache.js + // * `data` An Object containing the replacement values for the template. + // data is extended with the EventContext allowing you to call its methods within the template. + // * `partials` An Object containing one or more partials (String templates + // that are called from the main template). + // + var mustache = function(template, data, partials) { + data = $.extend({}, this, data); + partials = $.extend({}, data.partials, partials); + return Mustache.to_html(template, data, partials); + }; + + // set the default method name/extension + if (!method_alias) method_alias = 'mustache'; + app.helper(method_alias, mustache); + + }; + +})(jQuery); diff --git a/_attachments/script/sammy/plugins/sammy.nested_params.js b/_attachments/script/sammy/plugins/sammy.nested_params.js new file mode 100644 index 0000000..645f8c9 --- /dev/null +++ b/_attachments/script/sammy/plugins/sammy.nested_params.js @@ -0,0 +1,118 @@ +(function($) { + + Sammy = Sammy || {}; + + function parseValue(value) { + value = unescape(value); + if (value === "true") { + return true; + } else if (value === "false") { + return false; + } else { + return value; + } + }; + + function parseNestedParam(params, field_name, field_value) { + var match, name, rest; + + if (field_name.match(/^[^\[]+$/)) { + // basic value + params[field_name] = parseValue(field_value); + } else if (match = field_name.match(/^([^\[]+)\[\](.*)$/)) { + // array + name = match[1]; + rest = match[2]; + + if(params[name] && !$.isArray(params[name])) { throw('400 Bad Request'); } + + if (rest) { + // array is not at the end of the parameter string + match = rest.match(/^\[([^\]]+)\](.*)$/); + if(!match) { throw('400 Bad Request'); } + + if (params[name]) { + if(params[name][params[name].length - 1][match[1]]) { + params[name].push(parseNestedParam({}, match[1] + match[2], field_value)); + } else { + $.extend(true, params[name][params[name].length - 1], parseNestedParam({}, match[1] + match[2], field_value)); + } + } else { + params[name] = [parseNestedParam({}, match[1] + match[2], field_value)]; + } + } else { + // array is at the end of the parameter string + if (params[name]) { + params[name].push(parseValue(field_value)); + } else { + params[name] = [parseValue(field_value)]; + } + } + } else if (match = field_name.match(/^([^\[]+)\[([^\[]+)\](.*)$/)) { + // hash + name = match[1]; + rest = match[2] + match[3]; + + if (params[name] && $.isArray(params[name])) { throw('400 Bad Request'); } + + if (params[name]) { + $.extend(true, params[name], parseNestedParam(params[name], rest, field_value)); + } else { + params[name] = parseNestedParam({}, rest, field_value); + } + } + return params; + }; + + // Sammy.NestedParams overrides the default form parsing behavior to provide + // extended functionality for parsing Rack/Rails style form name/value pairs into JS + // Objects. In fact it passes the same suite of tests as Rack's nested query parsing. + // The code and tests were ported to JavaScript/Sammy by http://github.com/endor + // + // This allows you to translate a form with properly named inputs into a JSON object. + // + // ### Example + // + // Given an HTML form like so: + // + //
        + // + // + // + // + //
        + // + // And a Sammy app like: + // + // var app = $.sammy(function(app) { + // this.use(Sammy.NestedParams); + // + // this.post('#/parse_me', function(context) { + // $.log(this.params); + // }); + // }); + // + // If you filled out the form with some values and submitted it, you would see something + // like this in your log: + // + // { + // 'obj': { + // 'first': 'value', + // 'second': 'value', + // 'hash': { + // 'first': 'value', + // 'second': 'value' + // } + // } + // } + // + // It supports creating arrays with [] and other niceities. Check out the tests for + // full specs. + // + Sammy.NestedParams = function(app) { + + app._parseParamPair = parseNestedParam; + + }; + +})(jQuery); diff --git a/_attachments/script/sammy/plugins/sammy.path_location_proxy.js b/_attachments/script/sammy/plugins/sammy.path_location_proxy.js new file mode 100644 index 0000000..13f9014 --- /dev/null +++ b/_attachments/script/sammy/plugins/sammy.path_location_proxy.js @@ -0,0 +1,29 @@ +(function($) { + + Sammy = Sammy || {}; + + // `Sammy.PathLocationProxy` is a simple Location Proxy that just + // gets and sets window.location. This allows you to use + // Sammy to route on the full URL path instead of just the hash. It + // will take a full refresh to get the app to change state. + // + // To read more about location proxies, check out the + // documentation for `Sammy.HashLocationProxy` + Sammy.PathLocationProxy = function(app) { + this.app = app; + }; + + Sammy.PathLocationProxy.prototype = { + bind: function() {}, + unbind: function() {}, + + getLocation: function() { + return [window.location.pathname, window.location.search].join(''); + }, + + setLocation: function(new_location) { + return window.location = new_location; + } + }; + +})(jQuery); diff --git a/_attachments/script/sammy/plugins/sammy.pure.js b/_attachments/script/sammy/plugins/sammy.pure.js new file mode 100644 index 0000000..9afd22d --- /dev/null +++ b/_attachments/script/sammy/plugins/sammy.pure.js @@ -0,0 +1,757 @@ +(function($) { + +/*! + PURE Unobtrusive Rendering Engine for HTML + + Licensed under the MIT licenses. + More information at: http://www.opensource.org + + Copyright (c) 2010 Michael Cvilic - BeeBole.com + + Thanks to Rog Peppe for the functional JS jump + revision: 2.47 +*/ + +var $p, pure = $p = function(){ + var sel = arguments[0], + ctxt = false; + + if(typeof sel === 'string'){ + ctxt = arguments[1] || false; + } + return $p.core(sel, ctxt); +}; + +$p.core = function(sel, ctxt, plugins){ + //get an instance of the plugins + var plugins = getPlugins(), + templates = []; + + //search for the template node(s) + switch(typeof sel){ + case 'string': + templates = plugins.find(ctxt || document, sel); + if(templates.length === 0) { + error('The template "' + sel + '" was not found'); + } + break; + case 'undefined': + error('The template root is undefined, check your selector'); + break; + default: + templates = [sel]; + } + + for(var i = 0, ii = templates.length; i < ii; i++){ + plugins[i] = templates[i]; + } + plugins.length = ii; + + // set the signature string that will be replaced at render time + var Sig = '_s' + Math.floor( Math.random() * 1000000 ) + '_', + // another signature to prepend to attributes and avoid checks: style, height, on[events]... + attPfx = '_a' + Math.floor( Math.random() * 1000000 ) + '_', + // rx to parse selectors, e.g. "+tr.foo[class]" + selRx = /^(\+)?([^\@\+]+)?\@?([^\+]+)?(\+)?$/, + // set automatically attributes for some tags + autoAttr = { + IMG:'src', + INPUT:'value' + }, + // check if the argument is an array - thanks salty-horse (Ori Avtalion) + isArray = Array.isArray ? + function(o) { + return Array.isArray(o); + } : + function(o) { + return Object.prototype.toString.call(o) === "[object Array]"; + }; + + return plugins; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * + core functions + * * * * * * * * * * * * * * * * * * * * * * * * * */ + + + // error utility + function error(e){ + if(typeof console !== 'undefined'){ + console.log(e); + debugger; + }else{ alert(e); } + throw('pure error: ' + e); + } + + //return a new instance of plugins + function getPlugins(){ + var plugins = $p.plugins, + f = function(){}; + f.prototype = plugins; + + // do not overwrite functions if external definition + f.prototype.compile = plugins.compile || compile; + f.prototype.render = plugins.render || render; + f.prototype.autoRender = plugins.autoRender || autoRender; + f.prototype.find = plugins.find || find; + + // give the compiler and the error handling to the plugin context + f.prototype._compiler = compiler; + f.prototype._error = error; + + return new f(); + } + + // returns the outer HTML of a node + function outerHTML(node){ + // if IE take the internal method otherwise build one + return node.outerHTML || ( + function(n){ + var div = document.createElement('div'), h; + div.appendChild( n.cloneNode(true) ); + h = div.innerHTML; + div = null; + return h; + })(node); + } + + // returns the string generator function + function wrapquote(qfn, f){ + return function(ctxt){ + return qfn('' + f.call(ctxt.context, ctxt)); + }; + } + + // default find using querySelector when available on the browser + function find(n, sel){ + if(typeof n === 'string'){ + sel = n; + n = false; + } + if(typeof document.querySelectorAll !== 'undefined'){ + return (n||document).querySelectorAll( sel ); + }else{ + error('You can test PURE standalone with: iPhone, FF3.5+, Safari4+ and IE8+\n\nTo run PURE on your browser, you need a JS library/framework with a CSS selector engine'); + } + } + + // create a function that concatenates constant string + // sections (given in parts) and the results of called + // functions to fill in the gaps between parts (fns). + // fns[n] fills in the gap between parts[n-1] and parts[n]; + // fns[0] is unused. + // this is the inner template evaluation loop. + function concatenator(parts, fns){ + return function(ctxt){ + var strs = [ parts[ 0 ] ], + n = parts.length, + fnVal, pVal, attLine, pos; + + for(var i = 1; i < n; i++){ + fnVal = fns[i]( ctxt ); + pVal = parts[i]; + + // if the value is empty and attribute, remove it + if(fnVal === ''){ + attLine = strs[ strs.length - 1 ]; + if( ( pos = attLine.search( /[\w]+=\"?$/ ) ) > -1){ + strs[ strs.length - 1 ] = attLine.substring( 0, pos ); + pVal = pVal.substr( 1 ); + } + } + + strs[ strs.length ] = fnVal; + strs[ strs.length ] = pVal; + } + return strs.join(''); + }; + } + + // parse and check the loop directive + function parseloopspec(p){ + var m = p.match( /^(\w+)\s*<-\s*(\S+)?$/ ); + if(m === null){ + error('bad loop spec: "' + p + '"'); + } + if(m[1] === 'item'){ + error('"item<-..." is a reserved word for the current running iteration.\n\nPlease choose another name for your loop.'); + } + if( !m[2] || (m[2] && (/context/i).test(m[2]))){ //undefined or space(IE) + m[2] = function(ctxt){return ctxt.context;}; + } + return {name: m[1], sel: m[2]}; + } + + // parse a data selector and return a function that + // can traverse the data accordingly, given a context. + function dataselectfn(sel){ + if(typeof(sel) === 'function'){ + return sel; + } + //check for a valid js variable name with hyphen(for properties only), $, _ and : + var m = sel.match(/^[a-zA-Z\$_\@][\w\$:-]*(\.[\w\$:-]*[^\.])*$/); + if(m === null){ + var found = false, s = sel, parts = [], pfns = [], i = 0, retStr; + // check if literal + if(/\'|\"/.test( s.charAt(0) )){ + if(/\'|\"/.test( s.charAt(s.length-1) )){ + retStr = s.substring(1, s.length-1); + return function(){ return retStr; }; + } + }else{ + // check if literal + #{var} + while((m = s.match(/#\{([^{}]+)\}/)) !== null){ + found = true; + parts[i++] = s.slice(0, m.index); + pfns[i] = dataselectfn(m[1]); + s = s.slice(m.index + m[0].length, s.length); + } + } + if(!found){ + error('bad data selector syntax: ' + sel); + } + parts[i] = s; + return concatenator(parts, pfns); + } + m = sel.split('.'); + return function(ctxt){ + var data = ctxt.context; + if(!data){ + return ''; + } + var v = ctxt[m[0]], + i = 0; + if(v && v.item){ + data = v.item; + i += 1; + } + var n = m.length; + for(; i < n; i++){ + if(!data){break;} + data = data[m[i]]; + } + return (!data && data !== 0) ? '':data; + }; + } + + // wrap in an object the target node/attr and their properties + function gettarget(dom, sel, isloop){ + var osel, prepend, selector, attr, append, target = []; + if( typeof sel === 'string' ){ + osel = sel; + var m = sel.match(selRx); + if( !m ){ + error( 'bad selector syntax: ' + sel ); + } + + prepend = m[1]; + selector = m[2]; + attr = m[3]; + append = m[4]; + + if(selector === '.' || ( !selector && attr ) ){ + target[0] = dom; + }else{ + target = plugins.find(dom, selector); + } + if(!target || target.length === 0){ + return error('The node "' + sel + '" was not found in the template'); + } + }else{ + // autoRender node + prepend = sel.prepend; + attr = sel.attr; + append = sel.append; + target = [dom]; + } + + if( prepend || append ){ + if( prepend && append ){ + error('append/prepend cannot take place at the same time'); + }else if( isloop ){ + error('no append/prepend/replace modifiers allowed for loop target'); + }else if( append && isloop ){ + error('cannot append with loop (sel: ' + osel + ')'); + } + } + var setstr, getstr, quotefn, isStyle, isClass, attName, setfn; + if(attr){ + isStyle = (/^style$/i).test(attr); + isClass = (/^class$/i).test(attr); + attName = isClass ? 'className' : attr; + setstr = function(node, s) { + node.setAttribute(attPfx + attr, s); + if (attName in node && !isStyle) { + node[attName] = ''; + } + if (node.nodeType === 1) { + node.removeAttribute(attr); + isClass && node.removeAttribute(attName); + } + }; + if (isStyle || isClass) {//IE no quotes special care + if(isStyle){ + getstr = function(n){ return n.style.cssText; }; + }else{ + getstr = function(n){ return n.className; }; + } + quotefn = function(s){ return s.replace(/\"/g, '"'); }; + }else { + getstr = function(n){ return n.getAttribute(attr); }; + quotefn = function(s){ return s.replace(/\"/g, '"').replace(/\s/g, ' '); }; + } + if(prepend){ + setfn = function(node, s){ setstr( node, s + getstr( node )); }; + }else if(append){ + setfn = function(node, s){ setstr( node, getstr( node ) + s); }; + }else{ + setfn = function(node, s){ setstr( node, s ); }; + } + }else{ + if (isloop) { + setfn = function(node, s) { + var pn = node.parentNode; + if (pn) { + //replace node with s + pn.insertBefore(document.createTextNode(s), node.nextSibling); + pn.removeChild(node); + } + }; + } else { + if (prepend) { + setfn = function(node, s) { node.insertBefore(document.createTextNode(s), node.firstChild); }; + } else if (append) { + setfn = function(node, s) { node.appendChild(document.createTextNode(s));}; + } else { + setfn = function(node, s) { + while (node.firstChild) { node.removeChild(node.firstChild); } + node.appendChild(document.createTextNode(s)); + }; + } + } + quotefn = function(s) { return s; }; + } + return { attr: attr, nodes: target, set: setfn, sel: osel, quotefn: quotefn }; + } + + function setsig(target, n){ + var sig = Sig + n + ':'; + for(var i = 0; i < target.nodes.length; i++){ + // could check for overlapping targets here. + target.set( target.nodes[i], sig ); + } + } + + // read de loop data, and pass it to the inner rendering function + function loopfn(name, dselect, inner, sorter, filter){ + return function(ctxt){ + var a = dselect(ctxt), + old = ctxt[name], + temp = { items : a }, + filtered = 0, + length, + strs = [], + buildArg = function(idx, temp, ftr, len){ + ctxt.pos = temp.pos = idx; + ctxt.item = temp.item = a[ idx ]; + ctxt.items = a; + //if array, set a length property - filtered items + typeof len !== 'undefined' && (ctxt.length = len); + //if filter directive + if(typeof ftr === 'function' && ftr(ctxt) === false){ + filtered++; + return; + } + strs.push( inner.call(temp, ctxt ) ); + }; + ctxt[name] = temp; + if( isArray(a) ){ + length = a.length || 0; + // if sort directive + if(typeof sorter === 'function'){ + a.sort(sorter); + } + //loop on array + for(var i = 0, ii = length; i < ii; i++){ + buildArg(i, temp, filter, length - filtered); + } + }else{ + if(a && typeof sorter !== 'undefined'){ + error('sort is only available on arrays, not objects'); + } + //loop on collections + for(var prop in a){ + a.hasOwnProperty( prop ) && buildArg(prop, temp, filter); + } + } + + typeof old !== 'undefined' ? ctxt[name] = old : delete ctxt[name]; + return strs.join(''); + }; + } + // generate the template for a loop node + function loopgen(dom, sel, loop, fns){ + var already = false, ls, sorter, filter, prop; + for(prop in loop){ + if(loop.hasOwnProperty(prop)){ + if(prop === 'sort'){ + sorter = loop.sort; + continue; + }else if(prop === 'filter'){ + filter = loop.filter; + continue; + } + if(already){ + error('cannot have more than one loop on a target'); + } + ls = prop; + already = true; + } + } + if(!ls){ + error('Error in the selector: ' + sel + '\nA directive action must be a string, a function or a loop(<-)'); + } + var dsel = loop[ls]; + // if it's a simple data selector then we default to contents, not replacement. + if(typeof(dsel) === 'string' || typeof(dsel) === 'function'){ + loop = {}; + loop[ls] = {root: dsel}; + return loopgen(dom, sel, loop, fns); + } + var spec = parseloopspec(ls), + itersel = dataselectfn(spec.sel), + target = gettarget(dom, sel, true), + nodes = target.nodes; + + for(i = 0; i < nodes.length; i++){ + var node = nodes[i], + inner = compiler(node, dsel); + fns[fns.length] = wrapquote(target.quotefn, loopfn(spec.name, itersel, inner, sorter, filter)); + target.nodes = [node]; // N.B. side effect on target. + setsig(target, fns.length - 1); + } + } + + function getAutoNodes(n, data){ + var ns = n.getElementsByTagName('*'), + an = [], + openLoops = {a:[],l:{}}, + cspec, + isNodeValue, + i, ii, j, jj, ni, cs, cj; + //for each node found in the template + for(i = -1, ii = ns.length; i < ii; i++){ + ni = i > -1 ?ns[i]:n; + if(ni.nodeType === 1 && ni.className !== ''){ + //when a className is found + cs = ni.className.split(' '); + // for each className + for(j = 0, jj=cs.length;j -1 || isNodeValue){ + ni.className = ni.className.replace('@'+cspec.attr, ''); + if(isNodeValue){ + cspec.attr = false; + } + } + an.push({n:ni, cspec:cspec}); + } + } + } + } + return an; + + function checkClass(c, tagName){ + // read the class + var ca = c.match(selRx), + attr = ca[3] || autoAttr[tagName], + cspec = {prepend:!!ca[1], prop:ca[2], attr:attr, append:!!ca[4], sel:c}, + i, ii, loopi, loopil, val; + // check in existing open loops + for(i = openLoops.a.length-1; i >= 0; i--){ + loopi = openLoops.a[i]; + loopil = loopi.l[0]; + val = loopil && loopil[cspec.prop]; + if(typeof val !== 'undefined'){ + cspec.prop = loopi.p + '.' + cspec.prop; + if(openLoops.l[cspec.prop] === true){ + val = val[0]; + } + break; + } + } + // not found check first level of data + if(typeof val === 'undefined'){ + val = isArray(data) ? data[0][cspec.prop] : data[cspec.prop]; + // nothing found return + if(typeof val === 'undefined'){ + return false; + } + } + // set the spec for autoNode + if(isArray(val)){ + openLoops.a.push( {l:val, p:cspec.prop} ); + openLoops.l[cspec.prop] = true; + cspec.t = 'loop'; + }else{ + cspec.t = 'str'; + } + return cspec; + } + } + + // returns a function that, given a context argument, + // will render the template defined by dom and directive. + function compiler(dom, directive, data, ans){ + var fns = []; + // autoRendering nodes parsing -> auto-nodes + ans = ans || data && getAutoNodes(dom, data); + if(data){ + var j, jj, cspec, n, target, nodes, itersel, node, inner; + // for each auto-nodes + while(ans.length > 0){ + cspec = ans[0].cspec; + n = ans[0].n; + ans.splice(0, 1); + if(cspec.t === 'str'){ + // if the target is a value + target = gettarget(n, cspec, false); + setsig(target, fns.length); + fns[fns.length] = wrapquote(target.quotefn, dataselectfn(cspec.prop)); + }else{ + // if the target is a loop + itersel = dataselectfn(cspec.sel); + target = gettarget(n, cspec, true); + nodes = target.nodes; + for(j = 0, jj = nodes.length; j < jj; j++){ + node = nodes[j]; + inner = compiler(node, false, data, ans); + fns[fns.length] = wrapquote(target.quotefn, loopfn(cspec.sel, itersel, inner)); + target.nodes = [node]; + setsig(target, fns.length - 1); + } + } + } + } + // read directives + var target, dsel; + for(var sel in directive){ + if(directive.hasOwnProperty(sel)){ + dsel = directive[sel]; + if(typeof(dsel) === 'function' || typeof(dsel) === 'string'){ + // set the value for the node/attr + target = gettarget(dom, sel, false); + setsig(target, fns.length); + fns[fns.length] = wrapquote(target.quotefn, dataselectfn(dsel)); + }else{ + // loop on node + loopgen(dom, sel, dsel, fns); + } + } + } + // convert node to a string + var h = outerHTML(dom), pfns = []; + // IE adds an unremovable "selected, value" attribute + // hard replace while waiting for a better solution + h = h.replace(/<([^>]+)\s(value\=""|selected)\s?([^>]*)>/ig, "<$1 $3>"); + + // remove attribute prefix + h = h.split(attPfx).join(''); + + // slice the html string at "Sig" + var parts = h.split( Sig ), p; + // for each slice add the return string of + for(var i = 1; i < parts.length; i++){ + p = parts[i]; + // part is of the form "fn-number:..." as placed there by setsig. + pfns[i] = fns[ parseInt(p, 10) ]; + parts[i] = p.substring( p.indexOf(':') + 1 ); + } + return concatenator(parts, pfns); + } + // compile the template with directive + // if a context is passed, the autoRendering is triggered automatically + // return a function waiting the data as argument + function compile(directive, ctxt, template){ + var rfn = compiler( ( template || this[0] ).cloneNode(true), directive, ctxt); + return function(context){ + return rfn({context:context}); + }; + } + //compile with the directive as argument + // run the template function on the context argument + // return an HTML string + // should replace the template and return this + function render(ctxt, directive){ + var fn = typeof directive === 'function' ? directive : plugins.compile( directive, false, this[0] ); + for(var i = 0, ii = this.length; i < ii; i++){ + this[i] = replaceWith( this[i], fn( ctxt, false )); + } + context = null; + return this; + } + + // compile the template with autoRender + // run the template function on the context argument + // return an HTML string + function autoRender(ctxt, directive){ + var fn = plugins.compile( directive, ctxt, this[0] ); + for(var i = 0, ii = this.length; i < ii; i++){ + this[i] = replaceWith( this[i], fn( ctxt, false)); + } + context = null; + return this; + } + + function replaceWith(elm, html) { + var ne, + ep = elm.parentNode, + depth = 0; + switch (elm.tagName) { + case 'TBODY': case 'THEAD': case 'TFOOT': + html = '' + html + '
        '; + depth = 1; + break; + case 'TR': + html = '' + html + '
        '; + depth = 2; + break; + case 'TD': case 'TH': + html = '' + html + '
        '; + depth = 3; + break; + } + tmp = document.createElement('SPAN'); + tmp.style.display = 'none'; + document.body.appendChild(tmp); + tmp.innerHTML = html; + ne = tmp.firstChild; + while (depth--) { + ne = ne.firstChild; + } + ep.insertBefore(ne, elm); + ep.removeChild(elm); + document.body.removeChild(tmp); + elm = ne; + + ne = ep = null; + return elm; + } +}; + +$p.plugins = {}; + +$p.libs = { + dojo:function(){ + if(typeof document.querySelector === 'undefined'){ + $p.plugins.find = function(n, sel){ + return dojo.query(sel, n); + }; + } + }, + domassistant:function(){ + if(typeof document.querySelector === 'undefined'){ + $p.plugins.find = function(n, sel){ + return $(n).cssSelect(sel); + }; + } + DOMAssistant.attach({ + publicMethods : [ 'compile', 'render', 'autoRender'], + compile:function(directive, ctxt){ return $p(this).compile(directive, ctxt); }, + render:function(ctxt, directive){ return $( $p(this).render(ctxt, directive) )[0]; }, + autoRender:function(ctxt, directive){ return $( $p(this).autoRender(ctxt, directive) )[0]; } + }); + }, + jquery:function(){ + if(typeof document.querySelector === 'undefined'){ + $p.plugins.find = function(n, sel){ + return jQuery(n).find(sel); + }; + } + jQuery.fn.extend({ + compile:function(directive, ctxt){ return $p(this[0]).compile(directive, ctxt); }, + render:function(ctxt, directive){ return jQuery( $p( this[0] ).render( ctxt, directive ) ); }, + autoRender:function(ctxt, directive){ return jQuery( $p( this[0] ).autoRender( ctxt, directive ) ); } + }); + }, + mootools:function(){ + if(typeof document.querySelector === 'undefined'){ + $p.plugins.find = function(n, sel){ + return $(n).getElements(sel); + }; + } + Element.implement({ + compile:function(directive, ctxt){ return $p(this).compile(directive, ctxt); }, + render:function(ctxt, directive){ return $p(this).render(ctxt, directive); }, + autoRender:function(ctxt, directive){ return $p(this).autoRender(ctxt, directive); } + }); + }, + prototype:function(){ + if(typeof document.querySelector === 'undefined'){ + $p.plugins.find = function(n, sel){ + n = n === document ? n.body : n; + return typeof n === 'string' ? $$(n) : $(n).select(sel); + }; + } + Element.addMethods({ + compile:function(element, directive, ctxt){ return $p(element).compile(directive, ctxt); }, + render:function(element, ctxt, directive){ return $p(element).render(ctxt, directive); }, + autoRender:function(element, ctxt, directive){ return $p(element).autoRender(ctxt, directive); } + }); + }, + sizzle:function(){ + if(typeof document.querySelector === 'undefined'){ + $p.plugins.find = function(n, sel){ + return Sizzle(sel, n); + }; + } + }, + sly:function(){ + if(typeof document.querySelector === 'undefined'){ + $p.plugins.find = function(n, sel){ + return Sly(sel, n); + }; + } + } +}; + +// get lib specifics if available +(function(){ + var libkey = + typeof dojo !== 'undefined' && 'dojo' || + typeof DOMAssistant !== 'undefined' && 'domassistant' || + typeof jQuery !== 'undefined' && 'jquery' || + typeof MooTools !== 'undefined' && 'mootools' || + typeof Prototype !== 'undefined' && 'prototype' || + typeof Sizzle !== 'undefined' && 'sizzle' || + typeof Sly !== 'undefined' && 'sly'; + + libkey && $p.libs[libkey](); +})(); + + + Sammy = Sammy || {}; + + // `Sammy.Pure` is a simple wrapper around the pure.js templating engine for + // use in Sammy apps. + // + // See http://beebole.com/pure/ for detailed documentation. + Sammy.Pure = function(app, method_alias) { + + var pure = function(template, data, directives) { + return $(template).autoRender(data, directives); + }; + + // set the default method name/extension + if (!method_alias) method_alias = 'pure'; + app.helper(method_alias, pure); + + }; + +})(jQuery); diff --git a/_attachments/script/sammy/plugins/sammy.storage.js b/_attachments/script/sammy/plugins/sammy.storage.js new file mode 100644 index 0000000..02381fd --- /dev/null +++ b/_attachments/script/sammy/plugins/sammy.storage.js @@ -0,0 +1,577 @@ +(function($) { + + Sammy = Sammy || {}; + + // Sammy.Store is an abstract adapter class that wraps the multitude of in + // browser data storage into a single common set of methods for storing and + // retreiving data. The JSON library is used (through the inclusion of the + // Sammy.JSON) plugin, to automatically convert objects back and forth from + // stored strings. + // + // Sammy.Store can be used directly, but within a Sammy.Application it is much + // easier to use the Sammy.Storage plugin and its helper methods. + // + // Sammy.Store also supports the KVO pattern, by firing DOM/jQuery Events when + // a key is set. + // + // ### Example + // + // // create a new store named 'mystore', tied to the #main element, using HTML5 localStorage + // // Note: localStorage only works on browsers that support it + // var store = new Sammy.Store({name: 'mystore', element: '#element', type: 'local'}); + // store.set('foo', 'bar'); + // store.get('foo'); //=> 'bar' + // store.set('json', {obj: 'this is an obj'}); + // store.get('json'); //=> {obj: 'this is an obj'} + // store.keys(); //=> ['foo','json'] + // store.clear('foo'); + // store.keys(); //=> ['json'] + // store.clearAll(); + // store.keys(); //=> [] + // + // ### Arguments + // + // The constructor takes a single argument which is a Object containing these possible options. + // + // * `name` The name/namespace of this store. Stores are unique by name/type. (default 'store') + // * `element` A selector for the element that the store is bound to. (default 'body') + // * `type` The type of storage/proxy to use (default 'memory') + // + // Extra options are passed to the storage constructor. + // Sammy.Store supports the following methods of storage: + // + // * `memory` Basic object storage + // * `data` jQuery.data DOM Storage + // * `cookie` Access to document.cookie. Limited to 2K + // * `local` HTML5 DOM localStorage, browswer support is currently limited. + // * `session` HTML5 DOM sessionStorage, browswer support is currently limited. + // + Sammy.Store = function(options) { + var store = this; + this.options = options || {}; + this.name = this.options.name || 'store'; + this.element = this.options.element || 'body'; + this.$element = $(this.element); + if ($.isArray(this.options.type)) { + $.each(this.options.type, function(i, type) { + if (Sammy.Store.isAvailable(type)) { + store.type = type; + return false; + } + }); + } else { + this.type = this.options.type || 'memory'; + } + this.meta_key = this.options.meta_key || '__keys__'; + this.storage = new Sammy.Store[Sammy.Store.stores[this.type]](this.name, this.element, this.options); + }; + + Sammy.Store.stores = { + 'memory': 'Memory', + 'data': 'Data', + 'local': 'LocalStorage', + 'session': 'SessionStorage', + 'cookie': 'Cookie' + }; + + $.extend(Sammy.Store.prototype, { + // Checks for the availability of the current storage type in the current browser/config. + isAvailable: function() { + if ($.isFunction(this.storage.isAvailable)) { + return this.storage.isAvailable(); + } else { + true; + } + }, + // Checks for the existance of key in the current store. Returns a boolean. + exists: function(key) { + return this.storage.exists(key); + }, + // Sets the value of key with value. If value is an + // object, it is turned to and stored as a string with JSON.stringify. + // It also tries to conform to the KVO pattern triggering jQuery events on the + // element that the store is bound to. + // + // ### Example + // + // var store = new Sammy.Store({name: 'kvo'}); + // $('body').bind('set-kvo-foo', function(e, data) { + // Sammy.log(data.key + ' changed to ' + data.value); + // }); + // store.set('foo', 'bar'); // logged: foo changed to bar + // + set: function(key, value) { + var string_value = (typeof value == 'string') ? value : JSON.stringify(value); + key = key.toString(); + this.storage.set(key, string_value); + if (key != this.meta_key) { + this._addKey(key); + this.$element.trigger('set-' + this.name, {key: key, value: value}); + this.$element.trigger('set-' + this.name + '-' + key, {key: key, value: value}); + }; + // always return the original value + return value; + }, + // Returns the set value at key, parsing with JSON.parse and + // turning into an object if possible + get: function(key) { + var value = this.storage.get(key); + if (typeof value == 'undefined' || value == null || value == '') { + return value; + } + try { + return JSON.parse(value); + } catch(e) { + return value; + } + }, + // Removes the value at key from the current store + clear: function(key) { + this._removeKey(key); + return this.storage.clear(key); + }, + // Clears all the values for the current store. + clearAll: function() { + var self = this; + this.each(function(key, value) { + self.clear(key); + }); + }, + // Returns the all the keys set for the current store as an array. + // Internally Sammy.Store keeps this array in a 'meta_key' for easy access. + keys: function() { + return this.get(this.meta_key) || []; + }, + // Iterates over each key value pair passing them to the `callback` function + // + // ### Example + // + // store.each(function(key, value) { + // Sammy.log('key', key, 'value', value); + // }); + // + each: function(callback) { + var i = 0, + keys = this.keys(), + returned; + + for (i; i < keys.length; i++) { + returned = callback(keys[i], this.get(keys[i])); + if (returned === false) { return false; } + }; + }, + // Filters the store by a filter function that takes a key value. + // Returns an array of arrays where the first element of each array + // is the key and the second is the value of that key. + // + // ### Example + // + // var store = new Sammy.Store; + // store.set('one', 'two'); + // store.set('two', 'three'); + // store.set('1', 'two'); + // var returned = store.filter(function(key, value) { + // // only return + // return value === 'two'; + // }); + // // returned => [['one', 'two'], ['1', 'two']]; + // + filter: function(callback) { + var found = []; + this.each(function(key, value) { + if (callback(key, value)) { + found.push([key, value]); + } + return true; + }); + return found; + }, + // Works exactly like filter except only returns the first matching key + // value pair instead of all of them + first: function(callback) { + var found = false; + this.each(function(key, value) { + if (callback(key, value)) { + found = [key, value]; + return false; + } + }); + return found; + }, + // Returns the value at key if set, otherwise, runs the callback + // and sets the value to the value returned in the callback. + // + // ### Example + // + // var store = new Sammy.Store; + // store.exists('foo'); //=> false + // store.fetch('foo', function() { + // return 'bar!'; + // }); //=> 'bar!' + // store.get('foo') //=> 'bar!' + // store.fetch('foo', function() { + // return 'baz!'; + // }); //=> 'bar! + // + fetch: function(key, callback) { + if (!this.exists(key)) { + return this.set(key, callback.apply(this)); + } else { + return this.get(key); + } + }, + // loads the response of a request to path into key. + // + // ### Example + // + // In /mytemplate.tpl: + // + // My Template + // + // In app.js: + // + // var store = new Sammy.Store; + // store.load('mytemplate', '/mytemplate.tpl', function() { + // s.get('mytemplate') //=> My Template + // }); + // + load: function(key, path, callback) { + var s = this; + $.get(path, function(response) { + s.set(key, response); + if (callback) { callback.apply(this, [response]); } + }); + }, + + _addKey: function(key) { + var keys = this.keys(); + if ($.inArray(key, keys) == -1) { keys.push(key); } + this.set(this.meta_key, keys); + }, + _removeKey: function(key) { + var keys = this.keys(); + var index = $.inArray(key, keys); + if (index != -1) { keys.splice(index, 1); } + this.set(this.meta_key, keys); + } + }); + + // Tests if the type of storage is available/works in the current browser/config. + // Especially useful for testing the availability of the awesome, but not widely + // supported HTML5 DOM storage + Sammy.Store.isAvailable = function(type) { + try { + return Sammy.Store[Sammy.Store.stores[type]].prototype.isAvailable(); + } catch(e) { + return false; + } + }; + + // Memory ('memory') is the basic/default store. It stores data in a global + // JS object. Data is lost on refresh. + Sammy.Store.Memory = function(name, element) { + this.name = name; + this.element = element; + this.namespace = [this.element, this.name].join('.'); + Sammy.Store.Memory.store = Sammy.Store.Memory.store || {}; + Sammy.Store.Memory.store[this.namespace] = Sammy.Store.Memory.store[this.namespace] || {}; + this.store = Sammy.Store.Memory.store[this.namespace]; + }; + $.extend(Sammy.Store.Memory.prototype, { + isAvailable: function() { return true; }, + exists: function(key) { + return (typeof this.store[key] != "undefined"); + }, + set: function(key, value) { + return this.store[key] = value; + }, + get: function(key) { + return this.store[key]; + }, + clear: function(key) { + delete this.store[key]; + } + }); + + // Data ('data') stores objects using the jQuery.data() methods. This has the advantadge + // of scoping the data to the specific element. Like the 'memory' store its data + // will only last for the length of the current request (data is lost on refresh/etc). + Sammy.Store.Data = function(name, element) { + this.name = name; + this.element = element; + this.$element = $(element); + }; + $.extend(Sammy.Store.Data.prototype, { + isAvailable: function() { return true; }, + exists: function(key) { + return (typeof this.$element.data(this._key(key)) != "undefined"); + }, + set: function(key, value) { + return this.$element.data(this._key(key), value); + }, + get: function(key) { + return this.$element.data(this._key(key)); + }, + clear: function(key) { + this.$element.removeData(this._key(key)); + }, + _key: function(key) { + return ['store', this.name, key].join('.'); + } + }); + + // LocalStorage ('local') makes use of HTML5 DOM Storage, and the window.localStorage + // object. The great advantage of this method is that data will persist beyond + // the current request. It can be considered a pretty awesome replacement for + // cookies accessed via JS. The great disadvantage, though, is its only available + // on the latest and greatest browsers. + // + // For more info on DOM Storage: + // [https://developer.mozilla.org/en/DOM/Storage] + // [http://www.w3.org/TR/2009/WD-webstorage-20091222/] + // + Sammy.Store.LocalStorage = function(name, element) { + this.name = name; + this.element = element; + }; + $.extend(Sammy.Store.LocalStorage.prototype, { + isAvailable: function() { + return ('localStorage' in window) && (window.location.protocol != 'file:'); + }, + exists: function(key) { + return (this.get(key) != null); + }, + set: function(key, value) { + return window.localStorage.setItem(this._key(key), value); + }, + get: function(key) { + return window.localStorage.getItem(this._key(key)); + }, + clear: function(key) { + window.localStorage.removeItem(this._key(key));; + }, + _key: function(key) { + return ['store', this.element, this.name, key].join('.'); + } + }); + + // .SessionStorage ('session') is similar to LocalStorage (part of the same API) + // and shares similar browser support/availability. The difference is that + // SessionStorage is only persistant through the current 'session' which is defined + // as the length that the current window is open. This means that data will survive + // refreshes but not close/open or multiple windows/tabs. For more info, check out + // the LocalStorage documentation and links. + Sammy.Store.SessionStorage = function(name, element) { + this.name = name; + this.element = element; + }; + $.extend(Sammy.Store.SessionStorage.prototype, { + isAvailable: function() { + return ('sessionStorage' in window) && + (window.location.protocol != 'file:') && + ($.isFunction(window.sessionStorage.setItem)); + }, + exists: function(key) { + return (this.get(key) != null); + }, + set: function(key, value) { + return window.sessionStorage.setItem(this._key(key), value); + }, + get: function(key) { + var value = window.sessionStorage.getItem(this._key(key)); + if (value && typeof value.value != "undefined") { value = value.value } + return value; + }, + clear: function(key) { + window.sessionStorage.removeItem(this._key(key));; + }, + _key: function(key) { + return ['store', this.element, this.name, key].join('.'); + } + }); + + // .Cookie ('cookie') storage uses browser cookies to store data. JavaScript + // has access to a single document.cookie variable, which is limited to 2Kb in + // size. Cookies are also considered 'unsecure' as the data can be read easily + // by other sites/JS. Cookies do have the advantage, though, of being widely + // supported and persistent through refresh and close/open. Where available, + // HTML5 DOM Storage like LocalStorage and SessionStorage should be used. + // + // .Cookie can also take additional options: + // + // * `expires_in` Number of seconds to keep the cookie alive (default 2 weeks). + // * `path` The path to activate the current cookie for (default '/'). + // + // For more information about document.cookie, check out the pre-eminint article + // by ppk: [http://www.quirksmode.org/js/cookies.html] + // + Sammy.Store.Cookie = function(name, element, options) { + this.name = name; + this.element = element; + this.options = options || {}; + this.path = this.options.path || '/'; + // set the expires in seconds or default 14 days + this.expires_in = this.options.expires_in || (14 * 24 * 60 * 60); + }; + $.extend(Sammy.Store.Cookie.prototype, { + isAvailable: function() { + return ('cookie' in document) && (window.location.protocol != 'file:'); + }, + exists: function(key) { + return (this.get(key) != null); + }, + set: function(key, value) { + return this._setCookie(key, value); + }, + get: function(key) { + return this._getCookie(key); + }, + clear: function(key) { + this._setCookie(key, "", -1); + }, + _key: function(key) { + return ['store', this.element, this.name, key].join('.'); + }, + _getCookie: function(key) { + var escaped = this._key(key).replace(/(\.|\*|\(|\)|\[|\])/g, '\\$1'); + var match = document.cookie.match("(^|;\\s)" + escaped + "=([^;]*)(;|$)") + return (match ? match[2] : null); + }, + _setCookie: function(key, value, expires) { + if (!expires) { expires = (this.expires_in * 1000) } + var date = new Date(); + date.setTime(date.getTime() + expires); + var set_cookie = [ + this._key(key), "=", value, + "; expires=", date.toGMTString(), + "; path=", this.path + ].join(''); + document.cookie = set_cookie; + } + }); + + // Sammy.Storage is a plugin that provides shortcuts for creating and using + // Sammy.Store objects. Once included it provides the store() app level + // and helper methods. Depends on Sammy.JSON (or json2.js). + Sammy.Storage = function(app) { + this.use(Sammy.JSON); + + this.stores = this.stores || {}; + + // store() creates and looks up existing Sammy.Store objects + // for the current application. The first time used for a given 'name' + // initializes a Sammy.Store and also creates a helper under the store's + // name. + // + // ### Example + // + // var app = $.sammy(function() { + // this.use(Sammy.Storage); + // + // // initializes the store on app creation. + // this.store('mystore', {type: 'cookie'}); + // + // this.get('#/', function() { + // // returns the Sammy.Store object + // this.store('mystore'); + // // sets 'foo' to 'bar' using the shortcut/helper + // // equivilent to this.store('mystore').set('foo', 'bar'); + // this.mystore('foo', 'bar'); + // // returns 'bar' + // // equivilent to this.store('mystore').get('foo'); + // this.mystore('foo'); + // // returns 'baz!' + // // equivilent to: + // // this.store('mystore').fetch('foo!', function() { + // // return 'baz!'; + // // }) + // this.mystore('foo!', function() { + // return 'baz!'; + // }); + // + // this.clearMystore(); + // // equivilent to: + // // this.store('mystore').clearAll() + // }); + // + // }); + // + // ### Arguments + // + // * `name` The name of the store and helper. the name must be unique per application. + // * `options` A JS object of options that can be passed to the Store constuctor on initialization. + // + this.store = function(name, options) { + // if the store has not been initialized + if (typeof this.stores[name] == 'undefined') { + // create initialize the store + var clear_method_name = "clear" + name.substr(0,1).toUpperCase() + name.substr(1); + this.stores[name] = new Sammy.Store($.extend({ + name: name, + element: this.element_selector + }, options || {})); + // app.name() + this[name] = function(key, value) { + if (typeof value == 'undefined') { + return this.stores[name].get(key); + } else if ($.isFunction(value)) { + return this.stores[name].fetch(key, value); + } else { + return this.stores[name].set(key, value) + } + }; + // app.clearName(); + this[clear_method_name] = function() { + return this.stores[name].clearAll(); + } + // context.name() + this.helper(name, function() { + return this.app[name].apply(this.app, arguments); + }); + // context.clearName(); + this.helper(clear_method_name, function() { + return this.app[clear_method_name](); + }); + } + return this.stores[name]; + }; + + this.helpers({ + store: function() { + return this.app.store.apply(this.app, arguments); + } + }); + }; + + // Sammy.Session is an additional plugin for creating a common 'session' store + // for the given app. It is a very simple wrapper around Sammy.Storage + // that provides a simple fallback mechanism for trying to provide the best + // possible storage type for the session. This means, LocalStorage + // if available, otherwise Cookie, otherwise Memory. + // It provides the session() helper through Sammy.Storage#store(). + // + // See the Sammy.Storage plugin for full documentation. + // + Sammy.Session = function(app, options) { + this.use(Sammy.Storage); + // check for local storage, then cookie storage, then just use memory + this.store('session', $.extend({type: ['local', 'cookie', 'memory']}, options)); + }; + + // Sammy.Cache provides helpers for caching data within the lifecycle of a + // Sammy app. The plugin provides two main methods on Sammy.Application, + // cache and clearCache. Each app has its own cache store so that + // you dont have to worry about collisions. As of 0.5 the original Sammy.Cache module + // has been deprecated in favor of this one based on Sammy.Storage. The exposed + // API is almost identical, but Sammy.Storage provides additional backends including + // HTML5 Storage. Sammy.Cache will try to use these backends when available + // (in this order) LocalStorage, SessionStorage, and Memory + Sammy.Cache = function(app, options) { + this.use(Sammy.Storage); + // set cache_partials to true + this.cache_partials = true; + // check for local storage, then session storage, then just use memory + this.store('cache', $.extend({type: ['local', 'session', 'memory']}, options)); + }; + +})(jQuery); diff --git a/_attachments/script/sammy/plugins/sammy.template.js b/_attachments/script/sammy/plugins/sammy.template.js new file mode 100644 index 0000000..f24c23e --- /dev/null +++ b/_attachments/script/sammy/plugins/sammy.template.js @@ -0,0 +1,117 @@ +(function($) { + + // Simple JavaScript Templating + // John Resig - http://ejohn.org/ - MIT Licensed + // adapted from: http://ejohn.org/blog/javascript-micro-templating/ + // originally $.srender by Greg Borenstein http://ideasfordozens.com in Feb 2009 + // modified for Sammy by Aaron Quint for caching templates by name + var srender_cache = {}; + var srender = function(name, template, data) { + // target is an optional element; if provided, the result will be inserted into it + // otherwise the result will simply be returned to the caller + if (srender_cache[name]) { + fn = srender_cache[name]; + } else { + if (typeof template == 'undefined') { + // was a cache check, return false + return false; + } + // Generate a reusable function that will serve as a template + // generator (and which will be cached). + fn = srender_cache[name] = new Function("obj", + "var p=[],print=function(){p.push.apply(p,arguments);};" + + + // Introduce the data as local variables using with(){} + "with(obj){p.push(\"" + + + // Convert the template into pure JavaScript + template + .replace(/[\r\t\n]/g, " ") + .replace(/\"/g, '\\"') + .split("<%").join("\t") + .replace(/((^|%>)[^\t]*)/g, "$1\r") + .replace(/\t=(.*?)%>/g, "\",$1,\"") + .split("\t").join("\");") + .split("%>").join("p.push(\"") + .split("\r").join("") + + "\");}return p.join('');"); + } + + if (typeof data != 'undefined') { + return fn(data); + } else { + return fn; + } + }; + + Sammy = Sammy || {}; + + // Sammy.Template is a simple plugin that provides a way to create + // and render client side templates. The rendering code is based on John Resig's + // quick templates and Greg Borenstien's srender plugin. + // This is also a great template/boilerplate for Sammy plugins. + // + // Templates use <% %> tags to denote embedded javascript. + // + // ### Examples + // + // Here is an example template (user.template): + // + //
        + //
        <%= user.name %>
        + // <% if (user.photo_url) { %> + //
        + // <% } %> + //
        + // + // Given that is a publicly accesible file, you would render it like: + // + // $.sammy(function() { + // // include the plugin + // this.use(Sammy.Template); + // + // this.get('#/', function() { + // // the template is rendered in the current context. + // this.user = {name: 'Aaron Quint'}; + // // partial calls template() because of the file extension + // this.partial('user.template'); + // }) + // }); + // + // You can also pass a second argument to use() that will alias the template + // method and therefore allow you to use a different extension for template files + // in partial() + // + // // alias to 'tpl' + // this.use(Sammy.Template, 'tpl'); + // + // // now .tpl files will be run through srender + // this.get('#/', function() { + // this.partial('myfile.tpl'); + // }); + // + Sammy.Template = function(app, method_alias) { + + // *Helper:* Uses simple templating to parse ERB like templates. + // + // ### Arguments + // + // * `template` A String template. '<% %>' tags are evaluated as Javascript and replaced with the elements in data. + // * `data` An Object containing the replacement values for the template. + // data is extended with the EventContext allowing you to call its methods within the template. + // * `name` An optional String name to cache the template. + // + var template = function(template, data, name) { + // use name for caching + if (typeof name == 'undefined') name = template; + return srender(name, template, $.extend({}, this, data)); + }; + + // set the default method name/extension + if (!method_alias) method_alias = 'template'; + // create the helper at the method alias + app.helper(method_alias, template); + + }; + +})(jQuery); diff --git a/_attachments/script/sammy/plugins/sammy.title.js b/_attachments/script/sammy/plugins/sammy.title.js new file mode 100644 index 0000000..9874a1e --- /dev/null +++ b/_attachments/script/sammy/plugins/sammy.title.js @@ -0,0 +1,59 @@ +(function($) { + + Sammy = Sammy || {}; + + // Sammy.Title is a very simple plugin to easily set the document's title. + // It supplies a helper for setting the title (`title()`) within routes, + // and an app level method for setting the global title (`setTitle()`) + Sammy.Title = function() { + + // setTitle allows setting a global title or a function that modifies the + // title for each route/page. + // + // ### Example + // + // // setting a title prefix + // $.sammy(function() { + // + // this.setTitle('My App -'); + // + // this.get('#/', function() { + // this.title('Home'); // document's title == "My App - Home" + // }); + // }); + // + // // setting a title with a function + // $.sammy(function() { + // + // this.setTitle(function(title) { + // return [title, " /// My App"].join(''); + // }); + // + // this.get('#/', function() { + // this.title('Home'); // document's title == "Home /// My App"; + // }); + // }); + // + this.setTitle = function(title) { + if (!$.isFunction(title)) { + this.title_function = function(additional_title) { + return [title, additional_title].join(' '); + } + } else { + this.title_function = title; + } + }; + + // *Helper* title() sets the document title, passing it through the function + // defined by setTitle() if set. + this.helper('title', function() { + var new_title = $.makeArray(arguments).join(' '); + if (this.app.title_function) { + new_title = this.app.title_function(new_title); + } + document.title = new_title; + }); + + }; + +})(jQuery); diff --git a/_attachments/script/sammy/sammy.js b/_attachments/script/sammy/sammy.js new file mode 100644 index 0000000..de37ebd --- /dev/null +++ b/_attachments/script/sammy/sammy.js @@ -0,0 +1,1671 @@ +// name: sammy +// version: 0.6.0pre + +(function($) { + + var Sammy, + PATH_REPLACER = "([^\/]+)", + PATH_NAME_MATCHER = /:([\w\d]+)/g, + QUERY_STRING_MATCHER = /\?([^#]*)$/, + // mainly for making `arguments` an Array + _makeArray = function(nonarray) { return Array.prototype.slice.call(nonarray); }, + // borrowed from jQuery + _isFunction = function( obj ) { return Object.prototype.toString.call(obj) === "[object Function]"; }, + _isArray = function( obj ) { return Object.prototype.toString.call(obj) === "[object Array]"; }, + _decode = decodeURIComponent, + _escapeHTML = function(s) { + return s.replace(/&/g,'&').replace(//g,'>'); + }, + _routeWrapper = function(verb) { + return function(path, callback) { return this.route.apply(this, [verb, path, callback]); }; + }, + _template_cache = {}, + loggers = []; + + + // `Sammy` (also aliased as $.sammy) is not only the namespace for a + // number of prototypes, its also a top level method that allows for easy + // creation/management of `Sammy.Application` instances. There are a + // number of different forms for `Sammy()` but each returns an instance + // of `Sammy.Application`. When a new instance is created using + // `Sammy` it is added to an Object called `Sammy.apps`. This + // provides for an easy way to get at existing Sammy applications. Only one + // instance is allowed per `element_selector` so when calling + // `Sammy('selector')` multiple times, the first time will create + // the application and the following times will extend the application + // already added to that selector. + // + // ### Example + // + // // returns the app at #main or a new app + // Sammy('#main') + // + // // equivilent to "new Sammy.Application", except appends to apps + // Sammy(); + // Sammy(function() { ... }); + // + // // extends the app at '#main' with function. + // Sammy('#main', function() { ... }); + // + Sammy = function() { + var args = _makeArray(arguments), + app, selector; + Sammy.apps = Sammy.apps || {}; + if (args.length === 0 || args[0] && _isFunction(args[0])) { // Sammy() + return Sammy.apply(Sammy, ['body'].concat(args)); + } else if (typeof (selector = args.shift()) == 'string') { // Sammy('#main') + app = Sammy.apps[selector] || new Sammy.Application(args.shift()); + app.element_selector = selector; + if (args.length > 0) { + $.each(args, function(i, plugin) { + app.use(plugin); + }); + } + // if the selector changes make sure the refrence in Sammy.apps changes + if (app.element_selector != selector) { + delete Sammy.apps[selector]; + } + Sammy.apps[app.element_selector] = app; + return app; + } + }; + + Sammy.VERSION = '0.6.0pre'; + + // Add to the global logger pool. Takes a function that accepts an + // unknown number of arguments and should print them or send them somewhere + // The first argument is always a timestamp. + Sammy.addLogger = function(logger) { + loggers.push(logger); + }; + + // Sends a log message to each logger listed in the global + // loggers pool. Can take any number of arguments. + // Also prefixes the arguments with a timestamp. + Sammy.log = function() { + var args = _makeArray(arguments); + args.unshift("[" + Date() + "]"); + $.each(loggers, function(i, logger) { + logger.apply(Sammy, args); + }); + }; + + if (typeof window.console != 'undefined') { + if (_isFunction(console.log.apply)) { + Sammy.addLogger(function() { + window.console.log.apply(console, arguments); + }); + } else { + Sammy.addLogger(function() { + window.console.log(arguments); + }); + } + } else if (typeof console != 'undefined') { + Sammy.addLogger(function() { + console.log.apply(console, arguments); + }); + } + + $.extend(Sammy, { + makeArray: _makeArray, + isFunction: _isFunction, + isArray: _isArray + }) + + // Sammy.Object is the base for all other Sammy classes. It provides some useful + // functionality, including cloning, iterating, etc. + Sammy.Object = function(obj) { // constructor + return $.extend(this, obj || {}); + }; + + $.extend(Sammy.Object.prototype, { + + // Escape HTML in string, use in templates to prevent script injection. + // Also aliased as `h()` + escapeHTML: _escapeHTML, + h: _escapeHTML, + + // Returns a copy of the object with Functions removed. + toHash: function() { + var json = {}; + $.each(this, function(k,v) { + if (!_isFunction(v)) { + json[k] = v; + } + }); + return json; + }, + + // Renders a simple HTML version of this Objects attributes. + // Does not render functions. + // For example. Given this Sammy.Object: + // + // var s = new Sammy.Object({first_name: 'Sammy', last_name: 'Davis Jr.'}); + // s.toHTML() //=> 'first_name Sammy
        last_name Davis Jr.
        ' + // + toHTML: function() { + var display = ""; + $.each(this, function(k, v) { + if (!_isFunction(v)) { + display += "" + k + " " + v + "
        "; + } + }); + return display; + }, + + // Returns an array of keys for this object. If `attributes_only` + // is true will not return keys that map to a `function()` + keys: function(attributes_only) { + var keys = []; + for (var property in this) { + if (!_isFunction(this[property]) || !attributes_only) { + keys.push(property); + } + } + return keys; + }, + + // Checks if the object has a value at `key` and that the value is not empty + has: function(key) { + return this[key] && $.trim(this[key].toString()) != ''; + }, + + // convenience method to join as many arguments as you want + // by the first argument - useful for making paths + join: function() { + var args = _makeArray(arguments); + var delimiter = args.shift(); + return args.join(delimiter); + }, + + // Shortcut to Sammy.log + log: function() { + Sammy.log.apply(Sammy, arguments); + }, + + // Returns a string representation of this object. + // if `include_functions` is true, it will also toString() the + // methods of this object. By default only prints the attributes. + toString: function(include_functions) { + var s = []; + $.each(this, function(k, v) { + if (!_isFunction(v) || include_functions) { + s.push('"' + k + '": ' + v.toString()); + } + }); + return "Sammy.Object: {" + s.join(',') + "}"; + } + }); + + // The HashLocationProxy is the default location proxy for all Sammy applications. + // A location proxy is a prototype that conforms to a simple interface. The purpose + // of a location proxy is to notify the Sammy.Application its bound to when the location + // or 'external state' changes. The HashLocationProxy considers the state to be + // changed when the 'hash' (window.location.hash / '#') changes. It does this in two + // different ways depending on what browser you are using. The newest browsers + // (IE, Safari > 4, FF >= 3.6) support a 'onhashchange' DOM event, thats fired whenever + // the location.hash changes. In this situation the HashLocationProxy just binds + // to this event and delegates it to the application. In the case of older browsers + // a poller is set up to track changes to the hash. Unlike Sammy 0.3 or earlier, + // the HashLocationProxy allows the poller to be a global object, eliminating the + // need for multiple pollers even when thier are multiple apps on the page. + Sammy.HashLocationProxy = function(app, run_interval_every) { + this.app = app; + // set is native to false and start the poller immediately + this.is_native = false; + this._startPolling(run_interval_every); + }; + + Sammy.HashLocationProxy.prototype = { + + // bind the proxy events to the current app. + bind: function() { + var proxy = this, app = this.app; + $(window).bind('hashchange.' + this.app.eventNamespace(), function(e, non_native) { + // if we receive a native hash change event, set the proxy accordingly + // and stop polling + if (proxy.is_native === false && !non_native) { + Sammy.log('native hash change exists, using'); + proxy.is_native = true; + clearInterval(Sammy.HashLocationProxy._interval); + } + app.trigger('location-changed'); + }); + if (!Sammy.HashLocationProxy._bindings) { + Sammy.HashLocationProxy._bindings = 0; + } + Sammy.HashLocationProxy._bindings++; + }, + + // unbind the proxy events from the current app + unbind: function() { + $(window).unbind('hashchange.' + this.app.eventNamespace()); + Sammy.HashLocationProxy._bindings--; + if (Sammy.HashLocationProxy._bindings <= 0) { + clearInterval(Sammy.HashLocationProxy._interval); + } + }, + + // get the current location from the hash. + getLocation: function() { + // Bypass the `window.location.hash` attribute. If a question mark + // appears in the hash IE6 will strip it and all of the following + // characters from `window.location.hash`. + var matches = window.location.toString().match(/^[^#]*(#.+)$/); + return matches ? matches[1] : ''; + }, + + // set the current location to `new_location` + setLocation: function(new_location) { + return (window.location = new_location); + }, + + _startPolling: function(every) { + // set up interval + var proxy = this; + if (!Sammy.HashLocationProxy._interval) { + if (!every) { every = 10; } + var hashCheck = function() { + current_location = proxy.getLocation(); + if (!Sammy.HashLocationProxy._last_location || + current_location != Sammy.HashLocationProxy._last_location) { + setTimeout(function() { + $(window).trigger('hashchange', [true]); + }, 13); + } + Sammy.HashLocationProxy._last_location = current_location; + }; + hashCheck(); + Sammy.HashLocationProxy._interval = setInterval(hashCheck, every); + } + } + }; + + + // Sammy.Application is the Base prototype for defining 'applications'. + // An 'application' is a collection of 'routes' and bound events that is + // attached to an element when `run()` is called. + // The only argument an 'app_function' is evaluated within the context of the application. + Sammy.Application = function(app_function) { + var app = this; + this.routes = {}; + this.listeners = new Sammy.Object({}); + this.arounds = []; + this.befores = []; + // generate a unique namespace + this.namespace = (new Date()).getTime() + '-' + parseInt(Math.random() * 1000, 10); + this.context_prototype = function() { Sammy.EventContext.apply(this, arguments); }; + this.context_prototype.prototype = new Sammy.EventContext(); + + if (_isFunction(app_function)) { + app_function.apply(this, [this]); + } + // set the location proxy if not defined to the default (HashLocationProxy) + if (!this._location_proxy) { + this.setLocationProxy(new Sammy.HashLocationProxy(this, this.run_interval_every)); + } + if (this.debug) { + this.bindToAllEvents(function(e, data) { + app.log(app.toString(), e.cleaned_type, data || {}); + }); + } + }; + + Sammy.Application.prototype = $.extend({}, Sammy.Object.prototype, { + + // the four route verbs + ROUTE_VERBS: ['get','post','put','delete'], + + // An array of the default events triggered by the + // application during its lifecycle + APP_EVENTS: ['run','unload','lookup-route','run-route','route-found','event-context-before','event-context-after','changed','error','check-form-submission','redirect'], + + _last_route: null, + _location_proxy: null, + _running: false, + + // Defines what element the application is bound to. Provide a selector + // (parseable by `jQuery()`) and this will be used by `$element()` + element_selector: 'body', + + // When set to true, logs all of the default events using `log()` + debug: false, + + // When set to true, and the error() handler is not overriden, will actually + // raise JS errors in routes (500) and when routes can't be found (404) + raise_errors: false, + + // The time in milliseconds that the URL is queried for changes + run_interval_every: 50, + + // The default template engine to use when using `partial()` in an + // `EventContext`. `template_engine` can either be a string that + // corresponds to the name of a method/helper on EventContext or it can be a function + // that takes two arguments, the content of the unrendered partial and an optional + // JS object that contains interpolation data. Template engine is only called/refered + // to if the extension of the partial is null or unknown. See `partial()` + // for more information + template_engine: null, + + // //=> Sammy.Application: body + toString: function() { + return 'Sammy.Application:' + this.element_selector; + }, + + // returns a jQuery object of the Applications bound element. + $element: function() { + return $(this.element_selector); + }, + + // `use()` is the entry point for including Sammy plugins. + // The first argument to use should be a function() that is evaluated + // in the context of the current application, just like the `app_function` + // argument to the `Sammy.Application` constructor. + // + // Any additional arguments are passed to the app function sequentially. + // + // For much more detail about plugins, check out: + // http://code.quirkey.com/sammy/doc/plugins.html + // + // ### Example + // + // var MyPlugin = function(app, prepend) { + // + // this.helpers({ + // myhelper: function(text) { + // alert(prepend + " " + text); + // } + // }); + // + // }; + // + // var app = $.sammy(function() { + // + // this.use(MyPlugin, 'This is my plugin'); + // + // this.get('#/', function() { + // this.myhelper('and dont you forget it!'); + // //=> Alerts: This is my plugin and dont you forget it! + // }); + // + // }); + // + // If plugin is passed as a string it assumes your are trying to load + // Sammy."Plugin". This is the prefered way of loading core Sammy plugins + // as it allows for better error-messaging. + // + // ### Example + // + // $.sammy(function() { + // this.use('Mustache'); //=> Sammy.Mustache + // this.use('Storage'); //=> Sammy.Storage + // }); + // + use: function() { + // flatten the arguments + var args = _makeArray(arguments), + plugin = args.shift(), + plugin_name = plugin || ''; + try { + args.unshift(this); + if (typeof plugin == 'string') { + plugin_name = 'Sammy.' + plugin; + plugin = Sammy[plugin]; + } + plugin.apply(this, args); + } catch(e) { + if (typeof plugin === 'undefined') { + this.error("Plugin Error: called use() but plugin (" + plugin_name.toString() + ") is not defined", e); + } else if (!_isFunction(plugin)) { + this.error("Plugin Error: called use() but '" + plugin_name.toString() + "' is not a function", e); + } else { + this.error("Plugin Error", e); + } + } + return this; + }, + + // Sets the location proxy for the current app. By default this is set to + // a new `Sammy.HashLocationProxy` on initialization. However, you can set + // the location_proxy inside you're app function to give your app a custom + // location mechanism. See `Sammy.HashLocationProxy` and `Sammy.DataLocationProxy` + // for examples. + // + // `setLocationProxy()` takes an initialized location proxy. + // + // ### Example + // + // // to bind to data instead of the default hash; + // var app = $.sammy(function() { + // this.setLocationProxy(new Sammy.DataLocationProxy(this)); + // }); + // + setLocationProxy: function(new_proxy) { + var original_proxy = this._location_proxy; + this._location_proxy = new_proxy; + if (this.isRunning()) { + if (original_proxy) { + // if there is already a location proxy, unbind it. + original_proxy.unbind(); + } + this._location_proxy.bind(); + } + }, + + // `route()` is the main method for defining routes within an application. + // For great detail on routes, check out: http://code.quirkey.com/sammy/doc/routes.html + // + // This method also has aliases for each of the different verbs (eg. `get()`, `post()`, etc.) + // + // ### Arguments + // + // * `verb` A String in the set of ROUTE_VERBS or 'any'. 'any' will add routes for each + // of the ROUTE_VERBS. If only two arguments are passed, + // the first argument is the path, the second is the callback and the verb + // is assumed to be 'any'. + // * `path` A Regexp or a String representing the path to match to invoke this verb. + // * `callback` A Function that is called/evaluated whent the route is run see: `runRoute()`. + // It is also possible to pass a string as the callback, which is looked up as the name + // of a method on the application. + // + route: function(verb, path, callback) { + var app = this, param_names = [], add_route; + + // if the method signature is just (path, callback) + // assume the verb is 'any' + if (!callback && _isFunction(path)) { + path = verb; + callback = path; + verb = 'any'; + } + + verb = verb.toLowerCase(); // ensure verb is lower case + + // if path is a string turn it into a regex + if (path.constructor == String) { + + // Needs to be explicitly set because IE will maintain the index unless NULL is returned, + // which means that with two consecutive routes that contain params, the second set of params will not be found and end up in splat instead of params + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/lastIndex + PATH_NAME_MATCHER.lastIndex = 0; + + // find the names + while ((path_match = PATH_NAME_MATCHER.exec(path)) !== null) { + param_names.push(path_match[1]); + } + // replace with the path replacement + path = new RegExp("^" + path.replace(PATH_NAME_MATCHER, PATH_REPLACER) + "$"); + } + // lookup callback + if (typeof callback == 'string') { + callback = app[callback]; + } + + add_route = function(with_verb) { + var r = {verb: with_verb, path: path, callback: callback, param_names: param_names}; + // add route to routes array + app.routes[with_verb] = app.routes[with_verb] || []; + // place routes in order of definition + app.routes[with_verb].push(r); + }; + + if (verb === 'any') { + $.each(this.ROUTE_VERBS, function(i, v) { add_route(v); }); + } else { + add_route(verb); + } + + // return the app + return this; + }, + + // Alias for route('get', ...) + get: _routeWrapper('get'), + + // Alias for route('post', ...) + post: _routeWrapper('post'), + + // Alias for route('put', ...) + put: _routeWrapper('put'), + + // Alias for route('delete', ...) + del: _routeWrapper('delete'), + + // Alias for route('any', ...) + any: _routeWrapper('any'), + + // `mapRoutes` takes an array of arrays, each array being passed to route() + // as arguments, this allows for mass definition of routes. Another benefit is + // this makes it possible/easier to load routes via remote JSON. + // + // ### Example + // + // var app = $.sammy(function() { + // + // this.mapRoutes([ + // ['get', '#/', function() { this.log('index'); }], + // // strings in callbacks are looked up as methods on the app + // ['post', '#/create', 'addUser'], + // // No verb assumes 'any' as the verb + // [/dowhatever/, function() { this.log(this.verb, this.path)}]; + // ]); + // }) + // + mapRoutes: function(route_array) { + var app = this; + $.each(route_array, function(i, route_args) { + app.route.apply(app, route_args); + }); + return this; + }, + + // A unique event namespace defined per application. + // All events bound with `bind()` are automatically bound within this space. + eventNamespace: function() { + return ['sammy-app', this.namespace].join('-'); + }, + + // Works just like `jQuery.fn.bind()` with a couple noteable differences. + // + // * It binds all events to the application element + // * All events are bound within the `eventNamespace()` + // * Events are not actually bound until the application is started with `run()` + // * callbacks are evaluated within the context of a Sammy.EventContext + // + // See http://code.quirkey.com/sammy/docs/events.html for more info. + // + bind: function(name, data, callback) { + var app = this; + // build the callback + // if the arity is 2, callback is the second argument + if (typeof callback == 'undefined') { callback = data; } + var listener_callback = function() { + // pull off the context from the arguments to the callback + var e, context, data; + e = arguments[0]; + data = arguments[1]; + if (data && data.context) { + context = data.context; + delete data.context; + } else { + context = new app.context_prototype(app, 'bind', e.type, data, e.target); + } + e.cleaned_type = e.type.replace(app.eventNamespace(), ''); + callback.apply(context, [e, data]); + }; + + // it could be that the app element doesnt exist yet + // so attach to the listeners array and then run() + // will actually bind the event. + if (!this.listeners[name]) { this.listeners[name] = []; } + this.listeners[name].push(listener_callback); + if (this.isRunning()) { + // if the app is running + // *actually* bind the event to the app element + this._listen(name, listener_callback); + } + return this; + }, + + // Triggers custom events defined with `bind()` + // + // ### Arguments + // + // * `name` The name of the event. Automatically prefixed with the `eventNamespace()` + // * `data` An optional Object that can be passed to the bound callback. + // * `context` An optional context/Object in which to execute the bound callback. + // If no context is supplied a the context is a new `Sammy.EventContext` + // + trigger: function(name, data) { + this.$element().trigger([name, this.eventNamespace()].join('.'), [data]); + return this; + }, + + // Reruns the current route + refresh: function() { + this.last_location = null; + this.trigger('location-changed'); + return this; + }, + + // Takes a single callback that is pushed on to a stack. + // Before any route is run, the callbacks are evaluated in order within + // the current `Sammy.EventContext` + // + // If any of the callbacks explicitly return false, execution of any + // further callbacks and the route itself is halted. + // + // You can also provide a set of options that will define when to run this + // before based on the route it proceeds. + // + // ### Example + // + // var app = $.sammy(function() { + // + // // will run at #/route but not at #/ + // this.before('#/route', function() { + // //... + // }); + // + // // will run at #/ but not at #/route + // this.before({except: {path: '#/route'}}, function() { + // this.log('not before #/route'); + // }); + // + // this.get('#/', function() {}); + // + // this.get('#/route', function() {}); + // + // }); + // + // See `contextMatchesOptions()` for a full list of supported options + // + before: function(options, callback) { + if (_isFunction(options)) { + callback = options; + options = {}; + } + this.befores.push([options, callback]); + return this; + }, + + // A shortcut for binding a callback to be run after a route is executed. + // After callbacks have no guarunteed order. + after: function(callback) { + return this.bind('event-context-after', callback); + }, + + + // Adds an around filter to the application. around filters are functions + // that take a single argument `callback` which is the entire route + // execution path wrapped up in a closure. This means you can decide whether + // or not to proceed with execution by not invoking `callback` or, + // more usefuly wrapping callback inside the result of an asynchronous execution. + // + // ### Example + // + // The most common use case for around() is calling a _possibly_ async function + // and executing the route within the functions callback: + // + // var app = $.sammy(function() { + // + // var current_user = false; + // + // function checkLoggedIn(callback) { + // // /session returns a JSON representation of the logged in user + // // or an empty object + // if (!current_user) { + // $.getJSON('/session', function(json) { + // if (json.login) { + // // show the user as logged in + // current_user = json; + // // execute the route path + // callback(); + // } else { + // // show the user as not logged in + // current_user = false; + // // the context of aroundFilters is an EventContext + // this.redirect('#/login'); + // } + // }); + // } else { + // // execute the route path + // callback(); + // } + // }; + // + // this.around(checkLoggedIn); + // + // }); + // + around: function(callback) { + this.arounds.push(callback); + return this; + }, + + // Returns `true` if the current application is running. + isRunning: function() { + return this._running; + }, + + // Helpers extends the EventContext prototype specific to this app. + // This allows you to define app specific helper functions that can be used + // whenever you're inside of an event context (templates, routes, bind). + // + // ### Example + // + // var app = $.sammy(function() { + // + // helpers({ + // upcase: function(text) { + // return text.toString().toUpperCase(); + // } + // }); + // + // get('#/', function() { with(this) { + // // inside of this context I can use the helpers + // $('#main').html(upcase($('#main').text()); + // }}); + // + // }); + // + // + // ### Arguments + // + // * `extensions` An object collection of functions to extend the context. + // + helpers: function(extensions) { + $.extend(this.context_prototype.prototype, extensions); + return this; + }, + + // Helper extends the event context just like `helpers()` but does it + // a single method at a time. This is especially useful for dynamically named + // helpers + // + // ### Example + // + // // Trivial example that adds 3 helper methods to the context dynamically + // var app = $.sammy(function(app) { + // + // $.each([1,2,3], function(i, num) { + // app.helper('helper' + num, function() { + // this.log("I'm helper number " + num); + // }); + // }); + // + // this.get('#/', function() { + // this.helper2(); //=> I'm helper number 2 + // }); + // }); + // + // ### Arguments + // + // * `name` The name of the method + // * `method` The function to be added to the prototype at `name` + // + helper: function(name, method) { + this.context_prototype.prototype[name] = method; + return this; + }, + + // Actually starts the application's lifecycle. `run()` should be invoked + // within a document.ready block to ensure the DOM exists before binding events, etc. + // + // ### Example + // + // var app = $.sammy(function() { ... }); // your application + // $(function() { // document.ready + // app.run(); + // }); + // + // ### Arguments + // + // * `start_url` Optionally, a String can be passed which the App will redirect to + // after the events/routes have been bound. + run: function(start_url) { + if (this.isRunning()) { return false; } + var app = this; + + // actually bind all the listeners + $.each(this.listeners.toHash(), function(name, callbacks) { + $.each(callbacks, function(i, listener_callback) { + app._listen(name, listener_callback); + }); + }); + + this.trigger('run', {start_url: start_url}); + this._running = true; + // set last location + this.last_location = null; + if (this.getLocation() == '' && typeof start_url != 'undefined') { + this.setLocation(start_url); + } + // check url + this._checkLocation(); + this._location_proxy.bind(); + this.bind('location-changed', function() { + app._checkLocation(); + }); + + // bind to submit to capture post/put/delete routes + this.bind('submit', function(e) { + var returned = app._checkFormSubmission($(e.target).closest('form')); + return (returned === false) ? e.preventDefault() : false; + }); + + // bind unload to body unload + $(window).bind('beforeunload', function() { + app.unload(); + }); + + // trigger html changed + return this.trigger('changed'); + }, + + // The opposite of `run()`, un-binds all event listeners and intervals + // `run()` Automaticaly binds a `onunload` event to run this when + // the document is closed. + unload: function() { + if (!this.isRunning()) { return false; } + var app = this; + this.trigger('unload'); + // clear interval + this._location_proxy.unbind(); + // unbind form submits + this.$element().unbind('submit').removeClass(app.eventNamespace()); + // unbind all events + $.each(this.listeners.toHash() , function(name, listeners) { + $.each(listeners, function(i, listener_callback) { + app._unlisten(name, listener_callback); + }); + }); + this._running = false; + return this; + }, + + // Will bind a single callback function to every event that is already + // being listened to in the app. This includes all the `APP_EVENTS` + // as well as any custom events defined with `bind()`. + // + // Used internally for debug logging. + bindToAllEvents: function(callback) { + var app = this; + // bind to the APP_EVENTS first + $.each(this.APP_EVENTS, function(i, e) { + app.bind(e, callback); + }); + // next, bind to listener names (only if they dont exist in APP_EVENTS) + $.each(this.listeners.keys(true), function(i, name) { + if (app.APP_EVENTS.indexOf(name) == -1) { + app.bind(name, callback); + } + }); + return this; + }, + + // Returns a copy of the given path with any query string after the hash + // removed. + routablePath: function(path) { + return path.replace(QUERY_STRING_MATCHER, ''); + }, + + // Given a verb and a String path, will return either a route object or false + // if a matching route can be found within the current defined set. + lookupRoute: function(verb, path) { + var app = this, routed = false; + this.trigger('lookup-route', {verb: verb, path: path}); + if (typeof this.routes[verb] != 'undefined') { + $.each(this.routes[verb], function(i, route) { + if (app.routablePath(path).match(route.path)) { + routed = route; + return false; + } + }); + } + return routed; + }, + + // First, invokes `lookupRoute()` and if a route is found, parses the + // possible URL params and then invokes the route's callback within a new + // `Sammy.EventContext`. If the route can not be found, it calls + // `notFound()`. If `raise_errors` is set to `true` and + // the `error()` has not been overriden, it will throw an actual JS + // error. + // + // You probably will never have to call this directly. + // + // ### Arguments + // + // * `verb` A String for the verb. + // * `path` A String path to lookup. + // * `params` An Object of Params pulled from the URI or passed directly. + // + // ### Returns + // + // Either returns the value returned by the route callback or raises a 404 Not Found error. + // + runRoute: function(verb, path, params, target) { + var app = this, + route = this.lookupRoute(verb, path), + context, + wrapped_route, + arounds, + around, + befores, + before, + callback_args, + final_returned; + + this.log('runRoute', [verb, path].join(' ')); + this.trigger('run-route', {verb: verb, path: path, params: params}); + if (typeof params == 'undefined') { params = {}; } + + $.extend(params, this._parseQueryString(path)); + + if (route) { + this.trigger('route-found', {route: route}); + // pull out the params from the path + if ((path_params = route.path.exec(this.routablePath(path))) !== null) { + // first match is the full path + path_params.shift(); + // for each of the matches + $.each(path_params, function(i, param) { + // if theres a matching param name + if (route.param_names[i]) { + // set the name to the match + params[route.param_names[i]] = _decode(param); + } else { + // initialize 'splat' + if (!params.splat) { params.splat = []; } + params.splat.push(_decode(param)); + } + }); + } + + // set event context + context = new this.context_prototype(this, verb, path, params, target); + // ensure arrays + arounds = this.arounds.slice(0); + befores = this.befores.slice(0); + // set the callback args to the context + contents of the splat + callback_args = [context].concat(params.splat); + // wrap the route up with the before filters + wrapped_route = function() { + var returned; + while (befores.length > 0) { + before = befores.shift(); + // check the options + if (app.contextMatchesOptions(context, before[0])) { + returned = before[1].apply(context, [context]); + if (returned === false) { return false; } + } + } + app.last_route = route; + context.trigger('event-context-before', {context: context}); + returned = route.callback.apply(context, callback_args); + context.trigger('event-context-after', {context: context}); + return returned; + }; + $.each(arounds.reverse(), function(i, around) { + var last_wrapped_route = wrapped_route; + wrapped_route = function() { return around.apply(context, [last_wrapped_route]); }; + }); + try { + final_returned = wrapped_route(); + } catch(e) { + this.error(['500 Error', verb, path].join(' '), e); + } + return final_returned; + } else { + return this.notFound(verb, path); + } + }, + + // Matches an object of options against an `EventContext` like object that + // contains `path` and `verb` attributes. Internally Sammy uses this + // for matching `before()` filters against specific options. You can set the + // object to _only_ match certain paths or verbs, or match all paths or verbs _except_ + // those that match the options. + // + // ### Example + // + // var app = $.sammy(), + // context = {verb: 'get', path: '#/mypath'}; + // + // // match against a path string + // app.contextMatchesOptions(context, '#/mypath'); //=> true + // app.contextMatchesOptions(context, '#/otherpath'); //=> false + // // equivilent to + // app.contextMatchesOptions(context, {only: {path:'#/mypath'}}); //=> true + // app.contextMatchesOptions(context, {only: {path:'#/otherpath'}}); //=> false + // // match against a path regexp + // app.contextMatchesOptions(context, /path/); //=> true + // app.contextMatchesOptions(context, /^path/); //=> false + // // match only a verb + // app.contextMatchesOptions(context, {only: {verb:'get'}}); //=> true + // app.contextMatchesOptions(context, {only: {verb:'post'}}); //=> false + // // match all except a verb + // app.contextMatchesOptions(context, {except: {verb:'post'}}); //=> true + // app.contextMatchesOptions(context, {except: {verb:'get'}}); //=> false + // // match all except a path + // app.contextMatchesOptions(context, {except: {path:'#/otherpath'}}); //=> true + // app.contextMatchesOptions(context, {except: {path:'#/mypath'}}); //=> false + // + contextMatchesOptions: function(context, match_options, positive) { + // empty options always match + var options = match_options; + if (typeof options === 'undefined' || options == {}) { + return true; + } + if (typeof positive === 'undefined') { + positive = true; + } + // normalize options + if (typeof options === 'string' || _isFunction(options.test)) { + options = {path: options}; + } + if (options.only) { + return this.contextMatchesOptions(context, options.only, true); + } else if (options.except) { + return this.contextMatchesOptions(context, options.except, false); + } + var path_matched = true, verb_matched = true; + if (options.path) { + // wierd regexp test + if (_isFunction(options.path.test)) { + path_matched = options.path.test(context.path); + } else { + path_matched = (options.path.toString() === context.path); + } + } + if (options.verb) { + verb_matched = options.verb === context.verb; + } + return positive ? (verb_matched && path_matched) : !(verb_matched && path_matched); + }, + + + // Delegates to the `location_proxy` to get the current location. + // See `Sammy.HashLocationProxy` for more info on location proxies. + getLocation: function() { + return this._location_proxy.getLocation(); + }, + + // Delegates to the `location_proxy` to set the current location. + // See `Sammy.HashLocationProxy` for more info on location proxies. + // + // ### Arguments + // + // * `new_location` A new location string (e.g. '#/') + // + setLocation: function(new_location) { + return this._location_proxy.setLocation(new_location); + }, + + // Swaps the content of `$element()` with `content` + // You can override this method to provide an alternate swap behavior + // for `EventContext.partial()`. + // + // ### Example + // + // var app = $.sammy(function() { + // + // // implements a 'fade out'/'fade in' + // this.swap = function(content) { + // this.$element().hide('slow').html(content).show('slow'); + // } + // + // get('#/', function() { + // this.partial('index.html.erb') // will fade out and in + // }); + // + // }); + // + swap: function(content) { + return this.$element().html(content); + }, + + // a simple global cache for templates. Uses the same semantics as + // `Sammy.Cache` and `Sammy.Storage` so can easily be replaced with + // a persistant storage that lasts beyond the current request. + templateCache: function(key, value) { + if (typeof value != 'undefined') { + return _template_cache[key] = value; + } else { + return _template_cache[key]; + } + }, + + // This thows a '404 Not Found' error by invoking `error()`. + // Override this method or `error()` to provide custom + // 404 behavior (i.e redirecting to / or showing a warning) + notFound: function(verb, path) { + var ret = this.error(['404 Not Found', verb, path].join(' ')); + return (verb === 'get') ? ret : true; + }, + + // The base error handler takes a string `message` and an `Error` + // object. If `raise_errors` is set to `true` on the app level, + // this will re-throw the error to the browser. Otherwise it will send the error + // to `log()`. Override this method to provide custom error handling + // e.g logging to a server side component or displaying some feedback to the + // user. + error: function(message, original_error) { + if (!original_error) { original_error = new Error(); } + original_error.message = [message, original_error.message].join(' '); + this.trigger('error', {message: original_error.message, error: original_error}); + if (this.raise_errors) { + throw(original_error); + } else { + this.log(original_error.message, original_error); + } + }, + + _checkLocation: function() { + var location, returned; + // get current location + location = this.getLocation(); + // compare to see if hash has changed + if (location != this.last_location) { + // reset last location + this.last_location = location; + // lookup route for current hash + returned = this.runRoute('get', location); + } + return returned; + }, + + _checkFormSubmission: function(form) { + var $form, path, verb, params, returned; + this.trigger('check-form-submission', {form: form}); + $form = $(form); + path = $form.attr('action'); + verb = $.trim($form.attr('method').toString().toLowerCase()); + if (!verb || verb == '') { verb = 'get'; } + this.log('_checkFormSubmission', $form, path, verb); + if (verb === 'get') { + this.setLocation(path + '?' + $form.serialize()); + returned = false; + } else { + params = $.extend({}, this._parseFormParams($form)); + returned = this.runRoute(verb, path, params, form.get(0)); + }; + return (typeof returned == 'undefined') ? false : returned; + }, + + _parseFormParams: function($form) { + var params = {}, + form_fields = $form.serializeArray(), + i; + for (i = 0; i < form_fields.length; i++) { + params = this._parseParamPair(params, form_fields[i].name, form_fields[i].value); + } + return params; + }, + + _parseQueryString: function(path) { + var params = {}, parts, pairs, pair, i; + + parts = path.match(QUERY_STRING_MATCHER); + if (parts) { + pairs = parts[1].split('&'); + for (i = 0; i < pairs.length; i++) { + pair = pairs[i].split('='); + params = this._parseParamPair(params, _decode(pair[0]), _decode(pair[1])); + } + } + return params; + }, + + _parseParamPair: function(params, key, value) { + if (params[key]) { + if (_isArray(params[key])) { + params[key].push(value); + } else { + params[key] = [params[key], value]; + } + } else { + params[key] = value; + } + return params; + }, + + _listen: function(name, callback) { + return this.$element().bind([name, this.eventNamespace()].join('.'), callback); + }, + + _unlisten: function(name, callback) { + return this.$element().unbind([name, this.eventNamespace()].join('.'), callback); + } + + }); + + // `Sammy.RenderContext` is an object that makes sequential template loading, + // rendering and interpolation seamless even when dealing with asyncronous + // operations. + // + // `RenderContext` objects are not usually created directly, rather they are + // instatiated from an `Sammy.EventContext` by using `render()`, `load()` or + // `partial()` which all return `RenderContext` objects. + // + // `RenderContext` methods always returns a modified `RenderContext` + // for chaining (like jQuery itself). + // + // The core magic is in the `then()` method which puts the callback passed as + // an argument into a queue to be executed once the previous callback is complete. + // All the methods of `RenderContext` are wrapped in `then()` which allows you + // to queue up methods by chaining, but maintaing a guarunteed execution order + // even with remote calls to fetch templates. + // + Sammy.RenderContext = function(event_context) { + this.event_context = event_context; + this.callbacks = []; + this.previous_content = null; + this.content = null; + this.next_engine = false; + this.waiting = false; + }; + + $.extend(Sammy.RenderContext.prototype, { + + // The "core" of the `RenderContext` object, adds the `callback` to the + // queue. If the context is `waiting` (meaning an async operation is happening) + // then the callback will be executed in order, once the other operations are + // complete. If there is no currently executing operation, the `callback` + // is executed immediately. + // + // The value returned from the callback is stored in `content` for the + // subsiquent operation. If you return `false`, the queue will pause, and + // the next callback in the queue will not be executed until `next()` is + // called. This allows for the guarunteed order of execution while working + // with async operations. + // + // ### Example + // + // this.get('#/', function() { + // // initialize the RenderContext + // // Even though `load()` executes async, the next `then()` + // // wont execute until the load finishes + // this.load('myfile.txt') + // .then(function(content) { + // // the first argument to then is the content of the + // // prev operation + // $('#main').html(content); + // }); + // }); + // + then: function(callback) { + if (_isFunction(callback)) { + var context = this; + if (this.waiting) { + this.callbacks.push(callback); + } else { + this.wait(); + setTimeout(function() { + var returned = callback.apply(context, [context.content, context.previous_content]); + if (returned !== false) { + context.next(returned); + } + }, 13); + } + } + return this; + }, + + // Pause the `RenderContext` queue. Combined with `next()` allows for async + // operations. + // + // ### Example + // + // this.get('#/', function() { + // this.load('mytext.json') + // .then(function(content) { + // var context = this, + // data = JSON.parse(content); + // // pause execution + // context.wait(); + // // post to a url + // $.post(data.url, {}, function(response) { + // context.next(JSON.parse(response)); + // }); + // }) + // .then(function(data) { + // // data is json from the previous post + // $('#message').text(data.status); + // }); + // }); + wait: function() { + this.waiting = true; + }, + + // Resume the queue, setting `content` to be used in the next operation. + // See `wait()` for an example. + next: function(content) { + this.waiting = false; + if (typeof content !== 'undefined') { + this.previous_content = this.content; + this.content = content; + } + if (this.callbacks.length > 0) { + this.then(this.callbacks.shift()); + } + }, + + // Load a template into the context. + // The `location` can either be a string specifiying the remote path to the + // file, a jQuery object, or a DOM element. + // + // No interpolation happens by default, the content is stored in + // `content`. + // + // In the case of a path, unless the option `{cache: false}` is passed the + // data is stored in the app's `templateCache()`. + // + // If a jQuery or DOM object is passed the `innerHTML` of the node is pulled in. + // This is useful for nesting templates as part of the initial page load wrapped + // in invisible elements or ` + + + + + + + + + + +
        +

        Overview + Session

        +
        +

        Establish or Modify Your Session

        +
        +
        + + + or + + + Welcome ?! +
        + Logout +
        + + Welcome ?! +
        + Setup more admins or + Logout +
        + + Welcome to Admin Party! +
        + Everyone is admin. Fix this +
        +
        +
        +
        + +
        + + diff --git a/_attachments/spec/couch_js_class_methods_spec.js b/_attachments/spec/couch_js_class_methods_spec.js new file mode 100644 index 0000000..7eac234 --- /dev/null +++ b/_attachments/spec/couch_js_class_methods_spec.js @@ -0,0 +1,401 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// Specs for couch.js lines 313-470 + +describe 'CouchDB class' + describe 'session stuff' + before + useTestUserDb(); + end + + after + useOldUserDb(); + end + + before_each + userDoc = users_db.save(CouchDB.prepareUserDoc({name: "Gaius Baltar", roles: ["president"]}, "secretpass")); + end + + after_each + users_db.deleteDoc({_id : userDoc.id, _rev : userDoc.rev}) + end + + describe '.login' + it 'should return ok true' + CouchDB.login("Gaius Baltar", "secretpass").ok.should.be_true + end + + it 'should return the name of the logged in user' + CouchDB.login("Gaius Baltar", "secretpass").name.should.eql "Gaius Baltar" + end + + it 'should return the roles of the logged in user' + CouchDB.login("Gaius Baltar", "secretpass").roles.should.eql ["president"] + end + + it 'should post _session' + CouchDB.should.receive("request", "once").with_args("POST", "/_session") + CouchDB.login("Gaius Baltar", "secretpass"); + end + + it 'should create a session' + CouchDB.login("Gaius Baltar", "secretpass"); + CouchDB.session().userCtx.name.should.eql "Gaius Baltar" + end + end + + describe '.logout' + before_each + CouchDB.login("Gaius Baltar", "secretpass"); + end + + it 'should return ok true' + CouchDB.logout().ok.should.be_true + end + + it 'should delete _session' + CouchDB.should.receive("request", "once").with_args("DELETE", "/_session") + CouchDB.logout(); + end + + it 'should result in an invalid session' + CouchDB.logout(); + CouchDB.session().name.should.be_null + end + end + + describe '.session' + before_each + CouchDB.login("Gaius Baltar", "secretpass"); + end + + it 'should return ok true' + CouchDB.session().ok.should.be_true + end + + it 'should return the users name' + CouchDB.session().userCtx.name.should.eql "Gaius Baltar" + end + + it 'should return the users roles' + CouchDB.session().userCtx.roles.should.eql ["president"] + end + + it 'should return the name of the authentication db' + CouchDB.session().info.authentication_db.should.eql "spec_users_db" + end + + it 'should return the active authentication handler' + CouchDB.session().info.authenticated.should.eql "cookie" + end + end + end + + describe 'db stuff' + before_each + db = new CouchDB("spec_db", {"X-Couch-Full-Commit":"false"}); + db.createDb(); + end + + after_each + db.deleteDb(); + end + + describe '.prepareUserDoc' + before_each + userDoc = CouchDB.prepareUserDoc({name: "Laura Roslin"}, "secretpass"); + end + + it 'should return the users name' + userDoc.name.should.eql "Laura Roslin" + end + + it 'should prefix the id with the CouchDB user_prefix' + userDoc._id.should.eql "org.couchdb.user:Laura Roslin" + end + + it 'should return the users roles' + var userDocWithRoles = CouchDB.prepareUserDoc({name: "William Adama", roles: ["admiral", "commander"]}, "secretpass") + userDocWithRoles.roles.should.eql ["admiral", "commander"] + end + + it 'should return the hashed password' + userDoc.password_sha.length.should.be_at_least 30 + userDoc.password_sha.should.be_a String + end + end + + describe '.allDbs' + it 'should get _all_dbs' + CouchDB.should.receive("request", "once").with_args("GET", "/_all_dbs"); + CouchDB.allDbs(); + end + + it 'should return an array that includes a created database' + temp_db = new CouchDB("temp_spec_db", {"X-Couch-Full-Commit":"false"}); + temp_db.createDb(); + CouchDB.allDbs().should.include("temp_spec_db"); + temp_db.deleteDb(); + end + + it 'should return an array that does not include a database that does not exist' + CouchDB.allDbs().should.not.include("not_existing_temp_spec_db"); + end + end + + describe '.allDesignDocs' + it 'should return the total number of documents' + CouchDB.allDesignDocs().spec_db.total_rows.should.eql 0 + db.save({'type':'battlestar', 'name':'galactica'}); + CouchDB.allDesignDocs().spec_db.total_rows.should.eql 1 + end + + it 'should return undefined when the db does not exist' + CouchDB.allDesignDocs().non_existing_db.should.be_undefined + end + + it 'should return no documents when there are no design documents' + CouchDB.allDesignDocs().spec_db.rows.should.eql [] + end + + it 'should return all design documents' + var designDoc = { + "views" : { + "people" : { + "map" : "function(doc) { emit(doc._id, doc); }" + } + }, + "_id" : "_design/spec_db" + }; + db.save(designDoc); + + var allDesignDocs = CouchDB.allDesignDocs(); + allDesignDocs.spec_db.rows[0].id.should.eql "_design/spec_db" + allDesignDocs.spec_db.rows[0].key.should.eql "_design/spec_db" + allDesignDocs.spec_db.rows[0].value.rev.length.should.be_at_least 30 + end + end + + describe '.getVersion' + it 'should get the CouchDB version' + CouchDB.should.receive("request", "once").with_args("GET", "/") + CouchDB.getVersion(); + end + + it 'should return the CouchDB version' + CouchDB.getVersion().should_match /^\d\d?\.\d\d?\.\d\d?.*/ + end + end + + describe '.replicate' + before_each + db2 = new CouchDB("spec_db_2", {"X-Couch-Full-Commit":"false"}); + db2.createDb(); + host = window.location.protocol + "//" + window.location.host ; + end + + after_each + db2.deleteDb(); + end + + it 'should return no_changes true when there are no changes between the dbs' + CouchDB.replicate(host + db.uri, host + db2.uri).no_changes.should.be_true + end + + it 'should return the session ID' + db.save({'type':'battlestar', 'name':'galactica'}); + CouchDB.replicate(host + db.uri, host + db2.uri).session_id.length.should.be_at_least 30 + end + + it 'should return source_last_seq' + db.save({'type':'battlestar', 'name':'galactica'}); + db.save({'type':'battlestar', 'name':'pegasus'}); + + CouchDB.replicate(host + db.uri, host + db2.uri).source_last_seq.should.eql 2 + end + + it 'should return the replication history' + db.save({'type':'battlestar', 'name':'galactica'}); + db.save({'type':'battlestar', 'name':'pegasus'}); + + var result = CouchDB.replicate(host + db.uri, host + db2.uri); + result.history[0].docs_written.should.eql 2 + result.history[0].start_last_seq.should.eql 0 + end + + it 'should pass through replication options' + db.save({'type':'battlestar', 'name':'galactica'}); + db2.deleteDb(); + -{CouchDB.replicate(host + db.uri, host + db2.uri)}.should.throw_error + var result = CouchDB.replicate(host + db.uri, host + db2.uri, {"body" : {"create_target":true}}); + + result.ok.should.eql true + result.history[0].docs_written.should.eql 1 + db2.info().db_name.should.eql "spec_db_2" + end + end + + describe '.newXhr' + it 'should return a XMLHTTPRequest' + CouchDB.newXhr().should.have_prop 'readyState' + CouchDB.newXhr().should.have_prop 'responseText' + CouchDB.newXhr().should.have_prop 'status' + end + end + + describe '.request' + it 'should return a XMLHttpRequest' + var req = CouchDB.request("GET", '/'); + req.should.include "readyState" + req.should.include "responseText" + req.should.include "statusText" + end + + it 'should pass through the options headers' + var xhr = CouchDB.newXhr(); + stub(CouchDB, 'newXhr').and_return(xhr); + + xhr.should.receive("setRequestHeader", "once").with_args("X-Couch-Full-Commit", "true") + CouchDB.request("GET", "/", {'headers': {"X-Couch-Full-Commit":"true"}}); + end + + it 'should pass through the options body' + var xhr = CouchDB.newXhr(); + stub(CouchDB, 'newXhr').and_return(xhr); + + xhr.should.receive("send", "once").with_args({"body_key":"body_value"}) + CouchDB.request("GET", "/", {'body': {"body_key":"body_value"}}); + end + + it 'should prepend the urlPrefix to the uri' + var oldPrefix = CouchDB.urlPrefix; + CouchDB.urlPrefix = "/_utils"; + + var xhr = CouchDB.newXhr(); + stub(CouchDB, 'newXhr').and_return(xhr); + + xhr.should.receive("open", "once").with_args("GET", "/_utils/", false) + CouchDB.request("GET", "/", {'headers': {"X-Couch-Full-Commit":"true"}}); + + CouchDB.urlPrefix = oldPrefix; + end + end + + describe '.requestStats' + it 'should get the stats for specified module and key' + var stats = CouchDB.requestStats('couchdb', 'open_databases', null); + stats.description.should.eql 'number of open databases' + stats.current.should.be_a Number + end + + it 'should add flush true to the request when there is a test argument' + CouchDB.should.receive("request", "once").with_args("GET", "/_stats/httpd/requests?flush=true") + CouchDB.requestStats('httpd', 'requests', 'test'); + end + + it 'should still work when there is a test argument' + var stats = CouchDB.requestStats('httpd_status_codes', '200', 'test'); + stats.description.should.eql 'number of HTTP 200 OK responses' + stats.sum.should.be_a Number + end + end + + describe '.newUuids' + after_each + CouchDB.uuids_cache = []; + end + + it 'should return the specified amount of uuids' + var uuids = CouchDB.newUuids(45); + uuids.should.have_length 45 + end + + it 'should return an array with uuids' + var uuids = CouchDB.newUuids(1); + uuids[0].should.be_a String + uuids[0].should.have_length 32 + end + + it 'should leave the uuids_cache with 100 uuids when theres no buffer size specified' + CouchDB.newUuids(23); + CouchDB.uuids_cache.should.have_length 100 + end + + it 'should leave the uuids_cache with the specified buffer size' + CouchDB.newUuids(23, 150); + CouchDB.uuids_cache.should.have_length 150 + end + + it 'should get the uuids from the uuids_cache when there are enough uuids in there' + CouchDB.newUuids(10); + CouchDB.newUuids(25); + CouchDB.uuids_cache.should.have_length 75 + end + + it 'should create new uuids and add as many as specified to the uuids_cache when there are not enough uuids in the cache' + CouchDB.newUuids(10); + CouchDB.newUuids(125, 60); + CouchDB.uuids_cache.should.have_length 160 + end + end + + describe '.maybeThrowError' + it 'should throw an error when the request has status 404' + var req = CouchDB.request("GET", "/nonexisting_db"); + -{CouchDB.maybeThrowError(req)}.should.throw_error + end + + it 'should throw an error when the request has status 412' + var req = CouchDB.request("PUT", "/spec_db"); + -{CouchDB.maybeThrowError(req)}.should.throw_error + end + + it 'should throw an error when the request has status 405' + var req = CouchDB.request("DELETE", "/_utils"); + -{CouchDB.maybeThrowError(req)}.should.throw_error + end + + it 'should throw the responseText of the request' + var req = CouchDB.request("GET", "/nonexisting_db"); + try { + CouchDB.maybeThrowError(req) + } catch(e) { + e.error.should.eql JSON.parse(req.responseText).error + e.reason.should.eql JSON.parse(req.responseText).reason + } + end + + it 'should throw an unknown error when the responseText is invalid json' + mock_request().and_return("invalid json...", "application/json", 404, {}) + try { + CouchDB.maybeThrowError(CouchDB.newXhr()) + } catch(e) { + e.error.should.eql "unknown" + e.reason.should.eql "invalid json..." + } + end + end + + describe '.params' + it 'should turn a json object into a http params string' + var params = CouchDB.params({"president":"laura", "cag":"lee"}) + params.should.eql "president=laura&cag=lee" + end + + it 'should return a blank string when the object is empty' + var params = CouchDB.params({}) + params.should.eql "" + end + end + end +end \ No newline at end of file diff --git a/_attachments/spec/couch_js_instance_methods_1_spec.js b/_attachments/spec/couch_js_instance_methods_1_spec.js new file mode 100644 index 0000000..7f23bd2 --- /dev/null +++ b/_attachments/spec/couch_js_instance_methods_1_spec.js @@ -0,0 +1,311 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// Specs for couch.js lines 1-130 + +describe 'CouchDB instance' + before_each + db = new CouchDB("spec_db", {"X-Couch-Full-Commit":"false"}); + end + + describe '.request' + before_each + db.createDb(); + end + + after_each + db.deleteDb(); + end + + it 'should return a XMLHttpRequest' + var req = db.request("GET", "/spec_db"); + req.should.include "readyState" + req.should.include "responseText" + req.should.include "statusText" + // in Safari a XMLHttpRequest is actually a XMLHttpRequestConstructor, + // otherwise we could just do: + // req.should.be_a XMLHttpRequest + end + + it 'should set the options the CouchDB instance has got as httpHeaders' + CouchDB.should.receive("request", "once").with_args("GET", "/spec_db", {headers: {"X-Couch-Full-Commit": "false"}}) + db.request("GET", "/spec_db"); + end + + it 'should pass through the options' + CouchDB.should.receive("request", "once").with_args("GET", "/spec_db", {"X-Couch-Persist": "true", headers: {"X-Couch-Full-Commit": "false"}}) + db.request("GET", "/spec_db", {"X-Couch-Persist":"true"}); + end + end + + describe '.createDb' + after_each + db.deleteDb(); + end + + it 'should create the db' + db.createDb(); + db.last_req.status.should.eql 201 + end + + it 'should return the ok true' + db.createDb().should.eql {"ok" : true} + end + + it 'should result in a created db' + db.createDb(); + try{ + db.createDb(); + } catch(e) { + e.error.should.eql "file_exists" + } + end + + it 'should have create a db with update sequence 0' + db.createDb(); + db.info().update_seq.should.eql 0 + end + end + + describe '.deleteDb' + before_each + db.createDb(); + end + + it 'should delete the db' + db.deleteDb(); + db.last_req.status.should.eql 200 + end + + it 'should return the responseText of the request' + db.deleteDb().should.eql {"ok" : true} + end + + it 'should result in a deleted db' + db.deleteDb(); + db.deleteDb(); + db.last_req.status.should.eql 404 + end + end + + describe 'document methods' + before_each + doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"}; + db.createDb(); + end + + after_each + db.deleteDb(); + end + + describe '.save' + it 'should save the document' + db.save(doc); + db.last_req.status.should.eql 201 + end + + it 'should return ok true' + db.save(doc).ok.should.be_true + end + + it 'should return ID and revision of the document' + var response = db.save(doc); + response.id.should.be_a String + response.id.should.have_length 32 + response.rev.should.be_a String + response.rev.length.should.be_at_least 30 + end + + it 'should result in a saved document with generated ID' + var response = db.save(doc); + var saved_doc = db.open(response.id); + saved_doc.Name.should.eql "Kara Thrace" + saved_doc.Callsign.should.eql "Starbuck" + end + + it 'should save the document with the specified ID' + doc._id = "123"; + var response = db.save(doc); + response.id.should.eql "123" + end + + it 'should pass through the options' + doc._id = "123"; + CouchDB.should.receive("request", "once").with_args("PUT", "/spec_db/123?batch=ok") + db.save(doc, {"batch" : "ok"}); + end + end + + describe '.open' + before_each + doc._id = "123"; + db.save(doc); + end + + it 'should open the document' + db.open("123").should.eql doc + end + + it 'should return null when there is no document with the given ID' + db.open("non_existing").should.be_null + end + + it 'should pass through the options' + CouchDB.should.receive("request", "once").with_args("GET", "/spec_db/123?revs=true") + db.open("123", {"revs" : "true"}); + end + end + + describe '.deleteDoc' + before_each + doc._id = "123"; + saved_doc = db.save(doc); + delete_response = db.deleteDoc({_id : "123", _rev : saved_doc.rev}); + delete_last_req = db.last_req; + db.open("123"); + end + + it 'should send a successful request' + delete_last_req.status.should.eql 200 + end + + it 'should result in a deleted document' + db.open("123").should.be_null + end + + it 'should return ok true, the ID and the revision of the deleted document' + delete_response.ok.should.be_true + delete_response.id.should.eql "123" + delete_response.rev.should.be_a String + delete_response.rev.length.should.be_at_least 30 + end + + it 'should mark the document as deleted' + var responseText = db.request("GET", "/spec_db/123").responseText; + JSON.parse(responseText).should.eql {"error":"not_found","reason":"deleted"} + end + + it 'should record the revision in the deleted document' + var responseText = db.request("GET", "/spec_db/123?rev=" + delete_response.rev).responseText; + var deleted_doc = JSON.parse(responseText); + deleted_doc._rev.should.eql delete_response.rev + deleted_doc._id.should.eql delete_response.id + deleted_doc._deleted.should.be_true + end + end + + describe '.deleteDocAttachment' + before_each + doc._id = "123"; + doc._attachments = { + "friend.txt" : { + "content_type": "text\/plain", + // base64 encoded + "data": "TGVlIEFkYW1hIGlzIGEgZm9ybWVyIENvbG9uaWFsIEZsZWV0IFJlc2VydmUgb2ZmaWNlci4=" + } + }; + saved_doc = db.save(doc); + end + + it 'should be executed on a document with attachment' + db.open("123")._attachments.should.include "friend.txt" + db.open("123")._attachments["friend.txt"].stub.should.be_true + end + + describe 'after delete' + before_each + delete_response = db.deleteDocAttachment({_id : "123", _rev : saved_doc.rev}, "friend.txt"); + db.open("123"); + end + + it 'should send a successful request' + db.last_req.status.should.eql 200 + end + + it 'should leave the document untouched' + db.open("123").Callsign.should.eql "Starbuck" + end + + it 'should result in a deleted document attachment' + db.open("123").should.not.include "_attachments" + end + + it 'should record the revision in the document whose attachment has been deleted' + var responseText = db.request("GET", "/spec_db/123?rev=" + delete_response.rev).responseText; + var deleted_doc = JSON.parse(responseText); + deleted_doc._rev.should.eql delete_response.rev + deleted_doc._id.should.eql delete_response.id + end + + it 'should return ok true, the ID and the revision of the document whose attachment has been deleted' + delete_response.ok.should.be_true + delete_response.id.should.eql "123" + delete_response.should.have_property 'rev' + end + end + end + + describe '.bulkSave' + before_each + doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"}; + doc2 = {"Name" : "Karl C. Agathon", "Callsign" : "Helo"}; + doc3 = {"Name" : "Sharon Valerii", "Callsign" : "Boomer"}; + docs = [doc, doc2, doc3]; + end + + it 'should save the documents' + db.bulkSave(docs); + db.last_req.status.should.eql 201 + end + + it 'should return ID and revision of the documents' + var response = db.bulkSave(docs); + response[0].id.should.be_a String + response[0].id.should.have_length 32 + response[0].rev.should.be_a String + response[0].rev.length.should.be_at_least 30 + response[1].id.should.be_a String + response[1].id.should.have_length 32 + response[1].rev.should.be_a String + response[1].rev.length.should.be_at_least 30 + response[2].id.should.be_a String + response[2].id.should.have_length 32 + response[2].rev.should.be_a String + response[2].rev.length.should.be_at_least 30 + end + + it 'should result in saved documents' + var response = db.bulkSave(docs); + db.open(response[0].id).Name.should.eql "Kara Thrace" + db.open(response[1].id).Name.should.eql "Karl C. Agathon" + db.open(response[2].id).Name.should.eql "Sharon Valerii" + end + + it 'should save the document with specified IDs' + doc._id = "123"; + doc2._id = "456"; + docs = [doc, doc2, doc3]; + var response = db.bulkSave(docs); + response[0].id.should.eql "123" + response[1].id.should.eql "456" + response[2].id.should.have_length 32 + end + + it 'should pass through the options' + doc._id = "123"; + docs = [doc]; + CouchDB.should.receive("request", "once").with_args("POST", "/spec_db/_bulk_docs", {body: '{"docs":[{"Name":"Kara Thrace","Callsign":"Starbuck","_id":"123"}],"batch":"ok"}'}) + db.bulkSave(docs, {"batch" : "ok"}); + end + end + end +end \ No newline at end of file diff --git a/_attachments/spec/couch_js_instance_methods_2_spec.js b/_attachments/spec/couch_js_instance_methods_2_spec.js new file mode 100644 index 0000000..76df636 --- /dev/null +++ b/_attachments/spec/couch_js_instance_methods_2_spec.js @@ -0,0 +1,246 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// Specs for couch.js lines 132-199 + +describe 'CouchDB instance' + before_each + db = new CouchDB("spec_db", {"X-Couch-Full-Commit":"false"}); + db.createDb(); + end + + after_each + db.deleteDb(); + end + + describe '.ensureFullCommit' + it 'should return ok true' + db.ensureFullCommit().ok.should.be_true + end + + it 'should return the instance start time' + db.ensureFullCommit().instance_start_time.should.have_length 16 + end + + it 'should post _ensure_full_commit to the db' + db.should.receive("request", "once").with_args("POST", "/spec_db/_ensure_full_commit") + db.ensureFullCommit(); + end + end + + describe '.query' + before_each + db.save({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"}); + db.save({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"}); + db.save({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"}); + map_function = "function(doc) { emit(doc._id, 1); }"; + reduce_function = "function(key, values, rereduce) { return sum(values); }"; + end + + it 'should apply the map function' + var result = db.query(map_function); + + result.rows.should.have_length 3 + result.rows[0].id.should.eql "123" + result.rows[0].key.should.eql "123" + result.rows[0].value.should.eql 1 + result.rows[1].id.should.eql "456" + result.rows[1].key.should.eql "456" + result.rows[1].value.should.eql 1 + result.rows[2].id.should.eql "789" + result.rows[2].key.should.eql "789" + result.rows[2].value.should.eql 1 + end + + it 'should apply the reduce function' + var result = db.query(map_function, reduce_function); + + result.rows.should.have_length 1 + result.rows[0].key.should.be_null + result.rows[0].value.should_eql 3 + end + + it 'should pass through the options' + var result = db.query(map_function, null, {"startkey":"456"}); + + result.rows.should.have_length 2 + result.rows[0].id.should.eql "456" + result.rows[0].key.should.eql "456" + result.rows[0].value.should.eql 1 + result.rows[1].id.should.eql "789" + result.rows[1].key.should.eql "789" + result.rows[1].value.should.eql 1 + end + + it 'should pass through the keys' + var result = db.query(map_function, null, {}, ["456", "123"]); + + result.rows.should.have_length 2 + result.rows[0].id.should.eql "456" + result.rows[0].key.should.eql "456" + result.rows[0].value.should.eql 1 + result.rows[1].id.should.eql "123" + result.rows[1].key.should.eql "123" + result.rows[1].value.should.eql 1 + end + + it 'should pass through the options and the keys' + var result = db.query(map_function, null, {"include_docs":"true"}, ["456"]); + + result.rows.should.have_length 1 + result.rows[0].id.should.eql "456" + result.rows[0].key.should.eql "456" + result.rows[0].value.should.eql 1 + result.rows[0].doc["job"].should.eql "pilot" + result.rows[0].doc["_rev"].length.should.be_at_least 30 + end + + it 'should apply a view in erlang also' + // when this test fails, read this: http://wiki.apache.org/couchdb/EnableErlangViews + var erlang_map = 'fun({Doc}) -> ' + + 'ID = proplists:get_value(<<"_id">>, Doc, null), ' + + 'Emit(ID, 1) ' + + 'end.'; + var result = db.query(erlang_map, null, null, null, "erlang"); + + result.rows.should.have_length 3 + result.rows[0].id.should.eql "123" + result.rows[0].key.should.eql "123" + result.rows[0].value.should.eql 1 + result.rows[1].id.should.eql "456" + result.rows[1].key.should.eql "456" + result.rows[1].value.should.eql 1 + result.rows[2].id.should.eql "789" + result.rows[2].key.should.eql "789" + result.rows[2].value.should.eql 1 + end + end + + describe '.view' + before_each + db.save({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"}); + db.save({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"}); + db.save({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"}); + view = { + "views" : { + "people" : { + "map" : "function(doc) { emit(doc._id, doc.Name); }" + } + }, + "_id" : "_design/spec_db" + }; + db.save(view); + end + + it 'should apply the view' + var result = db.view('spec_db/people'); + + result.rows.should.have_length 3 + result.rows[0].id.should.eql "123" + result.rows[0].key.should.eql "123" + result.rows[0].value.should.eql "Felix Gaeta" + result.rows[1].id.should.eql "456" + result.rows[1].key.should.eql "456" + result.rows[1].value.should.eql "Samuel T. Anders" + result.rows[2].id.should.eql "789" + result.rows[2].key.should.eql "789" + result.rows[2].value.should.eql "Cally Tyrol" + end + + it 'should pass through the options' + var result = db.view('spec_db/people', {"skip":"2"}); + + result.rows.should.have_length 1 + result.rows[0].id.should.eql "789" + result.rows[0].key.should.eql "789" + result.rows[0].value.should.eql "Cally Tyrol" + end + + it 'should pass through the keys' + var result = db.view('spec_db/people', {}, ["456", "123"]); + + result.rows.should.have_length 2 + result.rows[0].id.should.eql "456" + result.rows[0].key.should.eql "456" + result.rows[0].value.should.eql "Samuel T. Anders" + result.rows[1].id.should.eql "123" + result.rows[1].key.should.eql "123" + result.rows[1].value.should.eql "Felix Gaeta" + end + + it 'should pass through the options and the keys' + var result = db.view('spec_db/people', {"include_docs":"true"}, ["456"]); + + result.rows.should.have_length 1 + result.rows[0].id.should.eql "456" + result.rows[0].key.should.eql "456" + result.rows[0].value.should.eql "Samuel T. Anders" + result.rows[0].doc["job"].should.eql "pilot" + result.rows[0].doc["_rev"].length.should.be_at_least 30 + end + + it 'should return null when the view doesnt exist' + var result = db.view('spec_db/non_existing_view'); + + result.should.be_null + end + end + + describe '.info' + before_each + result = db.info(); + end + + it 'should return the name of the database' + result.db_name.should.eql "spec_db" + end + + it 'should return the number of documents' + result.doc_count.should.eql 0 + end + + it 'should return the start time of the db instance' + result.instance_start_time.should.have_length 16 + end + end + + describe '.designInfo' + before_each + designDoc = { + "views" : { + "people" : { + "map" : "function(doc) { emit(doc._id, doc); }" + } + }, + "_id" : "_design/spec_db" + }; + db.save(designDoc); + result = db.designInfo("_design/spec_db"); + end + + it 'should return the database name' + result.name.should.eql "spec_db" + end + + it 'should return a views language' + result.view_index.language.should.eql "javascript" + end + + it 'should return a views update sequence' + result.view_index.update_seq.should.eql 0 + end + + it 'should return a views signature' + result.view_index.signature.should.have_length 32 + end + end +end \ No newline at end of file diff --git a/_attachments/spec/couch_js_instance_methods_3_spec.js b/_attachments/spec/couch_js_instance_methods_3_spec.js new file mode 100644 index 0000000..b7464c0 --- /dev/null +++ b/_attachments/spec/couch_js_instance_methods_3_spec.js @@ -0,0 +1,215 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// Specs for couch.js lines 201-265 + +describe 'CouchDB instance' + before_each + db = new CouchDB("spec_db", {"X-Couch-Full-Commit":"false"}); + db.createDb(); + end + + after_each + db.deleteDb(); + end + + describe '.allDocs' + it 'should return no docs when there arent any' + db.allDocs().total_rows.should.eql 0 + db.allDocs().rows.should.eql [] + end + + describe 'with docs' + before_each + db.save({"Name" : "Felix Gaeta", "_id" : "123"}); + db.save({"Name" : "Samuel T. Anders", "_id" : "456"}); + end + + it 'should return all docs' + var result = db.allDocs(); + + result.total_rows.should.eql 2 + result.rows.should.have_length 2 + result.rows[0].id.should.eql "123" + result.rows[0].key.should.eql "123" + result.rows[0].value.rev.length.should.be_at_least 30 + result.rows[1].id.should.eql "456" + end + + it 'should pass through the options' + var result = db.allDocs({"startkey": "123", "limit": "1"}); + + result.rows.should.have_length 1 + result.rows[0].id.should.eql "123" + end + + it 'should pass through the keys' + var result = db.allDocs({}, ["456"]); + + result.rows.should.have_length 1 + result.rows[0].id.should.eql "456" + result.rows[0].key.should.eql "456" + result.rows[0].value.rev.length.should.be_at_least 30 + end + + it 'should pass through the options and the keys' + var result = db.allDocs({"include_docs":"true"}, ["456"]); + + result.rows.should.have_length 1 + result.rows[0].id.should.eql "456" + result.rows[0].key.should.eql "456" + result.rows[0].value.rev.length.should.be_at_least 30 + result.rows[0].doc["Name"].should.eql "Samuel T. Anders" + result.rows[0].doc["_rev"].length.should.be_at_least 30 + end + + end + end + + describe '.designDocs' + it 'should return nothing when there arent any design docs' + db.save({"Name" : "Felix Gaeta", "_id" : "123"}); + db.designDocs().rows.should.eql [] + end + + it 'should return all design docs' + var designDoc = { + "views" : { + "people" : { + "map" : "function(doc) { emit(doc._id, doc); }" + } + }, + "_id" : "_design/spec_db" + }; + db.save(designDoc); + db.save({"Name" : "Felix Gaeta", "_id" : "123"}); + + var result = db.designDocs(); + + result.total_rows.should.eql 2 + result.rows.should.have_length 1 + result.rows[0].id.should.eql "_design/spec_db" + result.rows[0].key.should.eql "_design/spec_db" + result.rows[0].value.rev.length.should.be_at_least 30 + end + end + + describe '.changes' + it 'should return no changes when there arent any' + db.changes().last_seq.should.eql 0 + db.changes().results.should.eql [] + end + + describe 'with changes' + before_each + db.save({"Name" : "Felix Gaeta", "_id" : "123"}); + db.save({"Name" : "Samuel T. Anders", "_id" : "456"}); + end + + it 'should return changes' + var result = db.changes(); + + result.last_seq.should.eql 2 + result.results[0].id.should.eql "123" + result.results[0].seq.should.eql 1 + result.results[0].changes[0].rev.length.should.be_at_least 30 + result.results[1].id.should.eql "456" + result.results[1].seq.should.eql 2 + result.results[1].changes[0].rev.length.should.be_at_least 30 + end + + it 'should pass through the options' + var result = db.changes({"since":"1"}); + + result.last_seq.should.eql 2 + result.results[0].id.should.eql "456" + end + end + end + + describe '.compact' + it 'should return ok true' + db.compact().ok.should.be_true + end + + it 'should post _compact to the db' + db.should.receive("request", "once").with_args("POST", "/spec_db/_compact") + db.compact(); + end + end + + describe '.viewCleanup' + it 'should return ok true' + db.viewCleanup().ok.should.be_true + end + + it 'should post _view_cleanup to the db' + db.should.receive("request", "once").with_args("POST", "/spec_db/_view_cleanup") + db.viewCleanup(); + end + end + + describe '.setDbProperty' + it 'should return ok true' + db.setDbProperty("_revs_limit", 1500).ok.should.be_true + end + + it 'should set a db property' + db.setDbProperty("_revs_limit", 1500); + db.getDbProperty("_revs_limit").should.eql 1500 + db.setDbProperty("_revs_limit", 1200); + db.getDbProperty("_revs_limit").should.eql 1200 + end + end + + describe '.getDbProperty' + it 'should get a db property' + db.setDbProperty("_revs_limit", 1200); + db.getDbProperty("_revs_limit").should.eql 1200 + end + + it 'should throw an error when the property doesnt exist' + function(){ db.getDbProperty("_doesnt_exist")}.should.throw_error + end + end + + describe '.setSecObj' + it 'should return ok true' + db.setSecObj({"readers":{"names":["laura"],"roles":["president"]}}).ok.should.be_true + end + + it 'should save a well formed object into the _security object ' + db.should.receive("request", "once").with_args("PUT", "/spec_db/_security", {body: '{"readers":{"names":["laura"],"roles":["president"]}}'}) + db.setSecObj({"readers": {"names" : ["laura"], "roles" : ["president"]}}) + end + + it 'should throw an error when the readers or admins object is malformed' + -{ db.setSecObj({"admins":["cylon"]}) }.should.throw_error + end + + it 'should save any other object into the _security object' + db.setSecObj({"something" : "anything"}) + db.getSecObj().should.eql {"something" : "anything"} + end + end + + describe '.getSecObj' + it 'should get the security object' + db.setSecObj({"admins" : {"names" : ["bill"], "roles" : ["admiral"]}}) + db.getSecObj().should.eql {"admins" : {"names": ["bill"], "roles": ["admiral"]}} + end + + it 'should return an empty object when there is no security object' + db.getSecObj().should.eql {} + end + end +end \ No newline at end of file diff --git a/_attachments/spec/custom_helpers.js b/_attachments/spec/custom_helpers.js new file mode 100644 index 0000000..d29ee87 --- /dev/null +++ b/_attachments/spec/custom_helpers.js @@ -0,0 +1,51 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +function stubAlert(){ + if(typeof(old_alert) == 'undefined'){ + old_alert = alert; + } + alert = function(msg){ + alert_msg = msg; + }; +} + +function destubAlert(){ + alert = old_alert; +} + +function errorCallback(status, error, reason){ + console.log("Unexpected " + status + " error: " + error + " - " + reason) + throw("Unexpected " + status + " error: " + error + " - " + reason); +} + +function successCallback(resp){ + console.log("No error message here unexpectedly, successful response instead.") + throw("No error message here unexpectedly, successful response instead."); +} + +function useTestUserDb(){ + users_db = new CouchDB("spec_users_db"); + var xhr = CouchDB.request("PUT", "/_config/couch_httpd_auth/authentication_db", { + body: JSON.stringify("spec_users_db") + }); + if(typeof(old_auth_db) == 'undefined'){ + old_auth_db = xhr.responseText.replace(/\n/,'').replace(/"/g,''); + } +} + +function useOldUserDb(){ + CouchDB.request("PUT", "/_config/couch_httpd_auth/authentication_db", { + body: JSON.stringify(old_auth_db) + }); + users_db.deleteDb(); +} \ No newline at end of file diff --git a/_attachments/spec/jquery_couch_js_class_methods_spec.js b/_attachments/spec/jquery_couch_js_class_methods_spec.js new file mode 100644 index 0000000..f2df81b --- /dev/null +++ b/_attachments/spec/jquery_couch_js_class_methods_spec.js @@ -0,0 +1,523 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// Specs for jquery_couch.js lines 48-156 and 415-448 + +describe 'jQuery couchdb' + before + stubAlert(); + end + + after + destubAlert(); + end + + describe 'activeTasks' + before_each + db = $.couch.db("spec_db"); + db.create(); + end + + after_each + db.drop(); + end + + it 'should return an empty array when there are no active tasks' + $.couch.activeTasks({ + success: function(resp){ + resp.should.eql [] + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return an active task' + // doing a bit of stuff here so compaction has something to do and takes a while + var battlestar, civillian; + db.saveDoc({"type":"Battlestar", "name":"Galactica"}, { + success: function(resp){ + db.openDoc(resp.id, { + success: function(resp2){ + battlestar = resp2; + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + battlestar.name = "Pegasus"; + db.saveDoc(battlestar); + + db.saveDoc({"type":"Civillian", "name":"Cloud 9"}, { + success: function(resp){ + db.openDoc(resp.id, { + success: function(resp2){ + civillian = resp2; + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + civillian.name = "Olympic Carrier"; + db.saveDoc(civillian); + db.removeDoc(civillian); + + db.compact({ + ajaxStart: function(resp){ + $.couch.activeTasks({ + success: function(resp2){ + resp2[0].type.should.eql "Database Compaction" + resp2[0].task.should.eql "spec_db" + resp2[0].should.have_prop "status" + resp2[0].should.include "pid" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + } + }); + end + end + + describe 'allDbs' + it 'should return an array that includes a created database' + temp_db = new CouchDB("temp_spec_db", {"X-Couch-Full-Commit":"false"}); + temp_db.createDb(); + $.couch.allDbs({ + success: function(resp){ + resp.should.include "temp_spec_db" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + temp_db.deleteDb(); + end + + it 'should return an array that does not include a database that does not exist' + $.couch.allDbs({ + success: function(resp){ + resp.should.not.include("not_existing_temp_spec_db"); + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + end + + describe 'config' + it 'should get the config settings' + $.couch.config({ + success: function(resp){ + resp.httpd.port.should.eql window.location.port + resp.stats.samples.should.match /\[.*\]/ + resp.native_query_servers.should.have_prop "erlang" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should get a specific config setting' + $.couch.config({ + success: function(resp){ + parseInt(resp.max_document_size).should.be_a Number + resp.delayed_commits.should.be_a String + resp.database_dir.should.be_a String + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }, "couchdb"); + end + + it 'should update a config setting' + $.couch.config({ + success: function(resp){ + resp.should.eql "" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }, "test", "colony", "Caprica"); + + $.couch.config({ + success: function(resp){ + resp.colony.should.eql "Caprica" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }, "test"); + + $.couch.config({}, "test", "colony", null); + end + + it 'should delete a config setting' + $.couch.config({}, "test", "colony", "Caprica"); + + $.couch.config({ + success: function(resp){ + resp.should.eql "Caprica" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }, "test", "colony", null); + + $.couch.config({ + success: function(resp){ + resp.should.eql {} + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }, "test"); + end + + it 'should alert with an error message prefix' + $.couch.config("asdf", "asdf", "asdf"); + alert_msg.should.match /An error occurred retrieving\/updating the server configuration/ + end + end + + describe 'session' + it 'should return information about the session' + $.couch.session({ + success: function(resp){ + resp.info.should.have_prop 'authentication_db' + resp.userCtx.should.include 'name' + resp.userCtx.roles.should.be_an Array + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + end + + describe 'userDb' + it 'should return the userDb' + var authentication_db; + $.couch.session({ + success: function(resp){ + authentication_db = resp.info.authentication_db; + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + + $.couch.userDb(function(resp){ + resp.name.should.eql authentication_db + }); + end + + it 'should return a db instance' + $.couch.userDb(function(resp){ + resp.should.respond_to 'allDocs' + resp.should.respond_to 'bulkSave' + }); + end + end + + describe 'user_db stuff' + before + useTestUserDb(); + end + + after + useOldUserDb(); + end + + describe 'signup' + it 'should return a saved user' + $.couch.signup( + {name: "Tom Zarek"}, "secretpass", { + success: function(resp){ + resp.id.should.eql "org.couchdb.user:Tom Zarek" + resp.rev.length.should.be_at_least 30 + resp.ok.should.be_true + users_db.deleteDoc({_id : resp.id, _rev : resp.rev}) + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should create a userDoc in the user db' + $.couch.signup( + {name: "Tom Zarek"}, "secretpass", { + success: function(resp){ + var user = users_db.open(resp.id); + user.name.should.eql "Tom Zarek" + user._id.should.eql "org.couchdb.user:Tom Zarek" + user.roles.should.eql [] + user.password_sha.length.should.be_at_least 30 + user.password_sha.should.be_a String + users_db.deleteDoc({_id : resp.id, _rev : resp.rev}) + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should create a userDoc with roles when specified' + $.couch.signup( + {name: "Tom Zarek", roles: ["vice_president", "activist"]}, "secretpass", { + success: function(resp){ + var user = users_db.open(resp.id); + user.roles.should.eql ["vice_president", "activist"] + users_db.deleteDoc({_id : resp.id, _rev : resp.rev}) + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + end + + describe 'login' + before_each + user = {}; + $.couch.signup({name: "Tom Zarek", roles: ["vice_president", "activist"]}, "secretpass", { + success: function(resp){ + user.id = resp.id; + user.rev = resp.rev; + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + after_each + users_db.deleteDoc({_id : user.id, _rev : user.rev}) + end + + it 'should return the logged in user' + $.couch.login({ + name: "Tom Zarek", + password: "secretpass", + success: function(resp){ + resp.name.should.eql "Tom Zarek" + resp.ok.should.be_true + resp.roles.should.eql ["vice_president", "activist"] + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should result in a session for the logged in user' + $.couch.login({ + name: "Tom Zarek", + password: "secretpass" + }); + $.couch.session({ + success: function(resp){ + resp.info.authentication_db.should.eql "spec_users_db" + resp.userCtx.name.should.eql "Tom Zarek" + resp.userCtx.roles.should.eql ["vice_president", "activist"] + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return a 404 when password is wrong' + $.couch.login({ + name: "Tom Zarek", + password: "wrongpass", + error: function(status, error, reason){ + status.should.eql 401 + error.should.eql "unauthorized" + reason.should.eql "Name or password is incorrect." + }, + success: function(resp){successCallback(resp)} + }); + end + + it 'should return a 404 when the user doesnt exist in the users db' + $.couch.login({ + name: "Number Three", + password: "secretpass", + error: function(status, error, reason){ + status.should.eql 401 + error.should.eql "unauthorized" + reason.should.eql "Name or password is incorrect." + }, + success: function(resp){successCallback(resp)} + }); + end + + it 'should alert with an error message prefix' + $.couch.login("asdf"); + alert_msg.should.match /An error occurred logging in/ + end + end + + describe 'logout' + before_each + user = {}; + $.couch.signup({name: "Tom Zarek", roles: ["vice_president", "activist"]}, "secretpass", { + success: function(resp){ + user.id = resp.id; + user.rev = resp.rev; + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + $.couch.login({name: "Tom Zarek", password: "secretpass"}); + end + + after_each + users_db.deleteDoc({_id : user.id, _rev : user.rev}) + end + + it 'should return ok true' + $.couch.logout({ + success: function(resp){ + resp.ok.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should result in an empty session' + $.couch.logout(); + $.couch.session({ + success: function(resp){ + resp.userCtx.name.should.be_null + resp.userCtx.roles.should.not.include ["vice_president"] + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + end + end + + describe 'encodeDocId' + it 'should return the encoded docID when it is not a design document' + $.couch.encodeDocId("viper").should.eql(encodeURIComponent("viper")) + end + + it 'should encode only the name of the design document' + $.couch.encodeDocId("_design/raptor").should.eql("_design/" + encodeURIComponent("raptor")) + end + + it 'should also work when the name of the des' + $.couch.encodeDocId("_design/battlestar/_view/crew").should.eql("_design/" + encodeURIComponent("battlestar/_view/crew")) + end + end + + describe 'info' + it 'should return the CouchDB version' + $.couch.info({ + success: function(resp){ + resp.couchdb.should.eql "Welcome" + resp.version.should_match /^\d\d?\.\d\d?\.\d\d?.*/ + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + end + + describe 'replicate' + before_each + db = $.couch.db("spec_db"); + db.create(); + db2 = $.couch.db("spec_db_2"); + db2.create(); + host = window.location.protocol + "//" + window.location.host ; + end + + after_each + db.drop(); + db2.drop(); + end + + it 'should return no_changes true when there are no changes between the dbs' + $.couch.replicate(host + db.uri, host + db2.uri, { + success: function(resp){ + resp.ok.should.be_true + resp.no_changes.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return the session ID' + db.saveDoc({'type':'battlestar', 'name':'galactica'}); + $.couch.replicate(host + db.uri, host + db2.uri, { + success: function(resp){ + resp.session_id.length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return source_last_seq' + db.saveDoc({'type':'battlestar', 'name':'galactica'}); + db.saveDoc({'type':'battlestar', 'name':'pegasus'}); + + $.couch.replicate(host + db.uri, host + db2.uri, { + success: function(resp){ + resp.source_last_seq.should.eql 2 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return the replication history' + db.saveDoc({'type':'battlestar', 'name':'galactica'}); + db.saveDoc({'type':'battlestar', 'name':'pegasus'}); + + $.couch.replicate(host + db.uri, host + db2.uri, { + success: function(resp){ + resp.history[0].docs_written.should.eql 2 + resp.history[0].start_last_seq.should.eql 0 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through replication options' + db.saveDoc({'type':'battlestar', 'name':'galactica'}); + db2.drop(); + $.couch.replicate(host + db.uri, host + db2.uri, { + error: function(status, error, reason){ + status.should.eql 500 + reason.should.match /db_not_found/ + }, + success: function(resp){successCallback(resp)} + }); + + $.couch.replicate(host + db.uri, host + db2.uri, { + success: function(resp){ + resp.ok.should.eql true + resp.history[0].docs_written.should.eql 1 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }, { + "create_target":true + }); + + db2.info({ + success: function(resp){ + resp.db_name.should.eql "spec_db_2" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should alert with an error message prefix' + $.couch.replicate("asdf"); + alert_msg.should.match /Replication failed/ + end + end + + describe 'newUUID' + it 'should return a new UUID' + var new_uuid = $.couch.newUUID(1); + new_uuid.should.be_a String + new_uuid.should.have_length 32 + end + + it 'should fill the uuidCache with the specified number minus 1' + // we can't reach the uuidCache from here, so we mock the next request + // to test that the next uuid is not coming from the request, but from the cache. + $.couch.newUUID(2); + mock_request().and_return({'uuids':['a_sample_uuid']}) + $.couch.newUUID(1).should.not.eql 'a_sample_uuid' + $.couch.newUUID(1).should.eql 'a_sample_uuid' + end + + it 'should alert with an error message prefix' + $.couch.newUUID("asdf"); + alert_msg.should.match /Failed to retrieve UUID batch/ + end + end +end \ No newline at end of file diff --git a/_attachments/spec/jquery_couch_js_instance_methods_1_spec.js b/_attachments/spec/jquery_couch_js_instance_methods_1_spec.js new file mode 100644 index 0000000..8538c85 --- /dev/null +++ b/_attachments/spec/jquery_couch_js_instance_methods_1_spec.js @@ -0,0 +1,202 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// Specs for jquery_couch.js lines 163-209 + +describe 'jQuery couchdb db' + before + stubAlert(); + end + + after + destubAlert(); + end + + before_each + db = $.couch.db('spec_db'); + end + + describe 'constructor' + it 'should set the name' + db.name.should.eql 'spec_db' + end + + it 'should set the uri' + db.uri.should.eql '/spec_db/' + end + end + + describe 'triggering db functions' + before_each + db.create(); + end + + after_each + db.drop(); + end + + describe 'compact' + it 'should return ok true' + db.compact({ + success: function(resp) { + resp.ok.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should trigger _compact' + db.compact({ + success: function(resp, obj) { + obj.url.should.eql "/spec_db/_compact" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + end + + describe 'viewCleanup' + it 'should return ok true' + db.viewCleanup({ + success: function(resp) { + resp.ok.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should trigger _view_cleanup' + db.viewCleanup({ + success: function(resp, obj) { + obj.url.should.eql "/spec_db/_view_cleanup" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + end + + describe 'compactView' + before_each + var designDoc = { + "views" : { + "people" : { + "map" : "function(doc) { emit(doc._id, doc); }" + } + }, + "_id" : "_design/myview" + }; + db.saveDoc(designDoc); + db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"}); + end + + it 'should return ok true' + db.compactView("myview", { + success: function(resp) { + resp.ok.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should trigger _compact_view with the groupname' + db.compactView("myview", { + success: function(resp, obj) { + obj.url.should.eql "/spec_db/_compact/myview" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return raise a 404 error when the design name doesnt exist' + db.compactView("non_existing_design_name", { + error: function(status, error, reason){ + status.should.eql 404 + error.should.eql "not_found" + reason.should.eql "missing" + }, + success: function(resp){successCallback(resp)} + }); + end + + it 'should alert with an error message prefix' + db.compactView("asdf"); + alert_msg.should.match /The view could not be compacted/ + end + end + end + + describe 'create' + after_each + db.drop(); + end + + it 'should return ok true' + db.create({ + success: function(resp) { + resp.ok.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should result in a created db' + db.create(); + db.create({ + error: function(status, error, reason){ + status.should.eql 412 + error.should.eql "file_exists" + reason.should.eql "The database could not be created, the file already exists." + }, + success: function(resp){successCallback(resp)} + }); + end + + it 'should alert with an error message prefix' + db.create(); + db.create(); + alert_msg.should.match /The database could not be created/ + end + end + + describe 'drop' + before_each + db.create(); + end + + it 'should return ok true' + db.drop({ + success: function(resp) { + resp.ok.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should result in a deleted db' + db.drop(); + db.drop({ + error: function(status, error, reason){ + status.should.eql 404 + error.should.eql "not_found" + reason.should.eql "missing" + }, + success: function(resp){successCallback(resp)} + }); + end + + it 'should alert with an error message prefix' + db.drop(); + db.drop(); + alert_msg.should.match /The database could not be deleted/ + end + end +end \ No newline at end of file diff --git a/_attachments/spec/jquery_couch_js_instance_methods_2_spec.js b/_attachments/spec/jquery_couch_js_instance_methods_2_spec.js new file mode 100644 index 0000000..8f35aff --- /dev/null +++ b/_attachments/spec/jquery_couch_js_instance_methods_2_spec.js @@ -0,0 +1,433 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// Specs for jquery_couch.js lines 210-299 + +describe 'jQuery couchdb db' + before + stubAlert(); + end + + after + destubAlert(); + end + + before_each + db = $.couch.db('spec_db'); + db.create(); + end + + after_each + db.drop(); + end + + describe 'info' + before_each + result = {}; + db.info({ + success: function(resp) { result = resp; }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return the name of the database' + result.db_name.should.eql "spec_db" + end + + it 'should return the number of documents' + result.doc_count.should.eql 0 + end + + it 'should return the start time of the db instance' + result.instance_start_time.should.have_length 16 + end + end + + describe 'allDocs' + it 'should return no docs when there arent any' + db.allDocs({ + success: function(resp) { + resp.total_rows.should.eql 0 + resp.rows.should.eql [] + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + describe 'with docs' + before_each + db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"}); + db.saveDoc({"Name" : "Samuel T. Anders", "_id" : "456"}); + end + + it 'should return all docs' + db.allDocs({ + success: function(resp) { + resp.total_rows.should.eql 2 + resp.rows.should.have_length 2 + resp.rows[0].id.should.eql "123" + resp.rows[0].key.should.eql "123" + resp.rows[0].value.rev.length.should.be_at_least 30 + resp.rows[1].id.should.eql "456" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through the options' + db.allDocs({ + "startkey": "123", + "limit": "1", + success: function(resp) { + resp.rows.should.have_length 1 + resp.rows[0].id.should.eql "123" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + end + end + + describe 'allDesignDocs' + it 'should return nothing when there arent any design docs' + db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"}); + db.allDesignDocs({ + success: function(resp) { + resp.rows.should.eql [] + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return all design docs' + var designDoc = { + "views" : { + "people" : { + "map" : "function(doc) { emit(doc._id, doc); }" + } + }, + "_id" : "_design/spec_db" + }; + db.saveDoc(designDoc); + db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"}); + + db.allDesignDocs({ + success: function(resp) { + resp.total_rows.should.eql 2 + resp.rows.should.have_length 1 + resp.rows[0].id.should.eql "_design/spec_db" + resp.rows[0].key.should.eql "_design/spec_db" + resp.rows[0].value.rev.length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + end + + describe 'allApps' + it 'should provide a custom function with appName, appPath and design document when there is an attachment with index.html' + var designDoc = {"_id" : "_design/with_attachments"}; + + designDoc._attachments = { + "index.html" : { + "content_type": "text\/html", + // this is "

        Hi, here is index!

        ", base64 encoded + "data": "PGh0bWw+PHA+SGksIGhlcmUgaXMgaW5kZXghPC9wPjwvaHRtbD4=" + } + }; + db.saveDoc(designDoc); + + db.allApps({ + eachApp: function(appName, appPath, ddoc) { + appName.should.eql "with_attachments" + appPath.should.eql "/spec_db/_design/with_attachments/index.html" + ddoc._id.should.eql "_design/with_attachments" + ddoc._attachments["index.html"].content_type.should.eql "text/html" + ddoc._attachments["index.html"].length.should.eql "

        Hi, here is index!

        ".length + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should provide a custom function with appName, appPath and design document when there is a couchapp with index file' + var designDoc = {"_id" : "_design/with_index"}; + designDoc.couchapp = { + "index" : "cylon" + }; + db.saveDoc(designDoc); + + db.allApps({ + eachApp: function(appName, appPath, ddoc) { + appName.should.eql "with_index" + appPath.should.eql "/spec_db/_design/with_index/cylon" + ddoc._id.should.eql "_design/with_index" + ddoc.couchapp.index.should.eql "cylon" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should not call the eachApp function when there is neither index.html in _attachments nor a couchapp index file' + var designDoc = {"_id" : "_design/nothing"}; + db.saveDoc(designDoc); + + var eachApp_called = false; + db.allApps({ + eachApp: function(appName, appPath, ddoc) { + eachApp_called = true; + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + + eachApp_called.should.be_false + end + + it 'should alert with an error message prefix' + db.allApps(); + alert_msg.should.match /Please provide an eachApp function for allApps()/ + end + end + + describe 'openDoc' + before_each + doc = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "123"}; + db.saveDoc(doc); + end + + it 'should open the document' + db.openDoc("123", { + success: function(resp){ + resp.should.eql doc + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should raise a 404 error when there is no document with the given ID' + db.openDoc("non_existing", { + error: function(status, error, reason){ + status.should.eql 404 + error.should.eql "not_found" + reason.should.eql "missing" + }, + success: function(resp){successCallback(resp)} + }); + end + + it 'should pass through the options' + doc.Name = "Sasha"; + db.saveDoc(doc); + db.openDoc("123", { + revs: true, + success: function(resp){ + resp._revisions.start.should.eql 2 + resp._revisions.ids.should.have_length 2 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should alert with an error message prefix' + db.openDoc("asdf"); + alert_msg.should.match /The document could not be retrieved/ + end + end + + describe 'saveDoc' + before_each + doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"}; + end + + it 'should save the document' + db.saveDoc(doc, { + success: function(resp, status){ + status.should.eql 201 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return ok true' + db.saveDoc(doc, { + success: function(resp, status){ + resp.ok.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return ID and revision of the document' + db.saveDoc(doc, { + success: function(resp, status){ + resp.id.should.be_a String + resp.id.should.have_length 32 + resp.rev.should.be_a String + resp.rev.length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should result in a saved document with generated ID' + db.saveDoc(doc, { + success: function(resp, status){ + db.openDoc(resp.id, { + success: function(resp2){ + resp2.Name.should.eql "Kara Thrace" + resp2.Callsign.should.eql "Starbuck" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should save the document with the specified ID' + doc._id = "123"; + db.saveDoc(doc, { + success: function(resp, status){ + resp.id.should.eql "123" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through the options' + db.saveDoc(doc, { + "batch" : "ok", + success: function(resp, status){ + // when using batch ok, couch sends a 202 status immediately + status.should.eql 202 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should alert with an error message prefix' + db.saveDoc("asdf"); + alert_msg.should.match /The document could not be saved/ + end + end + + describe 'bulkSave' + before_each + doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"}; + doc2 = {"Name" : "Karl C. Agathon", "Callsign" : "Helo"}; + doc3 = {"Name" : "Sharon Valerii", "Callsign" : "Boomer"}; + docs = [doc, doc2, doc3]; + end + + it 'should save all documents' + db.bulkSave({"docs": docs}); + db.allDocs({ + success: function(resp) { + resp.total_rows.should.eql 3 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should result in saved documents' + doc3._id = "789"; + db.bulkSave({"docs": [doc3]}); + + db.openDoc("789", { + success: function(resp){ + resp.Name.should.eql "Sharon Valerii" + resp.Callsign.should.eql "Boomer" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return ID and revision of the documents' + db.bulkSave({"docs": docs},{ + success: function(resp){ + resp[0].id.should.be_a String + resp[0].id.should.have_length 32 + resp[0].rev.should.be_a String + resp[0].rev.length.should.be_at_least 30 + resp[1].id.should.be_a String + resp[1].id.should.have_length 32 + resp[1].rev.should.be_a String + resp[1].rev.length.should.be_at_least 30 + resp[2].id.should.be_a String + resp[2].id.should.have_length 32 + resp[2].rev.should.be_a String + resp[2].rev.length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should save the document with specified IDs' + doc._id = "123"; + doc2._id = "456"; + docs = [doc, doc2, doc3]; + + db.bulkSave({"docs": docs},{ + success: function(resp){ + resp[0].id.should.eql "123" + resp[1].id.should.eql "456" + resp[2].id.should.have_length 32 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through the options' + // a lengthy way to test that a conflict can't be created with the + // all_or_nothing option set to false, but can be when it's true. + + var old_doc = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "123"}; + db.saveDoc(old_doc, { + success: function(resp){ + old_doc._rev = resp.rev; + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + + var new_doc = {"Name" : "Sasha", "Callsign" : "Kat", "_id" : "123"}; + + db.bulkSave({"docs": [new_doc], "all_or_nothing": false}, { + success: function(resp){ + resp[0].id.should.eql "123" + resp[0].error.should.eql "conflict" + resp[0].reason.should.eql "Document update conflict." + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + + db.bulkSave({"docs": [new_doc], "all_or_nothing": true}, { + success: function(resp){ + resp[0].id.should.eql "123" + resp[0].rev.should.not.eql old_doc._rev + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + + db.openDoc("123", { + "conflicts": true, + success: function(resp){ + resp._conflicts[0].should.eql old_doc._rev + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should alert with an error message prefix' + db.bulkSave("asdf"); + alert_msg.should.match /The documents could not be saved/ + end + end +end \ No newline at end of file diff --git a/_attachments/spec/jquery_couch_js_instance_methods_3_spec.js b/_attachments/spec/jquery_couch_js_instance_methods_3_spec.js new file mode 100644 index 0000000..5d27d81 --- /dev/null +++ b/_attachments/spec/jquery_couch_js_instance_methods_3_spec.js @@ -0,0 +1,540 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// Specs for jquery_couch.js lines 300-411 + +describe 'jQuery couchdb db' + before + stubAlert(); + end + + after + destubAlert(); + end + + before_each + db = $.couch.db('spec_db'); + db.create(); + end + + after_each + db.drop(); + end + + describe 'removeDoc' + before_each + doc = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "345"}; + saved_doc = {}; + db.saveDoc(doc, { + success: function(resp){ + saved_doc = resp; + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should result in a deleted document' + db.removeDoc({_id : "345", _rev : saved_doc.rev}, { + success: function(resp){ + db.openDoc("345", { + error: function(status, error, reason){ + status.should.eql 404 + error.should.eql "not_found" + reason.should.eql "deleted" + }, + success: function(resp){successCallback(resp)} + }); + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return ok true, the ID and the revision of the deleted document' + db.removeDoc({_id : "345", _rev : saved_doc.rev}, { + success: function(resp){ + resp.ok.should.be_true + resp.id.should.eql "345" + resp.rev.should.be_a String + resp.rev.length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should record the revision in the deleted document' + db.removeDoc({_id : "345", _rev : saved_doc.rev}, { + success: function(resp){ + db.openDoc("345", { + rev: resp.rev, + success: function(resp2){ + resp2._rev.should.eql resp.rev + resp2._id.should.eql resp.id + resp2._deleted.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should alert with an error message prefix' + db.removeDoc({_id: "asdf"}); + alert_msg.should.match /The document could not be deleted/ + end + end + + describe 'bulkRemove' + before_each + doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck", "_id" : "123"}; + doc2 = {"Name" : "Karl C. Agathon", "Callsign" : "Helo", "_id" : "456"}; + doc3 = {"Name" : "Sharon Valerii", "Callsign" : "Boomer", "_id" : "789"}; + docs = [doc, doc2, doc3]; + + db.bulkSave({"docs": docs}, { + success: function(resp){ + for (var i = 0; i < docs.length; i++) { + docs[i]._rev = resp[i].rev; + } + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should remove all documents specified' + db.bulkRemove({"docs": docs}); + db.allDocs({ + success: function(resp) { + resp.total_rows.should.eql 0 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should not remove documents that should not have been deleted' + db.bulkRemove({"docs": [doc3]}); + db.allDocs({ + success: function(resp) { + resp.total_rows.should.eql 2 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should result in deleted documents' + db.bulkRemove({"docs": docs}, { + success: function(resp){ + db.openDoc("123", { + error: function(status, error, reason){ + status.should.eql 404 + error.should.eql "not_found" + reason.should.eql "deleted" + }, + success: function(resp){successCallback(resp)} + }); + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should return the ID and the revision of the deleted documents' + db.bulkRemove({"docs": docs}, { + success: function(resp){ + resp[0].id.should.eql "123" + resp[0].rev.should.be_a String + resp[0].rev.length.should.be_at_least 30 + resp[1].id.should.eql "456" + resp[1].rev.should.be_a String + resp[1].rev.length.should.be_at_least 30 + resp[2].id.should.eql "789" + resp[2].rev.should.be_a String + resp[2].rev.length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should record the revision in the deleted documents' + db.bulkRemove({"docs": docs}, { + success: function(resp){ + db.openDoc("123", { + rev: resp[0].rev, + success: function(resp2){ + resp2._rev.should.eql resp[0].rev + resp2._id.should.eql resp[0].id + resp2._deleted.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should alert with an error message prefix' + db.bulkRemove({docs: ["asdf"]}); + alert_msg.should.match /The documents could not be deleted/ + end + end + + describe 'copyDoc' + before_each + doc = {"Name" : "Sharon Agathon", "Callsign" : "Athena", "_id" : "123"}; + db.saveDoc(doc); + end + + it 'should result in another document with same data and new id' + db.copyDoc("123", { + success: function(resp){ + resp.id.should.eql "456" + resp.rev.length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }, { + headers: {"Destination":"456"} + }); + + db.openDoc("456", { + success: function(resp){ + resp.Name.should.eql "Sharon Agathon" + resp.Callsign.should.eql "Athena" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should throw an error when trying to overwrite a document without providing a revision' + doc2 = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "456"}; + db.saveDoc(doc2); + + db.copyDoc("123", { + error: function(status, error, reason){ + status.should.eql 409 + error.should.eql "conflict" + reason.should.eql "Document update conflict." + }, + success: function(resp){successCallback(resp)} + }, { + headers: {"Destination":"456"} + }); + end + + it 'should overwrite a document with the correct revision' + doc2 = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "456"}; + var doc2_rev; + db.saveDoc(doc2, { + success: function(resp){ + doc2_rev = resp.rev; + } + }); + + db.copyDoc("123", { + success: function(resp){ + resp.id.should.eql "456" + resp.rev.length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }, { + headers: {"Destination":"456?rev=" + doc2_rev} + }); + + db.openDoc("456", { + success: function(resp){ + resp.Name.should.eql "Sharon Agathon" + resp.Callsign.should.eql "Athena" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should alert with an error message prefix' + db.copyDoc("asdf", {}, {}); + alert_msg.should.match /The document could not be copied/ + end + end + + describe 'query' + before_each + db.saveDoc({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"}); + db.saveDoc({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"}); + db.saveDoc({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"}); + map_function = "function(doc) { emit(doc._id, 1); }"; + reduce_function = "function(key, values, rereduce) { return sum(values); }"; + end + + it 'should apply the map function' + db.query(map_function, null, null, { + success: function(resp){ + resp.rows.should.have_length 3 + resp.rows[0].id.should.eql "123" + resp.rows[0].key.should.eql "123" + resp.rows[0].value.should.eql 1 + resp.rows[1].id.should.eql "456" + resp.rows[1].key.should.eql "456" + resp.rows[1].value.should.eql 1 + resp.rows[2].id.should.eql "789" + resp.rows[2].key.should.eql "789" + resp.rows[2].value.should.eql 1 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should apply the reduce function' + db.query(map_function, reduce_function, null, { + success: function(resp){ + resp.rows.should.have_length 1 + resp.rows[0].key.should.be_null + resp.rows[0].value.should_eql 3 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through the options' + db.query(map_function, null, null, { + "startkey": "456", + success: function(resp){ + resp.rows.should.have_length 2 + resp.rows[0].id.should.eql "456" + resp.rows[0].key.should.eql "456" + resp.rows[0].value.should.eql 1 + resp.rows[1].id.should.eql "789" + resp.rows[1].key.should.eql "789" + resp.rows[1].value.should.eql 1 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through the keys' + //shouldn't this better work? TODO: implement in jquery.couch.js + console.log("shouldn't this better work? TODO: implement in jquery.couch.js") + db.query(map_function, null, null, { + "keys": ["456", "123"], + success: function(resp){ + resp.rows.should.have_length 2 + resp.rows[0].id.should.eql "456" + resp.rows[0].key.should.eql "456" + resp.rows[0].value.should.eql 1 + resp.rows[1].id.should.eql "123" + resp.rows[1].key.should.eql "123" + resp.rows[1].value.should.eql 1 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through the options and the keys' + //shouldn't this better work? TODO: implement in jquery.couch.js + console.log("shouldn't this better work? TODO: implement in jquery.couch.js") + db.query(map_function, null, null, { + "include_docs":"true", + "keys": ["456"], + success: function(resp){ + resp.rows.should.have_length 1 + resp.rows[0].id.should.eql "456" + resp.rows[0].key.should.eql "456" + resp.rows[0].value.should.eql 1 + resp.rows[0].doc["job"].should.eql "pilot" + resp.rows[0].doc["_rev"].length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should apply a query in erlang also' + // when this test fails, read this: http://wiki.apache.org/couchdb/EnableErlangViews + var erlang_map = 'fun({Doc}) -> ' + + 'ID = proplists:get_value(<<"_id">>, Doc, null), ' + + 'Emit(ID, 1) ' + + 'end.'; + db.query(erlang_map, null, "erlang", { + success: function(resp){ + resp.rows.should.have_length 3 + resp.rows[0].id.should.eql "123" + resp.rows[0].key.should.eql "123" + resp.rows[0].value.should.eql 1 + resp.rows[1].id.should.eql "456" + resp.rows[1].key.should.eql "456" + resp.rows[1].value.should.eql 1 + resp.rows[2].id.should.eql "789" + resp.rows[2].key.should.eql "789" + resp.rows[2].value.should.eql 1 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should alert with an error message prefix' + db.query("asdf"); + alert_msg.should.match /An error occurred querying the database/ + end + end + + describe 'view' + before_each + db.saveDoc({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"}); + db.saveDoc({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"}); + db.saveDoc({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"}); + view = { + "views" : { + "people" : { + "map" : "function(doc) { emit(doc._id, doc.Name); }" + } + }, + "_id" : "_design/spec_db" + }; + db.saveDoc(view); + end + + it 'should apply the view' + db.view('spec_db/people', { + success: function(resp){ + resp.rows.should.have_length 3 + resp.rows[0].id.should.eql "123" + resp.rows[0].key.should.eql "123" + resp.rows[0].value.should.eql "Felix Gaeta" + resp.rows[1].id.should.eql "456" + resp.rows[1].key.should.eql "456" + resp.rows[1].value.should.eql "Samuel T. Anders" + resp.rows[2].id.should.eql "789" + resp.rows[2].key.should.eql "789" + resp.rows[2].value.should.eql "Cally Tyrol" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through the options' + db.view('spec_db/people', { + "skip":"2", + success: function(resp){ + resp.rows.should.have_length 1 + resp.rows[0].id.should.eql "789" + resp.rows[0].key.should.eql "789" + resp.rows[0].value.should.eql "Cally Tyrol" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through the keys' + db.view('spec_db/people', { + "keys":["456", "123"], + success: function(resp){ + resp.rows.should.have_length 2 + resp.rows[0].id.should.eql "456" + resp.rows[0].key.should.eql "456" + resp.rows[0].value.should.eql "Samuel T. Anders" + resp.rows[1].id.should.eql "123" + resp.rows[1].key.should.eql "123" + resp.rows[1].value.should.eql "Felix Gaeta" + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should pass through the options and the keys' + db.view('spec_db/people', { + "include_docs":"true", + "keys":["456"], + success: function(resp){ + resp.rows.should.have_length 1 + resp.rows[0].id.should.eql "456" + resp.rows[0].key.should.eql "456" + resp.rows[0].value.should.eql "Samuel T. Anders" + resp.rows[0].doc["job"].should.eql "pilot" + resp.rows[0].doc["_rev"].length.should.be_at_least 30 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should throw a 404 when the view doesnt exist' + db.view('spec_db/non_existing_view', { + error: function(status, error, reason){ + status.should.eql 404 + error.should.eql "not_found" + reason.should.eql "missing_named_view" + }, + success: function(resp){successCallback(resp)} + }); + end + + it 'should alert with an error message prefix' + db.view("asdf"); + alert_msg.should.match /An error occurred accessing the view/ + end + end + + describe 'setDbProperty' + it 'should return ok true' + db.setDbProperty("_revs_limit", 1500, { + success: function(resp){ + resp.ok.should.be_true + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should set a db property' + db.setDbProperty("_revs_limit", 1500); + db.getDbProperty("_revs_limit", { + success: function(resp){ + resp.should.eql 1500 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + db.setDbProperty("_revs_limit", 1200); + db.getDbProperty("_revs_limit", { + success: function(resp){ + resp.should.eql 1200 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should alert with an error message prefix' + db.setDbProperty("asdf"); + alert_msg.should.match /The property could not be updated/ + end + end + + describe 'getDbProperty' + it 'should get a db property' + db.setDbProperty("_revs_limit", 1200); + db.getDbProperty("_revs_limit", { + success: function(resp){ + resp.should.eql 1200 + }, + error: function(status, error, reason){errorCallback(status, error, reason)} + }); + end + + it 'should throw a 404 when the property doesnt exist' + db.getDbProperty("_doesnt_exist", { + error: function(status, error, reason){ + status.should.eql 404 + error.should.eql "not_found" + reason.should.eql "missing" + }, + success: function(resp){successCallback(resp)} + }); + end + + it 'should alert with an error message prefix' + db.getDbProperty("asdf"); + alert_msg.should.match /The property could not be retrieved/ + end + end +end \ No newline at end of file diff --git a/_attachments/spec/run.html b/_attachments/spec/run.html new file mode 100644 index 0000000..e438333 --- /dev/null +++ b/_attachments/spec/run.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + +

        JSpec

        +
        +
        + + diff --git a/_attachments/style/layout.css b/_attachments/style/layout.css new file mode 100644 index 0000000..0953a74 --- /dev/null +++ b/_attachments/style/layout.css @@ -0,0 +1,1089 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + +*/ + +/* General styles */ + +html, body { color: #000; font: normal 90% arial, sans-serif; + height: 100%; margin: 0; padding: 0; overflow: hidden; +} + +div.spacer {clear:both;} + +th { font-weight: ;} + +:link, :visited { color: #ba1e16; text-decoration: none; } +:link img, :visited img { border: none; } +span.more { text-align: center; color: #ba1e16; cursor:pointer;} + +h1 { background: #333; border-right: 2px solid #111; + border-bottom: 1px solid #333; color: #999; + font: normal 125% arial, sans-serif; height: 32px; + line-height: 2.2em; margin: 0; padding: 0 0 0 .5em; position: relative; +} +h1 :link, h1 :visited, h1 strong { padding: .4em .5em; } +h1 :link, h1 :visited { + background: url(../image/path.gif) 100% 50% no-repeat; + color: #bbb; cursor: pointer; padding-right: 2.2em; + text-shadow: #333 2px 2px 1px; +} +h1 strong { color: #fff; font-weight: normal; padding-right: 25px; } +h1 strong a {color:#fff !important;background:none !important;} +h1 :link.raw, h1 :visited.raw { + background: url(../image/rarrow.png) 100% 50% no-repeat; position: absolute; + right: 20px; width: 35px; height: 100%; padding: 0; margin: 0; +} +h1 span#raw-link { + background: url(../image/rarrow.png) 100% 50% no-repeat; position: absolute; + right: 20px; width: 35px; height: 100%; padding: 0; margin: 0; + cursor: pointer; +} +body.loading h1 strong { + background: url(../image/spinner.gif) right center no-repeat; +} + +hr { border: 1px solid #999; border-width: 1px 0 0; } +dl dt { font-weight: bold; } +code, tt, pre { + font-family: "DejaVu Sans Mono",Menlo,Courier,monospace; +} +code.key { color: #333; font-weight: bold; } +code.string { color: #393; } +code.number, code.boolean { color: #339; } +code.null { color: #666; } + +button { font-size: 100%; -webkit-appearance: square-button; } +button[disabled] { color: #999; } +input, select, textarea { background: #fff; border: 1px solid; + border-color: #999 #ddd #ddd #999; color: #000; margin: 0; padding: 1px; +} +input.placeholder { color: #999; } +textarea { + font-family: "DejaVu Sans Mono",Menlo,Courier,monospace; + font-size: 100%; +} +fieldset { border: none; font-size: 95%; margin: 0; padding: .2em 0 0; } +fieldset legend { color: #666; font-weight: bold; padding: 0; } +fieldset input, fieldset select { font-size: 95%; } +fieldset p { margin: .4em; } + +p.help { color: #999; font-size: 90%; margin: 0 2em 1em; } + +/* Topbar */ +#topbar button { background: transparent 2px 2px no-repeat; border: none; + color: #666; margin: 0; padding: 2px 0em 2px 22px; cursor: pointer; + font-size: 95%; line-height: 16px; +} +#topbar button:hover { background-position: 2px -30px; color: #000; } +#topbar button:active { background-position: 2px -62px; color: #000; } +#topbar button.down { background-image: url(../image/down.png); } + +#topbar #current-db { position:relative} +#topbar ul#all-dbs { display: none; background: white; position: absolute; + min-width: 150px; left: 0; top: 30px; z-index: 100; + -moz-box-shadow: 0 1px 5px #333; -moz-border-radius: 5px; + border: 1px solid #ccc; color: black; + font-size: 80%; line-height: 1.5em; list-style: none; margin:0; + padding:0.5em 0; +} +#topbar strong ul#all-dbs li :link, #topbar strong ul#all-dbs li :visited { + display:block;padding:0.1em 1em; + color:black !important;text-shadow:none;white-space:nowrap; +} +#topbar strong ul#all-dbs li.current :link, #topbar strong ul#all-dbs li.current :visited { + color:#BA1E16 !important; +} +#topbar strong ul#all-dbs li a:hover { background:#E9E9E9 !important; } + +#topbar ul.tabs { float:right;margin-right:65px;margin-top:4px; + text-transform:lowercase;font-size:80%; +} +#topbar ul.tabs li { float:left;list-style:none;margin-right:8px} +#topbar ul.tabs li.current { background:white} +#topbar ul.tabs li a { background:none;padding:0 8px;text-shadow:none;display:block;float:left} +#topbar ul.tabs li.current a {color:black} + +/* Tabular listings */ + +table.listing { border-collapse: separate; border-spacing: 0; + border: 1px solid #a7a7a7; clear: both; width: 100%; +} +table.listing caption { display: none; } +table.listing th, table.listing td { padding: .2em .5em; } +table.listing thead th { background: #dadada url(../image/thead.gif) repeat-x; + border: 1px solid #a7a7a7; border-width: 0 0 1px 1px; color: #333; + font-size: 95%; text-align: left; + text-shadow: #999 2px 1px 2px; white-space: nowrap; +} +table.listing thead th:first-child { border-left: none; } +table.listing thead th.key { + background: #a7afb6 url(../image/thead-key.gif) 0 0 repeat-x; + padding-top: 2px; +} +table.listing thead th.key span { + background: url(../image/order-asc.gif) 100% 3px no-repeat; cursor: pointer; + padding-right: 20px; +} +table.listing thead th.desc span { + background-image: url(../image/order-desc.gif); +} +table.listing tbody tr th, table.listing tbody tr td { background: #feffea; } +table.listing tbody tr.odd th, table.listing tbody tr.odd td, +table.listing tbody.odd tr th, table.listing tbody.odd tr td { + background: #fff; +} +table.listing tbody th, table.listing tbody td { + border-left: 1px solid #d9d9d9; padding: .4em .5em; vertical-align: top; +} +table.listing tbody th:first-child, table.listing tbody td:first-child { + border-left: none; +} +table.listing tbody th { text-align: left; } +table.listing tbody th :link, table.listing tbody th :visited { + display: block; +} +table.listing tbody.footer tr td { background: #e9e9e9; + border-top: 1px solid #a7a7a7; color: #999; font-size: 90%; + line-height: 1.8em; +} +table.listing tbody.footer #paging { float: right; } +table.listing tbody.footer #paging a, +table.listing tbody.footer #paging label { + padding: 0 .5em; +} +table.listing tbody.footer #paging label { color: #666; } +table.listing tbody.footer #paging select { font-size: 90%; padding: 0; } + +/* Inline editing */ + +span.editinline-tools { margin: 2px 2px 0; float: right; margin-right: -45px; } +span.editinline-tools button { background: transparent 0 0 no-repeat; + border: none; cursor: pointer; display: block; float: left; margin: 0 .2em; + width: 11px; height: 11px; +} +span.editinline-tools button:hover { background-position: 0 -22px; } +span.editinline-tools button:active { background-position: 0 -44px; } +span.editinline-tools button.apply { + background-image: url(../image/apply.gif); +} +span.editinline-tools button.cancel { + background-image: url(../image/cancel.gif); +} + +/* Resizer grippies */ + +div.grippie { background: #e9e9e9 url(../image/grippie.gif) 50% 50% no-repeat; + border: 1px solid #aaa; border-top: none; min-height: 10px; +} + +/* Suggest results */ + +ul.suggest-dropdown { border: 1px solid #999; background-color: #eee; + padding: 0; margin: 0; list-style: none; opacity: .85; + -moz-box-shadow: 2px 2px 10px #333; -webkit-box-shadow: 2px 2px 10px #333; +} +ul.suggest-dropdown li { padding: 2px 5px; white-space: nowrap; color: #101010; + text-align: left; +} +ul.suggest-dropdown li.selected { cursor: pointer; background: Highlight; + color: HighlightText; +} + +/* Logo & Navigation */ + +#sidebar { background: #fff; position: absolute; top: 0; right: -210px; + width: 210px; height: 100%; +} +body.fullwidth #sidebar { border-bottom: 1px solid #333; right: 0; + width: 26px; +} +#sidebar-toggle { background: url(../image/sidebar-toggle.png) 0 0 no-repeat; + color: #999; cursor: pointer; display: block; position: absolute; right: 0; + top: 0; font-size: 110%; width: 26px; height: 32px; text-indent: -9999px; + cursor:pointer; +} +.sidebar-toggle-expand { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); +} + +#sidebar-toggle:hover { background-position: -26px 0; } +#sidebar-toggle:focus { outline: none; } +#sidebar.hidden #sidebar-toggle { background-position: 0 -32px; } +#sidebar.hidden #sidebar-toggle:hover { background-position: -26px -32px; } + +#logo { margin: 30px 0 0; padding: 0 18px 10px; } + +#nav { color: #333; font-size: 110%; font-weight: bold; list-style: none; + margin: 0; overflow: auto; overflow-x: hidden; padding: 0; width: 210px; +} +#nav ul { list-style: none; margin: 0; padding: 0; } +#nav li { color: #999; margin: 5px 0 0; padding: 3px 0; } +#nav li span { padding: 0 20px; } +#nav li.selected { background: #e9e9e9; } +#nav li li { font-size: 90%; font-weight: normal; margin: 0; + padding: 2px 20px 2px 40px; +} +#nav li li:hover { background: #e4e4e4; } +#nav li.selected li:hover { background: #d7d7d7; } +#nav li li :link, #nav li li :visited { color: #333; display: block; + overflow: hidden; text-decoration: none; text-overflow: ellipsis; +} +#nav li li :link:hover, #nav li li :visited:hover { color: #000; } +#nav li li :link:focus, #nav li li :visited:focus { outline: none; } +#nav li li.selected { background: #aaa !important; border-top: 1px solid #999; + color: #fff; padding-top: 1px; +} +#nav li li.selected :link, #nav li li.selected :visited { color: #fff; } +#nav li li.selected :link:hover, #nav li li.selected :visited:hover { + color: #fff; +} +#nav li button { background: transparent 0 0 no-repeat; border: none; + cursor: pointer; width: 15px; height: 15px; margin-left: -20px; + position: absolute; vertical-align: top; +} +#nav li li:hover button.remove { + background-image: url(../image/delete-mini.png); +} +#nav li button.remove:hover { background-position: -15px 0; } + +#footer { background: #ddd; border-top: 1px solid #bbb; color: #000; + font-size: 80%; opacity: .7; padding: 5px 10px; position: absolute; right: 0; + bottom: 0; min-height: 1.3em; width: 190px; text-align: right; +} +#footer .couch :link, #footer .couch :visited { color: #000; } + +#userCtx span { display:none; } + +#wrap { background: #fff url(../image/bg.png) 100% 0 repeat-y; + height: 100%; margin-right: 210px; position: relative; +} +body.fullwidth #wrap { margin-right: 0; } +#content { padding: 0.6em 16px 3em 10px; overflow: auto; overflow-y: scroll; + position: absolute; top: 33px; bottom: 0; left: 0; right: 0; +} + +/* Toolbar */ + +#toolbar { font-size: 90%; line-height: 16px; list-style: none; + margin: 0 0 .5em; padding: 1px 5px 5px 3px; +} +#toolbar li { display: inline; } +#toolbar li.current {float:right;} +#toolbar button { background: transparent 2px 2px no-repeat; border: none; + color: #666; margin: 0; padding: 2px 0em 2px 22px; cursor: pointer; + font-size: 95%; line-height: 16px; +} +#toolbar button:hover { background-position: 2px -30px; color: #000; } +#toolbar button:active { background-position: 2px -62px; color: #000; } +#toolbar button.add { background-image: url(../image/add.png); } +#toolbar button.security { background-image: url(../image/key.png); } +#toolbar button.compact { background-image: url(../image/compact.png); float:right;} +#toolbar button.delete { background-image: url(../image/delete.png); } +#toolbar button.load { background-image: url(../image/load.png); } +#toolbar button.run { background-image: url(../image/run.png); } +#toolbar button.save { background-image: url(../image/save.png); } +#toolbar button.share { background-image: url(../image/compact.png); } + +/* Dialogs */ + +#overlay { background: #bbb; cursor: wait; position: fixed; width: 100%; + height: 100%; top: 0; left: 0; +} +*html #overlay { position: absolute; + width: expression(document.body.clientWidth + 'px'); + height: expression(document.body.clientHeight + 'px'); +} +#dialog { background: #333 url(../image/progress.gif) 50% 50% no-repeat; + color: #f4f4f4; overflow: hidden; opacity: .95; max-width: 33em; + padding: 1em 1em 0; -moz-border-radius: 7px; + -moz-box-shadow: 4px 4px 6px #333; -webkit-border-radius: 7px; + -webkit-box-shadow: 4px 4px 6px #333; +} +*html #dialog { width: 33em; } +#dialog.loading { width: 220px; height: 80px; } +#dialog.loaded { background-image: none; } +#dialog h2 { background: #666 98% 50% no-repeat; + border-top: 1px solid #555; border-bottom: 1px solid #777; color: #ccc; + font-size: 110%; font-weight: bold; margin: 0 -1em; padding: .35em 1em; +} +body.loading #dialog h2 { + background-image: url(../image/spinner.gif); +} +#dialog h3 { color: #ccc; font-size: 100%; font-weight: bold; margin: 0 -2em; + padding: .35em 2em 0; +} +#dialog fieldset { background: #222; border-top: 1px solid #111; + margin: 0 0 1em; padding: .5em 1em 1em; + -moz-border-radius-bottomleft: 7px; -moz-border-radius-bottomright: 7px; + -webkit-border-bottom-left-radius: 7px; + -webkit-border-bottom-right-radius: 7px; +} +#dialog p.help { color: #bbb; font-size: 95%; margin: 0 0 1em; } +#dialog fieldset table { margin-top: 1em; } +#dialog fieldset th, #dialog fieldset td { padding: .5em; + vertical-align: top; +} +#dialog fieldset th { color: #999; font-weight: bold; + text-align: right; +} +#dialog fieldset input { background-color: #e9e9e9; vertical-align: middle; } +#dialog fieldset input.error { background-color: #f9e4e4; } +#dialog fieldset div.error { padding-top: .3em; color: #b33; } +#dialog fieldset.radiogroup { padding-top: 1em; } +#dialog fieldset.radiogroup label { position: relative; padding-left: 25px; } +#dialog fieldset.radiogroup input { position: absolute; left: 5px; top: 2px; } +#dialog fieldset.radiogroup p.help { margin-top: .5em; margin-left: 25px; } +#dialog fieldset.radiogroup hr { border-color: #333; margin-left: 25px; } +#dialog .buttons { padding: 0 .5em .5em; text-align: right; } +#dialog .buttons button { background: #444; border: 1px solid #aaa; + color: #ddd; cursor: pointer; font-size: 90%; font-weight: normal; + margin: 0 0 0 5px; padding: .2em 2em; -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} +#dialog .buttons button[type=submit] { font-weight: bold; } +#dialog .buttons button:hover { background: #555; } +#dialog .buttons button:active { background: #333; color: #fff; } + +#dialog fieldset td#progress { + background: url(../image/progress.gif) 50% 50% no-repeat; + visibility: hidden; +} + +/* Document quick jump */ + +#jumpto { float: right; padding: 0 5px; line-height: 16px; + font-weight: bold; color: #666; } + +#jumpto input { padding:2px } + +/* View selector */ + +#switch { color: #666; float: right; font-size: 90%; font-weight: bold; + line-height: 16px; padding: 5px; +} +#changes { color: #666; float: right; font-size: 90%; font-weight: bold; + line-height: 16px; ; +} +#switch select { font-size: 90%; } + +/* Stale views checkbox */ + +#staleviews { + color: #666; float: right; font-size: 90%; + font-weight: bold; line-height: 16px; padding: 5px; +} + +/* View function editing */ + +#viewcode { background: #fff; border: 1px solid; + border-color: #999 #ddd #ddd #999; margin: 0 0 1em; overflow: hidden; +} +#viewcode .top, #viewcode .bottom { background-color: #e9e9e9; + border: 1px solid; border-color: #ddd #ddd #e9e9e9 #ddd; color: #333; + padding: 0 .5em 2px; +} +#viewcode .top { border-bottom: 1px solid #ddd; color: #aaa; font-size: 95%; } +#viewcode .top span { border: none; color: #666; cursor: pointer; + display: block; font-size: 90%; margin: 0; padding: 2px 0 0; +} +#viewcode .top span#view-toggle { + background: url(../image/twisty.gif) 0 -96px no-repeat; padding-left: 15px; +} +#viewcode.collapsed .top span#view-toggle { background-position: 0 4px; } +#viewcode .top a { float: right; font-size: 90%; line-height: 1.4em; + padding: 2px 2px 0 0; +} +#viewcode .top a:link, #viewcode .top a:visited { color: #999; } +#viewcode table { border: none; border-collapse: separate; border-spacing: 0; + margin: 0; table-layout: fixed; width: 100%; max-width: 100%; +} +#viewcode table td { border: none; padding: 0; } +#viewcode table td.splitter { background: #e9e9e9; width: 4px; } +#viewcode table td.map { border-right: 1px solid #ccc; } +#viewcode table td.reduce { border-left: 1px solid #ccc; } +#viewcode .code label { font-size: 90%; color: #999; padding: 0 .5em; + white-space: nowrap; +} +#viewcode .code textarea { border: none; border-top: 1px solid #ccc; + color: #333; font-size: 11px; margin: 0; min-height: 50px; overflow: auto; + padding: .4em 0 0; resize: none; width: 100%; +} +#viewcode .code textarea:focus { background: #e9f4ff; } +#viewcode .bottom { border-bottom: none; clear: left; padding: 1px 3px; } +#viewcode .bottom button { font-size: 90%; margin: 0 1em 0 0; + padding-left: 2em; padding-right: 2em; +} +*html #viewcode .bottom button { padding: 0 .5em; } +*+html #viewcode .bottom button { padding: 0 .5em; } +#viewcode .bottom button.revert, #viewcode .bottom button.save, +#viewcode .bottom button.saveas { + float: right; margin: 0 0 0 1em; +} +#viewcode .bottom button.save { font-weight: bold; } +#viewcode .bottom label { color: #666; font-size: 90%; } +#viewcode .grippie { background-position: 50% 50%; } +#viewcode.collapsed { background: #e9e9e9; } +#viewcode.collapsed .top { border-bottom: none; } +#viewcode.collapsed .top span { background-position: 0 3px; } +#viewcode.collapsed table, #viewcode.collapsed .bottom { display: none; } + +#tempwarn { display: none; font-size: 90%; margin: 0 2em 1.5em; } +#grouptruenotice { display: none; font-size: 90%; margin: 1ex 2em 1.5em; } + +/* Database table */ + +#databases thead th.size, #databases thead th.count, #databases thead th.seq, +#databases tbody td.size, #databases tbody td.count, #databases tbody td.seq { + text-align: right; +} + +div#dbinfo {margin-bottom:8px;text-align:center} +.dbquery-select {float:right;} +#pagination {float:left;} +#pages-input {width:18px;} +span.to-views { color: #FF0000; font-weight: bold; + line-height: 16px; padding: 5px; +} +span.query-option { float:right; margin-left:8px;} +input.query-option { float:right; margin-left:5px; width:70px;} +div.info-title { display:inline; color: #666; margin-right:8px; } +div.info-value { display:inline; margin-right:30px; } + +/* Changes table */ + +#changes thead th label { color: #333; float: right; font-size: 90%; + text-shadow: none; +} +#changes thead th label.disabled { color: #777; } +#changes thead th label input { vertical-align: middle; } +#changes tbody.content td { color: #999; + font: normal 11px "DejaVu Sans Mono",Menlo,Courier,monospace; +} +#changes tbody.content td.key { color: #333; } +#changes tbody.content td.key a { display: block; } +#changes tbody.content td.key a strong { font-weight: normal; } +#changes tbody.content td.key span.docid { color: #999; + font: normal 10px arial, sans-serif; +} +#changes tbody.content td.value { font-size: 10px; } + +/* Documents table */ + +#documents thead th { line-height: 150%; } +#documents thead th label { color: #333; float: right; font-size: 90%; + text-shadow: none; +} +#documents thead th label.disabled { color: #777; } +#documents thead th label input { vertical-align: middle; } +#documents thead th label input[type=range] { width: 7em; } +#documents thead th label output { width: 4em; display: inline-block; } +#documents tbody.content td { color: #999; + font: normal 11px "DejaVu Sans Mono",Menlo,Courier,monospace; +} +#documents tbody.content td.key { color: #333; } +#documents tbody.content td.key a { display: block; } +#documents tbody.content td.key a strong { font-weight: normal; } +#documents tbody.content td.key span.docid { color: #999; + font: normal 10px arial, sans-serif; +} +#documents tbody.content td.value { font-size: 10px; } + +/* Document display tabs */ + +#tabs { float: right; list-style: none; margin: -1.4em 0 0; } +#tabs li { display: inline; font-size: 95%; padding: 0; } +#tabs li.active { font-weight: bold; } +#tabs :link, #tabs :visited { background: #dadada; color: #666; + border: 1px solid #a7a7a7; float: left; margin: 0 0 0 .5em; + padding: .5em 2em .3em; position: relative; top: 1px; +} +#tabs .active :link, #tabs .active :visited { background: #e9e9e9; + border-bottom-color: #e9e9e9; color: #333; +} +#tabs :link:focus, #tabs :visited:focus { outline: none; } + +/* Document fields table */ + +#fields { clear: right; table-layout: fixed; } +#fields col.field { width: 33%; } +#fields tbody.content th { padding-left: 25px; padding-right: 48px; } +#fields tbody.content th button.delete { + background: url(../image/delete-mini.png) no-repeat; border: none; + cursor: pointer; float: left; margin: .2em 5px 0 -20px; padding: 0; + width: 15px; height: 15px; +} +#fields tbody.content th button.delete:hover { background-position: -15px 0; } +#fields tbody.content th b { display: block; padding: 2px 2px 2px 3px; } +#fields tbody.content th b.editinline-container { padding: 0; } +#fields tbody.content td { color: #999; padding-left: 14px; + padding-right: 48px; +} +#fields tbody.content td code { display: block; font-size: 11px; + padding: 2px 2px 2px 3px; position: relative; +} +#fields tbody.content td code.string { white-space: pre-wrap; } +#fields tbody.content td code.string:before { color: #ccc; content: "“"; + position: absolute; left: -4px; +} +#fields tbody.content td code.string:after { color: #ccc; content: "”"; } + +#fields tbody.content td dl { margin: 0; padding: 0; } +#fields tbody.content td dt { + background: transparent url(../image/toggle-collapse.gif) 0 3px no-repeat; + clear: left; color: #333; cursor: pointer; line-height: 1em; + margin-left: -12px; padding-left: 14px; +} +#fields tbody.content td dd { line-height: 1em; margin: 0; + padding: 0 0 0 1em; +} +#fields tbody.content td dt.collapsed { + background-image: url(../image/toggle-expand.gif); +} +#fields tbody.content td dt.inline { background-image: none; cursor: default; + float: left; margin-left: 0; padding-left: 2px; padding-right: .5em; + padding-top: 2px; +} +#fields tbody.content td dd code.string { left: 4px; text-indent: -6px; } +#fields tbody.content td dd code.string:before { position: static; } +#fields tbody.content input, #fields tbody.content textarea, +#fields tbody.source textarea { + background: #fff; border: 1px solid; border-color: #999 #ddd #ddd #999; + margin: 0; padding: 1px; width: 100%; +} +#fields tbody.content th input { font-family: inherit; font-size: inherit; + font-weight: bold; +} +#fields tbody.content td input, #fields tbody.content td textarea, +#fields tbody.source textarea { + font: normal 11px "DejaVu Sans Mono",Menlo,Courier,monospace; +} +#fields tbody.content input.invalid, +#fields tbody.content textarea.invalid, +#fields tbody.source textarea.invalid { + background: #f9f4f4; border-color: #b66 #ebb #ebb #b66; +} +#fields tbody.content div.grippie, #fields tbody.source div.grippie { + padding: 0 1px; width: 100%; +} +#fields tbody.content div.error, #fields tbody.source div.error { + color: #d33; +} + +#fields tbody.content td ul.attachments { list-style: none; margin: 0; + padding: 0; +} +#fields tbody.content td ul.attachments li { + margin-bottom: .3em; min-height: 20px; padding-left: 20px; +} +#fields tbody.content td ul.attachments tt { font-size: 11px; } +#fields tbody.content td ul.attachments li span.info { color: #666; + display: block; font-size: 95%; +} +#fields tbody.content td ul.attachments li button { + background: transparent no-repeat; border: none; cursor: pointer; + float: left; margin: 0 2px 0 -20px; padding: 0; width: 15px; height: 15px; + vertical-align: middle; +} +#fields tbody.content td ul.attachments li button:hover { + background-position: -15px 0; +} +#fields tbody.content td ul.attachments li button.delete { + background-image: url(../image/delete-mini.png); +} +#fields tbody.source td pre { color: #999; font-size: 11px; line-height: 1.6em; + margin: 0; overflow: auto; white-space: pre-wrap; width: 100%; +} +#fields tbody.source td.editinline-container { padding-left: 14px; padding-right: 48px; } + +/* Test suite */ + +#tests { table-layout: fixed; } +#tests thead th.name { width: 20%; } +#tests thead th.status { padding-left: 20px; width: 10em; } +#tests thead th.duration { text-align: right; width: 7em; } +#tests tbody.content th { cursor: help; padding-left: 25px; + white-space: nowrap; +} +#tests tbody.content th button.run { + background: url(../image/run-mini.png) no-repeat; border: none; + cursor: pointer; float: left; margin: .2em 5px 0 -20px; padding: 0; + width: 15px; height: 15px; +} +#tests tbody.content th button.run:hover { background-position: -15px 0; } +#tests tbody.content td.duration { text-align: right; width: 6em; } +#tests tbody.content td.status { background-position: 5px 8px; + background-repeat: no-repeat; color: #999; padding-left: 20px; +} +#tests tbody.content td.details { width: 50%; } +#tests tbody.content td.details a { border-bottom: 1px dashed #ccc; + color: #999; float: right; font-size: 85%; +} +#tests tbody.content td.details ol { color: #999; margin: 0; + padding: 0 0 0 1.5em; +} +#tests tbody.content td.details ol b { color: #333; font-weight: normal; } +#tests tbody.content td.details ol code { color: #c00; font-size: 100%; } +#tests tbody.content td.details ol code.error { white-space: pre; } +#tests tbody.content td.running { + background-image: url(../image/running.png); color: #333; +} +#tests tbody.content td.success, span.success { + background-image: url(../image/test_success.gif) no-repeat; color: #060; +} +#tests tbody.content td.error, #tests tbody.content td.failure, span.failure { + background-image: url(../image/test_failure.gif) no-repeat; color: #c00; +} + +/* Configuration */ + +#config tbody th { background: #e6e6e6; border-right: none; + border-top: 1px solid #d9d9d9; +} +#config tbody td.name { border-left: 1px solid #d9d9d9; color: #333; + font-weight: bold; +} +#config tbody td.value { padding: 1px 48px 1px 1px; } +#config tbody td.value code { display: block; font-size: 11px; + padding: 2px 2px 2px 3px; +} +#config tbody td.value code.editinline-container { padding: 0; } +#config tbody td input { + background: #fff; border: 1px solid; border-color: #999 #ddd #ddd #999; + font: normal 11px "DejaVu Sans Mono",Menlo,Courier,monospace; + margin: 0; padding: 1px; width: 100%; +} + +/* Replication */ + +form#replicator { background: #f4f4f4; border: 1px solid; + border-color: #999 #ccc #ccc #999; margin: .5em 1em 1.5em; padding: .5em; + -moz-border-radius: 7px; -webkit-border-radius: 7px; +} +form#replicator fieldset { float: left; padding: 1px; } +form#replicator p.swap { float: left; margin: 2em 0 0; padding: 1px 1em; } +form#replicator p.swap button { background: transparent; border: none; + color: #666; cursor: pointer; font-size: 150%; +} +form#replicator p.swap button:hover { color: #000; } +form#replicator p.actions { padding: 1px; clear: left; margin: 0; + text-align: right; +} + +/* Stats */ + +div.stat-section {font-weight:bold; font-size:18px;} +div.stat-subsection {font-weight:bold; font-size:14px; padding-bottom:1px; padding-top:3px;} +span.stat-title {font-weight:bold; font-size:12px; float:left; color: #ACACAC;} +span.stat-value {font-size:12px; float:left; padding-left:5px; padding-right:20px;} +span.stat-subsection-description {font-size:12px; font-weight:normal; padding-left:8px;} + +/* Active tasks */ + +#interval { color: #666; float: right; font-size: 90%; font-weight: bold; + line-height: 16px; padding: 5px; +} +#interval input { vertical-align: top; } +#interval .secs { display: inline-block; width: 2em; text-align: right; } + +#status tr.none th { color: #666; font-weight: normal; } +#status td.object, #status td.pid { + font-family: "DejaVu Sans Mono",Menlo,Courier,monospace; + font-size: 11px; +} + +/* Session */ +#loginSignup { + font-size:200%; +} + +/* Views */ +.qinput {color:#A1A1A1; } +div#query-options {font-size:15px;} +div#view-selection {padding-bottom:5px;} +select#ddoc-select { padding-top:5px; font-size:15px;} +select#view-select { padding-top:5px; font-size:15px;} +div.error-type {color:#FF0000; font-weight:bold;} +div.error-reason {color:#FF0000; font-weight:bold;} +span.expand-doc {cursor:pointer; color:#CD5555;} +span.expand-all {cursor:pointer; color:#CD5555;} +table.listing tbody tr td span.docid { cursor:pointer; color:#CD9B9B } +table.listing tbody tr.showdoc td.showdoc {font-weight:bold; background: #e9e9e9; color:#000000; } +div.viewinfo {font-weight:bold; color:#000000; float:left;} +span.viewinfo-val {color:#FF0000; padding-left:10px; padding-right:30px;} +span.viewstart {float:right; color:#CD5555; padding-left:5px; padding-right:5px; cursor:pointer;} +span.viewend {float:right; color:#CD5555; padding-left:5px; cursor:pointer;} +span.viewkey {color:#CD5555; padding-left:5px; padding-right:20px; cursor:pointer;} + +div.primary-view-options {padding-left:3px; width:100%; padding-top:10px; padding-bottom:10px; float:left;} +div.main-view-options {width:100%; padding-top:5px; padding-bottom:5px; float:left;} +div.key-options {float:left; width:100%; padding-bottom:5px;} +div.key-half-options {float:left; width:50%;} +div.input-left {float:left; width:19%; padding-left:3px;} +div.input-right {float:left; width:75%; padding-right:5%; padding-left:3px;} +div.input-right-nospace {float:left; width:80%;} +div.docid-input-left {float:left; width:34%; padding-left:3px;} +div.docid-input-right {float:left; width:60%; padding-right:5%;} +div.docid-input-right-nospace {float:left; width:65%;} +input.input-right {text-align:left; width:100%; } +div.reduce-options {padding-left:3px; padding-top:10px; padding-bottom:10px; width:100%; float:left;} +div.view-options-other {padding-left:3px; padding-top:10px; padding-bottom:10px; width:100%; float:left;} +div.odd {background: #feffea; -moz-border-radius: 5px; -webkit-border-radius: 5px; float:left; } +input.reduce {text-align:right;} +input.number {width:30px;} +input.input-key {width:330px;} +div.main-input {float:left; padding-right:30px;} +span.main-input {padding-right:7px;} + +div#view-selection { + width:100%; +} +div#ddoc-selection { + width:50%; + float:left; + cursor:pointer; +} +div#ddoc-view-selection { + width:50%; + float:left; + cursor:pointer; +} +div#view-editor { + width:100%; + display:none; +} +textarea#view-editor-map { + width:49%; + height:200px; + float:left; +} +textarea#view-editor-reduce { + width:49%; + height:200px; + float:left; +} +span.edit-view { + color:blue; + font-size:10px; + padding-left:8px; + cursor:pointer; +} +span.add-view { + color:blue; + cursor:pointer; +} +span.add-ddoc { + color:blue; + cursor:pointer; +} +span.save-view-button { + float:right; + cursor:pointer; + background-color:#E0E0E0; + padding: 2px 2px 2px 2px; + -moz-border-radius: 35px; + -webkit-border-radius: 35px; + border-radius: 35px; + border-style:solid; + border-width:2px; + border-color:#D3D3D3; + margin-top:5px; + margin-right:5px; + margin-bottom:5px; +} + +/* Document Editor */ + +div#document-container span#expand-all { + cursor:pointer; color:#FF0000; +} + +div#document-editor { background: #fff; font-size:14px;} +div#document-editor span.expand {cursor:pointer; color:#FF0000;} +div#document-editor div.id-space { + border: none; float: left; margin: 3px 3px 0 3px; padding: 0; + width: 15px; height: 15px; +} +div#document-editor div.doc-field {width:100%; padding-bottom:2px;} +div#document-editor div.doc-field, div.doc-value, div.doc-key {float:left;} +div#document-editor div.doc-key {padding-right:5px;font-weight:bold;} + +div#document-editor div.string-type { white-space: pre-wrap; color:#393;} +div#document-editor div.string-type:before { color: #ccc; content: "“"; + left: -4px; +} +div#document-editor div.string-type:after { color: #ccc; content: "”"; } + +div#document-editor span.number-type { white-space: pre-wrap; color:#339; padding-left:6px;} + +div#document-editor span.array-type { color: #BD101D; float:left;} +div#document-editor span.object-type { color: #BD101D; float:left;} +div#document-editor span.null-type { color: #BD101D; float:left; color: #666666; padding-left:6px;} + +div#document-editor div.array-key {float:left; padding-right:15px; color:#666666;} +div#document-editor div.object-key {float:left; padding-right:15px; font-weight:bold; } +div#document-editor div.doc-key-base {float:left; padding-right:15px; font-weight:bold; } + +div#document-editor div.delete-button { + background: url(../image/delete-mini.png) no-repeat; border: none; + cursor: pointer; float: left; margin: 3px 3px 0 3px; padding: 0; + width: 15px; height: 15px; +} + +div#document-editor div.add-button { + /* Add button is just the delete button rotated with CSS */ + background: url(../image/delete-mini.png) no-repeat; + rotation: 45deg; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + border: none; + cursor: pointer; float: left; margin: 3px 3px 0 3px; padding: 0; + width: 15px; height: 15px; +} + +div#document-editor div.empty { + float:left; +} + +div#document-editor input { + font: normal 90% arial, sans-serif; +} + +div#document-editor span#save-button { + cursor:pointer; background-color:#ddd; + padding: 2px 5px 2px 5px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + border-radius: 15px; +} +span#restore { + cursor:pointer; background-color:#ddd; + padding: 2px 5px 2px 5px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + border-radius: 15px; +} + +div#autosave { + float:right; +} + +span.revision { + padding-right:10px; + cursor:pointer; + float:left; + margin-bottom:10px; +} +div#document-revisions { + padding-top:20px; +} +span#document-revisions-title { + padding-right:10px; + font-weight:bold; + float:left; + margin-bottom:80px; +} +span.current-revision { + font-weight:bold; + padding-right:10px; + float:left; + margin-bottom:10px; +} +span.revision { + padding-right:10px; + cursor:default; + float:left; + margin-bottom:10px; +} +span.revision-status-missing { + color:#8C8C8C; +} +span.revision-status-disk { + cursor:pointer; +} +span.revision-status-available { + cursor:pointer; +} +span.revision-status-deleted { + color:#8C8C8C; +} + +.tooltip { + position:absolute; + font-size:12px; + padding:2px; + background-color:#B3B3B3; + border:checked; + color:black; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} +span.tooltip-status-title { + color:black; + font-weight:normal; + text-align:center; +} +span.tooltip-status { + font-weight:bold; + color:red; + text-align:center; +} + +/* New Index Page */ + +input#create-db-input { + margin-right:10px; + padding-top:2px; + padding-bottom:2px; + font: normal arial, sans-serif; + font-size:16px; +} +span#add-db-button { + cursor:pointer; + background-color:#E0E0E0; + padding: 2px 2px 2px 2px; + -moz-border-radius: 35px; + -webkit-border-radius: 35px; + border-radius: 35px; + border-style:solid; + border-width:2px; + font-size:12px; + border-color:#D3D3D3; +} + +/* Error Panels */ + +div#error-container { + width:90%; + padding-bottom:5px; +} + +div#error-container div.error-bubble { + padding: 2px 15px 2px 15px; + -moz-border-radius: 35px; + -webkit-border-radius: 35px; + width:100%; + background-color:#E0E0E0; + border-color:#D3D3D3; + border-style:solid; + border-width:2px; + display:normal; + margin-bottom:2px; +} + +div#error-container span.error-code { + color:red; + font-weight:bold; + height:100%; + padding-right:10px; +} +div#error-container span.error-title { + font-weight:bold; +} +span.remove-error { + position:absolute; + background: url(../image/delete-mini.png) no-repeat; + cursor:pointer; + width: 15px; height: 15px; + border:none; +} + +/* +#fields tbody.content th button.delete { + background: url(../image/delete-mini.png) no-repeat; border: none; + cursor: pointer; float: left; margin: .2em 5px 0 -20px; padding: 0; + width: 15px; height: 15px; +} +#fields tbody.content th button.delete:hover { background-position: -15px 0; } +#fields tbody.content th b { display: block; padding: 2px 2px 2px 3px; } +#fields tbody.content th b.editinline-container { padding: 0; } +#fields tbody.content td { color: #999; padding-left: 14px; + padding-right: 48px; +} +#fields tbody.content td code { display: block; font-size: 11px; + padding: 2px 2px 2px 3px; position: relative; +} +#fields tbody.content td code.string { white-space: pre-wrap; } +#fields tbody.content td code.string:before { color: #ccc; content: "“"; + position: absolute; left: -4px; +} +#fields tbody.content td code.string:after { color: #ccc; content: "”"; } + +#fields tbody.content td dl { margin: 0; padding: 0; } +#fields tbody.content td dt { + background: transparent url(../image/toggle-collapse.gif) 0 3px no-repeat; + clear: left; color: #333; cursor: pointer; line-height: 1em; + margin-left: -12px; padding-left: 14px; +} +#fields tbody.content td dd { line-height: 1em; margin: 0; + padding: 0 0 0 1em; +} +#fields tbody.content td dt.collapsed { + background-image: url(../image/toggle-expand.gif); +} +#fields tbody.content td dt.inline { background-image: none; cursor: default; + float: left; margin-left: 0; padding-left: 2px; padding-right: .5em; + padding-top: 2px; +} +#fields tbody.content td dd code.string { left: 4px; text-indent: -6px; } +#fields tbody.content td dd code.string:before { position: static; } +#fields tbody.content input, #fields tbody.content textarea, +#fields tbody.source textarea { + background: #fff; border: 1px solid; border-color: #999 #ddd #ddd #999; + margin: 0; padding: 1px; width: 100%; +} +#fields tbody.content th input { font-family: inherit; font-size: inherit; + font-weight: bold; +} +#fields tbody.content td input, #fields tbody.content td textarea, +#fields tbody.source textarea { + font: normal 11px "DejaVu Sans Mono",Menlo,Courier,monospace; +} +#fields tbody.content input.invalid, +#fields tbody.content textarea.invalid, +#fields tbody.source textarea.invalid { + background: #f9f4f4; border-color: #b66 #ebb #ebb #b66; +} +#fields tbody.content div.grippie, #fields tbody.source div.grippie { + padding: 0 1px; width: 100%; +} +#fields tbody.content div.error, #fields tbody.source div.error { + color: #d33; +} + +#fields tbody.content td ul.attachments { list-style: none; margin: 0; + padding: 0; +} +#fields tbody.content td ul.attachments li { + margin-bottom: .3em; min-height: 20px; padding-left: 20px; +} +#fields tbody.content td ul.attachments tt { font-size: 11px; } +#fields tbody.content td ul.attachments li span.info { color: #666; + display: block; font-size: 95%; +} +#fields tbody.content td ul.attachments li button { + background: transparent no-repeat; border: none; cursor: pointer; + float: left; margin: 0 2px 0 -20px; padding: 0; width: 15px; height: 15px; + vertical-align: middle; +} +#fields tbody.content td ul.attachments li button:hover { + background-position: -15px 0; +} +#fields tbody.content td ul.attachments li button.delete { + background-image: url(../image/delete-mini.png); +} +#fields tbody.source td pre { color: #999; font-size: 11px; line-height: 1.6em; + margin: 0; overflow: auto; white-space: pre-wrap; width: 100%; +} +#fields tbody.source td.editinline-container { padding-left: 14px; padding-right: 48px; }*/ \ No newline at end of file diff --git a/_attachments/style/ui-lightness/jquery-ui-1.8.11.custom.css b/_attachments/style/ui-lightness/jquery-ui-1.8.11.custom.css new file mode 100644 index 0000000..a6b2f74 --- /dev/null +++ b/_attachments/style/ui-lightness/jquery-ui-1.8.11.custom.css @@ -0,0 +1,347 @@ +/* + * jQuery UI CSS Framework 1.8.11 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* + * jQuery UI CSS Framework 1.8.11 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee 50% top repeat-x; color: #333333; } +.ui-widget-content a { color: #333333; } +.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 50% 50% repeat-x; color: #ffffff; font-weight: bold; } +.ui-widget-header a { color: #ffffff; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 50% 50% repeat-x; font-weight: bold; color: #1c94c4; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce 50% 50% repeat-x; font-weight: bold; color: #c77405; } +.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff 50% 50% repeat-x; font-weight: bold; color: #eb8f00; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c 50% top repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 50% 50% repeat; color: #ffffff; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; } +.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; } + +/* Overlays */ +.ui-widget-overlay { background: #666666 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); } +.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/* + * jQuery UI Autocomplete 1.8.11 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete#theming + */ +.ui-autocomplete { position: absolute; cursor: default; } + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* + * jQuery UI Menu 1.8.11 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menu#theming + */ +.ui-menu { + list-style:none; + padding: 2px; + margin: 0; + display:block; + float: left; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + zoom: 1; + float: left; + clear: left; + width: 100%; +} +.ui-menu .ui-menu-item a { + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} diff --git a/_attachments/templates/changes.mustache b/_attachments/templates/changes.mustache new file mode 100644 index 0000000..1e2ca4b --- /dev/null +++ b/_attachments/templates/changes.mustache @@ -0,0 +1,39 @@ + + + + + + + + + + + + {{#results}} + + + + + {{/results}} + + + + + + +
        Documents
        seqidchanges
        {{seq}}{{id}}[{{#changes}}{{rev}}{{/changes}}]
        + Last Seq: {{last_seq}} +
        \ No newline at end of file diff --git a/_attachments/templates/config.mustache b/_attachments/templates/config.mustache new file mode 100644 index 0000000..62b554e --- /dev/null +++ b/_attachments/templates/config.mustache @@ -0,0 +1,124 @@ + + + + +

        + Note: Some configuration options may require + restarting the server to take effect after modification. +

        +

        + For the strongest consistency guarantees, delayed_commits should be set to false. The default value of true is designed for single-user performance. For more details see a discussion of durability on the CouchDB wiki. +

        + + + + + + + + + +
        Configuration
        SectionOptionValueDelete
        diff --git a/_attachments/templates/database.mustache b/_attachments/templates/database.mustache new file mode 100644 index 0000000..a369aae --- /dev/null +++ b/_attachments/templates/database.mustache @@ -0,0 +1,65 @@ + +
        + Views > +
        +
          +
        • + +
        + + + + + + + + + + + + + + + + +
        Documents
        idrev
        + +
        + +
        +
        +
          +
        • +
        • +
        + +
        +
        Documents
        +
        Deleted
        +
        Sequence
        +
        Purge Seq
        +
        Compacting
        +
        +
        Document Store Size
        +
        View Index Size
        +
        Full Database Size
        +
        \ No newline at end of file diff --git a/_attachments/templates/document.mustache b/_attachments/templates/document.mustache new file mode 100644 index 0000000..49e804a --- /dev/null +++ b/_attachments/templates/document.mustache @@ -0,0 +1,533 @@ + +
          +
        • +
        • +
        + +
        +
        + ... +
        + + auto-save + +
        +
        +
        +
        +
        + Revisions:
        +
      • + + \ No newline at end of file diff --git a/_attachments/templates/index.mustache b/_attachments/templates/index.mustache new file mode 100644 index 0000000..ae90de7 --- /dev/null +++ b/_attachments/templates/index.mustache @@ -0,0 +1,94 @@ + +
          + Create Database +
        + + + + + + + + + + + + + + + + + + + +
        Databases
        NameSizeNumber of DocumentsUpdate Seq
        +
        + + \ No newline at end of file diff --git a/_attachments/templates/replicator.mustache b/_attachments/templates/replicator.mustache new file mode 100644 index 0000000..58d40e8 --- /dev/null +++ b/_attachments/templates/replicator.mustache @@ -0,0 +1,158 @@ + + +
        +
        + Replicate changes from: +

        + + + +

        + + + +

        +
        +

        +
        + to: +

        + + + +

        + + + +

        +
        +

        + + +

        +
        + + + + + + + + + + +
        Replication History
        Event
        No replication
        diff --git a/_attachments/templates/stats.mustache b/_attachments/templates/stats.mustache new file mode 100644 index 0000000..8a6a326 --- /dev/null +++ b/_attachments/templates/stats.mustache @@ -0,0 +1,94 @@ + +
        + +
        + + + + + + + + + +
        Active Tasks
        TypeObjectPIDStatus
        + \ No newline at end of file diff --git a/_attachments/templates/tests.mustache b/_attachments/templates/tests.mustache new file mode 100644 index 0000000..21972e4 --- /dev/null +++ b/_attachments/templates/tests.mustache @@ -0,0 +1,83 @@ + + + + + + + + + +
          +
        • +
        • +
        • +
        • +
        • +
        +

        + Note: Each of the tests will block the browser. If the + connection to your CouchDB server is slow, running the tests will take + some time, and you'll not be able to do much with your browser while + a test is being executed. Also: The test suite is designed + to work with Firefox (with Firebug disabled). Patches are welcome for + convenience compatibility with other browsers, but official support is + for Firefox (latest stable version) only. +

        + + + + + + + + + + + + + + + + + + +
        Tests
        NameStatusElapsed TimeDetails
        \ No newline at end of file diff --git a/_attachments/templates/view.mustache b/_attachments/templates/view.mustache new file mode 100644 index 0000000..a7c3846 --- /dev/null +++ b/_attachments/templates/view.mustache @@ -0,0 +1,140 @@ + + + + +
        +
        +
        +
        +
        +
        +
        + + + save +
        +
        +
        +
        + key + +
        +
        + limit + +
        +
        + skip + +
        +
        +
        +
        + +
        +
        +
        + startkey +
        +
        + +
        +
        +
        +
        + endkey +
        +
        + +
        +
        +
        + +
        + +
        +
        +
        + startkey_docid +
        +
        + +
        +
        +
        +
        + endkey_docid +
        +
        + +
        +
        +
        +
        +
        +
        +
        + reduce + +
        +
        + group + +
        +
        + group_level + +
        +
        +
        +
        +
        + include_docs + +
        +
        + descending + +
        +
        + stale + +
        +
        + inclusive_end + +
        +
        +
        +
        + + + + + + + + + + + + + + + + +
        Documents
        keyvalue
        +
        diff --git a/_id b/_id new file mode 100644 index 0000000..33fd073 --- /dev/null +++ b/_id @@ -0,0 +1 @@ +_design/futon