aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.rspec1
-rw-r--r--.travis.yml8
-rw-r--r--Gemfile24
-rw-r--r--Gemfile.lock163
-rw-r--r--README-rails.txt188
-rw-r--r--README.md3
-rw-r--r--Rakefile9
-rw-r--r--app/controllers/admin_public_body_controller.rb10
-rw-r--r--app/controllers/admin_request_controller.rb2
-rw-r--r--app/controllers/api_controller.rb72
-rw-r--r--app/controllers/application_controller.rb40
-rw-r--r--app/controllers/general_controller.rb88
-rw-r--r--app/controllers/public_body_controller.rb6
-rw-r--r--app/controllers/request_controller.rb33
-rw-r--r--app/models/application_mailer.rb9
-rw-r--r--app/models/censor_rule.rb10
-rw-r--r--app/models/foi_attachment.rb68
-rw-r--r--app/models/incoming_message.rb475
-rw-r--r--app/models/info_request.rb62
-rw-r--r--app/models/outgoing_message.rb17
-rw-r--r--app/models/post_redirect.rb26
-rw-r--r--app/models/profile_photo.rb43
-rw-r--r--app/models/public_body.rb51
-rw-r--r--app/models/user.rb49
-rw-r--r--app/views/general/_frontpage_new_request.rhtml2
-rw-r--r--app/views/general/_frontpage_search_box.rhtml2
-rw-r--r--app/views/general/frontpage.rhtml3
-rw-r--r--app/views/help/_sidebar.rhtml18
-rw-r--r--app/views/help/about.rhtml4
-rw-r--r--app/views/help/api.rhtml10
-rw-r--r--app/views/help/contact.rhtml20
-rw-r--r--app/views/help/officers.rhtml38
-rw-r--r--app/views/help/privacy.rhtml28
-rw-r--r--app/views/help/requesting.rhtml38
-rw-r--r--app/views/help/unhappy.rhtml16
-rw-r--r--app/views/layouts/default.rhtml12
-rw-r--r--app/views/request/_describe_state.rhtml2
-rw-r--r--app/views/request/_hidden_correspondence.rhtml6
-rw-r--r--app/views/request/_sidebar.rhtml7
-rw-r--r--app/views/request/hidden.rhtml2
-rw-r--r--app/views/request/list.rhtml33
-rw-r--r--app/views/request/new_please_describe.rhtml2
-rw-r--r--app/views/request/show.rhtml2
-rw-r--r--app/views/track/_tracking_links.rhtml2
-rw-r--r--app/views/user/show.rhtml8
m---------commonlib0
-rw-r--r--config.ru6
-rw-r--r--config/application.rb72
-rw-r--r--config/boot.rb137
-rw-r--r--config/environment.rb165
-rw-r--r--config/environments/development.rb48
-rw-r--r--config/environments/production.rb32
-rw-r--r--config/environments/staging.rb30
-rw-r--r--config/environments/test.rb40
-rw-r--r--config/general.yml-example4
-rw-r--r--config/initializers/alaveteli.rb88
-rw-r--r--config/initializers/backtrace_silencers.rb7
-rw-r--r--config/initializers/fast_gettext.rb2
-rw-r--r--config/initializers/gettext_i18n_rails.rb3
-rw-r--r--config/initializers/inflections.rb (renamed from vendor/plugins/interlock/test/integration/app/config/initializers/inflections.rb)4
-rw-r--r--config/initializers/mime_types.rb (renamed from vendor/plugins/interlock/test/integration/app/config/initializers/mime_types.rb)0
-rw-r--r--config/initializers/secret_token.rb7
-rw-r--r--config/initializers/session_store.rb17
-rw-r--r--config/packages2
-rw-r--r--db/migrate/094_remove_old_tags_foreign_key.rb7
-rw-r--r--db/seeds.rb7
-rw-r--r--lib/alaveteli_external_command.rb14
-rw-r--r--lib/configuration.rb11
-rw-r--r--lib/i18n_fixes.rb10
-rw-r--r--lib/mail_handler/backends/mail_backend.rb278
-rw-r--r--lib/mail_handler/backends/mail_extensions.rb60
-rw-r--r--lib/mail_handler/backends/tmail_backend.rb228
-rw-r--r--lib/mail_handler/backends/tmail_extensions.rb2
-rw-r--r--lib/mail_handler/mail_handler.rb131
-rw-r--r--lib/old_rubygems_patch.rb46
-rw-r--r--lib/tasks/.gitkeep (renamed from vendor/plugins/globalize2/generators/db_backend.rb)0
-rw-r--r--lib/tasks/rspec.rake147
-rw-r--r--locale/cs/app.po214
-rw-r--r--locale/cy/app.po66
-rw-r--r--locale/es/app.po46
-rw-r--r--public/404.html34
-rw-r--r--public/422.html26
-rw-r--r--public/500.html36
-rwxr-xr-xpublic/dispatch.cgi10
-rwxr-xr-xpublic/dispatch.fcgi24
-rwxr-xr-xpublic/dispatch.rb10
-rw-r--r--public/javascripts/controls.js148
-rw-r--r--public/javascripts/dragdrop.js340
-rw-r--r--public/javascripts/effects.js357
-rw-r--r--public/javascripts/prototype.js5198
-rw-r--r--public/javascripts/rails.js202
-rw-r--r--public/stylesheets/main.css4
-rwxr-xr-x[-rw-r--r--]script/cache-incoming-emails0
-rwxr-xr-xscript/handle-mail-replies.rb41
-rwxr-xr-xscript/rails6
-rw-r--r--spec/controllers/admin_censor_rule_controller_spec.rb2
-rw-r--r--spec/controllers/admin_general_controller_spec.rb2
-rw-r--r--spec/controllers/admin_public_body_controller_spec.rb18
-rw-r--r--spec/controllers/admin_request_controller_spec.rb4
-rw-r--r--spec/controllers/admin_track_controller_spec.rb2
-rw-r--r--spec/controllers/admin_user_controller_spec.rb2
-rw-r--r--spec/controllers/api_controller_spec.rb134
-rw-r--r--spec/controllers/comment_controller_spec.rb2
-rw-r--r--spec/controllers/general_controller_spec.rb54
-rw-r--r--spec/controllers/help_controller_spec.rb2
-rw-r--r--spec/controllers/public_body_controller_spec.rb14
-rw-r--r--spec/controllers/request_controller_spec.rb45
-rw-r--r--spec/controllers/services_controller_spec.rb4
-rw-r--r--spec/controllers/track_controller_spec.rb8
-rw-r--r--spec/controllers/user_controller_spec.rb18
-rw-r--r--spec/fixtures/files/dos-linebreaks.email31
-rw-r--r--spec/fixtures/files/humberside-police-odd-mime-type.email25
-rw-r--r--spec/fixtures/files/incoming-request-attachment-headers.email50
-rw-r--r--spec/fixtures/files/incoming-request-attachment-unknown-extension.email5
-rw-r--r--spec/fixtures/files/many-attachments-date-header.email451
-rw-r--r--spec/fixtures/files/rfc822-attachment.email147
-rw-r--r--spec/lib/i18n_interpolation.rb3
-rw-r--r--spec/lib/mail_handler/mail_handler_spec.rb362
-rw-r--r--spec/models/incoming_message_spec.rb110
-rw-r--r--spec/models/info_request_event_spec.rb2
-rw-r--r--spec/models/info_request_spec.rb32
-rw-r--r--spec/models/public_body_spec.rb4
-rw-r--r--spec/models/user_spec.rb8
-rw-r--r--spec/spec_helper.rb271
-rw-r--r--spec/spec_helper.rb.rails2152
-rw-r--r--spec/support/email_helpers.rb23
-rw-r--r--spec/support/load_file_fixtures.rb14
-rw-r--r--spec/support/validate_html.rb65
-rw-r--r--spec/support/xapian_index.rb21
-rw-r--r--spec/views/public_body/show.rhtml_spec.rb64
-rw-r--r--spec/views/request/_after_actions.rhtml_spec.rb61
-rw-r--r--spec/views/request/_describe_state.rhtml_spec.rb28
-rw-r--r--spec/views/request/list.rhtml_spec.rb36
-rw-r--r--spec/views/request/show.rhtml_spec.rb26
-rw-r--r--spec/views/request_game/play.rhtml_spec.rb8
-rw-r--r--vendor/gems/fakeweb-1.3.0/.gitignore7
-rw-r--r--vendor/gems/fakeweb-1.3.0/.specification135
-rw-r--r--vendor/gems/fakeweb-1.3.0/CHANGELOG215
-rw-r--r--vendor/gems/fakeweb-1.3.0/LICENSE.txt19
-rw-r--r--vendor/gems/fakeweb-1.3.0/README.rdoc189
-rw-r--r--vendor/gems/fakeweb-1.3.0/Rakefile67
-rw-r--r--vendor/gems/fakeweb-1.3.0/fakeweb.gemspec126
-rw-r--r--vendor/gems/fakeweb-1.3.0/lib/fake_web.rb215
-rw-r--r--vendor/gems/fakeweb-1.3.0/lib/fake_web/ext/net_http.rb72
-rw-r--r--vendor/gems/fakeweb-1.3.0/lib/fake_web/registry.rb127
-rw-r--r--vendor/gems/fakeweb-1.3.0/lib/fake_web/responder.rb122
-rw-r--r--vendor/gems/fakeweb-1.3.0/lib/fake_web/response.rb10
-rw-r--r--vendor/gems/fakeweb-1.3.0/lib/fake_web/stub_socket.rb15
-rw-r--r--vendor/gems/fakeweb-1.3.0/lib/fake_web/utility.rb87
-rw-r--r--vendor/gems/fakeweb-1.3.0/lib/fakeweb.rb2
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_from_curl12
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_with_transfer_encoding17
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_without_transfer_encoding11
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/fixtures/test_example.txt1
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/fixtures/test_txt_file3
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_allow_net_connect.rb168
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_deprecations.rb54
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_fake_authentication.rb92
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_fake_web.rb590
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_fake_web_open_uri.rb58
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_helper.rb90
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_last_request.rb29
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_missing_open_uri.rb25
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_missing_pathname.rb37
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_other_net_http_libraries.rb36
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_precedence.rb79
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_query_string.rb45
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_regexes.rb157
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_response_headers.rb79
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_trailing_slashes.rb53
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/test_utility.rb83
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/History.txt59
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/Manifest.txt7
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/README.txt54
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/Rakefile103
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/lib/net_fix.rb160
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb435
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/setup.rb1585
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/.document5
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/.gitignore5
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/LICENSE20
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/README.rdoc70
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/Rakefile62
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/VERSION1
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel.rb52
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel/net_http.rb10
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel/request.rb96
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/samuel.gemspec69
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/request_test.rb193
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/samuel_test.rb42
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/test_helper.rb66
-rw-r--r--vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/thread_test.rb32
-rw-r--r--vendor/plugins/active_record_base_without_table/lib/active_record/base_without_table.rb3
-rw-r--r--vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb4
-rw-r--r--vendor/plugins/globalize2/LICENSE21
-rw-r--r--vendor/plugins/globalize2/README.textile86
-rw-r--r--vendor/plugins/globalize2/Rakefile39
-rw-r--r--vendor/plugins/globalize2/VERSION1
-rw-r--r--vendor/plugins/globalize2/generators/templates/db_backend_migration.rb25
-rw-r--r--vendor/plugins/globalize2/globalize2.gemspec81
-rw-r--r--vendor/plugins/globalize2/init.rb1
-rw-r--r--vendor/plugins/globalize2/lib/globalize.rb15
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record.rb238
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb80
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb25
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record/migration.rb44
-rw-r--r--vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb41
-rw-r--r--vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb25
-rw-r--r--vendor/plugins/globalize2/test/active_record/fallbacks_test.rb102
-rw-r--r--vendor/plugins/globalize2/test/active_record/migration_test.rb118
-rw-r--r--vendor/plugins/globalize2/test/active_record/sti_translated_test.rb49
-rw-r--r--vendor/plugins/globalize2/test/active_record/translates_test.rb96
-rw-r--r--vendor/plugins/globalize2/test/active_record/translation_class_test.rb30
-rw-r--r--vendor/plugins/globalize2/test/active_record/validation_tests.rb75
-rw-r--r--vendor/plugins/globalize2/test/active_record_test.rb467
-rw-r--r--vendor/plugins/globalize2/test/all.rb2
-rw-r--r--vendor/plugins/globalize2/test/data/models.rb56
-rw-r--r--vendor/plugins/globalize2/test/data/no_globalize_schema.rb11
-rw-r--r--vendor/plugins/globalize2/test/data/schema.rb55
-rw-r--r--vendor/plugins/globalize2/test/i18n/missing_translations_test.rb36
-rw-r--r--vendor/plugins/globalize2/test/test_helper.rb76
-rw-r--r--vendor/plugins/interlock/.gitignore10
-rw-r--r--vendor/plugins/interlock/CHANGELOG10
-rw-r--r--vendor/plugins/interlock/LICENSE184
-rw-r--r--vendor/plugins/interlock/Manifest85
-rw-r--r--vendor/plugins/interlock/README130
-rw-r--r--vendor/plugins/interlock/Rakefile34
-rw-r--r--vendor/plugins/interlock/TEST_HOWTO20
-rw-r--r--vendor/plugins/interlock/TODO11
-rw-r--r--vendor/plugins/interlock/examples/memcached.yml11
-rw-r--r--vendor/plugins/interlock/init.rb2
-rw-r--r--vendor/plugins/interlock/lib/interlock.rb28
-rw-r--r--vendor/plugins/interlock/lib/interlock/action_controller.rb253
-rw-r--r--vendor/plugins/interlock/lib/interlock/action_view.rb98
-rw-r--r--vendor/plugins/interlock/lib/interlock/active_record.rb61
-rw-r--r--vendor/plugins/interlock/lib/interlock/config.rb168
-rw-r--r--vendor/plugins/interlock/lib/interlock/core_extensions.rb74
-rw-r--r--vendor/plugins/interlock/lib/interlock/finders.rb155
-rw-r--r--vendor/plugins/interlock/lib/interlock/interlock.rb155
-rw-r--r--vendor/plugins/interlock/lib/interlock/lock.rb44
-rw-r--r--vendor/plugins/interlock/lib/interlock/pass_through_store.rb19
-rw-r--r--vendor/plugins/interlock/lib/tasks/interlock.rake10
-rw-r--r--vendor/plugins/interlock/test/integration/app/README203
-rw-r--r--vendor/plugins/interlock/test/integration/app/Rakefile10
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/controllers/application.rb10
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/controllers/eval_controller.rb7
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/controllers/items_controller.rb50
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/helpers/application_helper.rb3
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/helpers/eval_helper.rb2
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/helpers/items_helper.rb2
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/models/book.rb3
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/models/item.rb2
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/views/items/detail.rhtml20
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/views/items/list.rhtml16
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/views/items/recent.rhtml14
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/views/items/show.rhtml8
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/views/layouts/application.html.erb9
-rw-r--r--vendor/plugins/interlock/test/integration/app/app/views/shared/_related.rhtml6
-rw-r--r--vendor/plugins/interlock/test/integration/app/config/boot.rb110
-rw-r--r--vendor/plugins/interlock/test/integration/app/config/database.yml24
-rw-r--r--vendor/plugins/interlock/test/integration/app/config/environment.rb23
-rw-r--r--vendor/plugins/interlock/test/integration/app/config/environments/development.rb9
-rw-r--r--vendor/plugins/interlock/test/integration/app/config/environments/production.rb18
-rw-r--r--vendor/plugins/interlock/test/integration/app/config/environments/test.rb22
-rw-r--r--vendor/plugins/interlock/test/integration/app/config/memcached.yml10
-rw-r--r--vendor/plugins/interlock/test/integration/app/config/routes.rb35
-rw-r--r--vendor/plugins/interlock/test/integration/app/db/migrate/001_create_items.rb13
-rw-r--r--vendor/plugins/interlock/test/integration/app/db/migrate/002_create_books.rb14
-rw-r--r--vendor/plugins/interlock/test/integration/app/db/migrate/003_add_counter_to_items.rb9
-rw-r--r--vendor/plugins/interlock/test/integration/app/doc/README_FOR_APP2
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/.htaccess40
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/404.html30
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/422.html30
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/500.html30
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/public/dispatch.cgi10
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/public/dispatch.fcgi24
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/public/dispatch.rb10
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/favicon.ico0
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/images/rails.pngbin1787 -> 0 bytes
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/index.html277
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/javascripts/application.js2
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/javascripts/controls.js963
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/javascripts/dragdrop.js972
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/javascripts/effects.js1120
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/javascripts/prototype.js4200
-rw-r--r--vendor/plugins/interlock/test/integration/app/public/robots.txt5
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/about3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/console3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/destroy3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/generate3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/performance/benchmarker3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/performance/profiler3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/performance/request3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/plugin3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/process/inspector3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/process/reaper3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/process/spawner3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/runner3
-rwxr-xr-xvendor/plugins/interlock/test/integration/app/script/server3
-rw-r--r--vendor/plugins/interlock/test/integration/app/test/fixtures/books.yml12
-rw-r--r--vendor/plugins/interlock/test/integration/app/test/fixtures/items.yml13
-rw-r--r--vendor/plugins/interlock/test/integration/app/test/functional/eval_controller_test.rb10
-rw-r--r--vendor/plugins/interlock/test/integration/app/test/functional/items_controller_test.rb10
-rw-r--r--vendor/plugins/interlock/test/integration/app/test/test_helper.rb9
-rw-r--r--vendor/plugins/interlock/test/integration/app/test/unit/item_test.rb8
l---------vendor/plugins/interlock/test/integration/app/vendor/plugins/interlock1
-rw-r--r--vendor/plugins/interlock/test/integration/server_test.rb230
-rw-r--r--vendor/plugins/interlock/test/setup.rb22
-rw-r--r--vendor/plugins/interlock/test/teardown.rb0
-rw-r--r--vendor/plugins/interlock/test/test_helper.rb20
-rw-r--r--vendor/plugins/interlock/test/unit/active_record_test.rb8
-rw-r--r--vendor/plugins/interlock/test/unit/finder_test.rb176
-rw-r--r--vendor/plugins/interlock/test/unit/interlock_test.rb30
-rw-r--r--vendor/plugins/interlock/test/unit/lock_test.rb45
314 files changed, 7777 insertions, 23329 deletions
diff --git a/.rspec b/.rspec
new file mode 100644
index 000000000..4e1e0d2f7
--- /dev/null
+++ b/.rspec
@@ -0,0 +1 @@
+--color
diff --git a/.travis.yml b/.travis.yml
index d55b63e79..9cc3fd5a3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,12 +3,14 @@ branches:
only:
- develop
- master
+ - feature/fix-travis-ruby-19-errors
rvm:
- 1.8.7
+ - 1.9.3
before_install:
- - sudo gem install rubygems-update --version=1.6.2
- - sudo gem update --system 1.6.2
- - sudo gem install rake --version=0.8.7
+ - gem install rubygems-update --version=1.6.2
+ - gem update --system 1.6.2
+ - gem install rake --version=0.9.2.2
- git submodule update --init --recursive
- psql -c "create database foi_test template template0 encoding 'SQL_ASCII';" -U postgres
- cp config/database.yml-test config/database.yml
diff --git a/Gemfile b/Gemfile
index fab2ec689..b451cfffe 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,7 +7,7 @@ if File.exist? "/etc/debian_version" and File.open("/etc/debian_version").read.s
end
source :rubygems
-gem 'rails', '2.3.14'
+gem 'rails', '3.0.17'
gem 'pg'
gem 'fast_gettext', '>= 0.6.0'
@@ -21,17 +21,14 @@ gem 'memcache-client', :require => 'memcache'
gem 'locale', '>= 2.0.5'
gem 'net-http-local'
gem 'net-purge'
-gem 'rack', '~> 1.1.0'
-gem 'rdoc', '~> 2.4.3'
+gem 'rack'
+gem 'rdoc'
gem 'recaptcha', '~> 0.3.1', :require => 'recaptcha/rails'
# :require avoids "already initialized constant" warnings
gem 'rmagick', :require => 'RMagick'
gem 'routing-filter', '~> 0.2.4'
-gem 'rake', '~> 0.8.7'
-gem 'rspec', '~> 1.3.2'
-gem 'rspec-rails', '~> 1.3.4'
+gem 'rake', '0.9.2.2'
gem 'ruby-msg', '~> 1.5.0'
-gem 'test-unit', '~> 1.2.3', :platforms => :ruby_19
gem 'vpim'
gem 'will_paginate', '~> 2.3.11'
# when 1.2.9 is released by the maintainer, we can stop using this fork:
@@ -41,14 +38,13 @@ gem 'zip'
gem 'capistrano'
gem 'syslog_protocol'
gem 'newrelic_rpm'
-# We won't need rails_xss once we've upgrade to Rails 3. It's baked in.
-gem 'rails_xss'
-# erubis is required by rails_xss
-gem 'erubis'
+gem 'tmail'
+gem 'globalize3'
group :test do
gem 'fakeweb'
- gem 'rspec-rails', '~> 1.3.4'
+ gem 'test-unit', '~> 1.2.3', :platforms => :ruby_19
+ gem 'webrat'
end
group :development do
@@ -62,3 +58,7 @@ group :develop do
gem 'compass'
gem 'annotate'
end
+
+group :test, :development do
+ gem 'rspec-rails'
+end
diff --git a/Gemfile.lock b/Gemfile.lock
index e882c0b39..b6fc69219 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,20 +1,41 @@
GEM
remote: http://rubygems.org/
specs:
- actionmailer (2.3.14)
- actionpack (= 2.3.14)
- actionpack (2.3.14)
- activesupport (= 2.3.14)
- rack (~> 1.1.0)
- activerecord (2.3.14)
- activesupport (= 2.3.14)
- activeresource (2.3.14)
- activesupport (= 2.3.14)
- activesupport (2.3.14)
- annotate (2.4.0)
+ abstract (1.0.0)
+ actionmailer (3.0.17)
+ actionpack (= 3.0.17)
+ mail (~> 2.2.19)
+ actionpack (3.0.17)
+ activemodel (= 3.0.17)
+ activesupport (= 3.0.17)
+ builder (~> 2.1.2)
+ erubis (~> 2.6.6)
+ i18n (~> 0.5.0)
+ rack (~> 1.2.5)
+ rack-mount (~> 0.6.14)
+ rack-test (~> 0.5.7)
+ tzinfo (~> 0.3.23)
+ activemodel (3.0.17)
+ activesupport (= 3.0.17)
+ builder (~> 2.1.2)
+ i18n (~> 0.5.0)
+ activerecord (3.0.17)
+ activemodel (= 3.0.17)
+ activesupport (= 3.0.17)
+ arel (~> 2.0.10)
+ tzinfo (~> 0.3.23)
+ activeresource (3.0.17)
+ activemodel (= 3.0.17)
+ activesupport (= 3.0.17)
+ activesupport (3.0.17)
+ annotate (2.5.0)
+ rake
archive-tar-minitar (0.5.2)
- bootstrap-sass (2.1.1.0)
- capistrano (2.13.4)
+ arel (2.0.10)
+ bootstrap-sass (2.2.1.1)
+ sass (~> 3.2)
+ builder (2.1.2)
+ capistrano (2.13.5)
highline
net-scp (>= 1.0.0)
net-sftp (>= 2.0.0)
@@ -27,29 +48,36 @@ GEM
fssm (>= 0.2.7)
sass (~> 3.1)
daemons (1.1.9)
- erubis (2.7.0)
+ diff-lcs (1.1.3)
+ erubis (2.6.6)
+ abstract (>= 1.0.0)
eventmachine (1.0.0)
fakeweb (1.3.0)
- fast_gettext (0.6.8)
+ fast_gettext (0.6.11)
fastercsv (1.5.5)
fssm (0.2.9)
gettext (2.3.3)
locale
- gettext_i18n_rails (0.7.1)
+ gettext_i18n_rails (0.8.0)
fast_gettext (>= 0.4.8)
+ globalize3 (0.2.0)
+ activemodel (>= 3.0.0)
+ activerecord (>= 3.0.0)
+ paper_trail (~> 2)
haml (3.1.7)
- highline (1.6.13)
- hoe (3.0.8)
- rake (~> 0.8)
- i18n (0.6.1)
+ highline (1.6.15)
+ hoe (3.3.1)
+ rake (>= 0.8, < 11.0)
+ i18n (0.5.0)
json (1.5.4)
linecache (0.46)
rbx-require-relative (> 0.0.4)
linecache19 (0.5.12)
ruby_core_source (>= 0.1.4)
- locale (2.0.5)
+ locale (2.0.8)
mahoro (0.3)
- mail (2.4.4)
+ mail (2.2.19)
+ activesupport (>= 2.3.6)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
@@ -71,32 +99,54 @@ GEM
net-ssh (>= 1.99.1)
net-sftp (2.0.5)
net-ssh (>= 2.0.9)
- net-ssh (2.5.2)
+ net-ssh (2.6.2)
net-ssh-gateway (1.1.0)
net-ssh (>= 1.99.1)
- newrelic_rpm (3.4.1)
- pg (0.13.2)
+ newrelic_rpm (3.5.3.25)
+ nokogiri (1.5.5)
+ paper_trail (2.6.4)
+ activerecord (~> 3.0)
+ railties (~> 3.0)
+ pg (0.14.1)
polyglot (0.3.3)
- rack (1.1.3)
- rails (2.3.14)
- actionmailer (= 2.3.14)
- actionpack (= 2.3.14)
- activerecord (= 2.3.14)
- activeresource (= 2.3.14)
- activesupport (= 2.3.14)
- rake (>= 0.8.3)
- rails_xss (0.3.1)
- rake (0.8.7)
+ rack (1.2.5)
+ rack-mount (0.6.14)
+ rack (>= 1.0.0)
+ rack-test (0.5.7)
+ rack (>= 1.0)
+ rails (3.0.17)
+ actionmailer (= 3.0.17)
+ actionpack (= 3.0.17)
+ activerecord (= 3.0.17)
+ activeresource (= 3.0.17)
+ activesupport (= 3.0.17)
+ bundler (~> 1.0)
+ railties (= 3.0.17)
+ railties (3.0.17)
+ actionpack (= 3.0.17)
+ activesupport (= 3.0.17)
+ rake (>= 0.8.7)
+ rdoc (~> 3.4)
+ thor (~> 0.14.4)
+ rake (0.9.2.2)
rbx-require-relative (0.0.9)
- rdoc (2.4.3)
+ rdoc (3.12)
+ json (~> 1.4)
recaptcha (0.3.4)
rmagick (2.13.1)
routing-filter (0.2.4)
actionpack
- rspec (1.3.2)
- rspec-rails (1.3.4)
- rack (>= 1.0.0)
- rspec (~> 1.3.1)
+ rspec-core (2.12.1)
+ rspec-expectations (2.12.0)
+ diff-lcs (~> 1.1.3)
+ rspec-mocks (2.12.0)
+ rspec-rails (2.12.0)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ railties (>= 3.0)
+ rspec-core (~> 2.12.0)
+ rspec-expectations (~> 2.12.0)
+ rspec-mocks (~> 2.12.0)
ruby-debug (0.10.4)
columnize (>= 0.1)
ruby-debug-base (~> 0.10.4.0)
@@ -110,13 +160,13 @@ GEM
columnize (>= 0.3.1)
linecache19 (>= 0.5.11)
ruby-debug-base19 (>= 0.11.19)
- ruby-msg (1.5.0)
+ ruby-msg (1.5.1)
ruby-ole (>= 1.2.8)
vpim (>= 0.360)
- ruby-ole (1.2.11.3)
+ ruby-ole (1.2.11.6)
ruby_core_source (0.1.5)
archive-tar-minitar (>= 0.5.2)
- sass (3.2.1)
+ sass (3.2.3)
sinatra (1.2.8)
rack (~> 1.1)
tilt (>= 1.2.2, < 2.0)
@@ -133,14 +183,21 @@ GEM
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
+ thor (0.14.6)
tilt (1.3.3)
- treetop (1.4.11)
+ tmail (1.2.7.1)
+ treetop (1.4.12)
polyglot
polyglot (>= 0.3.1)
+ tzinfo (0.3.35)
vpim (0.695)
+ webrat (0.7.3)
+ nokogiri (>= 1.2.0)
+ rack (>= 1.0)
+ rack-test (>= 0.5.3)
will_paginate (2.3.16)
xapian-full-alaveteli (1.2.9.5)
- xml-simple (1.1.1)
+ xml-simple (1.1.2)
zip (2.0.2)
PLATFORMS
@@ -151,12 +208,12 @@ DEPENDENCIES
bootstrap-sass
capistrano
compass
- erubis
fakeweb
fast_gettext (>= 0.6.0)
fastercsv (>= 1.5.5)
gettext (~> 2.3.3)
gettext_i18n_rails (>= 0.7.1)
+ globalize3
json (~> 1.5.1)
locale (>= 2.0.5)
mahoro
@@ -167,22 +224,22 @@ DEPENDENCIES
net-purge
newrelic_rpm
pg
- rack (~> 1.1.0)
- rails (= 2.3.14)
- rails_xss
- rake (~> 0.8.7)
- rdoc (~> 2.4.3)
+ rack
+ rails (= 3.0.17)
+ rake (= 0.9.2.2)
+ rdoc
recaptcha (~> 0.3.1)
rmagick
routing-filter (~> 0.2.4)
- rspec (~> 1.3.2)
- rspec-rails (~> 1.3.4)
+ rspec-rails
ruby-debug
ruby-debug19
ruby-msg (~> 1.5.0)
syslog_protocol
test-unit (~> 1.2.3)
+ tmail
vpim
+ webrat
will_paginate (~> 2.3.11)
xapian-full-alaveteli (~> 1.2.9.5)
xml-simple
diff --git a/README-rails.txt b/README-rails.txt
deleted file mode 100644
index 025ee5e84..000000000
--- a/README-rails.txt
+++ /dev/null
@@ -1,188 +0,0 @@
-== Welcome to Rails
-
-Rails is a web-application and persistence framework that includes everything
-needed to create database-backed web-applications according to the
-Model-View-Control pattern of separation. This pattern splits the view (also
-called the presentation) into "dumb" templates that are primarily responsible
-for inserting pre-built data in between HTML tags. The model contains the
-"smart" domain objects (such as Account, Product, Person, Post) that holds all
-the business logic and knows how to persist themselves to a database. The
-controller handles the incoming requests (such as Save New Account, Update
-Product, Show Post) by manipulating the model and directing data to the view.
-
-In Rails, the model is handled by what's called an object-relational mapping
-layer entitled Active Record. This layer allows you to present the data from
-database rows as objects and embellish these data objects with business logic
-methods. You can read more about Active Record in
-link:files/vendor/rails/activerecord/README.html.
-
-The controller and view are handled by the Action Pack, which handles both
-layers by its two parts: Action View and Action Controller. These two layers
-are bundled in a single package due to their heavy interdependence. This is
-unlike the relationship between the Active Record and Action Pack that is much
-more separate. Each of these packages can be used independently outside of
-Rails. You can read more about Action Pack in
-link:files/vendor/rails/actionpack/README.html.
-
-
-== Getting started
-
-1. At the command prompt, start a new rails application using the rails command
- and your application name. Ex: rails myapp
- (If you've downloaded rails in a complete tgz or zip, this step is already done)
-2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
-3. Go to http://localhost:3000/ and get "Welcome aboard: You’re riding the Rails!"
-4. Follow the guidelines to start developing your application
-
-
-== Web Servers
-
-By default, Rails will try to use Mongrel and lighttpd if they are installed, otherwise
-Rails will use the WEBrick, the webserver that ships with Ruby. When you run script/server,
-Rails will check if Mongrel exists, then lighttpd and finally fall back to WEBrick. This ensures
-that you can always get up and running quickly.
-
-Mongrel is a Ruby-based webserver with a C-component (which requires compilation) that is
-suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
-getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
-More info at: http://mongrel.rubyforge.org
-
-If Mongrel is not installed, Rails will look for lighttpd. It's considerably faster than
-Mongrel and WEBrick and also suited for production use, but requires additional
-installation and currently only works well on OS X/Unix (Windows users are encouraged
-to start with Mongrel). We recommend version 1.4.11 and higher. You can download it from
-http://www.lighttpd.net.
-
-And finally, if neither Mongrel or lighttpd are installed, Rails will use the built-in Ruby
-web server, WEBrick. WEBrick is a small Ruby web server suitable for development, but not
-for production.
-
-But of course its also possible to run Rails on any platform that supports FCGI.
-Apache, LiteSpeed, IIS are just a few. For more information on FCGI,
-please visit: http://wiki.rubyonrails.com/rails/pages/FastCGI
-
-
-== Debugging Rails
-
-Have "tail -f" commands running on the server.log and development.log. Rails will
-automatically display debugging and runtime information to these files. Debugging
-info will also be shown in the browser on requests from 127.0.0.1.
-
-
-== Breakpoints
-
-Breakpoint support is available through the script/breakpointer client. This
-means that you can break out of execution at any point in the code, investigate
-and change the model, AND then resume execution! Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.find(:all)
- breakpoint "Breaking out from the list"
- end
- end
-
-So the controller will accept the action, run the first line, then present you
-with a IRB prompt in the breakpointer window. Here you can do things like:
-
-Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
-
- >> @posts.inspect
- => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
- #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
- >> @posts.first.title = "hello from a breakpoint"
- => "hello from a breakpoint"
-
-...and even better is that you can examine how your runtime objects actually work:
-
- >> f = @posts.first
- => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
- >> f.
- Display all 152 possibilities? (y or n)
-
-Finally, when you're ready to resume execution, you press CTRL-D
-
-
-== Console
-
-You can interact with the domain model by starting the console through <tt>script/console</tt>.
-Here you'll have all parts of the application configured, just like it is when the
-application is running. You can inspect domain models, change values, and save to the
-database. Starting the script without arguments will launch it in the development environment.
-Passing an argument will specify a different environment, like <tt>script/console production</tt>.
-
-To reload your controllers and models after launching the console run <tt>reload!</tt>
-
-To reload your controllers and models after launching the console run <tt>reload!</tt>
-
-
-
-== Description of contents
-
-app
- Holds all the code that's specific to this particular application.
-
-app/controllers
- Holds controllers that should be named like weblogs_controller.rb for
- automated URL mapping. All controllers should descend from ApplicationController
- which itself descends from ActionController::Base.
-
-app/models
- Holds models that should be named like post.rb.
- Most models will descend from ActiveRecord::Base.
-
-app/views
- Holds the template files for the view that should be named like
- weblogs/index.rhtml for the WeblogsController#index action. All views use eRuby
- syntax.
-
-app/views/layouts
- Holds the template files for layouts to be used with views. This models the common
- header/footer method of wrapping views. In your views, define a layout using the
- <tt>layout :default</tt> and create a file named default.rhtml. Inside default.rhtml,
- call <% yield %> to render the view using this layout.
-
-app/helpers
- Holds view helpers that should be named like weblogs_helper.rb. These are generated
- for you automatically when using script/generate for controllers. Helpers can be used to
- wrap functionality for your views into methods.
-
-config
- Configuration files for the Rails environment, the routing map, the database, and other dependencies.
-
-components
- Self-contained mini-applications that can bundle together controllers, models, and views.
-
-db
- Contains the database schema in schema.rb. db/migrate contains all
- the sequence of Migrations for your schema.
-
-doc
- This directory is where your application documentation will be stored when generated
- using <tt>rake doc:app</tt>
-
-lib
- Application specific libraries. Basically, any kind of custom code that doesn't
- belong under controllers, models, or helpers. This directory is in the load path.
-
-public
- The directory available for the web server. Contains subdirectories for images, stylesheets,
- and javascripts. Also contains the dispatchers and the default HTML files. This should be
- set as the DOCUMENT_ROOT of your web server.
-
-script
- Helper scripts for automation and generation.
-
-spec
- Unit and functional specifications along with fixtures, using rspec. We use
- this instead of test.
-
-test
- DEPRECATED for this project, see 'spec' instead. Unit and functional tests
- along with fixtures. When using the script/generate scripts, template test
- files will be generated for you and placed in this directory.
-
-vendor
- External libraries that the application depends on. Also includes the plugins subdirectory.
- This directory is in the load path.
-
diff --git a/README.md b/README.md
index 6c0c15782..7313df071 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,8 @@ move towards a world where governments approach transparency as the
norm, rather than the exception.
Please join our mailing list at
-https://groups.google.com/group/alaveteli-dev and introduce yourself.
+https://groups.google.com/group/alaveteli-dev and introduce yourself, or
+drop a line to hello@alaveteli.org to let us know that you're using Alaveteli.
Some documentation can be found in the
[`doc/` folder](https://github.com/mysociety/alaveteli/tree/master/doc).
diff --git a/Rakefile b/Rakefile
index d4ebade51..6e86b06f6 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,12 +1,7 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
-require(File.join(File.dirname(__FILE__), 'config', 'boot'))
-
+require File.expand_path('../config/application', __FILE__)
require 'rake'
-require 'rake/testtask'
-require 'rdoc/task'
-
-require 'tasks/rails'
-Dir[File.join(File.dirname(__FILE__),'commonlib','rblib','tests','*.rake')].each { |file| load(file) }
+Alaveteli::Application.load_tasks
diff --git a/app/controllers/admin_public_body_controller.rb b/app/controllers/admin_public_body_controller.rb
index ac12e97b2..c41d05c8d 100644
--- a/app/controllers/admin_public_body_controller.rb
+++ b/app/controllers/admin_public_body_controller.rb
@@ -14,7 +14,7 @@ class AdminPublicBodyController < AdminController
def _lookup_query_internal
@locale = self.locale_from_params()
- PublicBody.with_locale(@locale) do
+ I18n.with_locale(@locale) do
@query = params[:query]
if @query == ""
@query = nil
@@ -75,7 +75,7 @@ class AdminPublicBodyController < AdminController
def show
@locale = self.locale_from_params()
- PublicBody.with_locale(@locale) do
+ I18n.with_locale(@locale) do
@public_body = PublicBody.find(params[:id])
render
end
@@ -87,7 +87,7 @@ class AdminPublicBodyController < AdminController
end
def create
- PublicBody.with_locale(I18n.default_locale) do
+ I18n.with_locale(I18n.default_locale) do
params[:public_body][:last_edit_editor] = admin_current_user()
@public_body = PublicBody.new(params[:public_body])
if @public_body.save
@@ -106,7 +106,7 @@ class AdminPublicBodyController < AdminController
end
def update
- PublicBody.with_locale(I18n.default_locale) do
+ I18n.with_locale(I18n.default_locale) do
params[:public_body][:last_edit_editor] = admin_current_user()
@public_body = PublicBody.find(params[:id])
if @public_body.update_attributes(params[:public_body])
@@ -120,7 +120,7 @@ class AdminPublicBodyController < AdminController
def destroy
@locale = self.locale_from_params()
- PublicBody.with_locale(@locale) do
+ I18n.with_locale(@locale) do
public_body = PublicBody.find(params[:id])
if public_body.info_requests.size > 0
diff --git a/app/controllers/admin_request_controller.rb b/app/controllers/admin_request_controller.rb
index 1de63be59..c7c8d4972 100644
--- a/app/controllers/admin_request_controller.rb
+++ b/app/controllers/admin_request_controller.rb
@@ -277,7 +277,7 @@ class AdminRequestController < AdminController
if params[:incoming_message_id]
incoming_message = IncomingMessage.find(params[:incoming_message_id])
- email = incoming_message.from_address
+ email = incoming_message.from_email
name = incoming_message.safe_mail_from || info_request.public_body.name
else
email = info_request.public_body.request_email
diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb
index aa5e85db3..15fb4f5f9 100644
--- a/app/controllers/api_controller.rb
+++ b/app/controllers/api_controller.rb
@@ -1,30 +1,30 @@
class ApiController < ApplicationController
before_filter :check_api_key
-
+
def show_request
@request = InfoRequest.find(params[:id])
raise PermissionDenied if @request.public_body_id != @public_body.id
-
+
@request_data = {
:id => @request.id,
:url => make_url("request", @request.url_title),
:title => @request.title,
-
+
:created_at => @request.created_at,
:updated_at => @request.updated_at,
-
+
:status => @request.calculate_status,
-
+
:public_body_url => make_url("body", @request.public_body.url_name),
:requestor_url => make_url("user", @request.user.url_name),
:request_email => @request.incoming_email,
-
+
:request_text => @request.last_event_forming_initial_request.outgoing_message.body,
}
-
+
render :json => @request_data
end
-
+
def create_request
json = ActiveSupport::JSON.decode(params[:request_json])
request = InfoRequest.new(
@@ -34,7 +34,7 @@ class ApiController < ApplicationController
:external_user_name => json["external_user_name"],
:external_url => json["external_url"]
)
-
+
outgoing_message = OutgoingMessage.new(
:status => 'ready',
:message_type => 'initial_request',
@@ -44,7 +44,7 @@ class ApiController < ApplicationController
:info_request => request
)
request.outgoing_messages << outgoing_message
-
+
# Return an error if the request is invalid
# (Can this ever happen?)
if !request.valid?
@@ -53,7 +53,7 @@ class ApiController < ApplicationController
}
return
end
-
+
# Save the request, and add the corresponding InfoRequestEvent
request.save!
request.log_event("sent",
@@ -62,69 +62,69 @@ class ApiController < ApplicationController
:outgoing_message_id => outgoing_message.id,
:smtp_message_id => nil
)
-
+
# Return the URL and ID number.
render :json => {
'url' => make_url("request", request.url_title),
'id' => request.id
}
-
+
end
-
+
def add_correspondence
request = InfoRequest.find_by_id(params[:id])
if request.nil?
render :json => { "errors" => ["Could not find request #{params[:id]}"] }, :status => 404
return
end
-
+
json = ActiveSupport::JSON.decode(params[:correspondence_json])
attachments = params[:attachments]
-
+
direction = json["direction"]
body = json["body"]
sent_at_str = json["sent_at"]
-
+
errors = []
-
+
if !request.is_external?
render :json => { "errors" => ["Request #{params[:id]} cannot be updated using the API"] }, :status => 500
return
end
-
+
if request.public_body_id != @public_body.id
render :json => { "errors" => ["You do not own request #{params[:id]}"] }, :status => 500
return
end
-
+
if !["request", "response"].include?(direction)
errors << "The direction parameter must be 'request' or 'response'"
end
-
+
if body.nil?
errors << "The 'body' is missing"
elsif body.empty?
errors << "The 'body' is empty"
end
-
+
begin
sent_at = Time.iso8601(sent_at_str)
rescue ArgumentError
errors << "Failed to parse 'sent_at' field as ISO8601 time: #{sent_at_str}"
end
-
+
if direction == "request" && !attachments.nil?
errors << "You cannot attach files to messages in the 'request' direction"
end
-
+
if !errors.empty?
render :json => { "errors" => errors }, :status => 500
return
end
-
+
if direction == "request"
# In the 'request' direction, i.e. what we (Alaveteli) regard as outgoing
-
+
outgoing_message = OutgoingMessage.new(
:info_request => request,
:status => 'ready',
@@ -154,19 +154,19 @@ class ApiController < ApplicationController
:filename => filename
)
end
-
+
mail = RequestMailer.create_external_response(request, body, sent_at, attachment_hashes)
request.receive(mail, mail.encoded, true)
end
render :json => {
'url' => make_url("request", request.url_title),
- }
+ }
end
-
+
def body_request_events
feed_type = params[:feed_type]
raise PermissionDenied.new("#{@public_body.id} != #{params[:id]}") if @public_body.id != params[:id].to_i
-
+
since_date_str = params[:since_date]
if since_date_str.nil?
@events = InfoRequestEvent.find_by_sql([
@@ -213,7 +213,7 @@ class ApiController < ApplicationController
@event_data = []
@events.each do |event|
break if event.id == @since_event_id
-
+
request = event.info_request
this_event = {
:request_id => request.id,
@@ -224,13 +224,13 @@ class ApiController < ApplicationController
:request_email => request.incoming_email,
:title => request.title,
:body => event.outgoing_message.body,
-
+
:user_name => request.user_name,
}
if request.user
this_event[:user_url] = main_url(user_url(request.user))
end
-
+
@event_data.push(this_event)
end
render :json => @event_data
@@ -238,14 +238,14 @@ class ApiController < ApplicationController
raise ActiveRecord::RecordNotFound.new("Unrecognised feed type: #{feed_type}")
end
end
-
+
protected
def check_api_key
- raise "Missing required parameter 'k'" if params[:k].nil?
+ raise PermissionDenied.new("Missing required parameter 'k'") if params[:k].nil?
@public_body = PublicBody.find_by_api_key(params[:k].gsub(' ', '+'))
raise PermissionDenied if @public_body.nil?
end
-
+
private
def make_url(*args)
"http://" + Configuration::domain + "/" + args.join("/")
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f9649c868..3206df1d2 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -27,9 +27,6 @@ class ApplicationController < ActionController::Base
before_filter :set_vary_header
before_filter :set_popup_banner
- # scrub sensitive parameters from the logs
- filter_parameter_logging :password
-
def set_vary_header
response.headers['Vary'] = 'Cookie'
end
@@ -54,10 +51,15 @@ class ApplicationController < ActionController::Base
end
def set_gettext_locale
+ if Configuration::include_default_locale_in_urls == false
+ params_locale = params[:locale] ? params[:locale] : I18n.default_locale
+ else
+ params_locale = params[:locale]
+ end
if Configuration::use_default_browser_language
- requested_locale = params[:locale] || session[:locale] || cookies[:locale] || request.env['HTTP_ACCEPT_LANGUAGE'] || I18n.default_locale
+ requested_locale = params_locale || session[:locale] || cookies[:locale] || request.env['HTTP_ACCEPT_LANGUAGE'] || I18n.default_locale
else
- requested_locale = params[:locale] || session[:locale] || cookies[:locale] || I18n.default_locale
+ requested_locale = params_locale || session[:locale] || cookies[:locale] || I18n.default_locale
end
requested_locale = FastGettext.best_locale_in(requested_locale)
session[:locale] = FastGettext.set_locale(requested_locale)
@@ -69,9 +71,6 @@ class ApplicationController < ActionController::Base
end
end
- # scrub sensitive parameters from the logs
- filter_parameter_logging :password
-
helper_method :locale_from_params
# Help work out which request causes RAM spike.
@@ -149,19 +148,20 @@ class ApplicationController < ActionController::Base
render :template => "general/exception_caught.rhtml", :status => @status
end
- # For development sites.
- alias original_rescue_action_locally rescue_action_locally
- def rescue_action_locally(exception)
- # Make sure expiry time for session is set (before_filters are
- # otherwise missed by this override)
- session_remember_me
+ # FIXME: This was disabled during the Rails 3 upgrade as this is now handled by Rack
+ # # For development sites.
+ # alias original_rescue_action_locally rescue_action_locally
+ # def rescue_action_locally(exception)
+ # # Make sure expiry time for session is set (before_filters are
+ # # otherwise missed by this override)
+ # session_remember_me
- # Make sure the locale is set correctly too
- set_gettext_locale
+ # # Make sure the locale is set correctly too
+ # set_gettext_locale
- # Display default, detailed error for developers
- original_rescue_action_locally(exception)
- end
+ # # Display default, detailed error for developers
+ # original_rescue_action_locally(exception)
+ # end
def local_request?
false
@@ -240,7 +240,7 @@ class ApplicationController < ActionController::Base
# Check the user is logged in
def authenticated?(reason_params)
unless session[:user_id]
- post_redirect = PostRedirect.new(:uri => request.request_uri, :post_params => params,
+ post_redirect = PostRedirect.new(:uri => request.fullpath, :post_params => params,
:reason_params => reason_params)
post_redirect.save!
# 'modal' controls whether the sign-in form will be displayed in the typical full-blown
diff --git a/app/controllers/general_controller.rb b/app/controllers/general_controller.rb
index 3ba636e29..faf34aa04 100644
--- a/app/controllers/general_controller.rb
+++ b/app/controllers/general_controller.rb
@@ -19,53 +19,51 @@ class GeneralController < ApplicationController
# New, improved front page!
def frontpage
medium_cache
- behavior_cache :tag => [session[:user_id], request.url] do
- # get some example searches and public bodies to display
- # either from config, or based on a (slow!) query if not set
- body_short_names = Configuration::frontpage_publicbody_examples.split(/\s*;\s*/).map{|s| "'%s'" % s.gsub(/'/, "''") }.join(", ")
- @locale = self.locale_from_params()
- locale_condition = 'public_body_translations.locale = ?'
- conditions = [locale_condition, @locale]
- PublicBody.with_locale(@locale) do
- if body_short_names.empty?
- # This is too slow
- @popular_bodies = PublicBody.visible.find(:all,
- :order => "info_requests_count desc",
- :limit => 32,
- :conditions => conditions,
- :joins => :translations
- )
- else
- conditions[0] += " and public_bodies.url_name in (" + body_short_names + ")"
- @popular_bodies = PublicBody.find(:all,
- :conditions => conditions,
- :joins => :translations)
- end
+ # get some example searches and public bodies to display
+ # either from config, or based on a (slow!) query if not set
+ body_short_names = Configuration::frontpage_publicbody_examples.split(/\s*;\s*/).map{|s| "'%s'" % s.gsub(/'/, "''") }.join(", ")
+ @locale = self.locale_from_params()
+ locale_condition = 'public_body_translations.locale = ?'
+ conditions = [locale_condition, @locale]
+ I18n.with_locale(@locale) do
+ if body_short_names.empty?
+ # This is too slow
+ @popular_bodies = PublicBody.visible.find(:all,
+ :order => "info_requests_count desc",
+ :limit => 32,
+ :conditions => conditions,
+ :joins => :translations
+ )
+ else
+ conditions[0] += " and public_bodies.url_name in (" + body_short_names + ")"
+ @popular_bodies = PublicBody.find(:all,
+ :conditions => conditions,
+ :joins => :translations)
end
- # Get some successful requests
- begin
- query = 'variety:response (status:successful OR status:partially_successful)'
- sortby = "newest"
- max_count = 5
- xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_title_collapse', max_count)
- @request_events = xapian_object.results.map { |r| r[:model] }
-
- # If there are not yet enough successful requests, fill out the list with
- # other requests
- if @request_events.count < max_count
- @request_events_all_successful = false
- query = 'variety:sent'
- xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_title_collapse', max_count-@request_events.count)
- more_events = xapian_object.results.map { |r| r[:model] }
- @request_events += more_events
- # Overall we still want the list sorted with the newest first
- @request_events.sort!{|e1,e2| e2.created_at <=> e1.created_at}
- else
- @request_events_all_successful = true
- end
- rescue
- @request_events = []
+ end
+ # Get some successful requests
+ begin
+ query = 'variety:response (status:successful OR status:partially_successful)'
+ sortby = "newest"
+ max_count = 5
+ xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_title_collapse', max_count)
+ @request_events = xapian_object.results.map { |r| r[:model] }
+
+ # If there are not yet enough successful requests, fill out the list with
+ # other requests
+ if @request_events.count < max_count
+ @request_events_all_successful = false
+ query = 'variety:sent'
+ xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_title_collapse', max_count-@request_events.count)
+ more_events = xapian_object.results.map { |r| r[:model] }
+ @request_events += more_events
+ # Overall we still want the list sorted with the newest first
+ @request_events.sort!{|e1,e2| e2.created_at <=> e1.created_at}
+ else
+ @request_events_all_successful = true
end
+ rescue
+ @request_events = []
end
end
diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb
index 8a4a65820..5265706bf 100644
--- a/app/controllers/public_body_controller.rb
+++ b/app/controllers/public_body_controller.rb
@@ -16,7 +16,7 @@ class PublicBodyController < ApplicationController
return
end
@locale = self.locale_from_params()
- PublicBody.with_locale(@locale) do
+ I18n.with_locale(@locale) do
@public_body = PublicBody.find_by_url_name_with_historic(params[:url_name])
raise ActiveRecord::RecordNotFound.new("None found") if @public_body.nil?
if @public_body.url_name.nil?
@@ -71,7 +71,7 @@ class PublicBodyController < ApplicationController
@public_body = PublicBody.find_by_url_name_with_historic(params[:url_name])
raise ActiveRecord::RecordNotFound.new("None found") if @public_body.nil?
- PublicBody.with_locale(self.locale_from_params()) do
+ I18n.with_locale(self.locale_from_params()) do
if params[:submitted_view_email]
if verify_recaptcha
flash.discard(:error)
@@ -129,7 +129,7 @@ class PublicBodyController < ApplicationController
@description = _("in the category ‘{{category_name}}’", :category_name=>category_name)
end
end
- PublicBody.with_locale(@locale) do
+ I18n.with_locale(@locale) do
@public_bodies = PublicBody.paginate(
:order => "public_body_translations.name", :page => params[:page], :per_page => 100,
:conditions => conditions,
diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb
index e82491bbe..17d2d9428 100644
--- a/app/controllers/request_controller.rb
+++ b/app/controllers/request_controller.rb
@@ -52,7 +52,7 @@ class RequestController < ApplicationController
medium_cache
end
@locale = self.locale_from_params()
- PublicBody.with_locale(@locale) do
+ I18n.with_locale(@locale) do
# Look up by old style numeric identifiers
if params[:url_title].match(/^[0-9]+$/)
@@ -99,15 +99,13 @@ class RequestController < ApplicationController
# Sidebar stuff
# ... requests that have similar imporant terms
- behavior_cache :tag => ['similar', @info_request.id] do
- begin
- limit = 10
- @xapian_similar = ::ActsAsXapian::Similar.new([InfoRequestEvent], @info_request.info_request_events,
- :limit => limit, :collapse_by_prefix => 'request_collapse')
- @xapian_similar_more = (@xapian_similar.matches_estimated > limit)
- rescue
- @xapian_similar = nil
- end
+ begin
+ limit = 10
+ @xapian_similar = ::ActsAsXapian::Similar.new([InfoRequestEvent], @info_request.info_request_events,
+ :limit => limit, :collapse_by_prefix => 'request_collapse')
+ @xapian_similar_more = (@xapian_similar.matches_estimated > limit)
+ rescue
+ @xapian_similar = nil
end
# Track corresponding to this page
@@ -173,13 +171,10 @@ class RequestController < ApplicationController
query = make_query_from_params
@title = _("View and search requests")
sortby = "newest"
- @cache_tag = Digest::MD5.hexdigest(query + @page.to_s + I18n.locale.to_s)
- behavior_cache :tag => [@cache_tag] do
- xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_collapse')
- @list_results = xapian_object.results.map { |r| r[:model] }
- @matches_estimated = xapian_object.matches_estimated
- @show_no_more_than = (@matches_estimated > MAX_RESULTS) ? MAX_RESULTS : @matches_estimated
- end
+ xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_collapse')
+ @list_results = xapian_object.results.map { |r| r[:model] }
+ @matches_estimated = xapian_object.matches_estimated
+ @show_no_more_than = (@matches_estimated > MAX_RESULTS) ? MAX_RESULTS : @matches_estimated
@title = @title + " (page " + @page.to_s + ")" if (@page > 1)
@track_thing = TrackThing.create_track_for_search_query(query)
@@ -804,7 +799,7 @@ class RequestController < ApplicationController
# FOI officers can upload a response
def upload_response
@locale = self.locale_from_params()
- PublicBody.with_locale(@locale) do
+ I18n.with_locale(@locale) do
@info_request = InfoRequest.find_by_url_title!(params[:url_title])
@reason_params = {
@@ -861,7 +856,7 @@ class RequestController < ApplicationController
def download_entire_request
@locale = self.locale_from_params()
- PublicBody.with_locale(@locale) do
+ I18n.with_locale(@locale) do
info_request = InfoRequest.find_by_url_title!(params[:url_title])
if authenticated?(
:web => _("To download the zip file"),
diff --git a/app/models/application_mailer.rb b/app/models/application_mailer.rb
index cdb279c3c..859e8a0e2 100644
--- a/app/models/application_mailer.rb
+++ b/app/models/application_mailer.rb
@@ -67,7 +67,8 @@ class ApplicationMailer < ActionMailer::Base
return nil
end
- if ActionMailer::VERSION::MAJOR == 2
+ # FIXME: This check was disabled temporarily during the Rails 3 upgrade
+ #if ActionMailer::VERSION::MAJOR == 2
# This method is a customised version of ActionMailer::Base.create!
# modified to allow templates to be selected correctly for multipart
@@ -142,9 +143,9 @@ class ApplicationMailer < ActionMailer::Base
# build the mail object itself
@mail = create_mail
end
- else
- raise "ApplicationMailer.create! is obsolete - find another way to ensure that themes can override mail templates for multipart mails"
- end
+ # else
+ # raise "ApplicationMailer.create! is obsolete - find another way to ensure that themes can override mail templates for multipart mails"
+ # end
end
diff --git a/app/models/censor_rule.rb b/app/models/censor_rule.rb
index f40ab6fbb..ec66074b7 100644
--- a/app/models/censor_rule.rb
+++ b/app/models/censor_rule.rb
@@ -33,13 +33,15 @@ class CensorRule < ActiveRecord::Base
validate :require_valid_regexp, :if => proc{ |rule| rule.regexp? == true }
validates_presence_of :text
- named_scope :global, {:conditions => {:info_request_id => nil,
- :user_id => nil,
- :public_body_id => nil}}
+ scope :global, {:conditions => {:info_request_id => nil,
+ :user_id => nil,
+ :public_body_id => nil}}
def require_user_request_or_public_body
if self.info_request.nil? && self.user.nil? && self.public_body.nil?
- errors.add("Censor must apply to an info request a user or a body; ")
+ [:info_request, :user, :public_body].each do |a|
+ errors.add(a, "Rule must apply to an info request, a user or a body")
+ end
end
end
diff --git a/app/models/foi_attachment.rb b/app/models/foi_attachment.rb
index a40898aef..2f8a9ab04 100644
--- a/app/models/foi_attachment.rb
+++ b/app/models/foi_attachment.rb
@@ -38,7 +38,7 @@ class FoiAttachment < ActiveRecord::Base
BODY_MAX_DELAY = 5
def directory
- rails_env = ENV['RAILS_ENV']
+ rails_env = Rails.env
if rails_env.nil? || rails_env.empty?
raise "$RAILS_ENV is not set"
end
@@ -67,9 +67,22 @@ class FoiAttachment < ActiveRecord::Base
file.write d
}
update_display_size!
+ encode_cached_body!
@cached_body = d
end
+ # If the original mail part had a charset, it's some kind of string, so assume that
+ # it should be handled as a string in the stated charset, not a bytearray, and then
+ # convert it our default encoding. For ruby 1.8 this is a noop.
+ def encode_cached_body!
+ if RUBY_VERSION.to_f >= 1.9
+ if charset
+ @cached_body.force_encoding(charset)
+ @cached_body = @cached_body.encode(Encoding.default_internal, charset)
+ end
+ end
+ end
+
def body
if @cached_body.nil?
tries = 0
@@ -90,6 +103,7 @@ class FoiAttachment < ActiveRecord::Base
self.incoming_message.parse_raw_email!(force)
retry
end
+ encode_cached_body!
end
return @cached_body
end
@@ -310,31 +324,41 @@ class FoiAttachment < ActiveRecord::Base
# the extractions will also produce image files, which go in the
# current directory, so change to the directory the function caller
# wants everything in
- Dir.chdir(dir) do
- tempfile = Tempfile.new('foiextract', '.')
- tempfile.print self.body
- tempfile.flush
-
- html = nil
- if self.content_type == 'application/pdf'
- # We set a timeout here, because pdftohtml can spiral out of control
- # on some PDF files and we don’t want to crash the whole server.
- html = AlaveteliExternalCommand.run("pdftohtml", "-nodrm", "-zoom", "1.0", "-stdout", "-enc", "UTF-8", "-noframes", tempfile.path, :timeout => 30)
- elsif self.content_type == 'application/rtf'
- html = AlaveteliExternalCommand.run("unrtf", "--html", tempfile.path, :timeout => 120)
- end
-
- if html.nil?
- if self.has_google_docs_viewer?
- html = '' # force error and using Google docs viewer
+
+ html = nil
+ if ['application/pdf', 'application/rtf'].include?(self.content_type)
+ text = self.body
+ Dir.chdir(dir) do
+ if RUBY_VERSION.to_f >= 1.9
+ tempfile = Tempfile.new('foiextract', '.', :encoding => text.encoding)
else
- raise "No HTML conversion available for type " + self.content_type
+ tempfile = Tempfile.new('foiextract', '.')
end
- end
+ tempfile.print text
+ tempfile.flush
+
- tempfile.close
- tempfile.delete
+ if self.content_type == 'application/pdf'
+ # We set a timeout here, because pdftohtml can spiral out of control
+ # on some PDF files and we don't want to crash the whole server.
+ html = AlaveteliExternalCommand.run("pdftohtml", "-nodrm", "-zoom", "1.0", "-stdout", "-enc", "UTF-8", "-noframes", tempfile.path, :timeout => 30)
+ elsif self.content_type == 'application/rtf'
+ html = AlaveteliExternalCommand.run("unrtf", "--html", tempfile.path, :timeout => 120)
+ end
+
+ tempfile.close
+ tempfile.delete
+ end
end
+ if html.nil?
+ if self.has_google_docs_viewer?
+ html = '' # force error and using Google docs viewer
+ else
+ raise "No HTML conversion available for type " + self.content_type
+ end
+ end
+
+
# We need to look at:
# a) Any error code
diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb
index 98124b28e..ee8b92f11 100644
--- a/app/models/incoming_message.rb
+++ b/app/models/incoming_message.rb
@@ -70,41 +70,27 @@ class IncomingMessage < ActiveRecord::Base
@mail
end
- def from_address
- self.mail.from_addrs[0].address
- end
-
def empty_from_field?
self.mail.from_addrs.nil? || self.mail.from_addrs.size == 0
end
def from_email
- self.mail.from_addrs[0].spec
+ MailHandler.get_from_address(self.mail)
end
def addresses
- ((self.mail.to || []) +
- (self.mail.cc || []) +
- (self.mail.envelope_to || [])).uniq
+ MailHandler.get_all_addresses(self.mail)
end
def message_id
self.mail.message_id
end
- # Returns the name of the person the incoming message is from, or nil if
- # there isn't one or if there is only an email address. XXX can probably
- # remove from_name_if_present (which is a monkey patch) by just calling
- # .from_addrs[0].name here instead?
-
# Return false if for some reason this is a message that we shouldn't let them reply to
def _calculate_valid_to_reply_to
# check validity of email
- if empty_from_field?
- return false
- end
email = self.from_email
- if !MySociety::Validate.is_valid_email(email)
+ if email.nil? || !MySociety::Validate.is_valid_email(email)
return false
end
@@ -114,13 +100,13 @@ class IncomingMessage < ActiveRecord::Base
prefix = email
prefix =~ /^(.*)@/
prefix = $1
- if !prefix.nil? && prefix.downcase.match(/^(postmaster|mailer-daemon|auto_reply|donotreply|no.reply)$/)
+ if !prefix.nil? && prefix.downcase.match(/^(postmaster|mailer-daemon|auto_reply|do.?not.?reply|no.reply)$/)
return false
end
- if !self.mail['return-path'].nil? && self.mail['return-path'].addr == "<>"
+ if MailHandler.empty_return_path?(self.mail)
return false
end
- if !self.mail['auto-submitted'].nil?
+ if !MailHandler.get_auto_submitted(self.mail).nil?
return false
end
return true
@@ -138,13 +124,10 @@ class IncomingMessage < ActiveRecord::Base
self.extract_attachments!
self.sent_at = self.mail.date || self.created_at
self.subject = self.mail.subject
- # XXX can probably remove from_name_if_present (which is a
- # monkey patch) by just calling .from_addrs[0].name here
- # instead?
- self.mail_from = self.mail.from_name_if_present
- begin
+ self.mail_from = MailHandler.get_from_name(self.mail)
+ if self.from_email
self.mail_from_domain = PublicBody.extract_domain_from_email(self.from_email)
- rescue NoMethodError
+ else
self.mail_from_domain = ""
end
self.valid_to_reply_to = self._calculate_valid_to_reply_to
@@ -190,54 +173,8 @@ class IncomingMessage < ActiveRecord::Base
super
end
- # Number the attachments in depth first tree order, for use in URLs.
- # XXX This fills in part.rfc822_attachment and part.url_part_number within
- # all the parts of the email (see monkeypatches in lib/mail_handler/tmail_extensions and
- # lib/mail_handler/mail_extensions for how these attributes are added). ensure_parts_counted
- # must be called before using the attributes.
- def ensure_parts_counted
- @count_parts_count = 0
- _count_parts_recursive(self.mail)
- # we carry on using these numeric ids for attachments uudecoded from within text parts
- @count_first_uudecode_count = @count_parts_count
- end
- def _count_parts_recursive(part)
- if part.multipart?
- part.parts.each do |p|
- _count_parts_recursive(p)
- end
- else
- part_filename = MailHandler.get_part_file_name(part)
- begin
- if part.content_type == 'message/rfc822'
- # An email attached as text
- # e.g. http://www.whatdotheyknow.com/request/64/response/102
- part.rfc822_attachment = MailHandler.mail_from_raw_email(part.body, decode=false)
- elsif part.content_type == 'application/vnd.ms-outlook' || part_filename && AlaveteliFileTypes.filename_to_mimetype(part_filename) == 'application/vnd.ms-outlook'
- # An email attached as an Outlook file
- # e.g. http://www.whatdotheyknow.com/request/chinese_names_for_british_politi
- msg = Mapi::Msg.open(StringIO.new(part.body))
- part.rfc822_attachment = MailHandler.mail_from_raw_email(msg.to_mime.to_s, decode=false)
- elsif part.content_type == 'application/ms-tnef'
- # A set of attachments in a TNEF file
- part.rfc822_attachment = MailHandler.mail_from_tnef(part.body)
- end
- rescue
- # If attached mail doesn't parse, treat it as text part
- part.rfc822_attachment = nil
- else
- unless part.rfc822_attachment.nil?
- _count_parts_recursive(part.rfc822_attachment)
- end
- end
- if part.rfc822_attachment.nil?
- @count_parts_count += 1
- part.url_part_number = @count_parts_count
- end
- end
- end
# And look up by URL part number to get an attachment
- # XXX relies on extract_attachments calling ensure_parts_counted
+ # XXX relies on extract_attachments calling MailHandler.ensure_parts_counted
def self.get_attachment_by_url_part_number(attachments, found_url_part_number)
attachments.each do |a|
if a.url_part_number == found_url_part_number
@@ -458,95 +395,6 @@ class IncomingMessage < ActiveRecord::Base
return text
end
- # (This risks losing info if the unchosen alternative is the only one to contain
- # useful info, but let's worry about that another time)
- def get_attachment_leaves
- force = true
- return _get_attachment_leaves_recursive(self.mail(force))
- end
- def _get_attachment_leaves_recursive(curr_mail, within_rfc822_attachment = nil)
- leaves_found = []
- if curr_mail.multipart?
- if curr_mail.parts.size == 0
- raise "no parts on multipart mail"
- end
-
- if curr_mail.sub_type == 'alternative'
- # Choose best part from alternatives
- best_part = nil
- # Take the last text/plain one, or else the first one
- curr_mail.parts.each do |m|
- if not best_part
- best_part = m
- elsif m.content_type == 'text/plain'
- best_part = m
- end
- end
- # Take an HTML one as even higher priority. (They tend
- # to render better than text/plain, e.g. don't wrap links here:
- # http://www.whatdotheyknow.com/request/amount_and_cost_of_freedom_of_in#incoming-72238 )
- curr_mail.parts.each do |m|
- if m.content_type == 'text/html'
- best_part = m
- end
- end
- leaves_found += _get_attachment_leaves_recursive(best_part, within_rfc822_attachment)
- else
- # Add all parts
- curr_mail.parts.each do |m|
- leaves_found += _get_attachment_leaves_recursive(m, within_rfc822_attachment)
- end
- end
- else
- # XXX Yuck. this section alters various content_type's. That puts
- # it into conflict with ensure_parts_counted which it has to be
- # called both before and after. It will fail with cases of
- # attachments of attachments etc.
- charset = curr_mail.charset # save this, because overwriting content_type also resets charset
- # Don't allow nil content_types
- if curr_mail.content_type.nil?
- curr_mail.content_type = 'application/octet-stream'
- end
- # PDFs often come with this mime type, fix it up for view code
- if curr_mail.content_type == 'application/octet-stream'
- part_file_name = MailHandler.get_part_file_name(curr_mail)
- calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(part_file_name, curr_mail.body)
- if calc_mime
- curr_mail.content_type = calc_mime
- end
- end
-
- # Use standard content types for Word documents etc.
- curr_mail.content_type = normalise_content_type(curr_mail.content_type)
- if curr_mail.content_type == 'message/rfc822'
- ensure_parts_counted # fills in rfc822_attachment variable
- if curr_mail.rfc822_attachment.nil?
- # Attached mail didn't parse, so treat as text
- curr_mail.content_type = 'text/plain'
- end
- end
- if curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef'
- ensure_parts_counted # fills in rfc822_attachment variable
- if curr_mail.rfc822_attachment.nil?
- # Attached mail didn't parse, so treat as binary
- curr_mail.content_type = 'application/octet-stream'
- end
- end
- # If the part is an attachment of email
- if curr_mail.content_type == 'message/rfc822' || curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef'
- ensure_parts_counted # fills in rfc822_attachment variable
- leaves_found += _get_attachment_leaves_recursive(curr_mail.rfc822_attachment, curr_mail.rfc822_attachment)
- else
- # Store leaf
- curr_mail.within_rfc822_attachment = within_rfc822_attachment
- leaves_found += [curr_mail]
- end
- # restore original charset
- curr_mail.charset = charset
- end
- return leaves_found
- end
-
# Removes anything cached about the object in the database, and saves
def clear_in_database_caches!
self.cached_attachment_text_clipped = nil
@@ -609,7 +457,8 @@ class IncomingMessage < ActiveRecord::Base
text = "[ Email has no body, please see attachments ]"
source_charset = "utf-8"
else
- text = part.body # by default, TMail converts to UTF8 in this call
+ # by default, the body (coming from an foi_attachment) should have been converted to utf-8
+ text = part.body
source_charset = part.charset
if part.content_type == 'text/html'
# e.g. http://www.whatdotheyknow.com/request/35/response/177
@@ -617,42 +466,31 @@ class IncomingMessage < ActiveRecord::Base
# convert to text routine. Could instead call a
# sanitize HTML one.
- # If the text isn't UTF8, it means TMail had a problem
+ # If the text isn't UTF8, it means we had a problem
# converting it (invalid characters, etc), and we
# should instead tell elinks to respect the source
# charset
use_charset = "utf-8"
- begin
- text = Iconv.conv('utf-8', 'utf-8', text)
- rescue Iconv::IllegalSequence
- use_charset = source_charset
- end
- text = self.class._get_attachment_text_internal_one_file(part.content_type, text, use_charset)
- end
- end
-
- # If TMail can't convert text, it just returns it, so we sanitise it.
- begin
- # Test if it's good UTF-8
- text = Iconv.conv('utf-8', 'utf-8', text)
- rescue Iconv::IllegalSequence
- # Text looks like unlabelled nonsense,
- # strip out anything that isn't UTF-8
- begin
- source_charset = 'utf-8' if source_charset.nil?
- text = Iconv.conv('utf-8//IGNORE', source_charset, text) +
- _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]",
- :site_name => Configuration::site_name)
- rescue Iconv::InvalidEncoding, Iconv::IllegalSequence
- if source_charset != "utf-8"
- source_charset = "utf-8"
- retry
+ if RUBY_VERSION.to_f >= 1.9
+ begin
+ text.encode('utf-8')
+ rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
+ use_charset = source_charset
+ end
+ else
+ begin
+ text = Iconv.conv('utf-8', 'utf-8', text)
+ rescue Iconv::IllegalSequence
+ use_charset = source_charset
+ end
end
+ text = MailHandler.get_attachment_text_one_file(part.content_type, text, use_charset)
end
end
+ # If text hasn't been converted, we sanitise it.
+ text = _sanitize_text(text)
# Fix DOS style linefeeds to Unix style ones (or other later regexps won't work)
- # Needed for e.g. http://www.whatdotheyknow.com/request/60/response/98
text = text.gsub(/\r\n/, "\n")
# Compress extra spaces down to save space, and to stop regular expressions
@@ -662,6 +500,51 @@ class IncomingMessage < ActiveRecord::Base
return text
end
+
+ def _sanitize_text(text)
+ if RUBY_VERSION.to_f >= 1.9
+ begin
+ # Test if it's good UTF-8
+ text.encode('utf-8')
+ rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
+ source_charset = 'utf-8' if source_charset.nil?
+ # strip out anything that isn't UTF-8
+ begin
+ text = text.encode("utf-8", :invalid => :replace,
+ :undef => :replace,
+ :replace => "") +
+ _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]",
+ :site_name => MySociety::Config.get('SITE_NAME', 'Alaveteli'))
+ rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
+ if source_charset != "utf-8"
+ source_charset = "utf-8"
+ retry
+ end
+ end
+ end
+ else
+ begin
+ # Test if it's good UTF-8
+ text = Iconv.conv('utf-8', 'utf-8', text)
+ rescue Iconv::IllegalSequence
+ # Text looks like unlabelled nonsense,
+ # strip out anything that isn't UTF-8
+ begin
+ source_charset = 'utf-8' if source_charset.nil?
+ text = Iconv.conv('utf-8//IGNORE', source_charset, text) +
+ _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]",
+ :site_name => Configuration::site_name)
+ rescue Iconv::InvalidEncoding, Iconv::IllegalSequence
+ if source_charset != "utf-8"
+ source_charset = "utf-8"
+ retry
+ end
+ end
+ end
+ end
+ text
+ end
+
# Returns part which contains main body text, or nil if there isn't one
def get_main_body_text_part
leaves = self.foi_attachments
@@ -715,7 +598,7 @@ class IncomingMessage < ActiveRecord::Base
filename = uu.match(/^begin\s+[0-9]+\s+(.*)$/)[1]
calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(filename, content)
if calc_mime
- calc_mime = normalise_content_type(calc_mime)
+ calc_mime = MailHandler.normalise_content_type(calc_mime)
content_type = calc_mime
else
content_type = 'application/octet-stream'
@@ -744,55 +627,15 @@ class IncomingMessage < ActiveRecord::Base
end
def extract_attachments!
- leaves = get_attachment_leaves # XXX check where else this is called from
- # XXX we have to call ensure_parts_counted after get_attachment_leaves
- # which is really messy.
- ensure_parts_counted
+ force = true
+ attachment_attributes = MailHandler.get_attachment_attributes(self.mail(force))
attachments = []
- for leaf in leaves
- body = leaf.body
- # As leaf.body causes MIME decoding which uses lots of RAM, do garbage collection here
- # to prevent excess memory use. XXX not really sure if this helps reduce
- # peak RAM use overall. Anyway, maybe there is something better to do than this.
- GC.start
- if leaf.within_rfc822_attachment
- within_rfc822_subject = leaf.within_rfc822_attachment.subject
- # Test to see if we are in the first part of the attached
- # RFC822 message and it is text, if so add headers.
- # XXX should probably use hunting algorithm to find main text part, rather than
- # just expect it to be first. This will do for now though.
- # Example request that needs this:
- # http://www.whatdotheyknow.com/request/2923/response/7013/attach/2/Cycle%20Path%20Bank.txt
- if leaf.within_rfc822_attachment == leaf && leaf.content_type == 'text/plain'
- headers = ""
- for header in [ 'Date', 'Subject', 'From', 'To', 'Cc' ]
- if leaf.within_rfc822_attachment.header.include?(header.downcase)
- header_value = leaf.within_rfc822_attachment.header[header.downcase]
- # Example message which has a blank Date header:
- # http://www.whatdotheyknow.com/request/30747/response/80253/attach/html/17/Common%20Purpose%20Advisory%20Group%20Meeting%20Tuesday%202nd%20March.txt.html
- if !header_value.blank?
- headers = headers + header + ": " + header_value.to_s + "\n"
- end
- end
- end
- # XXX call _convert_part_body_to_text here, but need to get charset somehow
- # e.g. http://www.whatdotheyknow.com/request/1593/response/3088/attach/4/Freedom%20of%20Information%20request%20-%20car%20oval%20sticker:%20Article%2020,%20Convention%20on%20Road%20Traffic%201949.txt
- body = headers + "\n" + body
-
- # This is quick way of getting all headers, but instead we only add some a) to
- # make it more usable, b) as at least one authority accidentally leaked security
- # information into a header.
- #attachment.body = leaf.within_rfc822_attachment.port.to_s
- end
- end
- hexdigest = Digest::MD5.hexdigest(body)
- attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => hexdigest)
- attachment.update_attributes(:url_part_number => leaf.url_part_number,
- :content_type => leaf.content_type,
- :filename => MailHandler.get_part_file_name(leaf),
- :charset => leaf.charset,
- :within_rfc822_subject => within_rfc822_subject,
- :body => body)
+ attachment_attributes.each do |attrs|
+ attachment = self.foi_attachments.find_or_create_by_hexdigest(attrs[:hexdigest])
+ body = attrs.delete(:body)
+ attachment.update_attributes(attrs)
+ # Set the body separately as its handling can depend on the value of charset
+ attachment.body = body
attachment.save!
attachments << attachment.id
end
@@ -802,7 +645,7 @@ class IncomingMessage < ActiveRecord::Base
# e.g. for https://secure.mysociety.org/admin/foi/request/show_raw_email/24550
if !main_part.nil?
uudecoded_attachments = _uudecode_and_save_attachments(main_part.body)
- c = @count_first_uudecode_count
+ c = self.mail.count_first_uudecode_count
for uudecode_attachment in uudecoded_attachments
c += 1
uudecode_attachment.url_part_number = c
@@ -894,101 +737,15 @@ class IncomingMessage < ActiveRecord::Base
return self.cached_attachment_text_clipped
end
- def IncomingMessage._get_attachment_text_internal_one_file(content_type, body, charset = 'utf-8')
- # note re. charset: TMail always tries to convert email bodies
- # to UTF8 by default, so normally it should already be that.
- text = ''
- # XXX - tell all these command line tools to return utf-8
- if content_type == 'text/plain'
- text += body + "\n\n"
- else
- tempfile = Tempfile.new('foiextract')
- tempfile.print body
- tempfile.flush
- if content_type == 'application/vnd.ms-word'
- AlaveteliExternalCommand.run("wvText", tempfile.path, tempfile.path + ".txt")
- # Try catdoc if we get into trouble (e.g. for InfoRequestEvent 2701)
- if not File.exists?(tempfile.path + ".txt")
- AlaveteliExternalCommand.run("catdoc", tempfile.path, :append_to => text)
- else
- text += File.read(tempfile.path + ".txt") + "\n\n"
- File.unlink(tempfile.path + ".txt")
- end
- elsif content_type == 'application/rtf'
- # catdoc on RTF prodcues less comments and extra bumf than --text option to unrtf
- AlaveteliExternalCommand.run("catdoc", tempfile.path, :append_to => text)
- elsif content_type == 'text/html'
- # lynx wordwraps links in its output, which then don't
- # get formatted properly by Alaveteli. We use elinks
- # instead, which doesn't do that.
- AlaveteliExternalCommand.run("elinks", "-eval", "set document.codepage.assume = \"#{charset}\"", "-eval", "set document.codepage.force_assumed = 1", "-dump-charset", "utf-8", "-force-html", "-dump",
- tempfile.path, :append_to => text, :env => {"LANG" => "C"})
- elsif content_type == 'application/vnd.ms-excel'
- # Bit crazy using /usr/bin/strings - but xls2csv, xlhtml and
- # py_xls2txt only extract text from cells, not from floating
- # notes. catdoc may be fooled by weird character sets, but will
- # probably do for UK FOI requests.
- AlaveteliExternalCommand.run("/usr/bin/strings", tempfile.path, :append_to => text)
- elsif content_type == 'application/vnd.ms-powerpoint'
- # ppthtml seems to catch more text, but only outputs HTML when
- # we want text, so just use catppt for now
- AlaveteliExternalCommand.run("catppt", tempfile.path, :append_to => text)
- elsif content_type == 'application/pdf'
- AlaveteliExternalCommand.run("pdftotext", tempfile.path, "-", :append_to => text)
- elsif content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
- # This is Microsoft's XML office document format.
- # Just pull out the main XML file, and strip it of text.
- xml = AlaveteliExternalCommand.run("/usr/bin/unzip", "-qq", "-c", tempfile.path, "word/document.xml")
- if !xml.nil?
- doc = REXML::Document.new(xml)
- text += doc.each_element( './/text()' ){}.join(" ")
- end
- elsif content_type == 'application/zip'
- # recurse into zip files
- begin
- zip_file = Zip::ZipFile.open(tempfile.path)
- text += IncomingMessage._get_attachment_text_from_zip_file(zip_file)
- zip_file.close()
- rescue
- $stderr.puts("Error processing zip file: #{$!.inspect}")
- end
- end
- tempfile.close
- end
-
- return text
- end
- def IncomingMessage._get_attachment_text_from_zip_file(zip_file)
- text = ""
- for entry in zip_file
- if entry.file?
- filename = entry.to_s
- begin
- body = entry.get_input_stream.read
- rescue
- # move to next attachment silently if there were problems
- # XXX really should reduce this to specific exceptions?
- # e.g. password protected
- next
- end
- calc_mime = AlaveteliFileTypes.filename_to_mimetype(filename)
- if calc_mime
- content_type = calc_mime
- else
- content_type = 'application/octet-stream'
- end
- text += _get_attachment_text_internal_one_file(content_type, body)
- end
- end
- return text
- end
def _get_attachment_text_internal
# Extract text from each attachment
text = ''
attachments = self.get_attachments_for_display
for attachment in attachments
- text += IncomingMessage._get_attachment_text_internal_one_file(attachment.content_type, attachment.body, attachment.charset)
+ text += MailHandler.get_attachment_text_one_file(attachment.content_type,
+ attachment.body,
+ attachment.charset)
end
# Remove any bad characters
text = Iconv.conv('utf-8//IGNORE', 'utf-8', text)
@@ -1056,65 +813,11 @@ class IncomingMessage < ActiveRecord::Base
return AlaveteliFileTypes.all_extensions.join(" ")
end
- # Return false if for some reason this is a message that we shouldn't let them reply to
- def valid_to_reply_to?
- # check validity of email
- if empty_from_field?
- return false
- end
- email = self.from_email
- if !MySociety::Validate.is_valid_email(email)
- return false
- end
-
- # reject postmaster - authorities seem to nearly always not respond to
- # email to postmaster, and it tends to only happen after delivery failure.
- # likewise Mailer-Daemon, Auto_Reply...
- prefix = email
- prefix =~ /^(.*)@/
- prefix = $1
- if !prefix.nil? && prefix.downcase.match(/^(postmaster|mailer-daemon|auto_reply|do.?not.?reply|no.reply)$/)
- return false
- end
- if !self.mail['return-path'].nil? && self.mail['return-path'].addr == "<>"
- return false
- end
- if !self.mail['auto-submitted'].nil?
- return false
- end
- return true
- end
-
- def normalise_content_type(content_type)
- # e.g. http://www.whatdotheyknow.com/request/93/response/250
- if content_type == 'application/excel' or content_type == 'application/msexcel' or content_type == 'application/x-ms-excel'
- content_type = 'application/vnd.ms-excel'
- end
- if content_type == 'application/mspowerpoint' or content_type == 'application/x-ms-powerpoint'
- content_type = 'application/vnd.ms-powerpoint'
+ def for_admin_column
+ self.class.content_columns.each do |column|
+ yield(column.human_name, self.send(column.name), column.type.to_s, column.name)
end
- if content_type == 'application/msword' or content_type == 'application/x-ms-word'
- content_type = 'application/vnd.ms-word'
- end
- if content_type == 'application/x-zip-compressed'
- content_type = 'application/zip'
- end
-
- # e.g. http://www.whatdotheyknow.com/request/copy_of_current_swessex_scr_opt#incoming-9928
- if content_type == 'application/acrobat'
- content_type = 'application/pdf'
- end
-
- return content_type
- end
-
- def for_admin_column
- self.class.content_columns.each do |column|
- yield(column.human_name, self.send(column.name), column.type.to_s, column.name)
end
- end
-
- private :normalise_content_type
end
diff --git a/app/models/info_request.rb b/app/models/info_request.rb
index 194f8e105..08b331c26 100644
--- a/app/models/info_request.rb
+++ b/app/models/info_request.rb
@@ -27,7 +27,7 @@ require 'digest/sha1'
class InfoRequest < ActiveRecord::Base
include ActionView::Helpers::UrlHelper
- include ActionController::UrlWriter
+ include Rails.application.routes.url_helpers
strip_attributes!
@@ -51,7 +51,7 @@ class InfoRequest < ActiveRecord::Base
has_tag_string
- named_scope :visible, :conditions => {:prominence => "normal"}
+ scope :visible, :conditions => {:prominence => "normal"}
# user described state (also update in info_request_event, admin_request/edit.rhtml)
validate :must_be_valid_state
@@ -81,6 +81,11 @@ class InfoRequest < ActiveRecord::Base
'blackhole' # just dump them
]
+ # only check on create, so existing models with mixed case are allowed
+ validate :title_formatting, :on => :create
+
+ after_initialize :set_defaults
+
def self.enumerate_states
states = [
'waiting_response',
@@ -138,7 +143,7 @@ class InfoRequest < ActiveRecord::Base
if external_user_name.nil?
fake_slug = "anonymous"
else
- fake_slug = external_user_name.parameterize
+ fake_slug = MySociety::Format.simplify_url_part(external_user_name, 'external_user', 32)
end
(public_body.url_name || "") + "_" + fake_slug
else
@@ -156,31 +161,8 @@ class InfoRequest < ActiveRecord::Base
rescue MissingSourceFile, NameError
end
- # only check on create, so existing models with mixed case are allowed
- def validate_on_create
- if !self.title.nil? && !MySociety::Validate.uses_mixed_capitals(self.title, 10)
- errors.add(:title, _('Please write the summary using a mixture of capital and lower case letters. This makes it easier for others to read.'))
- end
- if !self.title.nil? && title.size > 200
- errors.add(:title, _('Please keep the summary short, like in the subject of an email. You can use a phrase, rather than a full sentence.'))
- end
- if !self.title.nil? && self.title =~ /^(FOI|Freedom of Information)\s*requests?$/i
- errors.add(:title, _('Please describe more what the request is about in the subject. There is no need to say it is an FOI request, we add that on anyway.'))
- end
- end
-
OLD_AGE_IN_DAYS = 21.days
- def after_initialize
- if self.described_state.nil?
- self.described_state = 'waiting_response'
- end
- # FOI or EIR?
- if !self.public_body.nil? && self.public_body.eir_only?
- self.law_used = 'eir'
- end
- end
-
def visible_comments
self.comments.find(:all, :conditions => 'visible')
end
@@ -434,11 +416,11 @@ public
elsif self.allow_new_responses_from == 'anybody'
allow = true
elsif self.allow_new_responses_from == 'authority_only'
- if email.from_addrs.nil? || email.from_addrs.size == 0
+ sender_email = MailHandler.get_from_address(email)
+ if sender_email.nil?
allow = false
reason = _('Only the authority can reply to this request, but there is no "From" address to check against')
else
- sender_email = email.from_addrs[0].spec
sender_domain = PublicBody.extract_domain_from_email(sender_email)
reason = _("Only the authority can reply to this request, and I don't recognise the address this reply was sent from")
allow = false
@@ -1155,5 +1137,29 @@ public
yield(column.human_name, self.send(column.name), column.type.to_s, column.name)
end
end
+
+ private
+
+ def set_defaults
+ if self.described_state.nil?
+ self.described_state = 'waiting_response'
+ end
+ # FOI or EIR?
+ if !self.public_body.nil? && self.public_body.eir_only?
+ self.law_used = 'eir'
+ end
+ end
+
+ def title_formatting
+ if !self.title.nil? && !MySociety::Validate.uses_mixed_capitals(self.title, 10)
+ errors.add(:title, _('Please write the summary using a mixture of capital and lower case letters. This makes it easier for others to read.'))
+ end
+ if !self.title.nil? && title.size > 200
+ errors.add(:title, _('Please keep the summary short, like in the subject of an email. You can use a phrase, rather than a full sentence.'))
+ end
+ if !self.title.nil? && self.title =~ /^(FOI|Freedom of Information)\s*requests?$/i
+ errors.add(:title, _('Please describe more what the request is about in the subject. There is no need to say it is an FOI request, we add that on anyway.'))
+ end
+ end
end
diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb
index 441813e5f..23b5c904b 100644
--- a/app/models/outgoing_message.rb
+++ b/app/models/outgoing_message.rb
@@ -50,6 +50,8 @@ class OutgoingMessage < ActiveRecord::Base
end
end
+ after_initialize :set_default_letter
+
# How the default letter starts and ends
def get_salutation
ret = ""
@@ -129,13 +131,6 @@ class OutgoingMessage < ActiveRecord::Base
MySociety::Validate.contains_postcode?(self.body)
end
- # Set default letter
- def after_initialize
- if self.body.nil?
- self.body = get_default_message
- end
- end
-
# Check have edited letter
def validate
if self.body.empty? || self.body =~ /\A#{get_salutation}\s+#{get_signoff}/ || self.body =~ /#{get_internal_review_insert_here_note}/
@@ -275,6 +270,14 @@ class OutgoingMessage < ActiveRecord::Base
yield(column.human_name, self.send(column.name), column.type.to_s, column.name)
end
end
+
+ private
+
+ def set_default_letter
+ if self.body.nil?
+ self.body = get_default_message
+ end
+ end
end
diff --git a/app/models/post_redirect.rb b/app/models/post_redirect.rb
index 31f08c21a..dfca936e2 100644
--- a/app/models/post_redirect.rb
+++ b/app/models/post_redirect.rb
@@ -32,6 +32,8 @@ class PostRedirect < ActiveRecord::Base
# Optional, does a login confirm before redirect for use in email links.
belongs_to :user
+ after_initialize :generate_token
+
# We store YAML version of POST parameters in the database
def post_params=(params)
self.post_params_yaml = params.to_yaml
@@ -62,18 +64,6 @@ class PostRedirect < ActiveRecord::Base
MySociety::Util.generate_token
end
- # Make the token
- def after_initialize
- # The token is used to return you to what you are doing after the login form.
- if not self.token
- self.token = PostRedirect.generate_random_token
- end
- # There is a separate token to use in the URL if we send a confirmation email.
- if not self.email_token
- self.email_token = PostRedirect.generate_random_token
- end
- end
-
# Used by (rspec) test code only
def self.get_last_post_redirect
# XXX yeuch - no other easy way of getting the token so we can check
@@ -89,6 +79,18 @@ class PostRedirect < ActiveRecord::Base
PostRedirect.delete_all "updated_at < (now() - interval '2 months')"
end
+ private
+
+ def generate_token
+ # The token is used to return you to what you are doing after the login form.
+ if not self.token
+ self.token = PostRedirect.generate_random_token
+ end
+ # There is a separate token to use in the URL if we send a confirmation email.
+ if not self.email_token
+ self.email_token = PostRedirect.generate_random_token
+ end
+ end
end
diff --git a/app/models/profile_photo.rb b/app/models/profile_photo.rb
index 6e605651d..41cb298b3 100644
--- a/app/models/profile_photo.rb
+++ b/app/models/profile_photo.rb
@@ -23,29 +23,15 @@ class ProfilePhoto < ActiveRecord::Base
belongs_to :user
+ validate :data_and_draft_checks
+
# deliberately don't strip_attributes, so keeps raw photo properly
attr_accessor :x, :y, :w, :h
- # convert binary data blob into ImageMagick image when assigned
attr_accessor :image
- def after_initialize
- if data.nil?
- self.image = nil
- return
- end
- image_list = Magick::ImageList.new
- begin
- image_list.from_blob(data)
- rescue Magick::ImageMagickError
- self.image = nil
- return
- end
-
- self.image = image_list[0] # XXX perhaps take largest image or somesuch if there were multiple in the file?
- self.convert_image
- end
+ after_initialize :convert_data_to_image
# make image valid format and size
def convert_image
@@ -81,7 +67,9 @@ class ProfilePhoto < ActiveRecord::Base
end
end
- def validate
+ private
+
+ def data_and_draft_checks
if self.data.nil?
errors.add(:data, N_("Please choose a file containing your photo."))
return
@@ -108,6 +96,25 @@ class ProfilePhoto < ActiveRecord::Base
raise "Internal error, real pictures must have a user"
end
end
+
+ # Convert binary data blob into ImageMagick image when assigned
+ def convert_data_to_image
+ if data.nil?
+ self.image = nil
+ return
+ end
+
+ image_list = Magick::ImageList.new
+ begin
+ image_list.from_blob(data)
+ rescue Magick::ImageMagickError
+ self.image = nil
+ return
+ end
+
+ self.image = image_list[0] # XXX perhaps take largest image or somesuch if there were multiple in the file?
+ self.convert_image
+ end
end
diff --git a/app/models/public_body.rb b/app/models/public_body.rb
index 6f6b37ba8..b48c57228 100644
--- a/app/models/public_body.rb
+++ b/app/models/public_body.rb
@@ -43,7 +43,7 @@ class PublicBody < ActiveRecord::Base
before_save :set_api_key, :set_default_publication_scheme
# Every public body except for the internal admin one is visible
- named_scope :visible, lambda {
+ scope :visible, lambda {
{
:conditions => "public_bodies.id <> #{PublicBody.internal_admin_body.id}"
}
@@ -104,28 +104,25 @@ class PublicBody < ActiveRecord::Base
# like find_by_url_name but also search historic url_name if none found
def self.find_by_url_name_with_historic(name)
- locale = self.locale || I18n.locale
- PublicBody.with_locale(locale) do
- found = PublicBody.find(:all,
- :conditions => ["public_body_translations.url_name=?", name],
- :joins => :translations,
- :readonly => false)
- # If many bodies are found (usually because the url_name is the same across
- # locales) return any of them
- return found.first if found.size >= 1
-
- # If none found, then search the history of short names
- old = PublicBody::Version.find_all_by_url_name(name)
- # Find unique public bodies in it
- old = old.map { |x| x.public_body_id }
- old = old.uniq
- # Maybe return the first one, so we show something relevant,
- # rather than throwing an error?
- raise "Two bodies with the same historical URL name: #{name}" if old.size > 1
- return unless old.size == 1
- # does acts_as_versioned provide a method that returns the current version?
- return PublicBody.find(old.first)
- end
+ found = PublicBody.find(:all,
+ :conditions => ["public_body_translations.url_name=?", name],
+ :joins => :translations,
+ :readonly => false)
+ # If many bodies are found (usually because the url_name is the same across
+ # locales) return any of them
+ return found.first if found.size >= 1
+
+ # If none found, then search the history of short names
+ old = PublicBody::Version.find_all_by_url_name(name)
+ # Find unique public bodies in it
+ old = old.map { |x| x.public_body_id }
+ old = old.uniq
+ # Maybe return the first one, so we show something relevant,
+ # rather than throwing an error?
+ raise "Two bodies with the same historical URL name: #{name}" if old.size > 1
+ return unless old.size == 1
+ # does acts_as_versioned provide a method that returns the current version?
+ return PublicBody.find(old.first)
end
# Set the first letter, which is used for faster queries
@@ -336,7 +333,7 @@ class PublicBody < ActiveRecord::Base
# The "internal admin" is a special body for internal use.
def PublicBody.internal_admin_body
- PublicBody.with_locale(I18n.default_locale) do
+ I18n.with_locale(I18n.default_locale) do
pb = PublicBody.find_by_url_name("internal_admin_authority")
if pb.nil?
pb = PublicBody.new(
@@ -374,7 +371,7 @@ class PublicBody < ActiveRecord::Base
# of updating them
bodies_by_name = {}
set_of_existing = Set.new()
- PublicBody.with_locale(I18n.default_locale) do
+ I18n.with_locale(I18n.default_locale) do
bodies = (tag.nil? || tag.empty?) ? PublicBody.find(:all) : PublicBody.find_by_tag(tag)
for existing_body in bodies
# Hide InternalAdminBody from import notes
@@ -417,7 +414,7 @@ class PublicBody < ActiveRecord::Base
if public_body = bodies_by_name[name] # Existing public body
available_locales.each do |locale|
- PublicBody.with_locale(locale) do
+ I18n.with_locale(locale) do
changed = ActiveSupport::OrderedHash.new
field_list.each do |field_name|
localized_field_name = (locale.to_s == I18n.default_locale.to_s) ? field_name : "#{field_name}.#{locale}"
@@ -452,7 +449,7 @@ class PublicBody < ActiveRecord::Base
else # New public body
public_body = PublicBody.new(:name=>"", :short_name=>"", :request_email=>"")
available_locales.each do |locale|
- PublicBody.with_locale(locale) do
+ I18n.with_locale(locale) do
changed = ActiveSupport::OrderedHash.new
field_list.each do |field_name|
localized_field_name = (locale.to_s == I18n.default_locale.to_s) ? field_name : "#{field_name}.#{locale}"
diff --git a/app/models/user.rb b/app/models/user.rb
index 6e1e21481..490587c39 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -50,12 +50,17 @@ class User < ActiveRecord::Base
'super',
], :message => N_('Admin level is not included in list')
+ validate :email_and_name_are_valid
+
acts_as_xapian :texts => [ :name, :about_me ],
:values => [
[ :created_at_numeric, 1, "created_at", :number ] # for sorting
],
:terms => [ [ :variety, 'V', "variety" ] ],
:if => :indexed_by_search?
+
+ after_initialize :set_defaults
+
def created_at_numeric
# format it here as no datetime support in Xapian's value ranges
return self.created_at.strftime("%Y%m%d%H%M%S")
@@ -65,17 +70,6 @@ class User < ActiveRecord::Base
"user"
end
- def after_initialize
- if self.admin_level.nil?
- self.admin_level = 'none'
- end
- if self.new_record?
- # make alert emails go out at a random time for each new user, so
- # overall they are spread out throughout the day.
- self.last_daily_track_email = User.random_time_in_last_day
- end
- end
-
# requested_by: and commented_by: search queries also need updating after save
after_update :reindex_referencing_models
def reindex_referencing_models
@@ -108,15 +102,6 @@ class User < ActiveRecord::Base
self.comments.find(:all, :conditions => 'visible')
end
- def validate
- if self.email != "" && !MySociety::Validate.is_valid_email(self.email)
- errors.add(:email, _("Please enter a valid email address"))
- end
- if MySociety::Validate.is_valid_email(self.name)
- errors.add(:name, _("Please enter your name, not your email address, in the name field."))
- end
- end
-
# Don't display any leading/trailing spaces
# XXX we have strip_attributes! now, so perhaps this can be removed (might
# be still needed for existing cases)
@@ -148,14 +133,14 @@ class User < ActiveRecord::Base
if user
# There is user with email, check password
if !user.has_this_password?(params[:password])
- user.errors.add_to_base(auth_fail_message)
+ user.errors.add(:base, auth_fail_message)
end
else
# No user of same email, make one (that we don't save in the database)
# for the forms code to use.
user = User.new(params)
# deliberately same message as above so as not to leak whether registered
- user.errors.add_to_base(auth_fail_message)
+ user.errors.add(:base, auth_fail_message)
end
user
end
@@ -413,6 +398,26 @@ class User < ActiveRecord::Base
self.salt = self.object_id.to_s + rand.to_s
end
+ def set_defaults
+ if self.admin_level.nil?
+ self.admin_level = 'none'
+ end
+ if self.new_record?
+ # make alert emails go out at a random time for each new user, so
+ # overall they are spread out throughout the day.
+ self.last_daily_track_email = User.random_time_in_last_day
+ end
+ end
+
+ def email_and_name_are_valid
+ if self.email != "" && !MySociety::Validate.is_valid_email(self.email)
+ errors.add(:email, _("Please enter a valid email address"))
+ end
+ if MySociety::Validate.is_valid_email(self.name)
+ errors.add(:name, _("Please enter your name, not your email address, in the name field."))
+ end
+ end
+
## Class methods
def User.encrypted_password(password, salt)
string_to_hash = password + salt # XXX need to add a secret here too?
diff --git a/app/views/general/_frontpage_new_request.rhtml b/app/views/general/_frontpage_new_request.rhtml
index fd4225069..499b60eb5 100644
--- a/app/views/general/_frontpage_new_request.rhtml
+++ b/app/views/general/_frontpage_new_request.rhtml
@@ -4,4 +4,4 @@
Information<br/>
request</strong>") %>
</h1>
-<a class="link_button_green_large" href="/select_authority"><%= _("Start now &raquo;") %></a>
+<a class="link_button_green_large" href="<%= select_authority_path %>"><%= _("Start now &raquo;") %></a>
diff --git a/app/views/general/_frontpage_search_box.rhtml b/app/views/general/_frontpage_search_box.rhtml
index 6de4eae98..d2718b3a3 100644
--- a/app/views/general/_frontpage_search_box.rhtml
+++ b/app/views/general/_frontpage_search_box.rhtml
@@ -4,7 +4,7 @@
<strong>{{number_of_authorities}} authorities</strong>",
:number_of_requests => InfoRequest.visible.count, :number_of_authorities => PublicBody.visible.count) %>
</h2>
-<form id="search_form" method="post" action="/search">
+<form id="search_form" method="post" action="<%= search_redirect_path %>">
<div>
<input id="query" type="text" size="30" name="query">
<input type="submit" value="<%= _('Search') %>">
diff --git a/app/views/general/frontpage.rhtml b/app/views/general/frontpage.rhtml
index acc7f4095..bf5261d15 100644
--- a/app/views/general/frontpage.rhtml
+++ b/app/views/general/frontpage.rhtml
@@ -1,4 +1,4 @@
-<% view_cache :ttl => 5.minutes.to_i, :tag => I18n.locale do %>
+<% # TODO: Cache for 5 minutes %>
<div id="frontpage_splash">
<div id="left_column">
<%= render :partial => "frontpage_new_request" %>
@@ -17,4 +17,3 @@
<%= render :partial => "frontpage_bodies_list" %>
<%= render :partial => "frontpage_requests_list" %>
</div>
-<% end %>
diff --git a/app/views/help/_sidebar.rhtml b/app/views/help/_sidebar.rhtml
index 783d35983..2b7ed5647 100644
--- a/app/views/help/_sidebar.rhtml
+++ b/app/views/help/_sidebar.rhtml
@@ -1,19 +1,19 @@
<div id="right_column_flip">
<h2>Help pages</h2>
<ul class="no_bullets">
- <li><%= link_to_unless_current "Introduction", "/help/about" %></li>
- <li><%= link_to_unless_current "Making requests", "/help/requesting" %></li>
- <li><%= link_to_unless_current "Your privacy", "/help/privacy" %></li>
- <li><%= link_to_unless_current "FOI officers", "/help/officers" %></li>
- <li><%= link_to_unless_current "About the software", "/help/alaveteli" %></li>
- <li><%= link_to_unless_current "Credits", "/help/credits" %></li>
- <li><%= link_to_unless_current "Programmers API", "/help/api" %></li>
- <li><%= link_to_unless_current "Advanced search", "/advancedsearch" %></li>
+ <li><%= link_to_unless_current "Introduction", help_about_path %></li>
+ <li><%= link_to_unless_current "Making requests", help_requesting_path %></li>
+ <li><%= link_to_unless_current "Your privacy", help_privacy_path %></li>
+ <li><%= link_to_unless_current "FOI officers", help_officers_path %></li>
+ <li><%= link_to_unless_current "About the software", help_alaveteli_path %></li>
+ <li><%= link_to_unless_current "Credits", help_credits_path %></li>
+ <li><%= link_to_unless_current "Programmers API", help_api_path %></li>
+ <li><%= link_to_unless_current "Advanced search", advanced_search_path %></li>
</ul>
<h2 id="contact">Contact us</h2>
<p>If your question isn't answered here, or you just wanted to let us know
- something about the site, <a href="/help/contact">contact&nbsp;us</a>.
+ something about the site, <a href="<%= help_contact_path %>">contact&nbsp;us</a>.
</p>
</div>
diff --git a/app/views/help/about.rhtml b/app/views/help/about.rhtml
index 9f75cac8b..477f0e750 100644
--- a/app/views/help/about.rhtml
+++ b/app/views/help/about.rhtml
@@ -41,13 +41,13 @@
</dd>
<dt id="updates">How can I keep up with news about WhatDoTheyKnow?<a href="#updates">#</a> </dt>
- <dd>We have a <a href="/blog">blog</a> and a <a href="https://twitter.com/whatdotheyknow">twitter feed</a>.
+ <dd>We have a <a href="<%= blog_path %>">blog</a> and a <a href="https://twitter.com/whatdotheyknow">twitter feed</a>.
</dd>
</dl>
- <p><strong>Next</strong>, read about <a href="/help/requesting">making requests</a> --&gt;
+ <p><strong>Next</strong>, read about <a href="<%= help_requesting_path %>">making requests</a> --&gt;
<div id="hash_link_padding"></div>
</div>
diff --git a/app/views/help/api.rhtml b/app/views/help/api.rhtml
index facddce41..da6253f87 100644
--- a/app/views/help/api.rhtml
+++ b/app/views/help/api.rhtml
@@ -19,7 +19,7 @@
<dt>Linking to new requests</dt>
<dd>
<p>To encourage your users to make links to a particular public authority, use URLs of the form
- <%= link_to new_request_to_body_url(:url_name => "liverpool_city_council") , new_request_to_body_url(:url_name => "liverpool_city_council") %>.
+ <%= link_to new_request_to_body_url(:url_name => "liverpool_city_council") , new_request_to_body_url(:url_name => "liverpool_city_council") %>.
These are the parameters you can add to those URLs, either in the URL or from a form.
<ul>
@@ -34,7 +34,7 @@
<dd>
<p>There are Atom feeds on most pages which list FOI requests, which you can
use to get updates and links in XML format. Find the URL of the Atom feed in
- one of these ways:
+ one of these ways:
<ul>
<li>Look for the <img src="/images/feed-16.png" alt=""> RSS feed links.</li>
<li>Examine the <tt>&lt;link rel="alternate" type="application/atom+xml"&gt;</tt> tag in the head of the HTML. </li>
@@ -43,7 +43,7 @@
<p>In particular, even complicated search queries have Atom feeds.
You can do all sorts of things with them, such as query by authority, by file
- type, by date range, or by status. See the <a href="/search">advanced search
+ type, by date range, or by status. See the <a href="<%= advanced_search_path %>">advanced search
tips</a> for details.
</dd>
@@ -62,7 +62,7 @@
information about the list of events in the feed.
</p>
</dd>
-
+
<dt>Spreadsheet of all authorities</dt>
<dd>
<p>
@@ -73,7 +73,7 @@
</dd>
</dl>
- <p>Please <a href="/help/contact">contact us</a> if you need an API feature that isn't there yet. It's
+ <p>Please <a href="<%= help_contact_path %>">contact us</a> if you need an API feature that isn't there yet. It's
very much a work in progress, and we do add things when people ask us to.</p>
<div id="hash_link_padding"></div>
diff --git a/app/views/help/contact.rhtml b/app/views/help/contact.rhtml
index 37df68f49..385c24a62 100644
--- a/app/views/help/contact.rhtml
+++ b/app/views/help/contact.rhtml
@@ -9,13 +9,13 @@
<% if !flash[:notice] %>
<h2>Contact an authority to get official information</h2>
<ul>
- <li><a href="/new">Go here</a> to make a request, in public, for information
+ <li><a href="<%= new_request_path %>">Go here</a> to make a request, in public, for information
from public authorities.</li>
<li>
Asking for private information about yourself?
Please read our
- <a href="/help/requesting#data_protection">help page</a>.
+ <a href="<%= help_requesting_path(:anchor => 'data_protection') %>">help page</a>.
</li>
</ul>
@@ -25,10 +25,10 @@
<% if !flash[:notice] %>
<ul>
<li>
- Please read the <a href="/help/about">help page</a> first, as it may
- answer your question quicker.
+ Please read the <a href="<%= help_about_path %>">help page</a> first, as it may
+ answer your question quicker.
</li>
-
+
<li>We'd love to hear how you've found using this site.
Either fill in this form, or send an email to <a
href="mailto:<%=@contact_email%>"><%=@contact_email%></a>
@@ -46,7 +46,7 @@
<p>
<label class="form_label" for="contact_name">Your name:</label>
<%= f.text_field :name, :size => 20 %>
- (or <%= link_to "sign in", signin_url(:r => request.request_uri) %>)
+ (or <%= link_to "sign in", signin_url(:r => request.fullpath) %>)
</p>
<p>
@@ -56,25 +56,25 @@
<% end %>
<p>
- <label class="form_label" for="contact_subject">Subject:</label>
+ <label class="form_label" for="contact_subject">Subject:</label>
<%= f.text_field :subject, :size => 50 %>
</p>
<p>
- <label class="form_label" for="contact_message">Message to website:</label>
+ <label class="form_label" for="contact_message">Message to website:</label>
<%= f.text_area :message, :rows => 10, :cols => 60 %>
</p>
<% if !@last_request.nil? %>
<p>
- <label class="form_label" for="contact_message">Include link to request:</label>
+ <label class="form_label" for="contact_message">Include link to request:</label>
<%=request_link(@last_request) %>
<%= submit_tag "remove", :name => 'remove' %>
</p>
<% end %>
<% if !@last_body.nil? %>
<p>
- <label class="form_label" for="contact_message">Include link to authority:</label>
+ <label class="form_label" for="contact_message">Include link to authority:</label>
<%=public_body_link(@last_body) %>
<%= submit_tag "remove", :name => 'remove' %>
</p>
diff --git a/app/views/help/officers.rhtml b/app/views/help/officers.rhtml
index 3defec62f..b13e225fe 100644
--- a/app/views/help/officers.rhtml
+++ b/app/views/help/officers.rhtml
@@ -19,13 +19,13 @@
</p>
<p>If you have privacy or other concerns, please read the answers below.
You might also like to read the <a
- href="/help/about">introduction to WhatDoTheyKnow</a> to find out more about what
+ href="<%= help_about_path %>">introduction to WhatDoTheyKnow</a> to find out more about what
the site does from the point of view of a user. You can also search the
site to find the authority that you work for, and view the status of
any requests made using the site.
<p>Finally, we welcome comments and
- thoughts from FOI officers, please <a href="/help/contact">get in touch</a>.
+ thoughts from FOI officers, please <a href="<%= help_contact_path %>">get in touch</a>.
</p>
</dd>
@@ -75,13 +75,13 @@
the authority by email. Any delivery failure messages will automatically
appear on the site. You can check the address we're using with the "View FOI
email address" link which appears on the page for the authority. <a
- href="/help/contact">Contact us</a> if there is a better address we can
+ href="<%= help_contact_path %>">Contact us</a> if there is a better address we can
use.</p>
<p>Requests are sometimes not delivered because they are quietly removed by
"spam filters" in the IT department of the authority. Authorities can make
- sure this doesn't happen by asking their IT departments to "whitelist"
+ sure this doesn't happen by asking their IT departments to "whitelist"
any email from <strong>@whatdotheyknow.com</strong>.
- If you <a href="/help/contact">ask us</a> we will resend any request,
+ If you <a href="<%= help_contact_path %>">ask us</a> we will resend any request,
and/or give technical details of delivery so an IT department can chase
up what happened to the message.
</p>
@@ -159,7 +159,7 @@
</li>
</ul>
- <p>If you're getting really nerdy about all this, read the <a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/detailed_specialist_guides/timeforcompliance.pdf">detailed ICO guidance</a>.
+ <p>If you're getting really nerdy about all this, read the <a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/detailed_specialist_guides/timeforcompliance.pdf">detailed ICO guidance</a>.
Meanwhile, remember that the law says authorities must respond
<strong>promptly</strong>. That's really what matters.</p>
@@ -173,14 +173,14 @@
extension when applying a <strong>public interest test</strong>. Information
Commissioner guidance says that it should only be used in "exceptionally
complex" cases
- (<a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/detailed_specialist_guides/foi_good_practice_guidance_4.pdf">FOI Good Practice Guidance No. 4</a>).
+ (<a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/detailed_specialist_guides/foi_good_practice_guidance_4.pdf">FOI Good Practice Guidance No. 4</a>).
WhatDoTheyKnow doesn't specifically handle this case, which is why we use the
phrase "should normally have responded by" when the 20 working day time is
- exceeded.
+ exceeded.
</p>
<p>The same guidance says that, even in exceptionally complex cases, no
- Freedom of Information request should take more than <strong>40 working days</strong>
+ Freedom of Information request should take more than <strong>40 working days</strong>
to answer. WhatDoTheyKnow displays requests which are overdue by that much
with stronger wording to indicate they are definitely late.
</p>
@@ -191,7 +191,7 @@
of 40 working days even with the extension (the House of Lords <a
href="http://www.publicwhip.org.uk/division.php?date=2000-10-17&amp;number=1&amp;house=lords">voted
to remove</a> provision for such a time limit during the initial passage
- of the UK Act through Parliament).
+ of the UK Act through Parliament).
</p>
</dd>
@@ -199,7 +199,7 @@
<dd>Instead of email, you can respond to a request directly from your web
browser, including uploading a file. To do this, choose "respond to request" at
- the bottom of the request's page. <a href="/help/contact">Contact us</a> if it
+ the bottom of the request's page. <a href="<%= help_contact_path %>">Contact us</a> if it
is too big for even that (more than, say, 50Mb).
</dd>
@@ -207,17 +207,17 @@
<dd>We consider what officers or servants do in the course of their employment
to be public information. We will only remove content in exceptional
- circumstances, see our <a href="/help/privacy#takedown">take down policy</a>.
+ circumstances, see our <a href="<%= help_privacy_path(:anchor => 'takedown') %>">take down policy</a>.
</dd>
<dt id="mobiles">Do you publish email addresses or mobile phone numbers? <a href="#mobiles">#</a> </dt>
<dd><p>To prevent spam, we automatically remove most emails and some mobile numbers from
- responses to requests. Please <a href="/help/contact">contact us</a> if we've
- missed one.
+ responses to requests. Please <a href="<%= help_contact_path %>">contact us</a> if we've
+ missed one.
For technical reasons we don't always remove them from attachments, such as certain PDFs.</p>
<p>If you need to know what an address was that we've removed, please <a
- href="/help/contact">get in touch with us</a>. Occasionally, an email address
+ href="<%= help_contact_path %>">get in touch with us</a>. Occasionally, an email address
forms an important part of a response and we will post it up in an obscured
form in an annotation.
</dd>
@@ -225,10 +225,10 @@
<dt id="copyright"><a name="commercial"></a>What is your policy on copyright of documents?<a href="#copyright">#</a> </dt>
<dd>Our Freedom of Information law is "applicant blind", so anyone in the
- world can request the same document and get a copy of it.
+ world can request the same document and get a copy of it.
If you think our making a document available on the internet infringes your
- copyright, you may <a href="/help/contact">contact us</a> and ask us
+ copyright, you may <a href="<%= help_contact_path %>">contact us</a> and ask us
to take it down. However, to save tax payers' money by preventing duplicate
requests, and for good public relations, we'd advise you not to do that.
</dd>
@@ -238,8 +238,8 @@
</dl>
- <p><strong>If you haven't already</strong>, read <a href="/help/about">the introduction</a> --&gt;
- <br><strong>Otherwise</strong>, the <a href="/help/credits">credits</a> or the <a href="/help/api">programmers API</a> --&gt;
+ <p><strong>If you haven't already</strong>, read <a href="<%= help_about_path %>">the introduction</a> --&gt;
+ <br><strong>Otherwise</strong>, the <a href="<%= help_credits_path %>">credits</a> or the <a href="<%= help_api_path %>">programmers API</a> --&gt;
<div id="hash_link_padding"></div>
</div>
diff --git a/app/views/help/privacy.rhtml b/app/views/help/privacy.rhtml
index bec0c8c23..8e5293892 100644
--- a/app/views/help/privacy.rhtml
+++ b/app/views/help/privacy.rhtml
@@ -10,7 +10,7 @@
<dd><p>We will not disclose your email address to anyone unless we are obliged to by law,
or you ask us to. This includes the public authority that you are sending a
- request to. They only get to see an email address
+ request to. They only get to see an email address
@whatdotheyknow.com which is specific to that request. </p>
<p>If you send a message to another user on the site, then it will reveal your
email address to them. You will be told that this is going to happen.</p>
@@ -35,7 +35,7 @@
Your name is tangled up with your request, so has to be published as well.
It is only fair, as we're going to publish the name of the civil servant who
writes the response to your request. Using your real name also helps people
- get in touch with you to assist you with your research or to campaign with you.
+ get in touch with you to assist you with your research or to campaign with you.
</p>
<p>By law, you must use your real name for the request to be a valid Freedom of
Information request. See the next question for alternatives if you do not want
@@ -66,16 +66,16 @@ Information Commissioner later about the handling of your request.
<ul>
<li>Use a different form of your name. The guidance says
that "Mr Arthur Thomas Roberts" can make a valid request as "Arthur Roberts",
-"A. T. Roberts", or "Mr Roberts", but <strong>not</strong> as "Arthur" or "A.T.R.".
+"A. T. Roberts", or "Mr Roberts", but <strong>not</strong> as "Arthur" or "A.T.R.".
</li>
<li>Women may use their maiden name.</li>
<li>In most cases, you may use any name by which you are "widely known and/or
is regularly used".
<li>Use the name of an organisation, the name of a company, the trading name of
a company, or the trading name of a sole trader.
-<li>Ask someone else to make the request on your behalf.
+<li>Ask someone else to make the request on your behalf.
<li>You may, if you are really stuck, ask us to make the request on
-your behalf. Please <a href="/help/contact">contact us</a> with
+your behalf. Please <a href="<%= help_contact_path %>">contact us</a> with
a good reason why you cannot make the request yourself and cannot
ask a friend to. We don't have the resources to do this for everyone.
</ul>
@@ -88,19 +88,19 @@ ask a friend to. We don't have the resources to do this for everyone.
<dd>
<p>If a public authority asks you for your full, physical address, reply to them saying
-that section 8.1.b of the FOI Act asks for an "address for correspondence",
-and that the email address you are using is sufficient.
+that section 8.1.b of the FOI Act asks for an "address for correspondence",
+and that the email address you are using is sufficient.
</p>
<p>
The Ministry of Justice has <a href="http://www.justice.gov.uk/guidance/foi-procedural-what.htm">guidance
on this</a> &ndash;
<em>"As well as hard copy written correspondence, requests that are
-transmitted electronically (for example, in emails) are acceptable
+transmitted electronically (for example, in emails) are acceptable
... If a request is received by email and no postal address is given, the email
address should be treated as the return address."
</em>
</p>
-<p>As if that isn't enough, the Information Commissioner's
+<p>As if that isn't enough, the Information Commissioner's
<a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/practical_application/foi_hints_for_practitioners_handing_foi_and_eir_requests_2008_final.pdf">Hints for Practitioners</a> say
<em>"Any correspondence could include a request for information. If it is written (this includes e-mail), legible, gives the name of the applicant, an address for reply (which could be electronic), and includes a description of the information required, then it will fall within the scope of the legislation."</em>
</p>
@@ -141,10 +141,10 @@ see the section on <a href="#real_name">pseudonyms</a>.</p>
<dt id="takedown">Can you take down personal information about me? <a href="#takedown">#</a> </dt>
-<dd>
+<dd>
<p>If you see any personal information about you on the site which you'd like
-us to remove or hide, then please <a href="/help/contact">let us know</a>.
+us to remove or hide, then please <a href="<%= help_contact_path %>">let us know</a>.
Specify exactly what information you believe to be problematic and why, and
where it appears on the site.</p>
@@ -165,8 +165,8 @@ which outweighs the public interest, and must demonstrate that efforts have
been made to conceal the name on the organisation's own website.</p>
<p>For all other requests we apply a public interest test to decide
-whether information should be removed.
-<a href="http://www.statutelaw.gov.uk/content.aspx?ActiveTextDocId=3190650"> Section 32</a>
+whether information should be removed.
+<a href="http://www.statutelaw.gov.uk/content.aspx?ActiveTextDocId=3190650"> Section 32</a>
of the Data Protection Act 1998 permits us to do this, as the material we
publish is journalistic. We cannot easily edit many types of attachments (such
as PDFs, or Microsoft Word or Excel files), so we will usually ask
@@ -177,7 +177,7 @@ that authorities resend these with the personal information removed.</p>
</dl>
-<p><strong>Learn more</strong> from the help for <a href="/help/officers">FOI officers</a> --&gt;
+<p><strong>Learn more</strong> from the help for <a href="<%= help_officers_path %>">FOI officers</a> --&gt;
<div id="hash_link_padding"></div>
</div>
diff --git a/app/views/help/requesting.rhtml b/app/views/help/requesting.rhtml
index af8f2e45d..e7cfdd199 100644
--- a/app/views/help/requesting.rhtml
+++ b/app/views/help/requesting.rhtml
@@ -20,7 +20,7 @@
<li>Don't worry excessively about getting the right authority. If you get it
wrong, they ought to advise you who to make the request to instead.
</li>
- <li>If you've got a thorny case, please <a href="/help/contact">contact us</a> for help.</li>
+ <li>If you've got a thorny case, please <a href="<%= help_contact_path %>">contact us</a> for help.</li>
</ul>
</dd>
@@ -30,7 +30,7 @@
<dt id="missing_body">You're missing the public authority that I want to request from! <a href="#missing_body">#</a> </dt>
<dd>
- <p>Please <a href="/help/contact">contact us</a> with the name of the public authority and,
+ <p>Please <a href="<%= help_contact_path %>">contact us</a> with the name of the public authority and,
if you can find it, their contact email address for Freedom of Information requests.
</p>
<p>If you'd like to help add a whole category of public authority to the site, we'd love
@@ -47,11 +47,11 @@
<ul>
<li> Those formally subject to the FOI Act</li>
- <li> Those formally subject to the Environmental Regulations (a less well
+ <li> Those formally subject to the Environmental Regulations (a less well
defined group)</li>
<li> Those which voluntarily comply with the FOI Act</li>
<li> Those which aren't subject to the Act but we think should be, on grounds
- such as them having significant public responsibilities.
+ such as them having significant public responsibilities.
</li>
</ul>
@@ -132,7 +132,7 @@
<p>Even if they are not prompt, in nearly all cases they must respond within
20 working days. If you had to clarify your request, or they are a school,
or one or two other cases, then they may have more time
- (<a href="/help/officers#days">full details</a>).
+ (<a href="<%= help_officers_path(:anchor => 'days') %>">full details</a>).
<p>WhatDoTheyKnow will email you if you don't get a timely response. You can
then send the public authority a message to remind them, and tell them if they
@@ -158,24 +158,24 @@
checking that they received the request. It was sent to them by email.
</li>
<li>If they have not received it, the problem is most likely due to
- "spam filters". Refer the authority to the measures in the answer
- '<a href="/help/officers#spam_problems">I can see a request on WhatDoTheyKnow, but we never got it by email!</a>'
- in the FOI officers section of this help.
+ "spam filters". Refer the authority to the measures in the answer
+ '<a href="<%= help_officers_path(:anchor => 'spam_problems') %>">I can see a request on WhatDoTheyKnow, but we never got it by email!</a>'
+ in the FOI officers section of this help.
</li>
<li>If you're still having no luck, then you can ask for an internal review,
and then complain to the Information Commissioner about the authority.
- Read our page '<a href="/help/unhappy">Unhappy about the response you got?</a>'.
+ Read our page '<a href="<%= help_general_path(:action => 'unhappy') %>">Unhappy about the response you got?</a>'.
</ul>
</dd>
<dt id="not_satifised">What if I'm not satisfied with the response? <a href="#not_satifised">#</a> </dt>
<dd>If you didn't get the information you asked for, or you didn't get it in time,
- then read our page '<a href="/help/unhappy">Unhappy about the response you got?</a>'.
+ then read our page '<a href="<%= help_general_path(:action => 'unhappy') %>">Unhappy about the response you got?</a>'.
</dd>
<dt id="reuse">It says I can't re-use the information I got!<a href="#reuse">#</a> </dt>
<dd>
- <p>Authorities often add legal boilerplate about the
+ <p>Authorities often add legal boilerplate about the
"<a href="http://www.opsi.gov.uk/si/si2005/20051515">Re-Use of Public Sector
Information Regulations 2005</a>", which at first glance implies you may not
be able do anything with the information.
@@ -184,7 +184,7 @@
<p>You can, of course, write articles about the information or summarise it, or
quote parts of it. We also think you should feel free to republish the
information in full, just as we do, even though in theory you might not be
- allowed to do so. See <a href="/help/officers#copyright">our policy on copyright</a>.</p>
+ allowed to do so. See <a href="<%= help_officers_path(:anchor => 'copyright') %>">our policy on copyright</a>.</p>
</dd>
@@ -199,7 +199,7 @@
the process is very similar. There are differences around time
limits for compliance.
See the <a href="http://www.itspublicknowledge.info/nmsruntime/saveasdialog.asp?lID=1858&amp;sID=321">Scottish
- Information Commissioner's guidance</a> for details.</p>
+ Information Commissioner's guidance</a> for details.</p>
</dd>
<dt id="data_protection">Can I request information about myself? <a href="#data_protection">#</a> </dt>
@@ -216,7 +216,7 @@
website) explains how to do this.</p>
<p>If you see that somebody has included personal information, perhaps
- unwittingly, in a request, please <a href="/help/contact">contact us</a>
+ unwittingly, in a request, please <a href="<%= help_contact_path %>">contact us</a>
immediately so we can remove it.</p>
</dd>
@@ -228,7 +228,7 @@
to read. </p>
<p>You should contact the public authority directly if you would like to
make a request in private. If you're interested in buying a system which helps
- you manage FOI requests in secret, then <a href="/help/contact">contact us</a>.
+ you manage FOI requests in secret, then <a href="<%= help_contact_path %>">contact us</a>.
</p>
</dd>
@@ -237,7 +237,7 @@
<dd>
<p>Some public authorities, such as <a href="http://www.whatdotheyknow.com/body/south_east_water">South East Water</a>,
don't come under the Freedom of Information Act, but do come under another law called
- the Environmental Information Regulations (EIR).
+ the Environmental Information Regulations (EIR).
</p>
<p>It's a very similar law, so you make a request
@@ -250,7 +250,7 @@
<p>You can, of course, request environmental information from other
authorities. Just make a Freedom of Information (FOI) request as normal. The
authority has a duty to work out if the Environmental Information Regulations
- (EIR) is the more appropriate legislation to reply under.
+ (EIR) is the more appropriate legislation to reply under.
</p>
</dd>
@@ -275,7 +275,7 @@
<dt id="moderation">How do you moderate request annotations? <a href="#moderation">#</a> </dt>
- <dd>
+ <dd>
<p>Annotations on WhatDoTheyKnow are to help
people get the information they want, or to give them pointers to places they
can go to help them act on it. We reserve the right to remove anything else.
@@ -286,7 +286,7 @@
</dl>
- <p><strong>Next</strong>, read about <a href="/help/privacy">your privacy</a> --&gt;
+ <p><strong>Next</strong>, read about <a href="<%= help_privacy_path %>">your privacy</a> --&gt;
<div id="hash_link_padding"></div>
</div>
diff --git a/app/views/help/unhappy.rhtml b/app/views/help/unhappy.rhtml
index 4f3c67b9e..2b00341c2 100644
--- a/app/views/help/unhappy.rhtml
+++ b/app/views/help/unhappy.rhtml
@@ -2,7 +2,7 @@
<% if !@info_request.nil? %>
-<h1>Unhappy about the response you got
+<h1>Unhappy about the response you got
to your request '<%=request_link(@info_request) %>'?
</h1>
<% else %>
@@ -58,10 +58,10 @@ authority, then <a href="http://www.itspublicknowledge.info/YourRights/HowToApp
to the Scottish Information Commissioner</a>.
</p>
-<p>To make it easier to send the relevant information to the
+<p>To make it easier to send the relevant information to the
Information Commissioner, either
<% if !@info_request.nil? %>
- include a link to your request
+ include a link to your request
<strong><%=h main_url(request_url(@info_request)) %></strong>
<% else %>
include a link to your request on <%= site_name %>
@@ -86,8 +86,8 @@ get the information by <strong>other means...</strong></p>
<ul>
<li>Make a <strong>new FOI request</strong> for summary information, or for
-documentation relating indirectly to matters in your refused request.
-<a href="/help/contact">Ask us for ideas</a> if you're stuck.</li>
+documentation relating indirectly to matters in your refused request.
+<a href="<%= help_contact_path %>">Ask us for ideas</a> if you're stuck.</li>
<li>If any <strong>other public authorities</strong> or publicly owned companies are involved,
then make FOI requests to them.</li>
<li>Write to <strong>your MP</strong> or other politician using <a
@@ -95,13 +95,13 @@ href="http://www.writetothem.com">WriteToThem</a> and ask for their help
finding the answer. MPs can write directly to ministers or departments, and
can ask written questions in the House of Commons. Councillors in local authorities
can talk directly to council officers.</li>
-<li>Ask <strong>other researchers</strong> who are interested in a similar
+<li>Ask <strong>other researchers</strong> who are interested in a similar
issue to yours for ideas. You can sometimes find them by browsing this site;
contact any registered user from their page. There may be an Internet
forum or group that they hang out in. If it is a local matter, use <a
-href="http://www.groupsnearyou.com">GroupsNearYou</a> to find such a
+href="http://www.groupsnearyou.com">GroupsNearYou</a> to find such a
forum.</li>
-<li><strong>Start a pledge</strong> on <a href="http://www.pledgebank.com">PledgeBank</a> to get
+<li><strong>Start a pledge</strong> on <a href="http://www.pledgebank.com">PledgeBank</a> to get
others to act together with you. For example, you could arrange a meeting with
staff from the authority. Or you could form a small local campaigns group.
</ul>
diff --git a/app/views/layouts/default.rhtml b/app/views/layouts/default.rhtml
index 8c4ae588b..28d099984 100644
--- a/app/views/layouts/default.rhtml
+++ b/app/views/layouts/default.rhtml
@@ -32,7 +32,7 @@
<% end %>
<% end %>
<% if @has_json %>
- <link rel="alternate" type="application/json" title="JSON version of this page" href="<%=h main_url(request.request_uri, '.json') %>">
+ <link rel="alternate" type="application/json" title="JSON version of this page" href="<%=h main_url(request.fullpath, '.json') %>">
<% end %>
<% if @no_crawl %>
@@ -69,9 +69,9 @@
<% if !@popup_banner.blank? %>
<div id="everypage" class="jshide">
- <p style="float:right"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p>
+ <p class="popup-close"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p>
<%= @popup_banner %>
- <p style="text-align: right"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p>
+ <p class="popup-close"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p>
</div>
<% end %>
@@ -92,15 +92,15 @@
<% end %>
- <%= link_to _("Sign out"), signout_url(:r => request.request_uri) %>
+ <%= link_to _("Sign out"), signout_url(:r => request.fullpath) %>
<% else %>
- <%= link_to _("Sign in or sign up"), signin_url(:r => request.request_uri) %>
+ <%= link_to _("Sign in or sign up"), signin_url(:r => request.fullpath) %>
<% end %>
</div>
<% end %>
<div id="navigation_search">
- <form id="navigation_search_form" method="post" action="/search">
+ <form id="navigation_search_form" method="post" action="<%= search_redirect_path %>">
<p>
<%= text_field_tag 'query', params[:query], { :size => 40, :id => "navigation_search_query" } %>
<input id="navigation_search_button" type="submit" value="search">
diff --git a/app/views/request/_describe_state.rhtml b/app/views/request/_describe_state.rhtml
index 5b6004e81..f70e5ed8b 100644
--- a/app/views/request/_describe_state.rhtml
+++ b/app/views/request/_describe_state.rhtml
@@ -108,7 +108,7 @@
<%= _('We don\'t know whether the most recent response to this request contains
information or not
&ndash;
- if you are {{user_link}} please <a href="{{url}}">sign in</a> and let everyone know.',:user_link=>user_link(@info_request.user), :url=>signin_url(:r => request.request_uri)) %>
+ if you are {{user_link}} please <a href="{{url}}">sign in</a> and let everyone know.',:user_link=>user_link(@info_request.user), :url=>signin_url(:r => request.fullpath)) %>
<% end %>
<% end %>
diff --git a/app/views/request/_hidden_correspondence.rhtml b/app/views/request/_hidden_correspondence.rhtml
index 0873b312f..a5e680385 100644
--- a/app/views/request/_hidden_correspondence.rhtml
+++ b/app/views/request/_hidden_correspondence.rhtml
@@ -8,20 +8,20 @@
<div class="correspondence" id="incoming-<%=incoming_message.id.to_s%>">
<p>
<%= raw(_('This response has been hidden. See annotations to find out why.
- If you are the requester, then you may <a href="%s">sign in</a> to view the response.') % [signin_url(:r => request.request_uri)]) %>
+ If you are the requester, then you may <a href="%s">sign in</a> to view the response.') % [signin_url(:r => request.fullpath)]) %>
</p>
</div>
<% elsif [ 'sent', 'followup_sent', 'resent', 'followup_resent' ].include?(info_request_event.event_type) %>
<div class="correspondence" id="outgoing-<%=outgoing_message.id.to_s%>">
<p>
<%= raw(_('This outgoing message has been hidden. See annotations to
- find out why. If you are the requester, then you may <a href="%s">sign in</a> to view the response.') % [signin_url(:r => request.request_uri)]) %>
+ find out why. If you are the requester, then you may <a href="%s">sign in</a> to view the response.') % [signin_url(:r => request.fullpath)]) %>
</p>
</div>
<% elsif info_request_event.event_type == 'comment' %>
<div class="comment_in_request" id="comment-<%=comment.id.to_s%>">
<p><%= raw(_('This comment has been hidden. See annotations to
- find out why. If you are the requester, then you may <a href="%s">sign in</a> to view the response.') % [signin_url(:r => request.request_uri)]) %>
+ find out why. If you are the requester, then you may <a href="%s">sign in</a> to view the response.') % [signin_url(:r => request.fullpath)]) %>
</p>
</div>
<% end %>
diff --git a/app/views/request/_sidebar.rhtml b/app/views/request/_sidebar.rhtml
index b669278f9..d349bee14 100644
--- a/app/views/request/_sidebar.rhtml
+++ b/app/views/request/_sidebar.rhtml
@@ -46,8 +46,8 @@
<%= render :partial => 'request/next_actions' %>
- <% view_cache :ttl => 1.day.to_i, :tag => ['similar', @info_request.id, I18n.locale] do %>
- <% if !@xapian_similar.nil? && @xapian_similar.results.size > 0 %>
+ <% # TODO: Cache for 1 day %>
+ <% if !@xapian_similar.nil? && @xapian_similar.results.size > 0 %>
<h2><%= _('Similar requests')%></h2>
<% for result in @xapian_similar.results %>
<%= render :partial => 'request/request_listing_short_via_event', :locals => { :event => result[:model], :info_request => result[:model].info_request } %>
@@ -56,8 +56,7 @@
<p><%= link_to _("More similar requests"), request_similar_url(@info_request) %></p>
<% end %>
<!-- Important terms: <%= @xapian_similar.important_terms.join(" ") %> -->
- <% end %>
- <% end %>
+ <% end %>
<p><%= link_to _('Event history details'), request_details_url(@info_request) %></p>
diff --git a/app/views/request/hidden.rhtml b/app/views/request/hidden.rhtml
index 2d038a663..41b2ff7e4 100644
--- a/app/views/request/hidden.rhtml
+++ b/app/views/request/hidden.rhtml
@@ -12,7 +12,7 @@ various reasons why we might have done this, sorry we can\'t be more specific he
</p>
<% if @info_request.prominence == 'requester_only' %>
<p>
- <%= raw(_('If you are the requester, then you may <a href="%s">sign in</a> to view the request.') % [signin_url(:r => request.request_uri)]) %>
+ <%= raw(_('If you are the requester, then you may <a href="%s">sign in</a> to view the request.') % [signin_url(:r => request.fullpath)]) %>
</p>
<% end %>
diff --git a/app/views/request/list.rhtml b/app/views/request/list.rhtml
index 7cbd982f1..062b77c3e 100644
--- a/app/views/request/list.rhtml
+++ b/app/views/request/list.rhtml
@@ -14,22 +14,21 @@
<div style="clear:both"></div>
<div class="results_section">
- <% view_cache :ttl => 5.minutes.to_i, :tag => [@cache_tag] do %>
- <% if @list_results.empty? %>
- <p> <%= _('No requests of this sort yet.')%></p>
- <% else %>
- <h2 class="foi_results"><%= _('{{count}} FOI requests found', :count => @matches_estimated) %></h2>
- <div class="results_block">
- <% for result in @list_results%>
- <% if result.class.to_s == 'InfoRequestEvent' %>
- <%= render :partial => 'request/request_listing_via_event', :locals => { :event => result, :info_request => result.info_request } %>
- <% else %>
- <p><strong><%= _('Unexpected search result type') %> <%=result.class.to_s%></strong></p>
- <% end %>
- <% end %>
- </div>
- <% end %>
+ <% # TODO: Cache for 5 minutes %>
+ <% if @list_results.empty? %>
+ <p> <%= _('No requests of this sort yet.')%></p>
+ <% else %>
+ <h2 class="foi_results"><%= _('{{count}} FOI requests found', :count => @matches_estimated) %></h2>
+ <div class="results_block">
+ <% for result in @list_results%>
+ <% if result.class.to_s == 'InfoRequestEvent' %>
+ <%= render :partial => 'request/request_listing_via_event', :locals => { :event => result, :info_request => result.info_request } %>
+ <% else %>
+ <p><strong><%= _('Unexpected search result type') %> <%=result.class.to_s%></strong></p>
+ <% end %>
+ <% end %>
+ </div>
+ <% end %>
- <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @show_no_more_than) %>
- <% end %>
+ <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @show_no_more_than) %>
</div>
diff --git a/app/views/request/new_please_describe.rhtml b/app/views/request/new_please_describe.rhtml
index ff27405b8..6a193e70d 100644
--- a/app/views/request/new_please_describe.rhtml
+++ b/app/views/request/new_please_describe.rhtml
@@ -13,7 +13,7 @@ if they are successful yet or not.') %>
</ul>
<p>
- <%= raw(_('When you\'re done, <strong>come back here</strong>, <a href="%s">reload this page</a> and file your new request.') % [request.request_uri]) %>
+ <%= raw(_('When you\'re done, <strong>come back here</strong>, <a href="%s">reload this page</a> and file your new request.') % [request.fullpath]) %>
</p>
<p>
diff --git a/app/views/request/show.rhtml b/app/views/request/show.rhtml
index 0cae3a9aa..fa75a6529 100644
--- a/app/views/request/show.rhtml
+++ b/app/views/request/show.rhtml
@@ -106,7 +106,7 @@
<%= _('The request is <strong>waiting for clarification</strong>.') %>
<% if !@info_request.is_external? %>
<%= _('If you are {{user_link}}, please',:user_link=>user_link_for_request(@info_request)) %>
- <%= link_to _("sign in"), signin_url(:r => request.request_uri) %> <%= _('to send a follow up message.') %>
+ <%= link_to _("sign in"), signin_url(:r => request.fullpath) %> <%= _('to send a follow up message.') %>
<% end %>
<% end %>
<% elsif @status == 'gone_postal' %>
diff --git a/app/views/track/_tracking_links.rhtml b/app/views/track/_tracking_links.rhtml
index 06e87ac74..ee18ec475 100644
--- a/app/views/track/_tracking_links.rhtml
+++ b/app/views/track/_tracking_links.rhtml
@@ -9,7 +9,7 @@
<% elsif existing_track %>
<p><%= track_thing.params[:verb_on_page_already] %></p>
<div class="feed_link feed_link_<%=location%>">
- <%= link_to _("Unsubscribe"), {:controller => 'track', :action => 'update', :track_id => existing_track.id, :track_medium => "delete", :r => request.request_uri}, :class => "link_button_green" %>
+ <%= link_to _("Unsubscribe"), {:controller => 'track', :action => 'update', :track_id => existing_track.id, :track_medium => "delete", :r => request.fullpath}, :class => "link_button_green" %>
</div>
<% elsif track_thing %>
<div class="feed_link feed_link_<%=location%>">
diff --git a/app/views/user/show.rhtml b/app/views/user/show.rhtml
index 31ea2a70b..e8e59b541 100644
--- a/app/views/user/show.rhtml
+++ b/app/views/user/show.rhtml
@@ -97,7 +97,7 @@
<% if not @is_you %>
<p id="user_not_logged_in">
- <%= raw(_('<a href="%s">Sign in</a> to change password, subscriptions and more ({{user_name}} only)',:user_name=>h(@display_user.name)) % [signin_url(:r => request.request_uri)]) %>
+ <%= raw(_('<a href="%s">Sign in</a> to change password, subscriptions and more ({{user_name}} only)',:user_name=>h(@display_user.name)) % [signin_url(:r => request.fullpath)]) %>
</p>
<% end %>
</div>
@@ -186,7 +186,7 @@
<%=TrackThing.track_type_description(@track_things[0].track_type)%>
<%= hidden_field_tag 'track_type', @track_things[0].track_type %>
<%= hidden_field_tag 'user', @display_user.id %>
- <%= hidden_field_tag 'r', request.request_uri %>
+ <%= hidden_field_tag 'r', request.fullpath %>
<% if @track_things.size > 1 %>
<%= submit_tag _('unsubscribe all') %>
<% end %>
@@ -200,7 +200,7 @@
<%=TrackThing.track_type_description(track_type)%>
<%= hidden_field_tag 'track_type', track_type %>
<%= hidden_field_tag 'user', @display_user.id %>
- <%= hidden_field_tag 'r', request.request_uri %>
+ <%= hidden_field_tag 'r', request.fullpath %>
<% if track_things.size > 1 %>
<%= submit_tag _('unsubscribe all')%>
<% end %>
@@ -215,7 +215,7 @@
<div>
<%= track_thing.params[:list_description] %>
<%= hidden_field_tag 'track_medium', "delete", { :id => 'track_medium_' + track_thing.id.to_s } %>
- <%= hidden_field_tag 'r', request.request_uri, { :id => 'r_' + track_thing.id.to_s } %>
+ <%= hidden_field_tag 'r', request.fullpath, { :id => 'r_' + track_thing.id.to_s } %>
<%= submit_tag _('unsubscribe') %>
</div>
<% end %>
diff --git a/commonlib b/commonlib
-Subproject 3bf8c34eed89a946f981363eb862f25ac843769
+Subproject 4e84e9ed8fc75764da5139a51722729f2ba96ce
diff --git a/config.ru b/config.ru
index 30b00bfa1..74748fd45 100644
--- a/config.ru
+++ b/config.ru
@@ -1,2 +1,4 @@
-require File.dirname(__FILE__) + '/config/environment'
-run ActionController::Dispatcher.new
+# This file is used by Rack-based servers to start the application.
+
+require ::File.expand_path('../config/environment', __FILE__)
+run Alaveteli::Application
diff --git a/config/application.rb b/config/application.rb
new file mode 100644
index 000000000..0e02e9e03
--- /dev/null
+++ b/config/application.rb
@@ -0,0 +1,72 @@
+require File.expand_path('../boot', __FILE__)
+
+require 'rails/all'
+
+require File.dirname(__FILE__) + '/../lib/configuration'
+
+# If you have a Gemfile, require the gems listed there, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(:default, Rails.env) if defined?(Bundler)
+
+module Alaveteli
+ class Application < Rails::Application
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration should go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded.
+
+ # Custom directories with classes and modules you want to be autoloadable.
+ # config.autoload_paths += %W(#{config.root}/extras)
+
+ # Only load the plugins named here, in the order given (default is alphabetical).
+ # :all can be used as a placeholder for all plugins not explicitly named.
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
+
+ # Activate observers that should always be running.
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
+
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
+ # config.time_zone = 'Central Time (US & Canada)'
+
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
+ # config.i18n.default_locale = :de
+
+ # JavaScript files you want as :defaults (application.js is always included).
+ # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
+
+ # Configure the default encoding used in templates for Ruby 1.9.
+ config.encoding = "utf-8"
+
+ # Configure sensitive parameters which will be filtered from the log file.
+ config.filter_parameters += [:password]
+
+ # Use SQL instead of Active Record's schema dumper when creating the test database.
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
+ # like if you have constraints or database-specific column types
+ config.active_record.schema_format = :sql
+
+ # Make Active Record use UTC-base instead of local time
+ config.active_record.default_timezone = :utc
+
+ # This is the timezone that times and dates are displayed in
+ # Note that having set a zone, the Active Record
+ # time_zone_aware_attributes flag is on, so times from models
+ # will be in this time zone
+ config.time_zone = ::Configuration::time_zone
+
+ config.after_initialize do
+ require 'routing_filters.rb'
+ end
+
+ config.autoload_paths << "#{Rails.root.to_s}/lib/mail_handler"
+
+ # See Rails::Configuration for more options
+ ENV['RECAPTCHA_PUBLIC_KEY'] = ::Configuration::recaptcha_public_key
+ ENV['RECAPTCHA_PRIVATE_KEY'] = ::Configuration::recaptcha_private_key
+
+ # Insert a bit of middleware code to prevent uneeded cookie setting.
+ require "#{Rails.root}/lib/whatdotheyknow/strip_empty_sessions"
+ config.middleware.insert_before ActionDispatch::Session::CookieStore, WhatDoTheyKnow::StripEmptySessions, :key => '_wdtk_cookie_session', :path => "/", :httponly => true
+ end
+end
diff --git a/config/boot.rb b/config/boot.rb
index 906a2bace..4489e5868 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -1,135 +1,6 @@
-# Don't change this file!
-# Configure your app in config/environment.rb and config/environments/*.rb
+require 'rubygems'
-# Hmmm, that's a bit daft - 'production' needs setting not only in the web
-# server, it also needs setting in all the scripts, so a central place seems
-# better. Look for a config/rails_env file, and read stuff from there if
-# it exists. Put just a line like this in there:
-# ENV['RAILS_ENV'] = 'production'
-rails_env_file = File.expand_path(File.join(File.dirname(__FILE__), 'rails_env.rb'))
-if File.exists?(rails_env_file)
- require rails_env_file
-end
-
-RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
-
-module Rails
- class << self
- def boot!
- unless booted?
- preinitialize
- pick_boot.run
- end
- end
-
- def booted?
- defined? Rails::Initializer
- end
-
- def pick_boot
- (vendor_rails? ? VendorBoot : GemBoot).new
- end
-
- def vendor_rails?
- File.exist?("#{RAILS_ROOT}/vendor/rails/Rakefile")
- end
-
- def preinitialize
- load(preinitializer_path) if File.exist?(preinitializer_path)
- end
-
- def preinitializer_path
- "#{RAILS_ROOT}/config/preinitializer.rb"
- end
- end
-
- class Boot
- def run
- load_initializer
-
- Rails::Initializer.class_eval do
- def load_gems
- @bundler_loaded ||= Bundler.require :default, Rails.env
- end
- end
-
- Rails::Initializer.run(:set_load_path)
- end
- end
-
- class VendorBoot < Boot
- def load_initializer
- require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
- Rails::Initializer.run(:install_gem_spec_stubs)
- Rails::GemDependency.add_frozen_gem_path
- end
- end
-
- class GemBoot < Boot
- def load_initializer
- self.class.load_rubygems
- load_rails_gem
- require 'initializer'
- end
-
- def load_rails_gem
- if version = self.class.gem_version
- gem 'rails', version
- else
- gem 'rails'
- end
- rescue Gem::LoadError => load_error
- if load_error.message =~ /Could not find RubyGem rails/
- STDERR.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
- exit 1
- else
- raise
- end
- end
-
- class << self
- def rubygems_version
- Gem::RubyGemsVersion rescue nil
- end
-
- def gem_version
- if defined? RAILS_GEM_VERSION
- RAILS_GEM_VERSION
- elsif ENV.include?('RAILS_GEM_VERSION')
- ENV['RAILS_GEM_VERSION']
- else
- parse_gem_version(read_environment_rb)
- end
- end
-
- def load_rubygems
- min_version = '1.3.2'
- require 'rubygems'
-
- unless rubygems_version >= min_version
- $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
- exit 1
- end
-
- rescue LoadError
- $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
- exit 1
- end
-
- def parse_gem_version(text)
- $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
- end
-
- private
- def read_environment_rb
- File.read("#{RAILS_ROOT}/config/environment.rb")
- end
- end
- end
-end
-
-
-
-# All that for this:
-Rails.boot!
+# Set up gems listed in the Gemfile.
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
diff --git a/config/environment.rb b/config/environment.rb
index e79efdcfa..196680b23 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -1,162 +1,5 @@
-# Be sure to restart your web server when you modify this file.
-if RUBY_VERSION.to_f >= 1.9
- # the default encoding for IO is utf-8, and we use utf-8 internally
- Encoding.default_external = Encoding.default_internal = Encoding::UTF_8
- # Suppress warning messages and require inflector to avoid iconv deprecation message
- # "iconv will be deprecated in the future, use String#encode instead." when loading
- # it as part of rails
- original_verbose, $VERBOSE = $VERBOSE, nil
- require 'active_support/inflector'
- # Activate warning messages again.
- $VERBOSE = original_verbose
-end
+# Load the rails application
+require File.expand_path('../application', __FILE__)
-# Uncomment below to force Rails into production mode when
-# you don't control web/app server and can't set it the proper way
-# ENV['RAILS_ENV'] ||= 'production'
-
-# Specifies gem version of Rails to use when vendor/rails is not present
-RAILS_GEM_VERSION = '2.3.14' unless defined? RAILS_GEM_VERSION
-
-# Bootstrap the Rails environment, frameworks, and default configuration
-require File.join(File.dirname(__FILE__), 'boot')
-
-# MySociety specific helper functions
-$:.push(File.join(File.dirname(__FILE__), '../commonlib/rblib'))
-# ... if these fail to include, you need the commonlib submodule from git
-# (type "git submodule update --init" in the whatdotheyknow directory)
-
-$:.unshift(File.join(File.dirname(__FILE__), '../vendor/plugins/globalize2/lib'))
-
-load "validate.rb"
-load "config.rb"
-load "format.rb"
-load "debug_helpers.rb"
-load "util.rb"
-# Patch Rails::GemDependency to cope with older versions of rubygems, e.g. in Debian Lenny
-# Restores override removed in https://github.com/rails/rails/commit/c20a4d18e36a13b5eea3155beba36bb582c0cc87
-# without effecting method behaviour
-# and adds fallback gem call removed in https://github.com/rails/rails/commit/4c3725723f15fab0a424cb1318b82b460714b72f
-require File.join(File.dirname(__FILE__), '../lib/old_rubygems_patch')
-require 'configuration'
-
-# Application version
-ALAVETELI_VERSION = '0.6.8'
-
-Rails::Initializer.run do |config|
- # Load intial mySociety config
- if ENV["RAILS_ENV"] == "test"
- MySociety::Config.set_file(File.join(config.root_path, 'config', 'test'), true)
- else
- MySociety::Config.set_file(File.join(config.root_path, 'config', 'general'), true)
- end
- MySociety::Config.load_default
-
- # Settings in config/environments/* take precedence over those specified here
-
- # Skip frameworks you're not going to use (only works if using vendor/rails)
- # config.frameworks -= [ :action_web_service, :action_mailer ]
-
- # Only load the plugins named here, by default all plugins in vendor/plugins are loaded
- # config.plugins = %W( exception_notification ssl_requirement )
-
- # Add additional load paths for your own custom dirs
- # config.load_paths += %W( #{Rails.root}/extras )
-
- # Force all environments to use the same logger level
- # (by default production uses :info, the others :debug)
- # TEMP: uncomment this to turn on logging in production environments
- # config.log_level = :debug
- #
- # Specify gems that this application depends on and have them installed with rake gems:install
- #GettextI18nRails.translations_are_html_safe = true
-
- # Use SQL instead of Active Record's schema dumper when creating the test database.
- # This is necessary if your schema can't be completely dumped by the schema dumper,
- # like if you have constraints or database-specific column types
- config.active_record.schema_format = :sql
-
- # Activate observers that should always be running
- # config.active_record.observers = :cacher, :garbage_collector
-
- # Make Active Record use UTC-base instead of local time
- config.active_record.default_timezone = :utc
-
- # This is the timezone that times and dates are displayed in
- # Note that having set a zone, the Active Record
- # time_zone_aware_attributes flag is on, so times from models
- # will be in this time zone
- config.time_zone = Configuration::time_zone
-
- config.after_initialize do
- require 'routing_filters.rb'
- end
-
- config.autoload_paths << "#{RAILS_ROOT}/lib/mail_handler"
-
- # See Rails::Configuration for more options
- ENV['RECAPTCHA_PUBLIC_KEY'] = Configuration::recaptcha_public_key
- ENV['RECAPTCHA_PRIVATE_KEY'] = Configuration::recaptcha_private_key
-end
-
-# Add new inflection rules using the following format
-# (all these examples are active by default):
-# Inflector.inflections do |inflect|
-# inflect.plural /^(ox)$/i, '\1en'
-# inflect.singular /^(ox)en/i, '\1'
-# inflect.irregular 'person', 'people'
-# inflect.uncountable %w( fish sheep )
-# end
-
-# Add new mime types for use in respond_to blocks:
-# Mime::Type.register "text/richtext", :rtf
-# Mime::Type.register "application/x-mobile", :mobile
-
-# The Rails cache is set up by the Interlock plugin to use memcached
-
-# Domain for URLs (so can work for scripts, not just web pages)
-ActionMailer::Base.default_url_options[:host] = Configuration::domain
-
-# So that javascript assets use full URL, so proxied admin URLs read javascript OK
-if (Configuration::domain != "")
- ActionController::Base.asset_host = Proc.new { |source, request|
- if ENV["RAILS_ENV"] != "test" && request.fullpath.match(/^\/admin\//)
- Configuration::admin_public_url
- else
- Configuration::domain
- end
- }
-end
-
-# fallback locale and available locales
-available_locales = Configuration::available_locales.split(/ /)
-default_locale = Configuration::default_locale
-
-FastGettext.default_available_locales = available_locales
-I18n.locale = default_locale
-I18n.available_locales = available_locales.map {|locale_name| locale_name.to_sym}
-I18n.default_locale = default_locale
-
-# Customise will_paginate URL generation
-WillPaginate::ViewHelpers.pagination_options[:renderer] = 'WillPaginateExtension::LinkRenderer'
-
-# Load monkey patches and other things from lib/
-require 'ruby19.rb'
-require 'activesupport_cache_extensions.rb'
-require 'timezone_fixes.rb'
-require 'use_spans_for_errors.rb'
-require 'make_html_4_compliant.rb'
-require 'activerecord_errors_extensions.rb'
-require 'willpaginate_extension.rb'
-require 'sendmail_return_path.rb'
-require 'i18n_fixes.rb'
-require 'rack_quote_monkeypatch.rb'
-require 'world_foi_websites.rb'
-require 'alaveteli_external_command.rb'
-require 'quiet_opener.rb'
-require 'mail_handler'
-
-if !Configuration.exception_notifications_from.blank? && !Configuration.exception_notifications_to.blank?
- ExceptionNotification::Notifier.sender_address = Configuration::exception_notifications_from
- ExceptionNotification::Notifier.exception_recipients = Configuration::exception_notifications_to
-end
+# Initialize the rails application
+Alaveteli::Application.initialize!
diff --git a/config/environments/development.rb b/config/environments/development.rb
index c43cdb049..5b91c3271 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -1,29 +1,31 @@
-# Settings specified here will take precedence over those in config/environment.rb
+Alaveteli::Application.configure do
+ # Settings specified here will take precedence over those in config/environment.rb
-config.log_level = :info
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the webserver when you make code changes.
+ config.cache_classes = false
-# In the development environment your application's code is reloaded on
-# every request. This slows down response time but is perfect for development
-# since you don't have to restart the webserver when you make code changes.
-config.cache_classes = false
+ # Log error messages when you accidentally call methods on nil.
+ config.whiny_nils = true
-# Log error messages when you accidentally call methods on nil.
-config.whiny_nils = true
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+ config.action_view.debug_rjs = true
-# Show full error reports and disable caching
-config.action_controller.consider_all_requests_local = true
-config.action_controller.perform_caching = false
-config.action_view.debug_rjs = true
+ # Don't care if the mailer can't send
+ config.action_mailer.raise_delivery_errors = false
+ config.action_mailer.perform_deliveries = true
+ # Use mailcatcher in development
+ config.action_mailer.delivery_method = :smtp # so is queued, rather than giving immediate errors
+ config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }
-# Don't care if the mailer can't send
-config.action_mailer.raise_delivery_errors = false
-config.action_mailer.perform_deliveries = true
-# Use mailcatcher in development
-config.action_mailer.delivery_method = :smtp # so is queued, rather than giving immediate errors
-config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }
+ # Writes useful log files to debug memory leaks, of the sort where have
+ # unintentionally kept references to objects, especially strings.
+ # require 'memory_profiler'
+ # MemoryProfiler.start :string_debug => true, :delay => 10
-
-# Writes useful log files to debug memory leaks, of the sort where have
-# unintentionally kept references to objects, especially strings.
-# require 'memory_profiler'
-# MemoryProfiler.start :string_debug => true, :delay => 10
+ # Print deprecation notices to the Rails logger
+ config.active_support.deprecation = :log
+end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 84a8f5965..ac4a2ccb6 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -1,19 +1,23 @@
-# Settings specified here will take precedence over those in config/environment.rb
+Alaveteli::Application.configure do
+ # Settings specified here will take precedence over those in config/environment.rb
-# The production environment is meant for finished, "live" apps.
-# Code is not reloaded between requests
-config.cache_classes = true
+ # The production environment is meant for finished, "live" apps.
+ # Code is not reloaded between requests
+ config.cache_classes = true
-# Use a different logger for distributed setups
-# config.logger = SyslogLogger.new
+ # Use a different logger for distributed setups
+ # config.logger = SyslogLogger.new
-# Full error reports are disabled and caching is turned on
-config.action_controller.consider_all_requests_local = false
-config.action_controller.perform_caching = true
+ # Full error reports are disabled and caching is turned on
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
-# Enable serving of images, stylesheets, and javascripts from an asset server
-# config.action_controller.asset_host = "http://assets.example.com"
+ # Enable serving of images, stylesheets, and javascripts from an asset server
+ # config.action_controller.asset_host = "http://assets.example.com"
-# Disable delivery errors, bad email addresses will be ignored
-# config.action_mailer.raise_delivery_errors = false
-config.action_mailer.delivery_method = :sendmail # so is queued, rather than giving immediate errors
+ # Disable delivery errors, bad email addresses will be ignored
+ # config.action_mailer.raise_delivery_errors = false
+ config.action_mailer.delivery_method = :sendmail # so is queued, rather than giving immediate errors
+
+ config.active_support.deprecation = :notify
+end
diff --git a/config/environments/staging.rb b/config/environments/staging.rb
index 84a8f5965..0bb0db71a 100644
--- a/config/environments/staging.rb
+++ b/config/environments/staging.rb
@@ -1,19 +1,21 @@
-# Settings specified here will take precedence over those in config/environment.rb
+Alaveteli::Application.configure do
+ # Settings specified here will take precedence over those in config/environment.rb
-# The production environment is meant for finished, "live" apps.
-# Code is not reloaded between requests
-config.cache_classes = true
+ # The production environment is meant for finished, "live" apps.
+ # Code is not reloaded between requests
+ config.cache_classes = true
-# Use a different logger for distributed setups
-# config.logger = SyslogLogger.new
+ # Use a different logger for distributed setups
+ # config.logger = SyslogLogger.new
-# Full error reports are disabled and caching is turned on
-config.action_controller.consider_all_requests_local = false
-config.action_controller.perform_caching = true
+ # Full error reports are disabled and caching is turned on
+ config.action_controller.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
-# Enable serving of images, stylesheets, and javascripts from an asset server
-# config.action_controller.asset_host = "http://assets.example.com"
+ # Enable serving of images, stylesheets, and javascripts from an asset server
+ # config.action_controller.asset_host = "http://assets.example.com"
-# Disable delivery errors, bad email addresses will be ignored
-# config.action_mailer.raise_delivery_errors = false
-config.action_mailer.delivery_method = :sendmail # so is queued, rather than giving immediate errors
+ # Disable delivery errors, bad email addresses will be ignored
+ # config.action_mailer.raise_delivery_errors = false
+ config.action_mailer.delivery_method = :sendmail # so is queued, rather than giving immediate errors
+end
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 784ea18d3..c569af8cd 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -1,25 +1,29 @@
-# Settings specified here will take precedence over those in config/environment.rb
+Alaveteli::Application.configure do
+ # Settings specified here will take precedence over those in config/environment.rb
-require 'patches/fixtures_constraint_disabling'
+ require 'patches/fixtures_constraint_disabling'
-# The test environment is used exclusively to run your application's
-# test suite. You never need to work with it otherwise. Remember that
-# your test database is "scratch space" for the test suite and is wiped
-# and recreated between test runs. Don't rely on the data there!
-config.cache_classes = true
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
-# Log error messages when you accidentally call methods on nil.
-config.whiny_nils = true
+ # Log error messages when you accidentally call methods on nil.
+ config.whiny_nils = true
-# Show full error reports and disable caching
-config.action_controller.consider_all_requests_local = true
-config.action_controller.perform_caching = false
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
-# Tell ActionMailer not to deliver emails to the real world.
-# The :test delivery method accumulates sent emails in the
-# ActionMailer::Base.deliveries array.
-config.action_mailer.delivery_method = :test
+ # Tell ActionMailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
-# Disable request forgery protection in test environment
-config.action_controller.allow_forgery_protection = false
+ # Disable request forgery protection in test environment
+ config.action_controller.allow_forgery_protection = false
+ # Print deprecation notices to the stderr
+ config.active_support.deprecation = :stderr
+end
diff --git a/config/general.yml-example b/config/general.yml-example
index fd134b0c2..bfe289541 100644
--- a/config/general.yml-example
+++ b/config/general.yml-example
@@ -32,6 +32,10 @@ AVAILABLE_LOCALES: 'en es'
DEFAULT_LOCALE: 'en'
USE_DEFAULT_BROWSER_LANGUAGE: true
+# If you don't want the default locale to be included in URLs generated
+# by the application, set this to false
+INCLUDE_DEFAULT_LOCALE_IN_URLS: true
+
# How many days should have passed before an answer to a request is officially late?
REPLY_LATE_AFTER_DAYS: 20
REPLY_VERY_LATE_AFTER_DAYS: 40
diff --git a/config/initializers/alaveteli.rb b/config/initializers/alaveteli.rb
new file mode 100644
index 000000000..349934ef4
--- /dev/null
+++ b/config/initializers/alaveteli.rb
@@ -0,0 +1,88 @@
+if RUBY_VERSION.to_f >= 1.9
+ # the default encoding for IO is utf-8, and we use utf-8 internally
+ Encoding.default_external = Encoding.default_internal = Encoding::UTF_8
+ # Suppress warning messages and require inflector to avoid iconv deprecation message
+ # "iconv will be deprecated in the future, use String#encode instead." when loading
+ # it as part of rails
+ original_verbose, $VERBOSE = $VERBOSE, nil
+ require 'active_support/inflector'
+ # Activate warning messages again.
+ $VERBOSE = original_verbose
+end
+
+# MySociety specific helper functions
+$:.push(File.join(File.dirname(__FILE__), '../../commonlib/rblib'))
+# ... if these fail to include, you need the commonlib submodule from git
+# (type "git submodule update --init" in the whatdotheyknow directory)
+
+load "validate.rb"
+load "config.rb"
+load "format.rb"
+load "debug_helpers.rb"
+load "util.rb"
+
+# Application version
+ALAVETELI_VERSION = '0.6.8'
+
+# Add new inflection rules using the following format
+# (all these examples are active by default):
+# Inflector.inflections do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
+# Mime::Type.register "application/x-mobile", :mobile
+
+# The Rails cache is set up by the Interlock plugin to use memcached
+
+# Domain for URLs (so can work for scripts, not just web pages)
+ActionMailer::Base.default_url_options[:host] = Configuration::domain
+
+# So that javascript assets use full URL, so proxied admin URLs read javascript OK
+if (Configuration::domain != "")
+ ActionController::Base.asset_host = Proc.new { |source, request|
+ if ENV["RAILS_ENV"] != "test" && request.fullpath.match(/^\/admin\//)
+ Configuration::admin_public_url
+ else
+ Configuration::domain
+ end
+ }
+end
+
+# fallback locale and available locales
+available_locales = Configuration::available_locales.split(/ /)
+default_locale = Configuration::default_locale
+
+FastGettext.default_available_locales = available_locales
+I18n.locale = default_locale
+I18n.available_locales = available_locales.map {|locale_name| locale_name.to_sym}
+I18n.default_locale = default_locale
+
+# Customise will_paginate URL generation
+WillPaginate::ViewHelpers.pagination_options[:renderer] = 'WillPaginateExtension::LinkRenderer'
+
+# Load monkey patches and other things from lib/
+require 'ruby19.rb'
+require 'activesupport_cache_extensions.rb'
+require 'timezone_fixes.rb'
+require 'use_spans_for_errors.rb'
+require 'make_html_4_compliant.rb'
+require 'activerecord_errors_extensions.rb'
+require 'willpaginate_extension.rb'
+require 'sendmail_return_path.rb'
+require 'i18n_fixes.rb'
+require 'rack_quote_monkeypatch.rb'
+require 'world_foi_websites.rb'
+require 'alaveteli_external_command.rb'
+require 'quiet_opener.rb'
+require 'mail_handler'
+require 'public_body_categories'
+
+if !Configuration.exception_notifications_from.blank? && !Configuration.exception_notifications_to.blank?
+ ExceptionNotification::Notifier.sender_address = Configuration::exception_notifications_from
+ ExceptionNotification::Notifier.exception_recipients = Configuration::exception_notifications_to
+end
diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb
new file mode 100644
index 000000000..59385cdf3
--- /dev/null
+++ b/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
diff --git a/config/initializers/fast_gettext.rb b/config/initializers/fast_gettext.rb
index 2ecf5cb5d..1cd6440e4 100644
--- a/config/initializers/fast_gettext.rb
+++ b/config/initializers/fast_gettext.rb
@@ -3,4 +3,4 @@ FastGettext.default_text_domain = 'app'
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-
+RoutingFilter::Locale.include_default_locale = Configuration::include_default_locale_in_urls \ No newline at end of file
diff --git a/config/initializers/gettext_i18n_rails.rb b/config/initializers/gettext_i18n_rails.rb
new file mode 100644
index 000000000..ef306682b
--- /dev/null
+++ b/config/initializers/gettext_i18n_rails.rb
@@ -0,0 +1,3 @@
+# FIXME: Audit the translations for XSS opportunities. Ultimately it would be
+# good to get rid of this and explicitly mark strings as html_safe
+GettextI18nRails.translations_are_html_safe = true
diff --git a/vendor/plugins/interlock/test/integration/app/config/initializers/inflections.rb b/config/initializers/inflections.rb
index 09158b865..9e8b0131f 100644
--- a/vendor/plugins/interlock/test/integration/app/config/initializers/inflections.rb
+++ b/config/initializers/inflections.rb
@@ -1,8 +1,8 @@
# Be sure to restart your server when you modify this file.
-# Add new inflection rules using the following format
+# Add new inflection rules using the following format
# (all these examples are active by default):
-# Inflector.inflections do |inflect|
+# ActiveSupport::Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
diff --git a/vendor/plugins/interlock/test/integration/app/config/initializers/mime_types.rb b/config/initializers/mime_types.rb
index 72aca7e44..72aca7e44 100644
--- a/vendor/plugins/interlock/test/integration/app/config/initializers/mime_types.rb
+++ b/config/initializers/mime_types.rb
diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb
new file mode 100644
index 000000000..5277e8927
--- /dev/null
+++ b/config/initializers/secret_token.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+Alaveteli::Application.config.secret_token = Configuration::cookie_store_session_secret
diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
index 8cfa333f2..ca283d4e0 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -1,17 +1,2 @@
# Be sure to restart your server when you modify this file.
-
-# Your secret key for verifying cookie session data integrity.
-# If you change this key, all old sessions will become invalid!
-# Make sure the secret is at least 30 characters and all random,
-# no regular words or you'll be exposed to dictionary attacks.
-
-ActionController::Base.session = {
- :key => '_wdtk_cookie_session',
- :secret => Configuration::cookie_store_session_secret
-}
-ActionController::Base.session_store = :cookie_store
-
-# Insert a bit of middleware code to prevent uneeded cookie setting.
-require "#{Rails.root}/lib/whatdotheyknow/strip_empty_sessions"
-ActionController::Dispatcher.middleware.insert_before ActionController::Base.session_store, WhatDoTheyKnow::StripEmptySessions, :key => '_wdtk_cookie_session', :path => "/", :httponly => true
-
+Rails.application.config.session_store :cookie_store, :key => '_wdtk_cookie_session'
diff --git a/config/packages b/config/packages
index ded8dc9a5..775a55e38 100644
--- a/config/packages
+++ b/config/packages
@@ -33,7 +33,7 @@ libpq-dev
uuid-dev
ruby1.8-dev
rubygems
-rake
+rake (>= 0.9.2.2)
build-essential
bundler
sqlite3
diff --git a/db/migrate/094_remove_old_tags_foreign_key.rb b/db/migrate/094_remove_old_tags_foreign_key.rb
index 1a87b97c2..839f76334 100644
--- a/db/migrate/094_remove_old_tags_foreign_key.rb
+++ b/db/migrate/094_remove_old_tags_foreign_key.rb
@@ -4,10 +4,11 @@ class RemoveOldTagsForeignKey < ActiveRecord::Migration
execute "ALTER TABLE has_tag_string_tags DROP CONSTRAINT fk_public_body_tags_public_body"
end
- remove_index :public_body_tags, [:public_body_id, :name, :value]
- remove_index :public_body_tags, :name
+ # This table was already removed in the previous migration
+ # remove_index :public_body_tags, [:public_body_id, :name, :value]
+ # remove_index :public_body_tags, :name
- add_index :has_tag_string_tags, [:model, :model_id, :name, :value]
+ add_index :has_tag_string_tags, [:model, :model_id, :name, :value], :name => 'by_model_and_model_id_and_name_and_value'
add_index :has_tag_string_tags, :name
end
diff --git a/db/seeds.rb b/db/seeds.rb
new file mode 100644
index 000000000..664d8c74c
--- /dev/null
+++ b/db/seeds.rb
@@ -0,0 +1,7 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#
+# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
+# Mayor.create(:name => 'Daley', :city => cities.first)
diff --git a/lib/alaveteli_external_command.rb b/lib/alaveteli_external_command.rb
index 3bfc34e3a..24b4b1aa8 100644
--- a/lib/alaveteli_external_command.rb
+++ b/lib/alaveteli_external_command.rb
@@ -2,6 +2,12 @@ require 'external_command'
module AlaveteliExternalCommand
class << self
+ # Final argument can be a hash of options.
+ # Valid options are:
+ # :append_to - string to append the output of the process to
+ # :stdin_string - stdin string to pass to the process
+ # :binary_output - boolean flag for treating the output as binary or text (only significant
+ # ruby 1.9 and above)
def run(program_name, *args)
# Run an external program, and return its output.
# Standard error is suppressed unless the program
@@ -10,7 +16,7 @@ module AlaveteliExternalCommand
if !args.empty? && args[-1].is_a?(Hash)
opts = args.pop
end
-
+
if program_name =~ %r(^/)
program_path = program_name
else
@@ -24,12 +30,16 @@ module AlaveteliExternalCommand
end
raise "Could not find #{program_name} in any of #{Configuration::utility_search_path.join(', ')}" if !found
end
-
+
xc = ExternalCommand.new(program_path, *args)
if opts.has_key? :append_to
xc.out = opts[:append_to]
end
+ if opts.has_key? :binary_output
+ xc.binary_mode = opts[:binary_output]
+ end
xc.run(opts[:stdin_string] || "", opts[:env] || {})
+
if xc.status != 0
# Error
$stderr.puts("Error from #{program_name} #{args.join(' ')}:")
diff --git a/lib/configuration.rb b/lib/configuration.rb
index abd0f5cdc..9c369b2e7 100644
--- a/lib/configuration.rb
+++ b/lib/configuration.rb
@@ -1,3 +1,13 @@
+require File.dirname(__FILE__) + '/../commonlib/rblib/config'
+
+# Load intial mySociety config
+if ENV["RAILS_ENV"] == "test"
+ MySociety::Config.set_file(File.join(File.dirname(__FILE__), 'test'), true)
+else
+ MySociety::Config.set_file(File.join(File.dirname(__FILE__), 'general'), true)
+end
+MySociety::Config.load_default
+
# Configuration values with defaults
# TODO: Make this return different values depending on the current rails environment
@@ -25,6 +35,7 @@ module Configuration
:GA_CODE => '',
:GAZE_URL => '',
:HTML_TO_PDF_COMMAND => '',
+ :INCLUDE_DEFAULT_LOCALE_IN_URLS => true,
:INCOMING_EMAIL_DOMAIN => 'localhost',
:INCOMING_EMAIL_PREFIX => '',
:INCOMING_EMAIL_SECRET => 'dummysecret',
diff --git a/lib/i18n_fixes.rb b/lib/i18n_fixes.rb
index f75b969c4..bb339fc55 100644
--- a/lib/i18n_fixes.rb
+++ b/lib/i18n_fixes.rb
@@ -6,10 +6,12 @@
# so that we can interpolate our translation strings nicely
def _(key, options = {})
- # Assume the result of doing any translation is html_safe.
- # In other words, we trust the translators.
- translation = FastGettext._(key).html_safe || key
- gettext_interpolate(translation, options)
+ # HACK: We should be going via GettextI18nRails instead of FastGettext below
+ # so that #translations_are_html_safe is respected but calling it directly
+ # doesn't work for me. I'm just marking the resulting string as html_safe.
+ # This whole hacky file should be removed
+ translation = FastGettext._(key) || key
+ gettext_interpolate(translation, options).html_safe
end
INTERPOLATION_RESERVED_KEYS = %w(scope default)
diff --git a/lib/mail_handler/backends/mail_backend.rb b/lib/mail_handler/backends/mail_backend.rb
index 0e198adf0..b75e6ed63 100644
--- a/lib/mail_handler/backends/mail_backend.rb
+++ b/lib/mail_handler/backends/mail_backend.rb
@@ -23,19 +23,291 @@ module MailHandler
main
end
+ # Returns an outlook message as a Mail object
+ def mail_from_outlook(content)
+ msg = Mapi::Msg.open(StringIO.new(content))
+ mail = mail_from_raw_email(msg.to_mime.to_s)
+ mail.ready_to_send!
+ mail
+ end
+
# Return a copy of the file name for the mail part
- def get_part_file_name(mail_part)
- part_file_name = mail_part.filename
+ def get_part_file_name(part)
+ part_file_name = part.filename
part_file_name.nil? ? nil : part_file_name.dup
end
+ # Get the body of a mail part
+ def get_part_body(part)
+ part.body.decoded
+ end
+
+ # Return the first from field if any
+ def first_from(mail)
+ if mail[:from]
+ begin
+ mail[:from].addrs[0]
+ mail[:from].decoded
+ return mail[:from].addrs[0]
+ rescue
+ return mail[:from].value
+ end
+ else
+ nil
+ end
+ end
+
+ # Return the first from address if any
+ def get_from_address(mail)
+ first_from = first_from(mail)
+ if first_from
+ if first_from.is_a?(String)
+ return nil
+ else
+ return first_from.address
+ end
+ else
+ return nil
+ end
+ end
+
+ # Return the first from name if any
+ def get_from_name(mail)
+ first_from = first_from(mail)
+ if first_from
+ if first_from.is_a?(String)
+ return nil
+ else
+ return first_from.display_name ? eval(%Q{"#{first_from.display_name}"}) : nil
+ end
+ else
+ return nil
+ end
+ end
+
+ def get_all_addresses(mail)
+ envelope_to = mail['envelope-to'] ? [mail['envelope-to'].value] : []
+ ((mail.to || []) +
+ (mail.cc || []) +
+ (envelope_to || [])).uniq
+ end
+
+ def empty_return_path?(mail)
+ return false if mail['return-path'].nil?
+ return true if mail['return-path'].value.blank?
+ return false
+ end
+
+ def get_auto_submitted(mail)
+ mail['auto-submitted'] ? mail['auto-submitted'].value : nil
+ end
+
+ def get_content_type(part)
+ part.content_type ? part.content_type.split(';')[0] : nil
+ end
+
+ def get_header_string(header, mail)
+ mail.header[header] ? mail.header[header].to_s : nil
+ end
+
+ # Detects whether a mail part is an Outlook email
+ def is_outlook?(part)
+ filename = get_part_file_name(part)
+ return true if get_content_type(part) == 'application/vnd.ms-outlook'
+ if filename && AlaveteliFileTypes.filename_to_mimetype(filename) == 'application/vnd.ms-outlook'
+ return true
+ end
+ return false
+ end
+
+ # Convert a mail part which is an attached mail in one of
+ # several formats into a mail object and set it as the
+ # rfc822_attachment on the part. If the mail part can't be
+ # converted, the content type on the part is updated to
+ # 'text/plain' for an RFC822 attachment, and 'application/octet-stream'
+ # for other types
+ def decode_attached_part(part, parent_mail)
+ if get_content_type(part) == 'message/rfc822'
+ # An email attached as text
+ part.rfc822_attachment = mail_from_raw_email(part.body)
+ if part.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as text
+ part.content_type = 'text/plain'
+ end
+ elsif is_outlook?(part)
+ part.rfc822_attachment = mail_from_outlook(part.body.decoded)
+ if part.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as binary
+ part.content_type = 'application/octet-stream'
+ end
+ elsif get_content_type(part) == 'application/ms-tnef'
+ # A set of attachments in a TNEF file
+ part.rfc822_attachment = mail_from_tnef(part.body.decoded)
+ if part.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as binary
+ part.content_type = 'application/octet-stream'
+ end
+ end
+ if part.rfc822_attachment
+ expand_and_normalize_parts(part.rfc822_attachment, parent_mail)
+ end
+ end
+
+ # Expand and normalize a mail part recursively. Decodes attached messages into
+ # Mail objects wherever possible. Sets a default content type if none is
+ # set. Tries to set a more specific content type for binary content types.
+ def expand_and_normalize_parts(part, parent_mail)
+ if part.multipart?
+ part.parts.each{ |sub_part| expand_and_normalize_parts(sub_part, parent_mail) }
+ else
+ part_filename = get_part_file_name(part)
+ charset = part.charset # save this, because overwriting content_type also resets charset
+
+ # Don't allow nil content_types
+ if get_content_type(part).nil?
+ part.content_type = 'application/octet-stream'
+ end
+
+ # PDFs often come with this mime type, fix it up for view code
+ if get_content_type(part) == 'application/octet-stream'
+ part_body = get_part_body(part)
+ calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(part_filename,
+ part_body)
+ if calc_mime
+ part.content_type = calc_mime
+ end
+ end
+
+ # Use standard content types for Word documents etc.
+ part.content_type = normalise_content_type(get_content_type(part))
+ decode_attached_part(part, parent_mail)
+ part.charset = charset
+ end
+ end
+
+ # Count the parts in a mail part recursively, including any attached messages.
+ # Set the count on the parent mail, and set a url_part_number on the part itself.
+ # Set the count for the first uudecoded part on the parent mail also.
+ def count_parts(part, parent_mail)
+ if part.multipart?
+ part.parts.each { |p| count_parts(p, parent_mail) }
+ else
+ if part.rfc822_attachment
+ count_parts(part.rfc822_attachment, parent_mail)
+ else
+ parent_mail.count_parts_count += 1
+ part.url_part_number = parent_mail.count_parts_count
+ end
+ end
+ parent_mail.count_first_uudecode_count = parent_mail.count_parts_count
+ end
+
+ # Choose the best part from alternatives
+ def choose_best_alternative(mail)
+ if mail.html_part
+ return mail.html_part
+ elsif mail.text_part
+ return mail.text_part
+ else
+ return mail.parts.first
+ end
+ end
+
+ # Expand and normalize the parts of a mail, select the best part
+ # wherever there is an alternative, and then count the returned
+ # leaves and assign url_part values to them
+ def get_attachment_leaves(mail)
+ expand_and_normalize_parts(mail, mail)
+ leaves = _get_attachment_leaves_recursive(mail, nil, mail)
+ mail.count_parts_count = 0
+ count_parts(mail, mail)
+ return leaves
+ end
+
+ # Recurse through a mail part, selecting the best part wherever there is
+ # an alternative
+ def _get_attachment_leaves_recursive(part, within_rfc822_attachment, parent_mail)
+ leaves_found = []
+ if part.multipart?
+ raise "no parts on multipart mail" if part.parts.size == 0
+ if part.sub_type == 'alternative'
+ best_part = choose_best_alternative(part)
+ leaves_found += _get_attachment_leaves_recursive(best_part,
+ within_rfc822_attachment,
+ parent_mail)
+ else
+ # Add all parts
+ part.parts.each do |sub_part|
+ leaves_found += _get_attachment_leaves_recursive(sub_part,
+ within_rfc822_attachment,
+ parent_mail)
+ end
+ end
+ else
+ # Add all the parts of a decoded attached message
+ if part.rfc822_attachment
+ leaves_found += _get_attachment_leaves_recursive(part.rfc822_attachment,
+ part.rfc822_attachment,
+ parent_mail)
+ else
+ # Store leaf
+ part.within_rfc822_attachment = within_rfc822_attachment
+ leaves_found += [part]
+ end
+ end
+ return leaves_found
+ end
+
+ # Add selected useful headers from an attached message to its body
+ def extract_attached_message_headers(leaf)
+ body = get_part_body(leaf)
+ # Test to see if we are in the first part of the attached
+ # RFC822 message and it is text, if so add headers.
+ if leaf.within_rfc822_attachment == leaf && get_content_type(leaf) == 'text/plain'
+ headers = ""
+ [ 'Date', 'Subject', 'From', 'To', 'Cc' ].each do |header|
+ if header_value = get_header_string(header, leaf.within_rfc822_attachment)
+ if !header_value.blank?
+ headers = headers + header + ": " + header_value.to_s + "\n"
+ end
+ end
+ end
+ # XXX call _convert_part_body_to_text here, but need to get charset somehow
+ # e.g. http://www.whatdotheyknow.com/request/1593/response/3088/attach/4/Freedom%20of%20Information%20request%20-%20car%20oval%20sticker:%20Article%2020,%20Convention%20on%20Road%20Traffic%201949.txt
+ body = headers + "\n" + body
+ end
+ body
+ end
+
+ # Generate a hash of the attributes associated with each significant part of a Mail object
+ def get_attachment_attributes(mail)
+ leaves = get_attachment_leaves(mail)
+ attachments = []
+ for leaf in leaves
+ body = get_part_body(leaf)
+ if leaf.within_rfc822_attachment
+ within_rfc822_subject = leaf.within_rfc822_attachment.subject
+ body = extract_attached_message_headers(leaf)
+ end
+ leaf_attributes = { :url_part_number => leaf.url_part_number,
+ :content_type => get_content_type(leaf),
+ :filename => get_part_file_name(leaf),
+ :charset => leaf.charset,
+ :within_rfc822_subject => within_rfc822_subject,
+ :body => body,
+ :hexdigest => Digest::MD5.hexdigest(body) }
+ attachments << leaf_attributes
+ end
+ return attachments
+ end
+
# Format
def address_from_name_and_email(name, email)
if !MySociety::Validate.is_valid_email(email)
raise "invalid email " + email + " passed to address_from_name_and_email"
end
if name.nil?
- return Mail::Address.new(email)
+ return Mail::Address.new(email).to_s
end
address = Mail::Address.new
address.display_name = name
diff --git a/lib/mail_handler/backends/mail_extensions.rb b/lib/mail_handler/backends/mail_extensions.rb
index cbe0491ed..f756abd1a 100644
--- a/lib/mail_handler/backends/mail_extensions.rb
+++ b/lib/mail_handler/backends/mail_extensions.rb
@@ -1,7 +1,67 @@
+require 'mail/message'
+require 'mail/fields/common/parameter_hash'
module Mail
class Message
attr_accessor :url_part_number
attr_accessor :rfc822_attachment # when a whole email message is attached as text
attr_accessor :within_rfc822_attachment # for parts within a message attached as text (for getting subject mainly)
+ attr_accessor :count_parts_count
+ attr_accessor :count_first_uudecode_count
+
+ # A patched version of the message initializer to work around a bug where stripping the original
+ # input removes meaningful spaces - e.g. in the case of uuencoded bodies.
+ def initialize(*args, &block)
+ @body = nil
+ @body_raw = nil
+ @separate_parts = false
+ @text_part = nil
+ @html_part = nil
+ @errors = nil
+ @header = nil
+ @charset = 'UTF-8'
+ @defaulted_charset = true
+
+ @perform_deliveries = true
+ @raise_delivery_errors = true
+
+ @delivery_handler = nil
+
+ @delivery_method = Mail.delivery_method.dup
+
+ @transport_encoding = Mail::Encodings.get_encoding('7bit')
+
+ @mark_for_delete = false
+
+ if args.flatten.first.respond_to?(:each_pair)
+ init_with_hash(args.flatten.first)
+ else
+ # The replacement of this commented out line is the change.
+ # init_with_string(args.flatten[0].to_s.strip)
+ init_with_string(args.flatten[0].to_s)
+ end
+
+ if block_given?
+ instance_eval(&block)
+ end
+
+ self
+ end
+ end
+
+ # A patched version of the parameter hash that handles nil values without throwing
+ # an error.
+ class ParameterHash < IndifferentHash
+
+ def encoded
+ map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
+ # The replacement of this commented out line is the change
+ # unless value.ascii_only?
+ unless value.nil? || value.ascii_only?
+ value = Mail::Encodings.param_encode(value)
+ key_name = "#{key_name}*"
+ end
+ %Q{#{key_name}=#{quote_token(value)}}
+ end.join(";\r\n\s")
+ end
end
end \ No newline at end of file
diff --git a/lib/mail_handler/backends/tmail_backend.rb b/lib/mail_handler/backends/tmail_backend.rb
index 87aba73d7..02124cdb1 100644
--- a/lib/mail_handler/backends/tmail_backend.rb
+++ b/lib/mail_handler/backends/tmail_backend.rb
@@ -41,6 +41,234 @@ module MailHandler
return part_file_name
end
+ # Get the body of a mail part
+ def get_part_body(mail_part)
+ mail_part.body
+ end
+
+ # Return the first from address if any
+ def get_from_address(mail)
+ if mail.from_addrs.nil? || mail.from_addrs.size == 0
+ return nil
+ end
+ mail.from_addrs[0].spec
+ end
+
+ # Return the first from name if any
+ def get_from_name(mail)
+ mail.from_name_if_present
+ end
+
+ def get_all_addresses(mail)
+ ((mail.to || []) +
+ (mail.cc || []) +
+ (mail.envelope_to || [])).uniq
+ end
+
+ def empty_return_path?(mail)
+ return false if mail['return-path'].nil?
+ return true if mail['return-path'].addr.to_s == '<>'
+ return false
+ end
+
+ def get_auto_submitted(mail)
+ mail['auto-submitted'] ? mail['auto-submitted'].body : nil
+ end
+
+ def get_content_type(part)
+ part.content_type
+ end
+
+ def get_header_string(header, mail)
+ mail.header_string(header)
+ end
+
+ # Number the attachments in depth first tree order, for use in URLs.
+ # XXX This fills in part.rfc822_attachment and part.url_part_number within
+ # all the parts of the email (see monkeypatches in lib/mail_handler/tmail_extensions and
+ # lib/mail_handler/mail_extensions for how these attributes are added). ensure_parts_counted
+ # must be called before using the attributes.
+ def ensure_parts_counted(mail)
+ mail.count_parts_count = 0
+ _count_parts_recursive(mail, mail)
+ # we carry on using these numeric ids for attachments uudecoded from within text parts
+ mail.count_first_uudecode_count = mail.count_parts_count
+ end
+ def _count_parts_recursive(part, mail)
+ if part.multipart?
+ part.parts.each do |p|
+ _count_parts_recursive(p, mail)
+ end
+ else
+ part_filename = get_part_file_name(part)
+ begin
+ if part.content_type == 'message/rfc822'
+ # An email attached as text
+ # e.g. http://www.whatdotheyknow.com/request/64/response/102
+ part.rfc822_attachment = mail_from_raw_email(part.body, decode=false)
+ elsif part.content_type == 'application/vnd.ms-outlook' || part_filename && AlaveteliFileTypes.filename_to_mimetype(part_filename) == 'application/vnd.ms-outlook'
+ # An email attached as an Outlook file
+ # e.g. http://www.whatdotheyknow.com/request/chinese_names_for_british_politi
+ msg = Mapi::Msg.open(StringIO.new(part.body))
+ part.rfc822_attachment = mail_from_raw_email(msg.to_mime.to_s, decode=false)
+ elsif part.content_type == 'application/ms-tnef'
+ # A set of attachments in a TNEF file
+ part.rfc822_attachment = mail_from_tnef(part.body)
+ end
+ rescue
+ # If attached mail doesn't parse, treat it as text part
+ part.rfc822_attachment = nil
+ else
+ unless part.rfc822_attachment.nil?
+ _count_parts_recursive(part.rfc822_attachment, mail)
+ end
+ end
+ if part.rfc822_attachment.nil?
+ mail.count_parts_count += 1
+ part.url_part_number = mail.count_parts_count
+ end
+ end
+ end
+
+ def get_attachment_attributes(mail)
+ leaves = get_attachment_leaves(mail)
+ # XXX we have to call ensure_parts_counted after get_attachment_leaves
+ # which is really messy.
+ ensure_parts_counted(mail)
+ attachment_attributes = []
+ for leaf in leaves
+ body = get_part_body(leaf)
+ # As leaf.body causes MIME decoding which uses lots of RAM, do garbage collection here
+ # to prevent excess memory use. XXX not really sure if this helps reduce
+ # peak RAM use overall. Anyway, maybe there is something better to do than this.
+ GC.start
+ if leaf.within_rfc822_attachment
+ within_rfc822_subject = leaf.within_rfc822_attachment.subject
+ # Test to see if we are in the first part of the attached
+ # RFC822 message and it is text, if so add headers.
+ # XXX should probably use hunting algorithm to find main text part, rather than
+ # just expect it to be first. This will do for now though.
+ if leaf.within_rfc822_attachment == leaf && leaf.content_type == 'text/plain'
+ headers = ""
+ for header in [ 'Date', 'Subject', 'From', 'To', 'Cc' ]
+ if leaf.within_rfc822_attachment.header.include?(header.downcase)
+ header_value = leaf.within_rfc822_attachment.header[header.downcase]
+ if !header_value.blank?
+ headers = headers + header + ": " + header_value.to_s + "\n"
+ end
+ end
+ end
+ # XXX call _convert_part_body_to_text here, but need to get charset somehow
+ # e.g. http://www.whatdotheyknow.com/request/1593/response/3088/attach/4/Freedom%20of%20Information%20request%20-%20car%20oval%20sticker:%20Article%2020,%20Convention%20on%20Road%20Traffic%201949.txt
+ body = headers + "\n" + body
+
+ # This is quick way of getting all headers, but instead we only add some a) to
+ # make it more usable, b) as at least one authority accidentally leaked security
+ # information into a header.
+ #attachment.body = leaf.within_rfc822_attachment.port.to_s
+ end
+ end
+ attachment_attributes << {:url_part_number => leaf.url_part_number,
+ :content_type => get_content_type(leaf),
+ :filename => get_part_file_name(leaf),
+ :charset => leaf.charset,
+ :within_rfc822_subject => within_rfc822_subject,
+ :body => body,
+ :hexdigest => Digest::MD5.hexdigest(body) }
+ end
+ attachment_attributes
+ end
+
+ # (This risks losing info if the unchosen alternative is the only one to contain
+ # useful info, but let's worry about that another time)
+ def get_attachment_leaves(mail)
+ return _get_attachment_leaves_recursive(mail, mail)
+ end
+ def _get_attachment_leaves_recursive(curr_mail, parent_mail, within_rfc822_attachment = nil)
+ leaves_found = []
+ if curr_mail.multipart?
+ if curr_mail.parts.size == 0
+ raise "no parts on multipart mail"
+ end
+
+ if curr_mail.sub_type == 'alternative'
+ # Choose best part from alternatives
+ best_part = nil
+ # Take the last text/plain one, or else the first one
+ curr_mail.parts.each do |m|
+ if not best_part
+ best_part = m
+ elsif m.content_type == 'text/plain'
+ best_part = m
+ end
+ end
+ # Take an HTML one as even higher priority. (They tend
+ # to render better than text/plain, e.g. don't wrap links here:
+ # http://www.whatdotheyknow.com/request/amount_and_cost_of_freedom_of_in#incoming-72238 )
+ curr_mail.parts.each do |m|
+ if m.content_type == 'text/html'
+ best_part = m
+ end
+ end
+ leaves_found += _get_attachment_leaves_recursive(best_part, parent_mail, within_rfc822_attachment)
+ else
+ # Add all parts
+ curr_mail.parts.each do |m|
+ leaves_found += _get_attachment_leaves_recursive(m, parent_mail, within_rfc822_attachment)
+ end
+ end
+ else
+ # XXX Yuck. this section alters various content_types. That puts
+ # it into conflict with ensure_parts_counted which it has to be
+ # called both before and after. It will fail with cases of
+ # attachments of attachments etc.
+ charset = curr_mail.charset # save this, because overwriting content_type also resets charset
+ # Don't allow nil content_types
+ if curr_mail.content_type.nil?
+ curr_mail.content_type = 'application/octet-stream'
+ end
+ # PDFs often come with this mime type, fix it up for view code
+ if curr_mail.content_type == 'application/octet-stream'
+ part_file_name = get_part_file_name(curr_mail)
+ part_body = get_part_body(curr_mail)
+ calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(part_file_name, part_body)
+ if calc_mime
+ curr_mail.content_type = calc_mime
+ end
+ end
+
+ # Use standard content types for Word documents etc.
+ curr_mail.content_type = normalise_content_type(curr_mail.content_type)
+ if curr_mail.content_type == 'message/rfc822'
+ ensure_parts_counted(parent_mail) # fills in rfc822_attachment variable
+ if curr_mail.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as text
+ curr_mail.content_type = 'text/plain'
+ end
+ end
+ if curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef'
+ ensure_parts_counted(parent_mail) # fills in rfc822_attachment variable
+ if curr_mail.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as binary
+ curr_mail.content_type = 'application/octet-stream'
+ end
+ end
+ # If the part is an attachment of email
+ if curr_mail.content_type == 'message/rfc822' || curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef'
+ ensure_parts_counted(parent_mail) # fills in rfc822_attachment variable
+ leaves_found += _get_attachment_leaves_recursive(curr_mail.rfc822_attachment, parent_mail, curr_mail.rfc822_attachment)
+ else
+ # Store leaf
+ curr_mail.within_rfc822_attachment = within_rfc822_attachment
+ leaves_found += [curr_mail]
+ end
+ # restore original charset
+ curr_mail.charset = charset
+ end
+ return leaves_found
+ end
+
+
def address_from_name_and_email(name, email)
if !MySociety::Validate.is_valid_email(email)
raise "invalid email " + email + " passed to address_from_name_and_email"
diff --git a/lib/mail_handler/backends/tmail_extensions.rb b/lib/mail_handler/backends/tmail_extensions.rb
index 9359dfeea..3576a8eca 100644
--- a/lib/mail_handler/backends/tmail_extensions.rb
+++ b/lib/mail_handler/backends/tmail_extensions.rb
@@ -20,6 +20,8 @@ module TMail
attr_accessor :url_part_number
attr_accessor :rfc822_attachment # when a whole email message is attached as text
attr_accessor :within_rfc822_attachment # for parts within a message attached as text (for getting subject mainly)
+ attr_accessor :count_parts_count
+ attr_accessor :count_first_uudecode_count
# Monkeypatch! (check to see if this becomes a standard function in
# TMail::Mail, then use that, whatever it is called)
diff --git a/lib/mail_handler/mail_handler.rb b/lib/mail_handler/mail_handler.rb
index 24d14b5c8..8b227b9ca 100644
--- a/lib/mail_handler/mail_handler.rb
+++ b/lib/mail_handler/mail_handler.rb
@@ -4,10 +4,12 @@ require 'tmpdir'
module MailHandler
if RUBY_VERSION.to_f >= 1.9
+ require 'mail'
require 'backends/mail_extensions'
require 'backends/mail_backend'
include Backends::MailBackend
else
+ require 'action_mailer'
require 'backends/tmail_extensions'
require 'backends/tmail_backend'
include Backends::TmailBackend
@@ -19,7 +21,7 @@ module MailHandler
def tnef_attachments(content)
attachments = []
Dir.mktmpdir do |dir|
- IO.popen("#{`which tnef`.chomp} -K -C #{dir}", "w") do |f|
+ IO.popen("#{`which tnef`.chomp} -K -C #{dir}", "wb") do |f|
f.write(content)
f.close
if $?.signaled?
@@ -32,7 +34,7 @@ module MailHandler
found = 0
Dir.new(dir).sort.each do |file| # sort for deterministic behaviour
if file != "." && file != ".."
- file_content = File.open("#{dir}/#{file}", "r").read
+ file_content = File.open("#{dir}/#{file}", "rb").read
attachments << { :content => file_content,
:filename => file }
found += 1
@@ -45,6 +47,131 @@ module MailHandler
attachments
end
+ def normalise_content_type(content_type)
+ # e.g. http://www.whatdotheyknow.com/request/93/response/250
+ if content_type == 'application/excel' or content_type == 'application/msexcel' or content_type == 'application/x-ms-excel'
+ content_type = 'application/vnd.ms-excel'
+ end
+ if content_type == 'application/mspowerpoint' or content_type == 'application/x-ms-powerpoint'
+ content_type = 'application/vnd.ms-powerpoint'
+ end
+ if content_type == 'application/msword' or content_type == 'application/x-ms-word'
+ content_type = 'application/vnd.ms-word'
+ end
+ if content_type == 'application/x-zip-compressed'
+ content_type = 'application/zip'
+ end
+
+ # e.g. http://www.whatdotheyknow.com/request/copy_of_current_swessex_scr_opt#incoming-9928
+ if content_type == 'application/acrobat'
+ content_type = 'application/pdf'
+ end
+
+ return content_type
+ end
+
+ def get_attachment_text_one_file(content_type, body, charset = 'utf-8')
+ # note re. charset: TMail always tries to convert email bodies
+ # to UTF8 by default, so normally it should already be that.
+ text = ''
+ # XXX - tell all these command line tools to return utf-8
+ if content_type == 'text/plain'
+ text += body + "\n\n"
+ else
+ tempfile = Tempfile.new('foiextract')
+ tempfile.binmode
+ tempfile.print body
+ tempfile.flush
+ default_params = { :append_to => text, :binary_output => false }
+ if content_type == 'application/vnd.ms-word'
+ AlaveteliExternalCommand.run("wvText", tempfile.path, tempfile.path + ".txt")
+ # Try catdoc if we get into trouble (e.g. for InfoRequestEvent 2701)
+ if not File.exists?(tempfile.path + ".txt")
+ AlaveteliExternalCommand.run("catdoc", tempfile.path, default_params)
+ else
+ text += File.read(tempfile.path + ".txt") + "\n\n"
+ File.unlink(tempfile.path + ".txt")
+ end
+ elsif content_type == 'application/rtf'
+ # catdoc on RTF prodcues less comments and extra bumf than --text option to unrtf
+ AlaveteliExternalCommand.run("catdoc", tempfile.path, default_params)
+ elsif content_type == 'text/html'
+ # lynx wordwraps links in its output, which then don't
+ # get formatted properly by Alaveteli. We use elinks
+ # instead, which doesn't do that.
+ AlaveteliExternalCommand.run("elinks", "-eval", "set document.codepage.assume = \"#{charset}\"",
+ "-eval", "set document.codepage.force_assumed = 1",
+ "-dump-charset", "utf-8",
+ "-force-html", "-dump",
+ tempfile.path,
+ default_params.merge(:env => {"LANG" => "C"}))
+ elsif content_type == 'application/vnd.ms-excel'
+ # Bit crazy using /usr/bin/strings - but xls2csv, xlhtml and
+ # py_xls2txt only extract text from cells, not from floating
+ # notes. catdoc may be fooled by weird character sets, but will
+ # probably do for UK FOI requests.
+ AlaveteliExternalCommand.run("/usr/bin/strings", tempfile.path, default_params)
+ elsif content_type == 'application/vnd.ms-powerpoint'
+ # ppthtml seems to catch more text, but only outputs HTML when
+ # we want text, so just use catppt for now
+ AlaveteliExternalCommand.run("catppt", tempfile.path, default_params)
+ elsif content_type == 'application/pdf'
+ AlaveteliExternalCommand.run("pdftotext", tempfile.path, "-", default_params)
+ elsif content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
+ # This is Microsoft's XML office document format.
+ # Just pull out the main XML file, and strip it of text.
+ xml = AlaveteliExternalCommand.run("/usr/bin/unzip", "-qq",
+ "-c",
+ tempfile.path,
+ "word/document.xml",
+ {:binary_output => false})
+ if !xml.nil?
+ doc = REXML::Document.new(xml)
+ text += doc.each_element( './/text()' ){}.join(" ")
+ end
+ elsif content_type == 'application/zip'
+ # recurse into zip files
+ begin
+ zip_file = Zip::ZipFile.open(tempfile.path)
+ text += get_attachment_text_from_zip_file(zip_file)
+ zip_file.close()
+ rescue
+ $stderr.puts("Error processing zip file: #{$!.inspect}")
+ end
+ end
+ tempfile.close
+ end
+
+ return text
+ end
+ def get_attachment_text_from_zip_file(zip_file)
+
+ text = ""
+ for entry in zip_file
+ if entry.file?
+ filename = entry.to_s
+ begin
+ body = entry.get_input_stream.read
+ rescue
+ # move to next attachment silently if there were problems
+ # XXX really should reduce this to specific exceptions?
+ # e.g. password protected
+ next
+ end
+ calc_mime = AlaveteliFileTypes.filename_to_mimetype(filename)
+ if calc_mime
+ content_type = calc_mime
+ else
+ content_type = 'application/octet-stream'
+ end
+
+ text += get_attachment_text_one_file(content_type, body)
+
+ end
+ end
+ return text
+ end
+
# Turn instance methods into class methods
extend self
diff --git a/lib/old_rubygems_patch.rb b/lib/old_rubygems_patch.rb
deleted file mode 100644
index 3001a7381..000000000
--- a/lib/old_rubygems_patch.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-if File.exist? File.join(File.dirname(__FILE__),'..','vendor','rails','railties','lib','rails','gem_dependency.rb')
- require File.join(File.dirname(__FILE__),'..','vendor','rails','railties','lib','rails','gem_dependency.rb')
-else
- require 'rails/gem_dependency'
-end
-
-module Rails
- class GemDependency < Gem::Dependency
-
- # This definition of the requirement method is a patch
- if !method_defined?(:requirement)
- def requirement
- req = version_requirements
- end
- end
-
- def add_load_paths
- self.class.add_frozen_gem_path
- return if @loaded || @load_paths_added
- if framework_gem?
- @load_paths_added = @loaded = @frozen = true
- return
- end
-
- begin
- dep = Gem::Dependency.new(name, requirement)
- spec = Gem.source_index.find { |_,s| s.satisfies_requirement?(dep) }.last
- spec.activate # a way that exists
- rescue
- begin
- gem self.name, self.requirement # < 1.8 unhappy way
- # This second rescue is a patch - fall back to passing Rails::GemDependency to gem
- # for older rubygems
- rescue ArgumentError
- gem self
- end
- end
-
- @spec = Gem.loaded_specs[name]
- @frozen = @spec.loaded_from.include?(self.class.unpacked_path) if @spec
- @load_paths_added = true
- rescue Gem::LoadError
- end
- end
-
-end
diff --git a/vendor/plugins/globalize2/generators/db_backend.rb b/lib/tasks/.gitkeep
index e69de29bb..e69de29bb 100644
--- a/vendor/plugins/globalize2/generators/db_backend.rb
+++ b/lib/tasks/.gitkeep
diff --git a/lib/tasks/rspec.rake b/lib/tasks/rspec.rake
deleted file mode 100644
index 1eee74aee..000000000
--- a/lib/tasks/rspec.rake
+++ /dev/null
@@ -1,147 +0,0 @@
-rspec_gem_dir = nil
-Dir["#{Rails.root}/vendor/gems/*"].each do |subdir|
- rspec_gem_dir = subdir if subdir.gsub("#{Rails.root}/vendor/gems/","") =~ /^(\w+-)?rspec-(\d+)/ && File.exist?("#{subdir}/lib/spec/rake/spectask.rb")
-end
-rspec_plugin_dir = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec')
-
-if rspec_gem_dir && (test ?d, rspec_plugin_dir)
- raise "\n#{'*'*50}\nYou have rspec installed in both vendor/gems and vendor/plugins\nPlease pick one and dispose of the other.\n#{'*'*50}\n\n"
-end
-
-if rspec_gem_dir
- $LOAD_PATH.unshift("#{rspec_gem_dir}/lib")
-elsif File.exist?(rspec_plugin_dir)
- $LOAD_PATH.unshift("#{rspec_plugin_dir}/lib")
-end
-
-# Don't load rspec if running "rake gems:*"
-unless ARGV.any? {|a| a =~ /^gems/}
-
-begin
- require 'spec/rake/spectask'
-rescue MissingSourceFile
- module Spec
- module Rake
- class SpecTask
- def initialize(name)
- task name do
- # if rspec-rails is a configured gem, this will output helpful material and exit ...
- require File.expand_path(File.join(File.dirname(__FILE__),"..","..","config","environment"))
-
- # ... otherwise, do this:
- raise <<-MSG
-
-#{"*" * 80}
-* You are trying to run an rspec rake task defined in
-* #{__FILE__},
-* but rspec can not be found in vendor/gems, vendor/plugins or system gems.
-#{"*" * 80}
-MSG
- end
- end
- end
- end
- end
-end
-
-Rake.application.instance_variable_get('@tasks').delete('default')
-
-spec_prereq = File.exist?(File.join(Rails.root, 'config', 'database.yml')) ? "db:test:prepare" : :noop
-task :noop do
-end
-
-task :default => :spec
-task :stats => "spec:statsetup"
-# XXX commonlib tests are not Ruby 1.9 compatible
-#task :spec => ['spec:commonlib']
-task :test => ['spec']
-task :cruise => ['spec']
-
-desc "Run all specs in spec directory (excluding plugin specs)"
-Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t|
- t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""]
- t.spec_files = FileList['spec/**/*_spec.rb']
-end
-
-namespace :spec do
- desc "Run all specs in spec directory with RCov (excluding plugin specs)"
- Spec::Rake::SpecTask.new(:rcov) do |t|
- t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""]
- t.spec_files = FileList['spec/**/*_spec.rb']
- t.rcov = true
- t.rcov_opts = lambda do
- IO.readlines("#{Rails.root}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
- end
- end
-
- desc "Print Specdoc for all specs (excluding plugin specs)"
- Spec::Rake::SpecTask.new(:doc) do |t|
- t.spec_opts = ["--format", "specdoc", "--dry-run"]
- t.spec_files = FileList['spec/**/*_spec.rb']
- end
-
- desc "Print Specdoc for all plugin examples"
- Spec::Rake::SpecTask.new(:plugin_doc) do |t|
- t.spec_opts = ["--format", "specdoc", "--dry-run"]
- t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*')
- end
-
- [:models, :controllers, :views, :helpers, :lib, :integration].each do |sub|
- desc "Run the code examples in spec/#{sub}"
- Spec::Rake::SpecTask.new(sub => spec_prereq) do |t|
- t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""]
- t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
- end
- end
-
- desc "Run the code examples in vendor/plugins (except RSpec's own)"
- Spec::Rake::SpecTask.new(:plugins => spec_prereq) do |t|
- t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""]
- t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*').exclude("vendor/plugins/rspec-rails/*")
- end
-
- namespace :plugins do
- desc "Runs the examples for rspec_on_rails"
- Spec::Rake::SpecTask.new(:rspec_on_rails) do |t|
- t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""]
- t.spec_files = FileList['vendor/plugins/rspec-rails/spec/**/*_spec.rb']
- end
- end
-
- # Setup specs for stats
- task :statsetup do
- require 'code_statistics'
- ::STATS_DIRECTORIES << %w(Model\ specs spec/models) if File.exist?('spec/models')
- ::STATS_DIRECTORIES << %w(View\ specs spec/views) if File.exist?('spec/views')
- ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) if File.exist?('spec/controllers')
- ::STATS_DIRECTORIES << %w(Helper\ specs spec/helpers) if File.exist?('spec/helpers')
- ::STATS_DIRECTORIES << %w(Library\ specs spec/lib) if File.exist?('spec/lib')
- ::STATS_DIRECTORIES << %w(Routing\ specs spec/routing) if File.exist?('spec/routing')
- ::STATS_DIRECTORIES << %w(Integration\ specs spec/integration) if File.exist?('spec/integration')
- ::CodeStatistics::TEST_TYPES << "Model specs" if File.exist?('spec/models')
- ::CodeStatistics::TEST_TYPES << "View specs" if File.exist?('spec/views')
- ::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers')
- ::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers')
- ::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib')
- ::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing')
- ::CodeStatistics::TEST_TYPES << "Integration specs" if File.exist?('spec/integration')
- end
-
- namespace :db do
- namespace :fixtures do
- desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z."
- task :load => :environment do
- ActiveRecord::Base.establish_connection(Rails.env)
- base_dir = File.join(Rails.root, 'spec', 'fixtures')
- fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir
-
- require 'active_record/fixtures'
- (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file|
- Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*'))
- end
- end
- end
- end
-end
-
-end
diff --git a/locale/cs/app.po b/locale/cs/app.po
index ae0e761cb..078ecd3c9 100644
--- a/locale/cs/app.po
+++ b/locale/cs/app.po
@@ -14,8 +14,8 @@ msgstr ""
"Project-Id-Version: alaveteli\n"
"Report-Msgid-Bugs-To: http://github.com/sebbacon/alaveteli/issues\n"
"POT-Creation-Date: 2012-11-06 16:35+0000\n"
-"PO-Revision-Date: 2012-11-07 09:52+0000\n"
-"Last-Translator: louisecrow <louise@mysociety.org>\n"
+"PO-Revision-Date: 2012-11-29 14:58+0000\n"
+"Last-Translator: janakneschke <jana.kneschke@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -299,7 +299,7 @@ msgid ""
"<p>Your request contains a <strong>postcode</strong>. Unless it directly "
"relates to the subject of your request, please remove any address as it will"
" <strong>appear publicly on the Internet</strong>.</p>"
-msgstr "<p>Váš dotaz obsahuje <strong>PSČ</strong>. Pokud to není údaj nutný k zodpovězení dotazu, prosíme odstraňte adresu či jiný identifikátor. Váš dotaz bude v celém znění<strong>uveřejněn na internetu</strong>.</p>"
+msgstr "<p>Váš dotaz obsahuje <strong>PSČ</strong>. Pokud to není údaj nutný k zodpovězení dotazu, prosíme odstraňte adresu či jiný identifikátor. Váš dotaz bude v celém znění a bez osobních údajů<strong>uveřejněn na internetu</strong>.</p>"
msgid ""
"<p>Your {{law_used_full}} request has been <strong>sent on its "
@@ -309,7 +309,7 @@ msgid ""
"<p>If you write about this request (for example in a forum or a blog) please"
" link to this page, and add an\\n annotation below telling people"
" about your writing.</p>"
-msgstr "<p><strong>Odesláno!</strong></p>\n<p><strong>Pošleme vám e-mail </strong> až vám instituce odpoví nebo po {{late_number_of_days}} dní po vypršení lhůty na odpověď.</p>\\n <p>Pokud o tomto dotazu budete dále psát (například na nějakém fóru či blogu) prosím přiložte odkaz k vašemu dotazu na těchto stránkách a přidejte k němu komentář.</p>"
+msgstr "<p><strong>Odesláno!</strong></p>\n<p><strong>Pošleme vám e-mail </strong> až vám instituce odpoví nebo po {{late_number_of_days}} dní po vypršení lhůty na odpověď.</p>\\n <p>Pokud o svém dotazu budete dále psát (například na nějakém fóru či blogu), přiložte odkaz na stránky IPV a k dotazu přidejte komentář.</p>"
msgid ""
"<p>{{site_name}} is currently in maintenance. You can only view existing "
@@ -353,12 +353,12 @@ msgstr "<strong><code>dotaz:</code></strong> k omezení na konkrétní dotaz vep
msgid ""
"<strong><code>requested_by:julian_todd</code></strong> to search requests "
"made by Julian Todd, typing the name as in the URL."
-msgstr "<strong><code>requested_by:julian_todd</code></strong> pokud chcete nalézt více poznámek od Tonyho Bowdena, napište jeho jméno jako URL."
+msgstr "<strong><code>rpožadováno od uživatele: julian_todd</code></strong> pokud chcete nalézt více poznámek od Tonyho Bowdena, napište jeho jméno jako URL."
msgid ""
"<strong><code>requested_from:home_office</code></strong> to search requests "
"from the Home Office, typing the name as in the URL."
-msgstr "<strong><code>vyžádáno_od:ministerstvo_vnitra</code></strong> pokud chcete nalézt více dotazů vznesených na Ministerstva vnitra, napište název instituce jako URL."
+msgstr "<strong><code>vyžádáno_od:ministerstvo_vnitra</code></strong> pokud chcete nalézt více dotazů vznesených na Ministerstvo vnitra, napište název instituce jako URL."
msgid ""
"<strong><code>status:</code></strong> to select based on the status or "
@@ -373,7 +373,7 @@ msgid ""
"tag:financial_transaction:335633</code>. Note that by default any of the "
"tags\\n can be present, you have to put <code>AND</code> explicitly if "
"you only want results them all present."
-msgstr "<strong><code>Tagujte:charity</code></strong> umožní vám najít instituce nebo žádosti s daným tagem. Můžete použít více tagů, ⏎ a hodnot tagů, např. <code>tag:openlylocal AND tag:financial_transaction:335633</code>. Jakýkoliv tag může být zahrnut⏎ je třeba vložit <code>AND</code> pokud chcete výsledek, který zahrnuje všechny tagy."
+msgstr "<strong><code>Tagujte:charity</code></strong> umožní vám najít instituce nebo dotazy s daným klíčových slovem. Můžete použít více tagů, ⏎ a hodnot tagů, např. <code>tag:openlylocal AND tag:financial_transaction:335633</code>. Jakýkoliv tag může být zahrnut⏎ je třeba vložit <code>AND</code> pokud chcete výsledek, který zahrnuje všechny tagy."
msgid ""
"<strong><code>variety:</code></strong> to select type of thing to search "
@@ -400,7 +400,7 @@ msgid ""
"FOI law, and the\\nway authorities use it. Plus you'll need to be an elite "
"statistician. Please\\n<a href=\"{{contact_path}}\">contact us</a> with "
"questions."
-msgstr "<strong>Varování!</strong>Aby datay na stránkách {{site_name}} byla správně seřazena, je vhodné chovat se podle zásad slušného jednání a netikety. Třídění vznesených dotazů není zas tak jednoduché a mohou \nvzniknout chyby a dojít k různým nejednoznačnostem. Musíte\ntaké rozumět zákonu o svobodném přístupu k informacím\na způsobu, jakým jej instituce používají. A ke všemu být vynikající statistik. Prosíme\n<a href=\"{{contact_path}}\">kontaktujte nás</a>, pokud máte otázky."
+msgstr "<strong>Doporučení</strong>Veškerá konverzace na stránkách {{site_name}} podléhá zásadám slušného jednání a netikety. Třídění vznesených dotazů není zas tak jednoduché a mohou \nvzniknout chyby a dojít k různým nejednoznačnostem. Je nutné\ntaké rozumět zákonu o svobodném přístupu k informacím\na způsobu, jak tento instituce interpretují. A ke všemu být vynikající statistik. Prosíme\n<a href=\"{{contact_path}}\">kontaktujte nás</a>, pokud máte otázky."
msgid "<strong>Clarification</strong> has been requested"
msgstr "Bylo vyžádáno <strong>upřesnění</strong> "
@@ -413,7 +413,7 @@ msgstr "<strong>Bez odpovědi</strong> \n <small>(možná je to j
msgid ""
"<strong>Note:</strong> Because we're testing, requests are being sent to "
"{{email}} rather than to the actual authority."
-msgstr "<strong>Note:</strong> Protože testujeme, žádosti jsou posílány na {{email}} a ne příslušné autoritě."
+msgstr "<strong>Note:</strong> Protože testujeme, dotazy jsou posílány na {{email}} a ne příslušné autoritě."
msgid ""
"<strong>Note:</strong> You're sending a message to yourself, presumably\\n"
@@ -438,7 +438,7 @@ msgstr "<strong>Důležité upozornění:</strong> Vaše fotografie bude zveřej
msgid ""
"<strong>Privacy warning:</strong> Your message, and any response\\n "
"to it, will be displayed publicly on this website."
-msgstr "<strong>Ochrana soukromí:</strong> Vámi vznesený dotaz, a všechny \n odpovědi na něj budou uveřejněny na těchto stránkách."
+msgstr "<strong>Ochrana soukromí:</strong> vámi vznesený dotaz, a všechny \n odpovědi na něj budou uveřejněny na stránkách IPV."
msgid "<strong>Some of the information</strong> has been sent "
msgstr "<strong>Některé informace</strong> byly odeslány"
@@ -555,7 +555,7 @@ msgstr "Upravit sledování dotazů"
msgid ""
"Although all responses are automatically published, we depend on\\nyou, the "
"original requester, to evaluate them."
-msgstr "I když jsou všechny odpovědi automaticky uveřejňovány, hodnocení dotazu závisí na vás, tedy tazateli."
+msgstr "I když jsou všechny odpovědi automaticky uveřejňovány, hodnocení dotazu závisí na vás, na tazateli."
msgid ""
"An <a href=\"{{request_url}}\">annotation</a> to <em>{{request_title}}</em> "
@@ -613,7 +613,7 @@ msgid "Awaiting classification."
msgstr "Čeká se zařazení."
msgid "Awaiting internal review."
-msgstr "Čeká se na přezkoumání dotazu. "
+msgstr "Čeká se na doplnění dotazu."
msgid "Awaiting response."
msgstr "Čeká se na odpověď."
@@ -637,12 +637,12 @@ msgstr "Procházejte všechny instituce"
msgid ""
"By law, under all circumstances, {{public_body_link}} should have responded "
"by now"
-msgstr "Podle zákona by {{public_body_link}} měla veřejná instituce v každém případě odpovědět."
+msgstr "Podle zákona by instituce {{public_body_link}} měla v každém případě odpovědět."
msgid ""
"By law, {{public_body_link}} should normally have responded "
"<strong>promptly</strong> and"
-msgstr "Podle zákona, by měla instituce {{public_body_link}} odpovědět okamžitě"
+msgstr "Podle zákona, by měla instituce {{public_body_link}} odpovědět okamžitě a "
msgid "Cancel a {{site_name}} alert"
msgstr "Zrušte tato upozornění stránek {{site_name}}"
@@ -818,7 +818,7 @@ msgstr "Kulturní památky (které mohou být ovlivěny tak, \n jak j
msgid ""
"Currently <strong>waiting for a response</strong> from {{public_body_link}},"
" they must respond promptly and"
-msgstr "V současnosti <strong> se čeká na odpověď</strong> od {{public_body_link}}, musí odpovědět co nejdříve a"
+msgstr "V současnosti <strong> se čeká na odpověď</strong> od instituce {{public_body_link}}, musí odpovědět co nejdříve a "
msgid "Date:"
msgstr "Datum:"
@@ -847,12 +847,12 @@ msgid ""
msgstr "Vyloučení odpovědnosti: Tato zpráva a jakékoliv odpovědi na ni budou uveřejněny na stránkách Informace pro všechny. Naše pravidla o ochraně osobních údajů a autorských právech si přečtěte:"
msgid "Disclosure log"
-msgstr ""
+msgstr "Disclosure log"
msgid ""
"Don't want to address your message to {{person_or_body}}? You can also "
"write to:"
-msgstr "Nechcete svou zprávu adresovat pro {{person_or_body}}? Můžete také napsat:"
+msgstr "Nechcete svou zprávu adresovat na {{person_or_body}}? Můžete také napsat:"
msgid "Done"
msgstr "Hotovo"
@@ -900,12 +900,12 @@ msgstr "Pošlete mi budoucí aktualizace tohoto dotazu. "
msgid ""
"Enter words that you want to find separated by spaces, e.g. <strong>climbing"
" lane</strong>"
-msgstr "Slova, která chcete najít musí být oddělena mezerou, například <strong>přechod pro chodce</strong>"
+msgstr "Slova, která chcete najít, musí být oddělena mezerou, například <strong>přechod pro chodce</strong>"
msgid ""
"Enter your response below. You may attach one file (use email, or\\n <a "
"href=\"%s\">contact us</a> if you need more)."
-msgstr "Níže můžete vložit svou odpověď. Pokud je to nutné, přiložte jeden dokument, (s použitím e-mailu, nebo⏎ <a href=\"%s\">nás kontaktujte</a> pokud potřebujete přiložit více dokumentů). ??"
+msgstr "Níže můžete vložit svou odpověď. Pokud je to nutné, přiložte jeden dokument, (s použitím e-mailu, nebo⏎ <a href=\"%s\">nás kontaktujte</a> pokud potřebujete přiložit více dokumentů)."
msgid "Environmental Information Regulations"
msgstr "Informace o životním prostředí"
@@ -1001,7 +1001,7 @@ msgid "Follow all new requests"
msgstr "Sledovat všechny nově vznesené dotazy"
msgid "Follow new successful responses"
-msgstr "Sledovat všechny zodpovězené dotazy ??"
+msgstr "Sledovat všechny zodpovězené dotazy"
msgid "Follow requests to {{public_body_name}}"
msgstr "Sledovat dotaz vznesený na {{public_body_name}}"
@@ -1144,7 +1144,7 @@ msgid ""
"hasn't told us whether or not it was successful. Would you mind taking\\n"
" a moment to read it and help us keep the place tidy for everyone?\\n "
"Thanks."
-msgstr "Haló! Potřebujeme vaši pomoc. Osoba, která vznesla tento dotaz nám nesdělila, jestli byla zodpovězena úspěšně. Můžete si ji přečíst a pomoci nám udržovat stránky přehledné? Děkujeme."
+msgstr "Haló! Potřebujeme vaši pomoc. Osoba, která vznesla tento dotaz nám nesdělila, jestli byla zodpovězena úspěšně. Můžete si dotaz i odpověď přečíst a pomoci nám tak udržovat stránky přehledné? Děkujeme."
msgid "Holiday"
msgstr "Státní svátek"
@@ -1173,7 +1173,7 @@ msgid "I am asking for <strong>new information</strong>"
msgstr "Žádám <strong>novou informaci</strong>"
msgid "I am requesting an <strong>internal review</strong>"
-msgstr "Žádám o <strong>přezkoumání</strong>"
+msgstr "Žádám <strong>o doplnění dotazu</strong>"
msgid "I don't like these ones &mdash; give me some more!"
msgstr "To se mi nelíbí &mdash; nabídněte nějaké další!"
@@ -1193,10 +1193,10 @@ msgid ""
msgstr "Stále <strong>čekám</strong> na svou informaci\n <small>(možná máte potvrzení)</small>"
msgid "I'm still <strong>waiting</strong> for the internal review"
-msgstr "Stále <strong>čekám</strong> na přezkoumání"
+msgstr "Stále <strong>čekám</strong> na doplnění dotazu"
msgid "I'm waiting for an <strong>internal review</strong> response"
-msgstr "Čekám na vyjádření týkající se <strong>přezkoumání</strong> "
+msgstr "Čekám na <strong>doplnění</strong> dotazu"
msgid "I've been asked to <strong>clarify</strong> my request"
msgstr "Byl/a jsem požádán/a o <strong>vyjasnění</strong> mého dotazu"
@@ -1246,7 +1246,7 @@ msgstr "Pokud jste {{user_link}}, prosíme"
msgid ""
"If you believe this request is not suitable, you can report it for attention"
" by the site administrators"
-msgstr "Pokud se domníváte, že je tato žádost nevhodná, můžete ji nahlásit administrátorům stránky. "
+msgstr "Pokud se domníváte, že je tento dotaz nevhodný, můžete jej nahlásit administrátorům stránky. "
msgid ""
"If you can't click on it in the email, you'll have to <strong>select and "
@@ -1334,13 +1334,13 @@ msgid "IncomingMessage|Valid to reply to"
msgstr "IncomingMessage | Platná zpráva pro"
msgid "Individual requests"
-msgstr "Jednotlivé žádosti"
+msgstr "Jednotlivé dotazy"
msgid "Info request"
-msgstr "Žádost o informaci"
+msgstr "Dotaz na informaci"
msgid "Info request event"
-msgstr "Žádost o informaci – akce ??"
+msgstr "Dotaz na informaci – akce ??"
msgid "InfoRequestEvent|Calculated state"
msgstr "InfoRequestEvent| V procesu"
@@ -1361,16 +1361,16 @@ msgid "InfoRequestEvent|Prominence"
msgstr "InfoRequestEvent | Prominence ??"
msgid "InfoRequest|Allow new responses from"
-msgstr "InfoRequestEvent | Povolit nové odpovědi od"
+msgstr "InfoRequestEvent | Povolit nové odpovědi od "
msgid "InfoRequest|Attention requested"
-msgstr "InfoRequestEvent | Podle zákona ??"
+msgstr "InfoRequestEvent|Podle zákona ??"
msgid "InfoRequest|Awaiting description"
msgstr "InfoRequestEvent | Očekává se popis"
msgid "InfoRequest|Comments allowed"
-msgstr "Info o žádosti|Komentáře dovoleny"
+msgstr "Info o dotazu|Komentáře dovoleny"
msgid "InfoRequest|Described state"
msgstr "InfoRequestEvent | Popsaný status"
@@ -1382,10 +1382,10 @@ msgid "InfoRequest|External user name"
msgstr "InfoRequest|External user name ??"
msgid "InfoRequest|Handle rejected responses"
-msgstr "InfoRequestEvent | Řešit odmítnuté odpovědi"
+msgstr "InfoRequestEvent | Řešit odmítnuté odpovědi "
msgid "InfoRequest|Idhash"
-msgstr "InfoRequestEvent I dhash ??"
+msgstr "InfoRequestEventIdhash ??"
msgid "InfoRequest|Law used"
msgstr "InfoRequestEvent | Podle zákona"
@@ -1394,10 +1394,10 @@ msgid "InfoRequest|Prominence"
msgstr "InfoRequestEvent| Prominence ??"
msgid "InfoRequest|Title"
-msgstr "InfoRequestEvent | Název"
+msgstr "InfoRequestEvent|Název"
msgid "InfoRequest|Url title"
-msgstr "InfoRequestEvent| Název URL"
+msgstr "InfoRequestEvent|Název URL"
msgid "Information not held."
msgstr "Informace není k dispozici."
@@ -1408,12 +1408,12 @@ msgid ""
msgstr "Informace o vypouštění imisí a emisí (např. energie, hluk, radiace, odpady)"
msgid "Internal review request"
-msgstr "Přezkoumání dotazu"
+msgstr "Doplnění dotazu"
msgid ""
"Is {{email_address}} the wrong address for {{type_of_request}} requests to "
"{{public_body_name}}? If so, please contact us using this form:"
-msgstr "Je {{email_address}} chybná adresa pro {{type_of_request}} dotazy vznesené na {{public_body_name}}? Pokud ano, prosíme kontaktujte nás vyplněním tohoto formuláře:"
+msgstr "Je {{email_address}} chybná adresa pro {{type_of_request}} dotazy vznesené na instituci {{public_body_name}}? Pokud ano, prosíme kontaktujte nás vyplněním tohoto formuláře:"
msgid ""
"It may be that your browser is not set to accept a thing called "
@@ -1470,22 +1470,22 @@ msgid "Made between"
msgstr "Vloženo mezi"
msgid "Mail server log"
-msgstr ""
+msgstr "Mail server log"
msgid "Mail server log done"
-msgstr ""
+msgstr "Mail server log done"
msgid "MailServerLogDone|Filename"
-msgstr ""
+msgstr "MailServerLogDone|Jméno souboru"
msgid "MailServerLogDone|Last stat"
-msgstr ""
+msgstr "MailServerLogDone|Nejnovější statistiky"
msgid "MailServerLog|Line"
-msgstr ""
+msgstr "MailServerLog|Řádek"
msgid "MailServerLog|Order"
-msgstr ""
+msgstr "MailServerLog|Příkaz"
msgid "Make a new <strong>Environmental Information</strong> request"
msgstr "Vzneste dotaz týkající se <strong>životního prostředí</strong> "
@@ -1513,7 +1513,7 @@ msgid "Make your own request"
msgstr "Vzneste vlastní dotaz"
msgid "Many requests"
-msgstr "Mnoho žádostí"
+msgstr "Mnoho dotazů"
msgid "Message"
msgstr "Zpráva"
@@ -1528,7 +1528,7 @@ msgid "More about this authority"
msgstr "Více o této instituci"
msgid "More requests..."
-msgstr "Více žádostí..."
+msgstr "Více dotazů..."
msgid "More similar requests"
msgstr "Více podobných dotazů"
@@ -1540,7 +1540,7 @@ msgid "My profile"
msgstr "Můj profil"
msgid "My request has been <strong>refused</strong>"
-msgstr "Můj dotaz byla <strong>odmítnut</strong>"
+msgstr "Zopodvězení mého dotazu bylo <strong>zadmítnuto</strong>"
msgid "My requests"
msgstr "Moje dotazy"
@@ -1616,7 +1616,7 @@ msgstr "Nic tu není"
msgid ""
"Note that the requester will not be notified about your annotation, because "
"the request was published by {{public_body_name}} on their behalf."
-msgstr "Rádi bychom vás uzporonili, že tazatel nebude na váš komentář upozorněn, jelikož tento dotaz byl zveřejněn na žádost instituce {{public_body_name}}."
+msgstr "Rádi bychom vás upozornili, že tazatel nebude o vašem komentáři informován, jelikož tento dotaz byl zveřejněn na žádost instituce {{public_body_name}}."
msgid "Now check your email!"
msgstr "Nyní zkontrolujte svou e-mailovou schránku!"
@@ -1638,7 +1638,7 @@ msgstr "Nevhodný obsah?"
msgid ""
"Oh no! Sorry to hear that your request was refused. Here is what to do now."
-msgstr "Ale ne! Je nám líto, že váš dotaz byla zamítnut. Pokud chcete, můžete učinit následující kroky."
+msgstr "Mrzí nás, že zodpovězení vašeho dotazu bylo zamítnuto. Pokud na poskytnutí informací trváte, máte následující možnosti."
msgid "Old e-mail:"
msgstr "Původní e-mail"
@@ -1848,7 +1848,7 @@ msgid "Please enter your password"
msgstr "Vložte heslo. "
msgid "Please give details explaining why you want a review"
-msgstr "Prosíme vysvětlete, proč potřebujete přezkoumání. "
+msgstr "Prosíme uveďte důvody, kvůli kterým žádáte o doplnění"
msgid "Please keep it shorter than 500 characters"
msgstr "Váš text musí být kratší než 500 znaků."
@@ -1954,7 +1954,7 @@ msgid "Preview follow up to '"
msgstr "Náhled odpovědi pro"
msgid "Preview new annotation on '{{info_request_title}}'"
-msgstr "Náhled nové anotace o '{{info_request_title}}'"
+msgstr "Náhled nové anotace týkající se '{{info_request_title}}'"
msgid "Preview your annotation"
msgstr "Náhled poznámky"
@@ -1990,7 +1990,7 @@ msgid "Public body/translation"
msgstr "PublicBody | Verze ??"
msgid "PublicBody::Translation|Disclosure log"
-msgstr ""
+msgstr "PublicBody::Translation|Disclosure log"
msgid "PublicBody::Translation|First letter"
msgstr "PublicBody | Začáteční písmeno"
@@ -2020,7 +2020,7 @@ msgid "PublicBody|Api key"
msgstr "PublicBody | Název ??"
msgid "PublicBody|Disclosure log"
-msgstr ""
+msgstr "PublicBody|Disclosure log"
msgid "PublicBody|First letter"
msgstr "PublicBody | První dopis"
@@ -2029,7 +2029,7 @@ msgid "PublicBody|Home page"
msgstr "PublicBody | Domovská stránka"
msgid "PublicBody|Info requests count"
-msgstr "Veřejný orgán | Info o počtu žádostí"
+msgstr "Veřejný orgán | Info o počtu dotazů"
msgid "PublicBody|Last edit comment"
msgstr "PublicBody | Naposled aktualizovaný komentář"
@@ -2065,7 +2065,7 @@ msgid "Purge request"
msgstr "Vyčistit formulář dotazu"
msgid "PurgeRequest|Model"
-msgstr "PurgeRequest|Verze ??"
+msgstr "PurgeRequest|Verze"
msgid "PurgeRequest|Url"
msgstr "PurgeRequest|URL"
@@ -2117,26 +2117,26 @@ msgid "Reported for administrator attention."
msgstr "Administrátor byl již upozorněn."
msgid "Request an internal review"
-msgstr "Požádat o přezkoumání dotazu"
+msgstr "Požádat o doplnění dotazu"
msgid "Request an internal review from {{person_or_body}}"
-msgstr "Požádat o přezkoumání dotazu u {{person_or_body}}"
+msgstr "Požádat o doplnění dotazu od instituce či jmenovitě jejím pracovníkem {{person_or_body}}"
msgid "Request has been removed"
msgstr "Dotaz byl odstraněn"
msgid ""
"Request sent to {{public_body_name}} by {{info_request_user}} on {{date}}."
-msgstr "Dotaz byl vznesen na {{public_body_name}} od {{info_request_user}} dne {{date}}."
+msgstr "Dotaz byl vznesen na instituci {{public_body_name}} uživatelem {{info_request_user}} dne {{date}}."
msgid ""
"Request to {{public_body_name}} by {{info_request_user}}. Annotated by "
"{{event_comment_user}} on {{date}}."
-msgstr "Dotaz byl vznesen na {{public_body_name}} od {{info_request_user}}. Poznámka od {{event_comment_user}} dne {{date}}."
+msgstr "Dotaz byl vznesen na instituci {{public_body_name}} uživatelem {{info_request_user}}. Poznámka od uživatele {{event_comment_user}} dne {{date}}."
msgid ""
"Requested from {{public_body_name}} by {{info_request_user}} on {{date}}"
-msgstr "Vyžádáno {{public_body_name}} od {{info_request_user}} dne {{date}}"
+msgstr "Vyžádáno institucí {{public_body_name}} od {{info_request_user}} dne {{date}}"
msgid "Requested on {{date}}"
msgstr "Dotaz byl vznesen dne {{date}}"
@@ -2162,10 +2162,10 @@ msgid "Respond using the web"
msgstr "Odpovězte na internetových stránkách"
msgid "Response"
-msgstr "Odpověď"
+msgstr "Odpověď "
msgid "Response from a public authority"
-msgstr "Odpověď od instituce"
+msgstr "Odpověď od instituce "
msgid "Response to '{{title}}'"
msgstr "Odpověď na '{{title}}'"
@@ -2180,7 +2180,7 @@ msgid "Response to your request"
msgstr "Odpověď na váš dotaz"
msgid "Response:"
-msgstr "Odpověď:"
+msgstr "Odpověď: "
msgid "Restrict to"
msgstr "Omezit na"
@@ -2210,10 +2210,10 @@ msgid ""
"Search over<br/>\\n <strong>{{number_of_requests}} requests</strong> "
"<span>and</span><br/>\\n <strong>{{number_of_authorities}} "
"authorities</strong>"
-msgstr "Prohledávejte více než<br/>\n <strong>{{number_of_requests}} dotazů</strong> <span>a</span><br/>\n <strong>{{number_of_authorities}} institucí</strong>"
+msgstr "Prohledávejte více než<br/>\n <strong>{{number_of_requests}} dotazů</strong> <span>a</span><br/>\n <strong>{{number_of_authorities}} institucí v adresáři</strong>"
msgid "Search queries"
-msgstr "Prohledávejte žádosti"
+msgstr "Prohledávejte dotazy"
msgid "Search results"
msgstr "Výsledky vyhledávání"
@@ -2288,7 +2288,7 @@ msgid "Sign out"
msgstr "Odhlásit"
msgid "Sign up"
-msgstr "Registovat"
+msgstr "Registrovat"
msgid "Similar requests"
msgstr "Podobné dotazy"
@@ -2314,7 +2314,7 @@ msgid "Somebody added a note to your FOI request - "
msgstr "Někdo přidal poznámku k vašemu dotazu – "
msgid "Someone has updated the status of your request"
-msgstr "Někdo aktualizovat status vaší žádosti"
+msgstr "Někdo aktualizoval status vašeho dotazu"
msgid ""
"Someone, perhaps you, just tried to change their email address "
@@ -2351,7 +2351,7 @@ msgid "Stay up to date"
msgstr "Buďte informováni"
msgid "Still awaiting an <strong>internal review</strong>"
-msgstr "Stále čekám na <strong>přezkoumání dotazu</strong>"
+msgstr "Stále <strong>čekám</strong> na doplnění dotazu"
msgid "Subject"
msgstr "Předmět"
@@ -2377,7 +2377,7 @@ msgstr "Úspěch."
msgid ""
"Suggest how the requester can find the <strong>rest of the "
"information</strong>."
-msgstr "Doporučte, jak tazatel může najít<strong>úplné informace</strong>."
+msgstr "Doporučte, jak tazatel může najít <strong>úplné informace</strong>."
msgid "Summary:"
msgstr "Shrnutí:"
@@ -2406,13 +2406,13 @@ msgstr "Děkujeme vám za vaši anotaci!"
msgid ""
"Thank you for responding to this FOI request! Your response has been "
"published below, and a link to your response has been emailed to "
-msgstr "Děkujeme vám za odpověď na tento dotaz! Vaše odpověď byla publikována níže a odkaz vaší odpovědi byl odeslán "
+msgstr "Děkujeme vám za odpověď na tento dotaz! Vaše odpověď byla publikována níže a odkaz s vaší odpovědí byl odeslán "
msgid ""
"Thank you for updating the status of the request '<a "
"href=\"{{url}}\">{{info_request_title}}</a>'. There are some more requests "
"below for you to classify."
-msgstr "Děkujeme za aktualizaci statusu dotazu '<a href=\"{{url}}\">{{info_request_title}}</a>'. Zde jsou další odkazy, které je třeba zatřídit. "
+msgstr "Děkujeme za aktualizaci statusu dotazu '<a href=\"{{url}}\">{{info_request_title}}</a>'. Zde jsou další odkazy, které čekají na zařazenít. "
msgid "Thank you for updating this request!"
msgstr "Děkujeme vám za aktualizaci tohoto dotazu!"
@@ -2523,7 +2523,7 @@ msgid ""
"The response to your request has been <strong>delayed</strong>. You can say"
" that, \\n by law, the authority should normally have "
"responded\\n <strong>promptly</strong> and"
-msgstr "Odpověď na váš dotaz má <strong>zpoždění</strong>. Můžete napsat, že podle zákona by instituce měla odpovědět nejpozději\n <strong>do 15 dnů</strong> a"
+msgstr "Odpověď na váš dotaz má <strong>zpoždění</strong>. Můžete napsat, že podle zákona by instituce měla odpovědět nejpozději\n <strong>do 15 dnů</strong> a "
msgid ""
"The response to your request is <strong>long overdue</strong>. You can say"
@@ -2575,7 +2575,7 @@ msgid "Then you can sign in to {{site_name}}"
msgstr "Poté se můžete přihlásit na stránkách {{site_name}}"
msgid "Then you can update the status of your request to "
-msgstr "Poté můžete aktualizovat status svého dotazu na"
+msgstr "Poté můžete aktualizovat status svého dotazu na "
msgid "Then you can upload an FOI response. "
msgstr "Poté můžete nahrát odpověď na váš dotaz."
@@ -2610,7 +2610,7 @@ msgstr "Od nynějška budete upozorněni jakmile bude na '{{public_body_name}}'
msgid ""
"Then you will be updated whenever the request '{{request_title}}' is "
"updated."
-msgstr "Budete upozorněni na zmeěu stavu u dotazu '{{request_title}}'."
+msgstr "Budete upozorněni na změnu stavu u dotazu '{{request_title}}'."
msgid "Then you'll be allowed to send FOI requests."
msgstr "Pak můžete vznést dotaz."
@@ -2701,7 +2701,7 @@ msgid ""
msgstr "Toto zahrnuje široké spektrum informací o stavu \n <strong>přírody a památek</strong>, například:"
msgid "This external request has been hidden"
-msgstr "This external request has been hidden ??"
+msgstr "Tento dotaz byl skryt."
msgid ""
"This is a plain-text version of the Freedom of Information request "
@@ -2781,7 +2781,7 @@ msgid ""
"This request has been <strong>withdrawn</strong> by the person who made "
"it.\\n There may be an explanation in the correspondence "
"below."
-msgstr "Dotaz byla <strong>stažen</strong> tazatelem. \nBližší vysvětlení můžete najít v níže uvedené korespondenci."
+msgstr "Dotaz byl <strong>stažen</strong> tazatelem. \nBližší vysvětlení můžete najít v níže uvedené korespondenci."
msgid ""
"This request has been marked for review by the site administrators, who have"
@@ -2795,7 +2795,7 @@ msgstr "Odesláno administrátorovi"
msgid ""
"This request has been set by an administrator to \"allow new responses from "
"nobody\""
-msgstr "Tento dotaz byl poslán administrátorovi, aby \"povolil nové odpovědi od nikoho\""
+msgstr "Tento dotaz byl poslán administrátorovi, aby \"povolil nové odpovědi od nikoho\"."
msgid ""
"This request has had an unusual response, and <strong>requires "
@@ -2854,7 +2854,7 @@ msgid "To change your email address used on {{site_name}}"
msgstr "Změnit e-mailovou adresu, kterou používáte na stránkách {{site_name}}"
msgid "To classify the response to this FOI request"
-msgstr "Pro zatřídění dotazů"
+msgstr "Pro utřídění dotazů"
msgid "To do that please send a private email to "
msgstr "Aby se tak stalo, prosíme pošlete e-mail soukromou cestou"
@@ -2894,7 +2894,7 @@ msgstr "Abychom udržovali stránky přehledné, někdo jiný upravil status va
msgid ""
"To let everyone know, follow this link and then select the appropriate box."
-msgstr "To let everyone know, follow this link and then select the appropriate box. ??"
+msgstr "Sdílejte tento link a vyberte nejvhodnější variantu. ??"
msgid "To log into the administrative interface"
msgstr "Přihlásit se do administrace"
@@ -2925,7 +2925,7 @@ msgstr "Aktualizovat stav dotazu"
msgid ""
"To upload a response, you must be logged in using an email address from "
-msgstr "Abyste mohli nahrát odpověď, musíte být přihlášeni pod e-mailovou adresou z"
+msgstr "Abyste mohli nahrát odpověď, musíte být přihlášeni pod e-mailovou adresou "
msgid ""
"To use the advanced search, combine phrases and labels as described in the "
@@ -3029,7 +3029,7 @@ msgid "Update the status of your request to "
msgstr "Aktualizujte stav svého dotazu vzneseného na "
msgid "Upload FOI response"
-msgstr "Nahrajte odpověď na žádost"
+msgstr "Nahrajte odpověď na dotaz "
msgid ""
"Use OR (in capital letters) where you don't mind which word, e.g. "
@@ -3131,12 +3131,12 @@ msgstr "Čeká se na vysvětlení. "
msgid ""
"Waiting for an <strong>internal review</strong> by {{public_body_link}} of "
"their handling of this request."
-msgstr "Čeká se na <strong>přezkoumání dotazu</strong> u {{public_body_link}}."
+msgstr "Stále <strong>čekám</strong> na doplnění dotazu institucí či jmenovitě jejím pracovníkem {{person_or_body}}."
msgid ""
"Waiting for the public authority to complete an internal review of their "
"handling of the request"
-msgstr "Čeká se na vnitřní přezkoumání u instituce, jak na dotaz odpoví"
+msgstr "Čekám, až instituce doplní požadované informace k mému dotazu"
msgid "Waiting for the public authority to reply"
msgstr "Čeká se na odpověď instituce"
@@ -3181,7 +3181,7 @@ msgstr "Čekáme, až si to někdo přečte"
msgid ""
"We've sent an email to your new email address. You'll need to click the link"
" in\\nit before your email address will be changed."
-msgstr "Poslali jsme e-mail na vaši novou adresu. Je třeba kliknout na zaslaný odkaz,\naby byla vaše nová e-mailová adresa aktivována."
+msgstr "Poslali jsme e-mail na vaši novou adresu. Je třeba kliknout na zaslaný odkaz, aby vaše nová e-mailová adresa byla správně přiřazena."
msgid ""
"We've sent you an email, and you'll need to click the link in it before you "
@@ -3208,17 +3208,17 @@ msgstr "Jaká informace byla požadována?"
msgid ""
"When you get there, please update the status to say if the response "
"\\ncontains any useful information."
-msgstr "Až se přihlásíte, aktualizujte status s ohledem na to,\nzda odpověď je dostačující či nikoliv."
+msgstr "Přihlašte se na stránky IPV, přečtěte si odpověď a aktualizujte status tohoto dotazu s ohledem na poskytnutou odpověď. Status aktualizujete jednoduše zaškrtnutím jedné z možností.\n\nStav svého dotazu či odpověď instituce včetně svého dotazu můžete také sdílet na sociálních sítích nebo na svých stránkách či blogu. Pokud tak uděláte, vždy sdílejte i link na svůj dotaz na stránkách IPV. Děkujeme Vám."
msgid ""
"When you receive the paper response, please help\\n others find "
"out what it says:"
-msgstr "Pokud obdržíte písemnou odpověď, prosíme shrňte\n pro ostatní co se v odpovědi říká:"
+msgstr "Pokud obdržíte písemnou odpověď, prosíme shrňte\n pro ostatní, co se v odpovědi říká:"
msgid ""
"When you're done, <strong>come back here</strong>, <a href=\"%s\">reload "
"this page</a> and file your new request."
-msgstr "Až budete hotovi, <strong>vraťte se sem</strong>, <a href=\"%s\">obnovte stránku</a> a vzneste svůj nový dotaz."
+msgstr "Až budete hotovi, <strong>vraťte se sem</strong>, <a href=\"%s\">obnovte stránku</a> a vzneste nový dotaz."
msgid "Which of these is happening?"
msgstr "Vyberte nejvhodnější popis z následujících možností."
@@ -3245,7 +3245,7 @@ msgid "Write your FOI follow up message to "
msgstr "Napište svou odpověď pro "
msgid "Write your request in <strong>simple, precise language</strong>."
-msgstr "Napište svůj dotaz <strong>jednoduše a jasně</strong>.\nSvůj dotaz formulujte v jasných bodech. \nPokud žádáte o více nesouvisejících informací, rozdělte je do více žádostí. "
+msgstr "Napište svůj dotaz <strong>jednoduše a jasně</strong>.\nSvůj dotaz formulujte v jasných bodech. \nPokud žádáte o více nesouvisejících informací, rozdělte je do více dotazů. "
msgid "You"
msgstr "Vy"
@@ -3303,7 +3303,7 @@ msgid ""
msgstr "Od této instituce můžete požadovat pouze informace podle zákona o životním prostředí. "
msgid "You have a new response to the {{law_used_full}} request "
-msgstr "Máte novou odpověďna {{law_used_full}} "
+msgstr "Máte novou odpověď na {{law_used_full}} "
msgid ""
"You have found a bug. Please <a href=\"{{contact_url}}\">contact us</a> to "
@@ -3400,10 +3400,10 @@ msgid ""
msgstr "Odpověď na váš dotaz obdržíte, pouze pokud odpovíte \nbližším vysvětlením."
msgid "You're in. <a href=\"#\" id=\"send-request\">Continue sending your request</a>"
-msgstr ""
+msgstr "Odesláno. <a href=\"#\" id=\"send-request\"> Můžete vznést další dotaz.</a>"
msgid "You're long overdue a response to your FOI request - "
-msgstr "Dlouho jste nereagovali na odpověď k vašemu dotazu –"
+msgstr "Dlouho jste nereagovali na odpověď ke svému dotazu –"
msgid "You're not following anything."
msgstr "Nesledujete žádný vznesený dotaz."
@@ -3428,7 +3428,7 @@ msgid ""
"href=\"%s\">why?</a>)\\n on this website and in search engines. If "
"you\\n are thinking of using a pseudonym, please \\n <a "
"href=\"%s\">read this first</a>."
-msgstr "Vaše<strong> jméno bude uveřejněno</strong> \n (<a href=\"%s\">proč?</a>)\n na těchto stránkách a ve vyhledavačích. Pokud\n chcete použít přezdívku, prosíme, \n <a href=\"%s\">přečtěte nejdříve toto</a>."
+msgstr "Vaše<strong> jméno bude uveřejněno</strong> \n (<a href=\"%s\">proč?</a>)\n na těchto stránkách a ve vyhledávačích. Pokud\n chcete použít přezdívku, prosíme, \n <a href=\"%s\">přečtěte si nejdříve toto</a>."
msgid "Your annotations"
msgstr "Vaše poznámky"
@@ -3444,13 +3444,13 @@ msgid ""
"Your follow up has not been sent because this request has been stopped to "
"prevent spam. Please <a href=\"%s\">contact us</a> if you really want to "
"send a follow up message."
-msgstr "Vaše odpověď nebyla odeslána, protože tento dotaz byla identifikován jako nevyžádaná zpráva. Prosíme <a href=\"%s\">kontaktujte nás</a> pokud svou zprávu chcete odeslat. "
+msgstr "Vaše odpověď nebyla odeslána, protože tento dotaz byl identifikován jako nevyžádaná zpráva. Prosíme <a href=\"%s\">kontaktujte nás</a> pokud svou zprávu chcete odeslat. "
msgid "Your follow up message has been sent on its way."
msgstr "Zpráva s vaší odpovědí byla odeslána. "
msgid "Your internal review request has been sent on its way."
-msgstr "Vaše žádost o přezkoumání byla odeslána."
+msgstr "Dotaz týkající se doplnění byla odeslána."
msgid ""
"Your message has been sent. Thank you for getting in touch! We'll get back "
@@ -3495,18 +3495,18 @@ msgstr "<strong>Důležité upozornění:</strong> Vaše fotografie bude zveřej
msgid ""
"Your request was called {{info_request}}. Letting everyone know whether you "
"got the information will help us keep tabs on"
-msgstr "Informace se týká vašeho dotazu {{info_request}}. Můžete všechny informovat, zda jste požadovanou informaci obdrželi a budeme mít přehled o odpovědích instituce"
+msgstr "Informace se týká vašeho dotazu {{info_request}}. Můžete všechny informovat, zda jste požadovanou informaci obdrželi a bude tak přehled o odpovědích této instituce"
msgid "Your request:"
msgstr "Váš dotaz:"
msgid "Your response to an FOI request was not delivered"
-msgstr "Vaše odpověď na žádost nebyla doručena"
+msgstr "Vaše odpověď na dotaz nebyla doručena"
msgid ""
"Your response will <strong>appear on the Internet</strong>, <a "
"href=\"%s\">read why</a> and answers to other questions."
-msgstr "Vaše odpověď <strong>bude uveřejněna na internetu</strong>, <a href=\"%s\">čtěte proč</a>odpovědi na další otázky."
+msgstr "Vaše odpověď <strong>bude uveřejněna na internetu</strong>, <a href=\"%s\">čtěte proč</a> spolu s historií konverzace."
msgid ""
"Your thoughts on what the {{site_name}} <strong>administrators</strong> "
@@ -3581,7 +3581,7 @@ msgid "authorities"
msgstr "instituce"
msgid "awaiting a response"
-msgstr "očekává se odpověď"
+msgstr "očekává se odpověď "
msgid "beginning with ‘{{first_letter}}’"
msgstr "Začínající na ‘{{first_letter}}’"
@@ -3596,7 +3596,7 @@ msgid "by <strong>{{date}}</strong>"
msgstr "od <strong>{{date}}</strong>"
msgid "by {{public_body_name}} to {{info_request_user}} on {{date}}."
-msgstr "od {{public_body_name}} pro {{info_request_user}} dne {{date}}."
+msgstr "od instituce {{public_body_name}} pro uživatele {{info_request_user}} dne {{date}}."
msgid "by {{user_link_absolute}}"
msgstr "by {{user_link_absolute}}"
@@ -3689,7 +3689,7 @@ msgid "please sign in as "
msgstr "prosíme přihlašte se jako"
msgid "requesting an internal review"
-msgstr "požadavek na přezkoumání dotazu"
+msgstr "požádat o doplnění dotazu "
msgid "requests"
msgstr "dotazy"
@@ -3700,13 +3700,13 @@ msgstr "dotazy které jsou {{list_of_statuses}}"
msgid ""
"response as needing administrator attention. Take a look, and reply to "
"this\\nemail to let them know what you are going to do about it."
-msgstr "jako odpověď, která vyžaduje pozornost administrátora. Podívejte se na ni, odpovězte\nna tento e-mail a oznamte, co budete dělat. ??"
+msgstr "adminsitrátorovi. Podívejte se prosím na vznesený dotaz i odpověď. Až uděláte nezbytné kroky, odpovězte na tento e-mail a stručně je popište."
msgid "send a follow up message"
msgstr "poslat odpověď"
msgid "sent to {{public_body_name}} by {{info_request_user}} on {{date}}."
-msgstr "posláno {{public_body_name}} od {{info_request_user}} v {{date}}."
+msgstr "posláno instituci {{public_body_name}} uživatelem {{info_request_user}} dne {{date}}."
msgid "show quoted sections"
msgstr "ukázat citované pasáže"
@@ -3727,7 +3727,7 @@ msgid "that you made to"
msgstr "kterou jste vznesli na"
msgid "the main FOI contact address for {{public_body}}"
-msgstr "hlavní kontaktní adresa pro poskytování informací institucí {{public_body}}"
+msgstr "hlavní kontaktní adresa instituce {{public_body}} pro poskytování informací "
msgid "the main FOI contact at {{public_body}}"
msgstr "hlavní kontakt pro vznesení dotazu na instituci {{public_body}}"
@@ -3791,7 +3791,7 @@ msgid "{{info_request_user_name}} only:"
msgstr "Pouze {{info_request_user_name}}:"
msgid "{{law_used_full}} request - {{title}}"
-msgstr "Žádost o informace podle {{law_used_full}} - {{title}}"
+msgstr "Dotaz o informace podle {{law_used_full}} - {{title}}"
msgid "{{law_used_full}} request GQ - {{title}}"
msgstr "{{law_used_full}} žádejte GQ - {{title}} ??"
@@ -3809,10 +3809,10 @@ msgid "{{number_of_comments}} comments"
msgstr "{{number_of_comments}} komentářů"
msgid "{{public_body_link}} answered a request about"
-msgstr "{{public_body_link}} zodpověděl dotaz týkající se"
+msgstr "Instituce {{public_body_link}} zodpověděla dotaz týkající se "
msgid "{{public_body_link}} was sent a request about"
-msgstr "Na {{public_body_link}} byl odeslán dotaz"
+msgstr "Na instituci {{public_body_link}} byl vznesen dotaz "
msgid "{{public_body_name}} only:"
msgstr "Pouze {{public_body_name}}:"
@@ -3838,7 +3838,7 @@ msgstr "Stránky {{site_name}} zahrnují dotazy vznesené na {{number_of_authori
msgid ""
"{{site_name}} sends new requests to <strong>{{request_email}}</strong> for "
"this authority."
-msgstr "Stránky {{site_name}} vznáší nové dotazy na <strong>{{request_email}}</strong> tuto instituci."
+msgstr "Pomocí stránek {{site_name}} vznesete nové dotazy na <strong>{{request_email}}</strong> tuto instituci."
msgid ""
"{{site_name}} users have made {{number_of_requests}} requests, including:"
@@ -3880,7 +3880,7 @@ msgid ""
"{{user}} ({{user_admin_link}}) made this {{law_used_full}} request (<a "
"href=\"{{request_admin_url}}\">admin</a>) to {{public_body_link}} (<a "
"href=\"{{public_body_admin_url}}\">admin</a>)"
-msgstr "{{user}} ({{user_admin_link}}) podal tuto {{law_used_full}} (<a href=\"{{request_admin_url}}\">admin</a>) pro {{public_body_link}} (<a href=\"{{public_body_admin_url}}\">admin</a>)"
+msgstr "{{user}} ({{user_admin_link}}) vznesl tento {{law_used_full}} dotaz (<a href=\"{{request_admin_url}}\">admin</a>) na instuci {{public_body_link}} (<a href=\"{{public_body_admin_url}}\">admin</a>)"
msgid "{{user}} made this {{law_used_full}} request"
msgstr "{{user}} vložil tuto {{law_used_full}} "
diff --git a/locale/cy/app.po b/locale/cy/app.po
index 346bb698e..b55a61b5d 100644
--- a/locale/cy/app.po
+++ b/locale/cy/app.po
@@ -9,8 +9,8 @@ msgstr ""
"Project-Id-Version: alaveteli\n"
"Report-Msgid-Bugs-To: http://github.com/sebbacon/alaveteli/issues\n"
"POT-Creation-Date: 2012-11-06 16:35+0000\n"
-"PO-Revision-Date: 2012-11-06 18:09+0000\n"
-"Last-Translator: louisecrow <louise@mysociety.org>\n"
+"PO-Revision-Date: 2012-11-20 22:54+0000\n"
+"Last-Translator: skenaja <alex@alexskene.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -45,7 +45,7 @@ msgstr ""
msgid ""
" <strong>Note:</strong>\\n We will send you an email. Follow the "
"instructions in it to change\\n your password."
-msgstr ""
+msgstr " <strong>Sylwer:</strong>\\n Byddwn yn anfon e-bost atoch. Dilynwch y cyfarwyddiadau ynddo i newid eich cyfrinair."
msgid " <strong>Privacy note:</strong> Your email address will be given to"
msgstr ""
@@ -139,7 +139,7 @@ msgstr[2] ""
msgstr[3] ""
msgid "'Crime statistics by ward level for Wales'"
-msgstr ""
+msgstr "'Ystadegau troseddu yn ôl lefel ward ar gyfer Cymru'"
msgid "'Pollution levels over time for the River Tyne'"
msgstr ""
@@ -332,7 +332,7 @@ msgstr[3] ""
msgid ""
"<strong> Can I request information about myself?</strong>\\n\t\t\t<a "
"href=\"%s\">No! (Click here for details)</a>"
-msgstr ""
+msgstr "<strong> A gaf i wneud cais am wybodaeth amdanaf fi fy hun?</strong>\\n\t\t\t<a href=\"%s\">Na chewch! (Cliciwch yma am fanylion)</a>"
msgid ""
"<strong><code>commented_by:tony_bowden</code></strong> to search annotations"
@@ -595,7 +595,7 @@ msgstr ""
msgid ""
"Ask for <strong>specific</strong> documents or information, this site is not"
" suitable for general enquiries."
-msgstr ""
+msgstr "Gofynnwch am ddogfennau neu wybodaeth <strong>benodol</strong>. Nid yw'r wefan hon yn addas ar gyfer ymholiadau cyffredinol."
msgid ""
"At the bottom of this page, write a reply to them trying to persuade them to"
@@ -631,7 +631,7 @@ msgid ""
msgstr ""
msgid "Browse all authorities..."
-msgstr ""
+msgstr "Mwy o awdurdodau..."
msgid ""
"By law, under all circumstances, {{public_body_link}} should have responded "
@@ -689,13 +689,13 @@ msgid "Change your email address used on {{site_name}}"
msgstr ""
msgid "Change your password"
-msgstr ""
+msgstr "Newid eich cyfrinair"
msgid "Change your password on {{site_name}}"
msgstr "Newid eich cyfrinair ar {{site_name}}"
msgid "Change your password {{site_name}}"
-msgstr ""
+msgstr "Newid eich cyfrinair {{site_name}}"
msgid "Charity registration"
msgstr "Elusen gofrestredig"
@@ -823,7 +823,7 @@ msgid "Date:"
msgstr "Dyddiad"
msgid "Dear {{public_body_name}},"
-msgstr ""
+msgstr "Annwyl {{public_body_name}},"
msgid "Delayed response to your FOI request - "
msgstr ""
@@ -967,7 +967,7 @@ msgid ""
"First, type in the <strong>name of the UK public authority</strong> you'd "
"\\n like information from. <strong>By law, they have to "
"respond</strong>\\n (<a href=\"%s#%s\">why?</a>)."
-msgstr ""
+msgstr "Yn gyntaf, teipiwch <strong>enw awdurdod cyhoeddus yn y DU</strong> yr hoffech gael gwybodaeth ganddo. <strong>Yn ôl y gyfraith, mae'n rhaid iddynt ymateb</strong> (<a href=\"%s#%s\">pam?</a>)."
msgid "Foi attachment"
msgstr ""
@@ -1052,7 +1052,7 @@ msgid ""
msgstr ""
msgid "Forgotten your password?"
-msgstr ""
+msgstr "Wedi anghofio eich cyfrinair?"
msgid "Found %d public authority %s"
msgid_plural "Found %d public authorities %s"
@@ -1238,7 +1238,7 @@ msgstr ""
msgid ""
"If you are thinking of using a pseudonym,\\n please <a "
"href=\"%s\">read this first</a>."
-msgstr ""
+msgstr "Os ydych yn ystyried defnyddio ffugenw, <a href=\"%s\">darllenwch hwn yn gyntaf</a> os gwelwch yn dda."
msgid "If you are {{user_link}}, please"
msgstr ""
@@ -1293,10 +1293,10 @@ msgid ""
msgstr ""
msgid "If you're new to {{site_name}}"
-msgstr ""
+msgstr "Os ydych yn newydd i {{site_name}}"
msgid "If you've used {{site_name}} before"
-msgstr ""
+msgstr "Os ydych chi wedi defnyddio {{site_name}} o'r blaen "
msgid ""
"If your browser is set to accept cookies and you are seeing this "
@@ -1435,7 +1435,7 @@ msgstr ""
msgid ""
"Keep it <strong>focused</strong>, you'll be more likely to get what you want"
" (<a href=\"%s\">why?</a>)."
-msgstr ""
+msgstr "Cadwch at <strong>y prif bwynt</strong>. Byddwch yn fwy tebygol o gael beth rydych yn chwilio amdano (<a href=\"%s\">pam?</a>)."
msgid "Keywords"
msgstr ""
@@ -1713,7 +1713,7 @@ msgid "Password:"
msgstr "Cyfrinair"
msgid "Password: (again)"
-msgstr ""
+msgstr "Cyfrinair: (eto)"
msgid "Paste this link into emails, tweets, and anywhere else:"
msgstr ""
@@ -1963,7 +1963,7 @@ msgid "Preview your message"
msgstr ""
msgid "Preview your public request"
-msgstr ""
+msgstr "Rhagolwg o'ch cais cyhoeddus"
msgid "Profile photo"
msgstr ""
@@ -2102,7 +2102,7 @@ msgstr ""
msgid ""
"Remember me</label> (keeps you signed in longer;\\n do not use on a "
"public computer) "
-msgstr ""
+msgstr "Cofiwch fi</label> (yn eich cadw wedi eich mewngofnodi yn hwy; peidiwch â defnyddio hwn ar gyfrifiadur cyhoeddus)"
msgid "Report abuse"
msgstr ""
@@ -2277,7 +2277,7 @@ msgid "Showing"
msgstr ""
msgid "Sign in"
-msgstr ""
+msgstr "Mewngofnodi"
msgid "Sign in or make a new account"
msgstr "Mewngofnodi neu gofrestru"
@@ -2289,7 +2289,7 @@ msgid "Sign out"
msgstr ""
msgid "Sign up"
-msgstr ""
+msgstr "Cofrestrwch"
msgid "Similar requests"
msgstr ""
@@ -2381,7 +2381,7 @@ msgid ""
msgstr ""
msgid "Summary:"
-msgstr ""
+msgstr "Crynodeb:"
msgid "Table of statuses"
msgstr ""
@@ -2948,7 +2948,7 @@ msgid "To {{public_body_link_absolute}}"
msgstr ""
msgid "To:"
-msgstr ""
+msgstr "I:"
msgid "Today"
msgstr ""
@@ -3164,17 +3164,17 @@ msgstr ""
msgid ""
"We will not reveal your email address to anybody unless you or\\n the"
" law tell us to (<a href=\"%s\">details</a>). "
-msgstr ""
+msgstr "Ni fyddwn yn datgelu eich cyfeiriad e-bost i neb oni bai eich bod chi neu'r gyfraith yn dweud wrthym i wneud (<a href=\"%s\">manylion</a>)."
msgid ""
"We will not reveal your email address to anybody unless you\\nor the law "
"tell us to."
-msgstr ""
+msgstr "Ni fyddwn yn datgelu eich cyfeiriad e-bost i neb oni bai eich bod chi neu'r gyfraith yn dweud wrthym i wneud."
msgid ""
"We will not reveal your email addresses to anybody unless you\\nor the law "
"tell us to."
-msgstr ""
+msgstr "Ni fyddwn yn datgelu eich cyfeiriad e-bost i neb oni bai eich bod chi neu'r gyfraith yn dweud wrthym i wneud."
msgid "We're waiting for"
msgstr ""
@@ -3249,7 +3249,7 @@ msgid "Write your FOI follow up message to "
msgstr ""
msgid "Write your request in <strong>simple, precise language</strong>."
-msgstr ""
+msgstr "Ysgrifennwch eich cais mewn <strong>iaith syml, gryno</strong>."
msgid "You"
msgstr ""
@@ -3434,7 +3434,7 @@ msgid ""
"href=\"%s\">why?</a>)\\n on this website and in search engines. If "
"you\\n are thinking of using a pseudonym, please \\n <a "
"href=\"%s\">read this first</a>."
-msgstr ""
+msgstr "Bydd <strong>eich enw yn ymddangos yn gyhoeddus</strong> ( <a href=\"%s\">pam?</a>) ar y wefan hon ac mewn peiriannau chwilio.\nOs ydych yn ystyried defnyddio ffugenw, os gwelwch yn dda <a href=\"%s\">darllenwch hwn yn gyntaf</a>."
msgid "Your annotations"
msgstr ""
@@ -3444,7 +3444,7 @@ msgid ""
msgstr ""
msgid "Your e-mail:"
-msgstr ""
+msgstr "Eich cyfeiriad e-bost:"
msgid ""
"Your follow up has not been sent because this request has been stopped to "
@@ -3482,7 +3482,7 @@ msgid ""
msgstr ""
msgid "Your name:"
-msgstr ""
+msgstr "Eich enw:"
msgid "Your original message is attached."
msgstr ""
@@ -3523,7 +3523,7 @@ msgid "Your {{site_name}} email alert"
msgstr ""
msgid "Yours faithfully,"
-msgstr "Yn gywir"
+msgstr "Yr eiddoch yn gywir,"
msgid "Yours sincerely,"
msgstr "Yn gywir"
@@ -3543,7 +3543,7 @@ msgid ""
msgstr ""
msgid "a one line summary of the information you are requesting, \\n\t\t\te.g."
-msgstr ""
+msgstr "crynodeb un llinell o'r wybodaeth rydych yn gofyn amdano, \n\\n \t\t\tee "
msgid "admin"
msgstr ""
@@ -3718,7 +3718,7 @@ msgid "show quoted sections"
msgstr ""
msgid "sign in"
-msgstr "Mewngofnodi neu gofrestru"
+msgstr "Mewngofnodi"
msgid "simple_date_format"
msgstr ""
diff --git a/locale/es/app.po b/locale/es/app.po
index c211b65a0..720bab544 100644
--- a/locale/es/app.po
+++ b/locale/es/app.po
@@ -13,8 +13,8 @@ msgstr ""
"Project-Id-Version: alaveteli\n"
"Report-Msgid-Bugs-To: http://github.com/sebbacon/alaveteli/issues\n"
"POT-Creation-Date: 2012-11-06 16:35+0000\n"
-"PO-Revision-Date: 2012-11-06 18:20+0000\n"
-"Last-Translator: louisecrow <louise@mysociety.org>\n"
+"PO-Revision-Date: 2012-11-29 00:22+0000\n"
+"Last-Translator: David Cabo <david.cabo@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -2580,7 +2580,7 @@ msgid "Then you can write your reply to "
msgstr "Entonces podrás escribir tu respuesta a "
msgid "Then you will be following all new FOI requests."
-msgstr ""
+msgstr "Entonces recibirás actualizaciones por correo de todas las nuevas solicitudes."
msgid ""
"Then you will be notified whenever '{{user_name}}' requests something or "
@@ -2603,7 +2603,7 @@ msgstr "Entonces vas a ser avisado cuando alguien haga un pedido o "
msgid ""
"Then you will be updated whenever the request '{{request_title}}' is "
"updated."
-msgstr ""
+msgstr "Entonces recibirás correos siempre que la solicitud '{{request_title}}' se actualice."
msgid "Then you'll be allowed to send FOI requests."
msgstr "Entonces podrá enviar solicitudes de información."
@@ -2777,7 +2777,7 @@ msgid ""
"This request has been marked for review by the site administrators, who have"
" not hidden it at this time. If you believe it should be hidden, please <a "
"href=\"%s\">contact us</a>."
-msgstr ""
+msgstr "Esta solicitud va a ser revisada por los administradores de la web, que no la han ocultado de momento. Si crees que debe ser ocultada, por favor <a href=\"%s\">contáctanos</a>."
msgid "This request has been reported for administrator attention"
msgstr "Este pedido ha sido reportado al administrador del sitio."
@@ -2859,7 +2859,7 @@ msgid "To follow all successful requests"
msgstr "Sigue todos los pedidos exitosos"
msgid "To follow new requests"
-msgstr ""
+msgstr "Seguir nuevas solicitudes"
msgid "To follow requests and responses matching your search"
msgstr "Para seguir solicitudes y respuestas que encajen con tu búsqueda"
@@ -3247,16 +3247,16 @@ msgid "You are already following requests to {{public_body_name}}"
msgstr "Tu ya estas siguiendo pedidos a {{public_body_name}}"
msgid "You are already following things matching this search"
-msgstr ""
+msgstr "Ya estás siguiendo esta búsqueda por correo"
msgid "You are already following this person"
-msgstr ""
+msgstr "Ya estás siguiendo a esta persona por correo"
msgid "You are already following this request"
-msgstr ""
+msgstr "Ya estás siguiendo esta solicitud por correo"
msgid "You are already following updates about {{track_description}}"
-msgstr ""
+msgstr "Ya estás recibiendo actualizaciones por correo sobre la solicitud."
msgid ""
"You are currently receiving notification of new activity on your wall by "
@@ -3264,7 +3264,7 @@ msgid ""
msgstr "Actualmente estas recibiendo notificaciones de nueva actividad en tu muro por correo electronico."
msgid "You are following all new successful responses"
-msgstr ""
+msgstr "Estás recibiendo correos sobre cualquier nueva respuesta exitosa"
msgid "You are no longer following {{track_description}}."
msgstr ""
@@ -3272,7 +3272,7 @@ msgstr ""
msgid ""
"You are now <a href=\"{{wall_url_user}}\">following</a> updates about "
"{{track_description}}"
-msgstr ""
+msgstr "Ahora estás recibiendo <a href=\"{{wall_url_user}}\">actualizaciones</a> sobre {{track_description}}"
msgid "You can <strong>complain</strong> by"
msgstr "Puede <strong>apelar</strong>"
@@ -3382,7 +3382,7 @@ msgstr "Ya no recibirá correos para esas alertas"
msgid ""
"You will now be emailed updates about {{track_description}}. <a "
"href=\"{{change_email_alerts_url}}\">Prefer not to receive emails?</a>"
-msgstr ""
+msgstr "Ahora recibirás actualizaciones por correo sobre {{track_description}}. <a href=\"{{change_email_alerts_url}}\">¿Prefieres no recibir correos?</a>"
msgid ""
"You will only get an answer to your request if you follow up\\nwith the "
@@ -3396,7 +3396,7 @@ msgid "You're long overdue a response to your FOI request - "
msgstr "La respuesta a tu solicitud de información está muy retrasada - "
msgid "You're not following anything."
-msgstr ""
+msgstr "No estás recibiendo actualizaciones por correo."
msgid "You've now cleared your profile photo"
msgstr "Has borrado la foto de tu perfil"
@@ -3511,13 +3511,13 @@ msgid "Yours sincerely,"
msgstr "Un saludo,"
msgid "[FOI #{{request}} email]"
-msgstr ""
+msgstr "[Dirección de correo de la solicitud #{{request}}]"
msgid "[{{public_body}} request email]"
-msgstr ""
+msgstr "[Dirección de correo del organismo {{public_body}}]"
msgid "[{{site_name}} contact email]"
-msgstr ""
+msgstr "[Correo de contacto de {{site_name}}]"
msgid ""
"\\n\\n[ {{site_name}} note: The above text was badly encoded, and has had "
@@ -3598,7 +3598,7 @@ msgid ""
msgstr "incluyendo tu dirección postal, y pidiéndoles que contesten a tu solicitud.\n O prueba a llamarles por teléfono."
msgid "details"
-msgstr ""
+msgstr "detalles"
msgid "display_status only works for incoming and outgoing messages right now"
msgstr "display_status sólo funciona para mensajes de entrada y salida ahora mismo"
@@ -3625,7 +3625,7 @@ msgid "have delayed."
msgstr "han retrasado."
msgid "hide quoted sections"
-msgstr ""
+msgstr "ocultar partes citadas"
msgid "in term time"
msgstr "durante el periodo escolar"
@@ -3697,13 +3697,13 @@ msgid "sent to {{public_body_name}} by {{info_request_user}} on {{date}}."
msgstr "enviada a {{public_body_name}} por {{info_request_user}} el {{date}}."
msgid "show quoted sections"
-msgstr ""
+msgstr "mostrar partes citadas"
msgid "sign in"
msgstr "abrir sesión"
msgid "simple_date_format"
-msgstr ""
+msgstr "simple_date_format"
msgid "successful"
msgstr "exitosas"
@@ -3785,7 +3785,7 @@ msgid "{{law_used_full}} request GQ - {{title}}"
msgstr "solicitud {{law_used_full}} - {{title}}"
msgid "{{law_used}} requests at {{public_body}}"
-msgstr ""
+msgstr "Solicitudes de información a {{public_body}}"
msgid "{{length_of_time}} ago"
msgstr "hace {{length_of_time}}"
@@ -3833,7 +3833,7 @@ msgid ""
msgstr "Los usuarios de {{site_name}} han hecho {{number_of_requests}} solicitudes, incluyendo:"
msgid "{{title}} - a Freedom of Information request to {{public_body}}"
-msgstr ""
+msgstr "{{title}} - una solicitud de información a {{public_body}}"
msgid "{{user_name}} (Account suspended)"
msgstr "{{user_name}} (Expulsado)"
diff --git a/public/404.html b/public/404.html
index eff660b90..9a48320a5 100644
--- a/public/404.html
+++ b/public/404.html
@@ -1,23 +1,19 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
+<!DOCTYPE html>
+<html>
<head>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>The page you were looking for doesn't exist (404)</title>
- <style type="text/css">
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
- div.dialog {
- width: 25em;
- padding: 0 4em;
- margin: 4em auto 0 auto;
- border: 1px solid #ccc;
- border-right-color: #999;
- border-bottom-color: #999;
- }
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
- </style>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
</head>
<body>
@@ -27,4 +23,4 @@
<p>You may have mistyped the address or the page may have moved.</p>
</div>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/public/422.html b/public/422.html
new file mode 100644
index 000000000..83660ab18
--- /dev/null
+++ b/public/422.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The change you wanted was rejected (422)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/422.html -->
+ <div class="dialog">
+ <h1>The change you wanted was rejected.</h1>
+ <p>Maybe you tried to change something you didn't have access to.</p>
+ </div>
+</body>
+</html>
diff --git a/public/500.html b/public/500.html
index f0aee0e9f..b80307fc1 100644
--- a/public/500.html
+++ b/public/500.html
@@ -1,23 +1,19 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
+<!DOCTYPE html>
+<html>
<head>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
- <title>We're sorry, but something went wrong</title>
- <style type="text/css">
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
- div.dialog {
- width: 25em;
- padding: 0 4em;
- margin: 4em auto 0 auto;
- border: 1px solid #ccc;
- border-right-color: #999;
- border-bottom-color: #999;
- }
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
- </style>
+ <title>We're sorry, but something went wrong (500)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
</head>
<body>
@@ -27,4 +23,4 @@
<p>We've been notified about this issue and we'll take a look at it shortly.</p>
</div>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/public/dispatch.cgi b/public/dispatch.cgi
deleted file mode 100755
index 3848806db..000000000
--- a/public/dispatch.cgi
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/ruby1.8
-
-require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
-
-# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
-# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
-require "dispatcher"
-
-ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
-Dispatcher.dispatch \ No newline at end of file
diff --git a/public/dispatch.fcgi b/public/dispatch.fcgi
deleted file mode 100755
index ad34d90e4..000000000
--- a/public/dispatch.fcgi
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/ruby1.8
-#
-# You may specify the path to the FastCGI crash log (a log of unhandled
-# exceptions which forced the FastCGI instance to exit, great for debugging)
-# and the number of requests to process before running garbage collection.
-#
-# By default, the FastCGI crash log is Rails.root/log/fastcgi.crash.log
-# and the GC period is nil (turned off). A reasonable number of requests
-# could range from 10-100 depending on the memory footprint of your app.
-#
-# Example:
-# # Default log path, normal GC behavior.
-# RailsFCGIHandler.process!
-#
-# # Default log path, 50 requests between GC.
-# RailsFCGIHandler.process! nil, 50
-#
-# # Custom log path, normal GC behavior.
-# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
-#
-require File.dirname(__FILE__) + "/../config/environment"
-require 'fcgi_handler'
-
-RailsFCGIHandler.process!
diff --git a/public/dispatch.rb b/public/dispatch.rb
deleted file mode 100755
index 3848806db..000000000
--- a/public/dispatch.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/ruby1.8
-
-require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
-
-# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
-# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
-require "dispatcher"
-
-ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
-Dispatcher.dispatch \ No newline at end of file
diff --git a/public/javascripts/controls.js b/public/javascripts/controls.js
index 5aaf0bb2b..7392fb664 100644
--- a/public/javascripts/controls.js
+++ b/public/javascripts/controls.js
@@ -1,22 +1,24 @@
-// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
-// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+// script.aculo.us controls.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009
+
+// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005-2009 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005-2009 Jon Tirsen (http://www.tirsen.com)
// Contributors:
// Richard Livsey
// Rahul Bhargava
// Rob Wills
-//
+//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/
-// Autocompleter.Base handles all the autocompletion functionality
+// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
-// Specific autocompleters need to provide, at the very least,
+// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
-// the text inside the monitored textbox changes. This method
+// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
@@ -30,23 +32,23 @@
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
-// enables autocompletion on multiple tokens. This is most
-// useful when one of the tokens is \n (a newline), as it
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.
if(typeof Effect == 'undefined')
throw("controls.js requires including script.aculo.us' effects.js library");
-var Autocompleter = { }
+var Autocompleter = { };
Autocompleter.Base = Class.create({
baseInitialize: function(element, update, options) {
- element = $(element)
- this.element = element;
- this.update = $(update);
- this.hasFocus = false;
- this.changed = false;
- this.active = false;
- this.index = 0;
+ element = $(element);
+ this.element = element;
+ this.update = $(update);
+ this.hasFocus = false;
+ this.changed = false;
+ this.active = false;
+ this.index = 0;
this.entryCount = 0;
this.oldElementValue = this.element.value;
@@ -59,28 +61,28 @@ Autocompleter.Base = Class.create({
this.options.tokens = this.options.tokens || [];
this.options.frequency = this.options.frequency || 0.4;
this.options.minChars = this.options.minChars || 1;
- this.options.onShow = this.options.onShow ||
- function(element, update){
+ this.options.onShow = this.options.onShow ||
+ function(element, update){
if(!update.style.position || update.style.position=='absolute') {
update.style.position = 'absolute';
Position.clone(element, update, {
- setHeight: false,
+ setHeight: false,
offsetTop: element.offsetHeight
});
}
Effect.Appear(update,{duration:0.15});
};
- this.options.onHide = this.options.onHide ||
+ this.options.onHide = this.options.onHide ||
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
- if(typeof(this.options.tokens) == 'string')
+ if(typeof(this.options.tokens) == 'string')
this.options.tokens = new Array(this.options.tokens);
// Force carriage returns as token delimiters anyway
if (!this.options.tokens.include('\n'))
this.options.tokens.push('\n');
this.observer = null;
-
+
this.element.setAttribute('autocomplete','off');
Element.hide(this.update);
@@ -91,10 +93,10 @@ Autocompleter.Base = Class.create({
show: function() {
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
- if(!this.iefix &&
+ if(!this.iefix &&
(Prototype.Browser.IE) &&
(Element.getStyle(this.update, 'position')=='absolute')) {
- new Insertion.After(this.update,
+ new Insertion.After(this.update,
'<iframe id="' + this.update.id + '_iefix" '+
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
@@ -102,7 +104,7 @@ Autocompleter.Base = Class.create({
}
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
},
-
+
fixIEOverlapping: function() {
Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
this.iefix.style.zIndex = 1;
@@ -150,15 +152,15 @@ Autocompleter.Base = Class.create({
Event.stop(event);
return;
}
- else
- if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
+ else
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
(Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
this.changed = true;
this.hasFocus = true;
if(this.observer) clearTimeout(this.observer);
- this.observer =
+ this.observer =
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
},
@@ -170,35 +172,35 @@ Autocompleter.Base = Class.create({
onHover: function(event) {
var element = Event.findElement(event, 'LI');
- if(this.index != element.autocompleteIndex)
+ if(this.index != element.autocompleteIndex)
{
this.index = element.autocompleteIndex;
this.render();
}
Event.stop(event);
},
-
+
onClick: function(event) {
var element = Event.findElement(event, 'LI');
this.index = element.autocompleteIndex;
this.selectEntry();
this.hide();
},
-
+
onBlur: function(event) {
// needed to make click events working
setTimeout(this.hide.bind(this), 250);
this.hasFocus = false;
- this.active = false;
- },
-
+ this.active = false;
+ },
+
render: function() {
if(this.entryCount > 0) {
for (var i = 0; i < this.entryCount; i++)
- this.index==i ?
- Element.addClassName(this.getEntry(i),"selected") :
+ this.index==i ?
+ Element.addClassName(this.getEntry(i),"selected") :
Element.removeClassName(this.getEntry(i),"selected");
- if(this.hasFocus) {
+ if(this.hasFocus) {
this.show();
this.active = true;
}
@@ -207,27 +209,27 @@ Autocompleter.Base = Class.create({
this.hide();
}
},
-
+
markPrevious: function() {
- if(this.index > 0) this.index--
+ if(this.index > 0) this.index--;
else this.index = this.entryCount-1;
this.getEntry(this.index).scrollIntoView(true);
},
-
+
markNext: function() {
- if(this.index < this.entryCount-1) this.index++
+ if(this.index < this.entryCount-1) this.index++;
else this.index = 0;
this.getEntry(this.index).scrollIntoView(false);
},
-
+
getEntry: function(index) {
return this.update.firstChild.childNodes[index];
},
-
+
getCurrentEntry: function() {
return this.getEntry(this.index);
},
-
+
selectEntry: function() {
this.active = false;
this.updateElement(this.getCurrentEntry());
@@ -244,7 +246,7 @@ Autocompleter.Base = Class.create({
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
} else
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
-
+
var bounds = this.getTokenBounds();
if (bounds[0] != -1) {
var newValue = this.element.value.substr(0, bounds[0]);
@@ -257,7 +259,7 @@ Autocompleter.Base = Class.create({
}
this.oldElementValue = this.element.value;
this.element.focus();
-
+
if (this.options.afterUpdateElement)
this.options.afterUpdateElement(this.element, selectedElement);
},
@@ -269,20 +271,20 @@ Autocompleter.Base = Class.create({
Element.cleanWhitespace(this.update.down());
if(this.update.firstChild && this.update.down().childNodes) {
- this.entryCount =
+ this.entryCount =
this.update.down().childNodes.length;
for (var i = 0; i < this.entryCount; i++) {
var entry = this.getEntry(i);
entry.autocompleteIndex = i;
this.addObservers(entry);
}
- } else {
+ } else {
this.entryCount = 0;
}
this.stopIndicator();
this.index = 0;
-
+
if(this.entryCount==1 && this.options.autoSelect) {
this.selectEntry();
this.hide();
@@ -298,7 +300,7 @@ Autocompleter.Base = Class.create({
},
onObserverEvent: function() {
- this.changed = false;
+ this.changed = false;
this.tokenBounds = null;
if(this.getToken().length>=this.options.minChars) {
this.getUpdatedChoices();
@@ -351,16 +353,16 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
getUpdatedChoices: function() {
this.startIndicator();
-
- var entry = encodeURIComponent(this.options.paramName) + '=' +
+
+ var entry = encodeURIComponent(this.options.paramName) + '=' +
encodeURIComponent(this.getToken());
this.options.parameters = this.options.callback ?
this.options.callback(this.element, entry) : entry;
- if(this.options.defaultParams)
+ if(this.options.defaultParams)
this.options.parameters += '&' + this.options.defaultParams;
-
+
new Ajax.Request(this.url, this.options);
},
@@ -382,7 +384,7 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
-// text only at the beginning of strings in the
+// text only at the beginning of strings in the
// autocomplete array. Defaults to true, which will
// match text at the beginning of any *word* in the
// strings in the autocomplete array. If you want to
@@ -399,7 +401,7 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
// - ignoreCase - Whether to ignore case when autocompleting.
// Defaults to true.
//
-// It's possible to pass in a custom function as the 'selector'
+// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.
@@ -427,20 +429,20 @@ Autocompleter.Local = Class.create(Autocompleter.Base, {
var entry = instance.getToken();
var count = 0;
- for (var i = 0; i < instance.options.array.length &&
- ret.length < instance.options.choices ; i++) {
+ for (var i = 0; i < instance.options.array.length &&
+ ret.length < instance.options.choices ; i++) {
var elem = instance.options.array[i];
- var foundPos = instance.options.ignoreCase ?
- elem.toLowerCase().indexOf(entry.toLowerCase()) :
+ var foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
elem.indexOf(entry);
while (foundPos != -1) {
- if (foundPos == 0 && elem.length != entry.length) {
- ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
+ if (foundPos == 0 && elem.length != entry.length) {
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
elem.substr(entry.length) + "</li>");
break;
- } else if (entry.length >= instance.options.partialChars &&
+ } else if (entry.length >= instance.options.partialChars &&
instance.options.partialSearch && foundPos != -1) {
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
@@ -450,14 +452,14 @@ Autocompleter.Local = Class.create(Autocompleter.Base, {
}
}
- foundPos = instance.options.ignoreCase ?
- elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+ foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
elem.indexOf(entry, foundPos + 1);
}
}
if (partial.length)
- ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
return "<ul>" + ret.join('') + "</ul>";
}
}, options || { });
@@ -474,7 +476,7 @@ Field.scrollFreeActivate = function(field) {
setTimeout(function() {
Field.activate(field);
}, 1);
-}
+};
Ajax.InPlaceEditor = Class.create({
initialize: function(element, url, options) {
@@ -604,7 +606,7 @@ Ajax.InPlaceEditor = Class.create({
this.triggerCallback('onEnterHover');
},
getText: function() {
- return this.element.innerHTML;
+ return this.element.innerHTML.unescapeHTML();
},
handleAJAXFailure: function(transport) {
this.triggerCallback('onFailure', transport);
@@ -780,7 +782,7 @@ Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
onSuccess: function(transport) {
var js = transport.responseText.strip();
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
- throw 'Server returned an invalid collection representation.';
+ throw('Server returned an invalid collection representation.');
this._collection = eval(js);
this.checkForExternalText();
}.bind(this),
@@ -937,7 +939,7 @@ Ajax.InPlaceCollectionEditor.DefaultOptions = {
loadingCollectionText: 'Loading options...'
};
-// Delayed observer, like Form.Element.Observer,
+// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields
@@ -947,7 +949,7 @@ Form.Element.DelayedObserver = Class.create({
this.element = $(element);
this.callback = callback;
this.timer = null;
- this.lastValue = $F(this.element);
+ this.lastValue = $F(this.element);
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
},
delayedListener: function(event) {
@@ -960,4 +962,4 @@ Form.Element.DelayedObserver = Class.create({
this.timer = null;
this.callback(this.element, $F(this.element));
}
-});
+}); \ No newline at end of file
diff --git a/public/javascripts/dragdrop.js b/public/javascripts/dragdrop.js
index bf5cfea66..15c6dbca6 100644
--- a/public/javascripts/dragdrop.js
+++ b/public/javascripts/dragdrop.js
@@ -1,6 +1,7 @@
-// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
-//
+// script.aculo.us dragdrop.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009
+
+// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/
@@ -32,7 +33,7 @@ var Droppables = {
options._containers.push($(containment));
}
}
-
+
if(options.accept) options.accept = [options.accept].flatten();
Element.makePositioned(element); // fix IE
@@ -40,34 +41,34 @@ var Droppables = {
this.drops.push(options);
},
-
+
findDeepestChild: function(drops) {
deepest = drops[0];
-
+
for (i = 1; i < drops.length; ++i)
if (Element.isParent(drops[i].element, deepest.element))
deepest = drops[i];
-
+
return deepest;
},
isContained: function(element, drop) {
var containmentNode;
if(drop.tree) {
- containmentNode = element.treeNode;
+ containmentNode = element.treeNode;
} else {
containmentNode = element.parentNode;
}
return drop._containers.detect(function(c) { return containmentNode == c });
},
-
+
isAffected: function(point, element, drop) {
return (
(drop.element!=element) &&
((!drop._containers) ||
this.isContained(element, drop)) &&
((!drop.accept) ||
- (Element.classNames(element).detect(
+ (Element.classNames(element).detect(
function(v) { return drop.accept.include(v) } ) )) &&
Position.within(drop.element, point[0], point[1]) );
},
@@ -87,12 +88,12 @@ var Droppables = {
show: function(point, element) {
if(!this.drops.length) return;
var drop, affected = [];
-
+
this.drops.each( function(drop) {
if(Droppables.isAffected(point, element, drop))
affected.push(drop);
});
-
+
if(affected.length>0)
drop = Droppables.findDeepestChild(affected);
@@ -101,7 +102,7 @@ var Droppables = {
Position.within(drop.element, point[0], point[1]);
if(drop.onHover)
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
-
+
if (drop != this.last_active) Droppables.activate(drop);
}
},
@@ -112,8 +113,8 @@ var Droppables = {
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
if (this.last_active.onDrop) {
- this.last_active.onDrop(element, this.last_active.element, event);
- return true;
+ this.last_active.onDrop(element, this.last_active.element, event);
+ return true;
}
},
@@ -121,25 +122,25 @@ var Droppables = {
if(this.last_active)
this.deactivate(this.last_active);
}
-}
+};
var Draggables = {
drags: [],
observers: [],
-
+
register: function(draggable) {
if(this.drags.length == 0) {
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
this.eventKeypress = this.keyPress.bindAsEventListener(this);
-
+
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
Event.observe(document, "keypress", this.eventKeypress);
}
this.drags.push(draggable);
},
-
+
unregister: function(draggable) {
this.drags = this.drags.reject(function(d) { return d==draggable });
if(this.drags.length == 0) {
@@ -148,24 +149,24 @@ var Draggables = {
Event.stopObserving(document, "keypress", this.eventKeypress);
}
},
-
+
activate: function(draggable) {
- if(draggable.options.delay) {
- this._timeout = setTimeout(function() {
- Draggables._timeout = null;
- window.focus();
- Draggables.activeDraggable = draggable;
- }.bind(this), draggable.options.delay);
+ if(draggable.options.delay) {
+ this._timeout = setTimeout(function() {
+ Draggables._timeout = null;
+ window.focus();
+ Draggables.activeDraggable = draggable;
+ }.bind(this), draggable.options.delay);
} else {
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
this.activeDraggable = draggable;
}
},
-
+
deactivate: function() {
this.activeDraggable = null;
},
-
+
updateDrag: function(event) {
if(!this.activeDraggable) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
@@ -173,36 +174,36 @@ var Draggables = {
// the same coordinates, prevent needless redrawing (moz bug?)
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
this._lastPointer = pointer;
-
+
this.activeDraggable.updateDrag(event, pointer);
},
-
+
endDrag: function(event) {
- if(this._timeout) {
- clearTimeout(this._timeout);
- this._timeout = null;
+ if(this._timeout) {
+ clearTimeout(this._timeout);
+ this._timeout = null;
}
if(!this.activeDraggable) return;
this._lastPointer = null;
this.activeDraggable.endDrag(event);
this.activeDraggable = null;
},
-
+
keyPress: function(event) {
if(this.activeDraggable)
this.activeDraggable.keyPress(event);
},
-
+
addObserver: function(observer) {
this.observers.push(observer);
this._cacheObserverCallbacks();
},
-
+
removeObserver: function(element) { // element instead of observer fixes mem leaks
this.observers = this.observers.reject( function(o) { return o.element==element });
this._cacheObserverCallbacks();
},
-
+
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
if(this[eventName+'Count'] > 0)
this.observers.each( function(o) {
@@ -210,7 +211,7 @@ var Draggables = {
});
if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
},
-
+
_cacheObserverCallbacks: function() {
['onStart','onEnd','onDrag'].each( function(eventName) {
Draggables[eventName+'Count'] = Draggables.observers.select(
@@ -218,7 +219,7 @@ var Draggables = {
).length;
});
}
-}
+};
/*--------------------------------------------------------------------------*/
@@ -234,12 +235,12 @@ var Draggable = Class.create({
},
endeffect: function(element) {
var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
- new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
+ new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
queue: {scope:'_draggable', position:'end'},
- afterFinish: function(){
- Draggable._dragging[element] = false
+ afterFinish: function(){
+ Draggable._dragging[element] = false
}
- });
+ });
},
zindex: 1000,
revert: false,
@@ -250,57 +251,57 @@ var Draggable = Class.create({
snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
delay: 0
};
-
+
if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
Object.extend(defaults, {
starteffect: function(element) {
element._opacity = Element.getOpacity(element);
Draggable._dragging[element] = true;
- new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
+ new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
}
});
-
+
var options = Object.extend(defaults, arguments[1] || { });
this.element = $(element);
-
+
if(options.handle && Object.isString(options.handle))
this.handle = this.element.down('.'+options.handle, 0);
-
+
if(!this.handle) this.handle = $(options.handle);
if(!this.handle) this.handle = this.element;
-
+
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
options.scroll = $(options.scroll);
this._isScrollChild = Element.childOf(this.element, options.scroll);
}
- Element.makePositioned(this.element); // fix IE
+ Element.makePositioned(this.element); // fix IE
this.options = options;
- this.dragging = false;
+ this.dragging = false;
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
Event.observe(this.handle, "mousedown", this.eventMouseDown);
-
+
Draggables.register(this);
},
-
+
destroy: function() {
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
Draggables.unregister(this);
},
-
+
currentDelta: function() {
return([
parseInt(Element.getStyle(this.element,'left') || '0'),
parseInt(Element.getStyle(this.element,'top') || '0')]);
},
-
+
initDrag: function(event) {
if(!Object.isUndefined(Draggable._dragging[this.element]) &&
Draggable._dragging[this.element]) return;
- if(Event.isLeftClick(event)) {
+ if(Event.isLeftClick(event)) {
// abort on form elements, fixes a Firefox issue
var src = Event.element(event);
if((tag_name = src.tagName.toUpperCase()) && (
@@ -309,34 +310,34 @@ var Draggable = Class.create({
tag_name=='OPTION' ||
tag_name=='BUTTON' ||
tag_name=='TEXTAREA')) return;
-
+
var pointer = [Event.pointerX(event), Event.pointerY(event)];
- var pos = Position.cumulativeOffset(this.element);
+ var pos = this.element.cumulativeOffset();
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
-
+
Draggables.activate(this);
Event.stop(event);
}
},
-
+
startDrag: function(event) {
this.dragging = true;
if(!this.delta)
this.delta = this.currentDelta();
-
+
if(this.options.zindex) {
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
this.element.style.zIndex = this.options.zindex;
}
-
+
if(this.options.ghosting) {
this._clone = this.element.cloneNode(true);
- this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
- if (!this.element._originallyAbsolute)
+ this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
+ if (!this._originallyAbsolute)
Position.absolutize(this.element);
this.element.parentNode.insertBefore(this._clone, this.element);
}
-
+
if(this.options.scroll) {
if (this.options.scroll == window) {
var where = this._getWindowScroll(this.options.scroll);
@@ -347,28 +348,28 @@ var Draggable = Class.create({
this.originalScrollTop = this.options.scroll.scrollTop;
}
}
-
+
Draggables.notify('onStart', this, event);
-
+
if(this.options.starteffect) this.options.starteffect(this.element);
},
-
+
updateDrag: function(event, pointer) {
if(!this.dragging) this.startDrag(event);
-
+
if(!this.options.quiet){
Position.prepare();
Droppables.show(pointer, this.element);
}
-
+
Draggables.notify('onDrag', this, event);
-
+
this.draw(pointer);
if(this.options.change) this.options.change(this);
-
+
if(this.options.scroll) {
this.stopScrolling();
-
+
var p;
if (this.options.scroll == window) {
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
@@ -386,16 +387,16 @@ var Draggable = Class.create({
if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
this.startScrolling(speed);
}
-
+
// fix AppleWebKit rendering
if(Prototype.Browser.WebKit) window.scrollBy(0,0);
-
+
Event.stop(event);
},
-
+
finishDrag: function(event, success) {
this.dragging = false;
-
+
if(this.options.quiet){
Position.prepare();
var pointer = [Event.pointerX(event), Event.pointerY(event)];
@@ -403,24 +404,24 @@ var Draggable = Class.create({
}
if(this.options.ghosting) {
- if (!this.element._originallyAbsolute)
+ if (!this._originallyAbsolute)
Position.relativize(this.element);
- delete this.element._originallyAbsolute;
+ delete this._originallyAbsolute;
Element.remove(this._clone);
this._clone = null;
}
- var dropped = false;
- if(success) {
- dropped = Droppables.fire(event, this.element);
- if (!dropped) dropped = false;
+ var dropped = false;
+ if(success) {
+ dropped = Droppables.fire(event, this.element);
+ if (!dropped) dropped = false;
}
if(dropped && this.options.onDropped) this.options.onDropped(this.element);
Draggables.notify('onEnd', this, event);
var revert = this.options.revert;
if(revert && Object.isFunction(revert)) revert = revert(this.element);
-
+
var d = this.currentDelta();
if(revert && this.options.reverteffect) {
if (dropped == 0 || revert != 'failure')
@@ -433,67 +434,67 @@ var Draggable = Class.create({
if(this.options.zindex)
this.element.style.zIndex = this.originalZ;
- if(this.options.endeffect)
+ if(this.options.endeffect)
this.options.endeffect(this.element);
-
+
Draggables.deactivate(this);
Droppables.reset();
},
-
+
keyPress: function(event) {
if(event.keyCode!=Event.KEY_ESC) return;
this.finishDrag(event, false);
Event.stop(event);
},
-
+
endDrag: function(event) {
if(!this.dragging) return;
this.stopScrolling();
this.finishDrag(event, true);
Event.stop(event);
},
-
+
draw: function(point) {
- var pos = Position.cumulativeOffset(this.element);
+ var pos = this.element.cumulativeOffset();
if(this.options.ghosting) {
var r = Position.realOffset(this.element);
pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
}
-
+
var d = this.currentDelta();
pos[0] -= d[0]; pos[1] -= d[1];
-
+
if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
}
-
- var p = [0,1].map(function(i){
- return (point[i]-pos[i]-this.offset[i])
+
+ var p = [0,1].map(function(i){
+ return (point[i]-pos[i]-this.offset[i])
}.bind(this));
-
+
if(this.options.snap) {
if(Object.isFunction(this.options.snap)) {
p = this.options.snap(p[0],p[1],this);
} else {
if(Object.isArray(this.options.snap)) {
p = p.map( function(v, i) {
- return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
+ return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
} else {
p = p.map( function(v) {
- return (v/this.options.snap).round()*this.options.snap }.bind(this))
+ return (v/this.options.snap).round()*this.options.snap }.bind(this));
}
}}
-
+
var style = this.element.style;
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
style.left = p[0] + "px";
if((!this.options.constraint) || (this.options.constraint=='vertical'))
style.top = p[1] + "px";
-
+
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
},
-
+
stopScrolling: function() {
if(this.scrollInterval) {
clearInterval(this.scrollInterval);
@@ -501,14 +502,14 @@ var Draggable = Class.create({
Draggables._lastScrollPointer = null;
}
},
-
+
startScrolling: function(speed) {
if(!(speed[0] || speed[1])) return;
this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
this.lastScrolled = new Date();
this.scrollInterval = setInterval(this.scroll.bind(this), 10);
},
-
+
scroll: function() {
var current = new Date();
var delta = current - this.lastScrolled;
@@ -524,7 +525,7 @@ var Draggable = Class.create({
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
}
-
+
Position.prepare();
Droppables.show(Draggables._lastPointer, this.element);
Draggables.notify('onDrag', this);
@@ -538,10 +539,10 @@ var Draggable = Class.create({
Draggables._lastScrollPointer[1] = 0;
this.draw(Draggables._lastScrollPointer);
}
-
+
if(this.options.change) this.options.change(this);
},
-
+
_getWindowScroll: function(w) {
var T, L, W, H;
with (w.document) {
@@ -560,7 +561,7 @@ var Draggable = Class.create({
H = documentElement.clientHeight;
} else {
W = body.offsetWidth;
- H = body.offsetHeight
+ H = body.offsetHeight;
}
}
return { top: T, left: L, width: W, height: H };
@@ -577,11 +578,11 @@ var SortableObserver = Class.create({
this.observer = observer;
this.lastValue = Sortable.serialize(this.element);
},
-
+
onStart: function() {
this.lastValue = Sortable.serialize(this.element);
},
-
+
onEnd: function() {
Sortable.unmark();
if(this.lastValue != Sortable.serialize(this.element))
@@ -591,11 +592,11 @@ var SortableObserver = Class.create({
var Sortable = {
SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
-
+
sortables: { },
-
+
_findRootElement: function(element) {
- while (element.tagName.toUpperCase() != "BODY") {
+ while (element.tagName.toUpperCase() != "BODY") {
if(element.id && Sortable.sortables[element.id]) return element;
element = element.parentNode;
}
@@ -606,22 +607,23 @@ var Sortable = {
if(!element) return;
return Sortable.sortables[element.id];
},
-
+
destroy: function(element){
- var s = Sortable.options(element);
-
+ element = $(element);
+ var s = Sortable.sortables[element.id];
+
if(s) {
Draggables.removeObserver(s.element);
s.droppables.each(function(d){ Droppables.remove(d) });
s.draggables.invoke('destroy');
-
+
delete Sortable.sortables[s.element.id];
}
},
create: function(element) {
element = $(element);
- var options = Object.extend({
+ var options = Object.extend({
element: element,
tag: 'li', // assumes li children, override with tag: 'tagname'
dropOnEmpty: false,
@@ -635,17 +637,17 @@ var Sortable = {
delay: 0,
hoverclass: null,
ghosting: false,
- quiet: false,
+ quiet: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
format: this.SERIALIZE_RULE,
-
- // these take arrays of elements or ids and can be
+
+ // these take arrays of elements or ids and can be
// used for better initialization performance
elements: false,
handles: false,
-
+
onChange: Prototype.emptyFunction,
onUpdate: Prototype.emptyFunction
}, arguments[1] || { });
@@ -682,24 +684,24 @@ var Sortable = {
if(options.zindex)
options_for_draggable.zindex = options.zindex;
- // build options for the droppables
+ // build options for the droppables
var options_for_droppable = {
overlap: options.overlap,
containment: options.containment,
tree: options.tree,
hoverclass: options.hoverclass,
onHover: Sortable.onHover
- }
-
+ };
+
var options_for_tree = {
onHover: Sortable.onEmptyHover,
overlap: options.overlap,
containment: options.containment,
hoverclass: options.hoverclass
- }
+ };
// fix for gecko engine
- Element.cleanWhitespace(element);
+ Element.cleanWhitespace(element);
options.draggables = [];
options.droppables = [];
@@ -712,14 +714,14 @@ var Sortable = {
(options.elements || this.findElements(element, options) || []).each( function(e,i) {
var handle = options.handles ? $(options.handles[i]) :
- (options.handle ? $(e).select('.' + options.handle)[0] : e);
+ (options.handle ? $(e).select('.' + options.handle)[0] : e);
options.draggables.push(
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
Droppables.add(e, options_for_droppable);
if(options.tree) e.treeNode = element;
- options.droppables.push(e);
+ options.droppables.push(e);
});
-
+
if(options.tree) {
(Sortable.findTreeElements(element, options) || []).each( function(e) {
Droppables.add(e, options_for_tree);
@@ -729,7 +731,7 @@ var Sortable = {
}
// keep reference
- this.sortables[element.id] = options;
+ this.sortables[element.identify()] = options;
// for onupdate
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
@@ -741,7 +743,7 @@ var Sortable = {
return Element.findChildren(
element, options.only, options.tree ? true : false, options.tag);
},
-
+
findTreeElements: function(element, options) {
return Element.findChildren(
element, options.only, options.tree ? true : false, options.treeTag);
@@ -758,7 +760,7 @@ var Sortable = {
var oldParentNode = element.parentNode;
element.style.visibility = "hidden"; // fix gecko rendering
dropon.parentNode.insertBefore(element, dropon);
- if(dropon.parentNode!=oldParentNode)
+ if(dropon.parentNode!=oldParentNode)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
@@ -769,26 +771,26 @@ var Sortable = {
var oldParentNode = element.parentNode;
element.style.visibility = "hidden"; // fix gecko rendering
dropon.parentNode.insertBefore(element, nextElement);
- if(dropon.parentNode!=oldParentNode)
+ if(dropon.parentNode!=oldParentNode)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
}
},
-
+
onEmptyHover: function(element, dropon, overlap) {
var oldParentNode = element.parentNode;
var droponOptions = Sortable.options(dropon);
-
+
if(!Element.isParent(dropon, element)) {
var index;
-
+
var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
var child = null;
-
+
if(children) {
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
-
+
for (index = 0; index < children.length; index += 1) {
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
offset -= Element.offsetSize (children[index], droponOptions.overlap);
@@ -801,9 +803,9 @@ var Sortable = {
}
}
}
-
+
dropon.insertBefore(element, child);
-
+
Sortable.options(oldParentNode).onChange(element);
droponOptions.onChange(element);
}
@@ -816,34 +818,34 @@ var Sortable = {
mark: function(dropon, position) {
// mark on ghosting only
var sortable = Sortable.options(dropon.parentNode);
- if(sortable && !sortable.ghosting) return;
+ if(sortable && !sortable.ghosting) return;
if(!Sortable._marker) {
- Sortable._marker =
+ Sortable._marker =
($('dropmarker') || Element.extend(document.createElement('DIV'))).
hide().addClassName('dropmarker').setStyle({position:'absolute'});
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
- }
- var offsets = Position.cumulativeOffset(dropon);
+ }
+ var offsets = dropon.cumulativeOffset();
Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
-
+
if(position=='after')
- if(sortable.overlap == 'horizontal')
+ if(sortable.overlap == 'horizontal')
Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
else
Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
-
+
Sortable._marker.show();
},
-
+
_tree: function(element, options, parent) {
var children = Sortable.findElements(element, options) || [];
-
+
for (var i = 0; i < children.length; ++i) {
var match = children[i].id.match(options.format);
if (!match) continue;
-
+
var child = {
id: encodeURIComponent(match ? match[1] : null),
element: element,
@@ -851,16 +853,16 @@ var Sortable = {
children: [],
position: parent.children.length,
container: $(children[i]).down(options.treeTag)
- }
-
+ };
+
/* Get the element containing the children and recurse over it */
if (child.container)
- this._tree(child.container, options, child)
-
+ this._tree(child.container, options, child);
+
parent.children.push (child);
}
- return parent;
+ return parent;
},
tree: function(element) {
@@ -873,15 +875,15 @@ var Sortable = {
name: element.id,
format: sortableOptions.format
}, arguments[1] || { });
-
+
var root = {
id: null,
parent: null,
children: [],
container: element,
position: 0
- }
-
+ };
+
return Sortable._tree(element, options, root);
},
@@ -897,7 +899,7 @@ var Sortable = {
sequence: function(element) {
element = $(element);
var options = Object.extend(this.options(element), arguments[1] || { });
-
+
return $(this.findElements(element, options) || []).map( function(item) {
return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
});
@@ -906,14 +908,14 @@ var Sortable = {
setSequence: function(element, new_sequence) {
element = $(element);
var options = Object.extend(this.options(element), arguments[2] || { });
-
+
var nodeMap = { };
this.findElements(element, options).each( function(n) {
if (n.id.match(options.format))
nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
n.parentNode.removeChild(n);
});
-
+
new_sequence.each(function(ident) {
var n = nodeMap[ident];
if (n) {
@@ -922,16 +924,16 @@ var Sortable = {
}
});
},
-
+
serialize: function(element) {
element = $(element);
var options = Object.extend(Sortable.options(element), arguments[1] || { });
var name = encodeURIComponent(
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
-
+
if (options.tree) {
return Sortable.tree(element, arguments[1]).children.map( function (item) {
- return [name + Sortable._constructIndex(item) + "[id]=" +
+ return [name + Sortable._constructIndex(item) + "[id]=" +
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
}).flatten().join('&');
} else {
@@ -940,16 +942,16 @@ var Sortable = {
}).join('&');
}
}
-}
+};
// Returns true if child is contained within element
Element.isParent = function(child, element) {
if (!child.parentNode || child == element) return false;
if (child.parentNode == element) return true;
return Element.isParent(child.parentNode, element);
-}
+};
-Element.findChildren = function(element, only, recursive, tagName) {
+Element.findChildren = function(element, only, recursive, tagName) {
if(!element.hasChildNodes()) return null;
tagName = tagName.toUpperCase();
if(only) only = [only].flatten();
@@ -965,8 +967,8 @@ Element.findChildren = function(element, only, recursive, tagName) {
});
return (elements.length>0 ? elements.flatten() : []);
-}
+};
Element.offsetSize = function (element, type) {
return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
-}
+}; \ No newline at end of file
diff --git a/public/javascripts/effects.js b/public/javascripts/effects.js
index f030b5dbe..c81e6c7d5 100644
--- a/public/javascripts/effects.js
+++ b/public/javascripts/effects.js
@@ -1,48 +1,50 @@
-// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// script.aculo.us effects.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009
+
+// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
// Justin Palmer (http://encytemedia.com/)
// Mark Pilgrim (http://diveintomark.org/)
// Martin Bialasinki
-//
+//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
-// For details, see the script.aculo.us web site: http://script.aculo.us/
+// For details, see the script.aculo.us web site: http://script.aculo.us/
-// converts rgb() and #xxx to #xxxxxx format,
-// returns self (or first argument) if not convertable
-String.prototype.parseColor = function() {
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
var color = '#';
- if (this.slice(0,4) == 'rgb(') {
- var cols = this.slice(4,this.length-1).split(',');
- var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
- } else {
- if (this.slice(0,1) == '#') {
- if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
- if (this.length==7) color = this.toLowerCase();
- }
- }
- return (color.length==7 ? color : (arguments[0] || this));
+ if (this.slice(0,4) == 'rgb(') {
+ var cols = this.slice(4,this.length-1).split(',');
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+ } else {
+ if (this.slice(0,1) == '#') {
+ if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+ if (this.length==7) color = this.toLowerCase();
+ }
+ }
+ return (color.length==7 ? color : (arguments[0] || this));
};
/*--------------------------------------------------------------------------*/
-Element.collectTextNodes = function(element) {
+Element.collectTextNodes = function(element) {
return $A($(element).childNodes).collect( function(node) {
- return (node.nodeType==3 ? node.nodeValue :
+ return (node.nodeType==3 ? node.nodeValue :
(node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
}).flatten().join('');
};
-Element.collectTextNodesIgnoreClass = function(element, className) {
+Element.collectTextNodesIgnoreClass = function(element, className) {
return $A($(element).childNodes).collect( function(node) {
- return (node.nodeType==3 ? node.nodeValue :
- ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
+ return (node.nodeType==3 ? node.nodeValue :
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
Element.collectTextNodesIgnoreClass(node, className) : ''));
}).flatten().join('');
};
Element.setContentZoom = function(element, percent) {
- element = $(element);
- element.setStyle({fontSize: (percent/100) + 'em'});
+ element = $(element);
+ element.setStyle({fontSize: (percent/100) + 'em'});
if (Prototype.Browser.WebKit) window.scrollBy(0,0);
return element;
};
@@ -70,28 +72,23 @@ var Effect = {
Transitions: {
linear: Prototype.K,
sinoidal: function(pos) {
- return (-Math.cos(pos*Math.PI)/2) + 0.5;
+ return (-Math.cos(pos*Math.PI)/2) + .5;
},
reverse: function(pos) {
return 1-pos;
},
flicker: function(pos) {
- var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+ var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
return pos > 1 ? 1 : pos;
},
wobble: function(pos) {
- return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
},
- pulse: function(pos, pulses) {
- pulses = pulses || 5;
- return (
- ((pos % (1/pulses)) * pulses).round() == 0 ?
- ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
- 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
- );
+ pulse: function(pos, pulses) {
+ return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
},
- spring: function(pos) {
- return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
+ spring: function(pos) {
+ return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
},
none: function(pos) {
return 0;
@@ -112,14 +109,14 @@ var Effect = {
tagifyText: function(element) {
var tagifyStyle = 'position:relative';
if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
-
+
element = $(element);
$A(element.childNodes).each( function(child) {
if (child.nodeType==3) {
child.nodeValue.toArray().each( function(character) {
element.insertBefore(
new Element('span', {style: tagifyStyle}).update(
- character == ' ' ? String.fromCharCode(160) : character),
+ character == ' ' ? String.fromCharCode(160) : character),
child);
});
Element.remove(child);
@@ -128,13 +125,13 @@ var Effect = {
},
multiple: function(element, effect) {
var elements;
- if (((typeof element == 'object') ||
- Object.isFunction(element)) &&
+ if (((typeof element == 'object') ||
+ Object.isFunction(element)) &&
(element.length))
elements = element;
else
elements = $(element).childNodes;
-
+
var options = Object.extend({
speed: 0.1,
delay: 0.0
@@ -150,14 +147,13 @@ var Effect = {
'blind': ['BlindDown','BlindUp'],
'appear': ['Appear','Fade']
},
- toggle: function(element, effect) {
+ toggle: function(element, effect, options) {
element = $(element);
- effect = (effect || 'appear').toLowerCase();
- var options = Object.extend({
+ effect = (effect || 'appear').toLowerCase();
+
+ return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({
queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
- }, arguments[2] || { });
- Effect[element.visible() ?
- Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+ }, options || {}));
}
};
@@ -168,20 +164,20 @@ Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
Effect.ScopedQueue = Class.create(Enumerable, {
initialize: function() {
this.effects = [];
- this.interval = null;
+ this.interval = null;
},
_each: function(iterator) {
this.effects._each(iterator);
},
add: function(effect) {
var timestamp = new Date().getTime();
-
- var position = Object.isString(effect.options.queue) ?
+
+ var position = Object.isString(effect.options.queue) ?
effect.options.queue : effect.options.queue.position;
-
+
switch(position) {
case 'front':
- // move unstarted effects after this effect
+ // move unstarted effects after this effect
this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
e.startOn += effect.finishOn;
e.finishOn += effect.finishOn;
@@ -195,13 +191,13 @@ Effect.ScopedQueue = Class.create(Enumerable, {
timestamp = this.effects.pluck('finishOn').max() || timestamp;
break;
}
-
+
effect.startOn += timestamp;
effect.finishOn += timestamp;
if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
this.effects.push(effect);
-
+
if (!this.interval)
this.interval = setInterval(this.loop.bind(this), 15);
},
@@ -214,7 +210,7 @@ Effect.ScopedQueue = Class.create(Enumerable, {
},
loop: function() {
var timePos = new Date().getTime();
- for(var i=0, len=this.effects.length;i<len;i++)
+ for(var i=0, len=this.effects.length;i<len;i++)
this.effects[i] && this.effects[i].loop(timePos);
}
});
@@ -223,7 +219,7 @@ Effect.Queues = {
instances: $H(),
get: function(queueName) {
if (!Object.isString(queueName)) return queueName;
-
+
return this.instances.get(queueName) ||
this.instances.set(queueName, new Effect.ScopedQueue());
}
@@ -233,12 +229,6 @@ Effect.Queue = Effect.Queues.get('global');
Effect.Base = Class.create({
position: null,
start: function(options) {
- function codeForEvent(options,eventName){
- return (
- (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
- (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
- );
- }
if (options && options.transition === false) options.transition = Effect.Transitions.linear;
this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
this.currentFrame = 0;
@@ -248,23 +238,35 @@ Effect.Base = Class.create({
this.fromToDelta = this.options.to-this.options.from;
this.totalTime = this.finishOn-this.startOn;
this.totalFrames = this.options.fps*this.options.duration;
-
- eval('this.render = function(pos){ '+
- 'if (this.state=="idle"){this.state="running";'+
- codeForEvent(this.options,'beforeSetup')+
- (this.setup ? 'this.setup();':'')+
- codeForEvent(this.options,'afterSetup')+
- '};if (this.state=="running"){'+
- 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
- 'this.position=pos;'+
- codeForEvent(this.options,'beforeUpdate')+
- (this.update ? 'this.update(pos);':'')+
- codeForEvent(this.options,'afterUpdate')+
- '}}');
-
+
+ this.render = (function() {
+ function dispatch(effect, eventName) {
+ if (effect.options[eventName + 'Internal'])
+ effect.options[eventName + 'Internal'](effect);
+ if (effect.options[eventName])
+ effect.options[eventName](effect);
+ }
+
+ return function(pos) {
+ if (this.state === "idle") {
+ this.state = "running";
+ dispatch(this, 'beforeSetup');
+ if (this.setup) this.setup();
+ dispatch(this, 'afterSetup');
+ }
+ if (this.state === "running") {
+ pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
+ this.position = pos;
+ dispatch(this, 'beforeUpdate');
+ if (this.update) this.update(pos);
+ dispatch(this, 'afterUpdate');
+ }
+ };
+ })();
+
this.event('beforeStart');
if (!this.options.sync)
- Effect.Queues.get(Object.isString(this.options.queue) ?
+ Effect.Queues.get(Object.isString(this.options.queue) ?
'global' : this.options.queue.scope).add(this);
},
loop: function(timePos) {
@@ -273,9 +275,9 @@ Effect.Base = Class.create({
this.render(1.0);
this.cancel();
this.event('beforeFinish');
- if (this.finish) this.finish();
+ if (this.finish) this.finish();
this.event('afterFinish');
- return;
+ return;
}
var pos = (timePos - this.startOn) / this.totalTime,
frame = (pos * this.totalFrames).round();
@@ -287,7 +289,7 @@ Effect.Base = Class.create({
},
cancel: function() {
if (!this.options.sync)
- Effect.Queues.get(Object.isString(this.options.queue) ?
+ Effect.Queues.get(Object.isString(this.options.queue) ?
'global' : this.options.queue.scope).remove(this);
this.state = 'finished';
},
@@ -325,10 +327,10 @@ Effect.Parallel = Class.create(Effect.Base, {
Effect.Tween = Class.create(Effect.Base, {
initialize: function(object, from, to) {
object = Object.isString(object) ? $(object) : object;
- var args = $A(arguments), method = args.last(),
+ var args = $A(arguments), method = args.last(),
options = args.length == 5 ? args[3] : null;
this.method = Object.isFunction(method) ? method.bind(object) :
- Object.isFunction(object[method]) ? object[method].bind(object) :
+ Object.isFunction(object[method]) ? object[method].bind(object) :
function(value) { object[method] = value };
this.start(Object.extend({ from: from, to: to }, options || { }));
},
@@ -392,7 +394,7 @@ Effect.Move = Class.create(Effect.Base, {
// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
- return new Effect.Move(element,
+ return new Effect.Move(element,
Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
};
@@ -414,15 +416,15 @@ Effect.Scale = Class.create(Effect.Base, {
setup: function() {
this.restoreAfterFinish = this.options.restoreAfterFinish || false;
this.elementPositioning = this.element.getStyle('position');
-
+
this.originalStyle = { };
['top','left','width','height','fontSize'].each( function(k) {
this.originalStyle[k] = this.element.style[k];
}.bind(this));
-
+
this.originalTop = this.element.offsetTop;
this.originalLeft = this.element.offsetLeft;
-
+
var fontSize = this.element.getStyle('font-size') || '100%';
['em','px','%','pt'].each( function(fontSizeType) {
if (fontSize.indexOf(fontSizeType)>0) {
@@ -430,9 +432,9 @@ Effect.Scale = Class.create(Effect.Base, {
this.fontSizeType = fontSizeType;
}
}.bind(this));
-
+
this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
-
+
this.dims = null;
if (this.options.scaleMode=='box')
this.dims = [this.element.offsetHeight, this.element.offsetWidth];
@@ -507,17 +509,16 @@ Effect.Highlight = Class.create(Effect.Base, {
Effect.ScrollTo = function(element) {
var options = arguments[1] || { },
- scrollOffsets = document.viewport.getScrollOffsets(),
- elementOffsets = $(element).cumulativeOffset(),
- max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
+ scrollOffsets = document.viewport.getScrollOffsets(),
+ elementOffsets = $(element).cumulativeOffset();
if (options.offset) elementOffsets[1] += options.offset;
return new Effect.Tween(null,
scrollOffsets.top,
- elementOffsets[1] > max ? max : elementOffsets[1],
+ elementOffsets[1],
options,
- function(p){ scrollTo(scrollOffsets.left, p.round()) }
+ function(p){ scrollTo(scrollOffsets.left, p.round()); }
);
};
@@ -529,9 +530,9 @@ Effect.Fade = function(element) {
var options = Object.extend({
from: element.getOpacity() || 1.0,
to: 0.0,
- afterFinishInternal: function(effect) {
+ afterFinishInternal: function(effect) {
if (effect.options.to!=0) return;
- effect.element.hide().setStyle({opacity: oldOpacity});
+ effect.element.hide().setStyle({opacity: oldOpacity});
}
}, arguments[1] || { });
return new Effect.Opacity(element,options);
@@ -547,15 +548,15 @@ Effect.Appear = function(element) {
effect.element.forceRerendering();
},
beforeSetup: function(effect) {
- effect.element.setOpacity(effect.options.from).show();
+ effect.element.setOpacity(effect.options.from).show();
}}, arguments[1] || { });
return new Effect.Opacity(element,options);
};
Effect.Puff = function(element) {
element = $(element);
- var oldStyle = {
- opacity: element.getInlineOpacity(),
+ var oldStyle = {
+ opacity: element.getInlineOpacity(),
position: element.getStyle('position'),
top: element.style.top,
left: element.style.left,
@@ -563,12 +564,12 @@ Effect.Puff = function(element) {
height: element.style.height
};
return new Effect.Parallel(
- [ new Effect.Scale(element, 200,
- { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
- new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
- Object.extend({ duration: 1.0,
+ [ new Effect.Scale(element, 200,
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+ Object.extend({ duration: 1.0,
beforeSetupInternal: function(effect) {
- Position.absolutize(effect.effects[0].element)
+ Position.absolutize(effect.effects[0].element);
},
afterFinishInternal: function(effect) {
effect.effects[0].element.hide().setStyle(oldStyle); }
@@ -580,12 +581,12 @@ Effect.BlindUp = function(element) {
element = $(element);
element.makeClipping();
return new Effect.Scale(element, 0,
- Object.extend({ scaleContent: false,
- scaleX: false,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
restoreAfterFinish: true,
afterFinishInternal: function(effect) {
effect.element.hide().undoClipping();
- }
+ }
}, arguments[1] || { })
);
};
@@ -593,15 +594,15 @@ Effect.BlindUp = function(element) {
Effect.BlindDown = function(element) {
element = $(element);
var elementDimensions = element.getDimensions();
- return new Effect.Scale(element, 100, Object.extend({
- scaleContent: false,
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
scaleX: false,
scaleFrom: 0,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
- effect.element.makeClipping().setStyle({height: '0px'}).show();
- },
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
afterFinishInternal: function(effect) {
effect.element.undoClipping();
}
@@ -616,16 +617,16 @@ Effect.SwitchOff = function(element) {
from: 0,
transition: Effect.Transitions.flicker,
afterFinishInternal: function(effect) {
- new Effect.Scale(effect.element, 1, {
+ new Effect.Scale(effect.element, 1, {
duration: 0.3, scaleFromCenter: true,
scaleX: false, scaleContent: false, restoreAfterFinish: true,
- beforeSetup: function(effect) {
+ beforeSetup: function(effect) {
effect.element.makePositioned().makeClipping();
},
afterFinishInternal: function(effect) {
effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
}
- })
+ });
}
}, arguments[1] || { }));
};
@@ -637,16 +638,16 @@ Effect.DropOut = function(element) {
left: element.getStyle('left'),
opacity: element.getInlineOpacity() };
return new Effect.Parallel(
- [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
Object.extend(
{ duration: 0.5,
beforeSetup: function(effect) {
- effect.effects[0].element.makePositioned();
+ effect.effects[0].element.makePositioned();
},
afterFinishInternal: function(effect) {
effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
- }
+ }
}, arguments[1] || { }));
};
@@ -674,7 +675,7 @@ Effect.Shake = function(element) {
new Effect.Move(effect.element,
{ x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
effect.element.undoPositioned().setStyle(oldStyle);
- }}) }}) }}) }}) }}) }});
+ }}); }}); }}); }}); }}); }});
};
Effect.SlideDown = function(element) {
@@ -682,9 +683,9 @@ Effect.SlideDown = function(element) {
// SlideDown need to have the content of the element wrapped in a container element with fixed height!
var oldInnerBottom = element.down().getStyle('bottom');
var elementDimensions = element.getDimensions();
- return new Effect.Scale(element, 100, Object.extend({
- scaleContent: false,
- scaleX: false,
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
scaleFrom: window.opera ? 0 : 1,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
@@ -692,11 +693,11 @@ Effect.SlideDown = function(element) {
effect.element.makePositioned();
effect.element.down().makePositioned();
if (window.opera) effect.element.setStyle({top: ''});
- effect.element.makeClipping().setStyle({height: '0px'}).show();
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
},
afterUpdateInternal: function(effect) {
effect.element.down().setStyle({bottom:
- (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
},
afterFinishInternal: function(effect) {
effect.element.undoClipping().undoPositioned();
@@ -710,8 +711,8 @@ Effect.SlideUp = function(element) {
var oldInnerBottom = element.down().getStyle('bottom');
var elementDimensions = element.getDimensions();
return new Effect.Scale(element, window.opera ? 0 : 1,
- Object.extend({ scaleContent: false,
- scaleX: false,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
scaleMode: 'box',
scaleFrom: 100,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
@@ -721,7 +722,7 @@ Effect.SlideUp = function(element) {
effect.element.down().makePositioned();
if (window.opera) effect.element.setStyle({top: ''});
effect.element.makeClipping().show();
- },
+ },
afterUpdateInternal: function(effect) {
effect.element.down().setStyle({bottom:
(effect.dims[0] - effect.element.clientHeight) + 'px' });
@@ -734,15 +735,15 @@ Effect.SlideUp = function(element) {
);
};
-// Bug in opera makes the TD containing this element expand for a instance after finish
+// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
- return new Effect.Scale(element, window.opera ? 1 : 0, {
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
restoreAfterFinish: true,
beforeSetup: function(effect) {
- effect.element.makeClipping();
- },
+ effect.element.makeClipping();
+ },
afterFinishInternal: function(effect) {
- effect.element.hide().undoClipping();
+ effect.element.hide().undoClipping();
}
});
};
@@ -762,13 +763,13 @@ Effect.Grow = function(element) {
width: element.style.width,
opacity: element.getInlineOpacity() };
- var dims = element.getDimensions();
+ var dims = element.getDimensions();
var initialMoveX, initialMoveY;
var moveX, moveY;
-
+
switch (options.direction) {
case 'top-left':
- initialMoveX = initialMoveY = moveX = moveY = 0;
+ initialMoveX = initialMoveY = moveX = moveY = 0;
break;
case 'top-right':
initialMoveX = dims.width;
@@ -793,11 +794,11 @@ Effect.Grow = function(element) {
moveY = -dims.height / 2;
break;
}
-
+
return new Effect.Move(element, {
x: initialMoveX,
y: initialMoveY,
- duration: 0.01,
+ duration: 0.01,
beforeSetup: function(effect) {
effect.element.hide().makeClipping().makePositioned();
},
@@ -806,17 +807,17 @@ Effect.Grow = function(element) {
[ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
new Effect.Scale(effect.element, 100, {
- scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
], Object.extend({
beforeSetup: function(effect) {
- effect.effects[0].element.setStyle({height: '0px'}).show();
+ effect.effects[0].element.setStyle({height: '0px'}).show();
},
afterFinishInternal: function(effect) {
- effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
}
}, options)
- )
+ );
}
});
};
@@ -838,7 +839,7 @@ Effect.Shrink = function(element) {
var dims = element.getDimensions();
var moveX, moveY;
-
+
switch (options.direction) {
case 'top-left':
moveX = moveY = 0;
@@ -855,19 +856,19 @@ Effect.Shrink = function(element) {
moveX = dims.width;
moveY = dims.height;
break;
- case 'center':
+ case 'center':
moveX = dims.width / 2;
moveY = dims.height / 2;
break;
}
-
+
return new Effect.Parallel(
[ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
- ], Object.extend({
+ ], Object.extend({
beforeStartInternal: function(effect) {
- effect.effects[0].element.makePositioned().makeClipping();
+ effect.effects[0].element.makePositioned().makeClipping();
},
afterFinishInternal: function(effect) {
effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
@@ -877,12 +878,14 @@ Effect.Shrink = function(element) {
Effect.Pulsate = function(element) {
element = $(element);
- var options = arguments[1] || { };
- var oldOpacity = element.getInlineOpacity();
- var transition = options.transition || Effect.Transitions.sinoidal;
- var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
- reverser.bind(transition);
- return new Effect.Opacity(element,
+ var options = arguments[1] || { },
+ oldOpacity = element.getInlineOpacity(),
+ transition = options.transition || Effect.Transitions.linear,
+ reverser = function(pos){
+ return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
+ };
+
+ return new Effect.Opacity(element,
Object.extend(Object.extend({ duration: 2.0, from: 0,
afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
}, options), {transition: reverser}));
@@ -896,12 +899,12 @@ Effect.Fold = function(element) {
width: element.style.width,
height: element.style.height };
element.makeClipping();
- return new Effect.Scale(element, 5, Object.extend({
+ return new Effect.Scale(element, 5, Object.extend({
scaleContent: false,
scaleX: false,
afterFinishInternal: function(effect) {
- new Effect.Scale(element, 1, {
- scaleContent: false,
+ new Effect.Scale(element, 1, {
+ scaleContent: false,
scaleY: false,
afterFinishInternal: function(effect) {
effect.element.hide().undoClipping().setStyle(oldStyle);
@@ -916,7 +919,7 @@ Effect.Morph = Class.create(Effect.Base, {
var options = Object.extend({
style: { }
}, arguments[1] || { });
-
+
if (!Object.isString(options.style)) this.style = $H(options.style);
else {
if (options.style.include(':'))
@@ -934,18 +937,18 @@ Effect.Morph = Class.create(Effect.Base, {
effect.transforms.each(function(transform) {
effect.element.style[transform.style] = '';
});
- }
+ };
}
}
this.start(options);
},
-
+
setup: function(){
function parseColor(color){
if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
color = color.parseColor();
return $R(0,2).map(function(i){
- return parseInt( color.slice(i*2+1,i*2+3), 16 )
+ return parseInt( color.slice(i*2+1,i*2+3), 16 );
});
}
this.transforms = this.style.map(function(pair){
@@ -965,9 +968,9 @@ Effect.Morph = Class.create(Effect.Base, {
}
var originalValue = this.element.getStyle(property);
- return {
- style: property.camelize(),
- originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
+ return {
+ style: property.camelize(),
+ originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
targetValue: unit=='color' ? parseColor(value) : value,
unit: unit
};
@@ -978,13 +981,13 @@ Effect.Morph = Class.create(Effect.Base, {
transform.unit != 'color' &&
(isNaN(transform.originalValue) || isNaN(transform.targetValue))
)
- )
+ );
});
},
update: function(position) {
var style = { }, transform, i = this.transforms.length;
while(i--)
- style[(transform = this.transforms[i]).style] =
+ style[(transform = this.transforms[i]).style] =
transform.unit=='color' ? '#'+
(Math.round(transform.originalValue[0]+
(transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
@@ -993,7 +996,7 @@ Effect.Morph = Class.create(Effect.Base, {
(Math.round(transform.originalValue[2]+
(transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
(transform.originalValue +
- (transform.targetValue - transform.originalValue) * position).toFixed(3) +
+ (transform.targetValue - transform.originalValue) * position).toFixed(3) +
(transform.unit === null ? '' : transform.unit);
this.element.setStyle(style, true);
}
@@ -1030,7 +1033,7 @@ Effect.Transform = Class.create({
});
Element.CSS_PROPERTIES = $w(
- 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
+ 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
@@ -1039,7 +1042,7 @@ Element.CSS_PROPERTIES = $w(
'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
'right textIndent top width wordSpacing zIndex');
-
+
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
String.__parseStyleElement = document.createElement('div');
@@ -1051,11 +1054,11 @@ String.prototype.parseStyle = function(){
String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
style = String.__parseStyleElement.childNodes[0].style;
}
-
+
Element.CSS_PROPERTIES.each(function(property){
- if (style[property]) styleRules.set(property, style[property]);
+ if (style[property]) styleRules.set(property, style[property]);
});
-
+
if (Prototype.Browser.IE && this.include('opacity'))
styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
@@ -1074,14 +1077,14 @@ if (document.defaultView && document.defaultView.getComputedStyle) {
Element.getStyles = function(element) {
element = $(element);
var css = element.currentStyle, styles;
- styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
- hash.set(property, css[property]);
- return hash;
+ styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
+ results[property] = css[property];
+ return results;
});
- if (!styles.opacity) styles.set('opacity', element.getOpacity());
+ if (!styles.opacity) styles.opacity = element.getOpacity();
return styles;
};
-};
+}
Effect.Methods = {
morph: function(element, style) {
@@ -1090,7 +1093,7 @@ Effect.Methods = {
return element;
},
visualEffect: function(element, effect, options) {
- element = $(element)
+ element = $(element);
var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
new Effect[klass](element, options);
return element;
@@ -1104,17 +1107,17 @@ Effect.Methods = {
$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
'pulsate shake puff squish switchOff dropOut').each(
- function(effect) {
+ function(effect) {
Effect.Methods[effect] = function(element, options){
element = $(element);
Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
return element;
- }
+ };
}
);
-$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
function(f) { Effect.Methods[f] = Element[f]; }
);
-Element.addMethods(Effect.Methods);
+Element.addMethods(Effect.Methods); \ No newline at end of file
diff --git a/public/javascripts/prototype.js b/public/javascripts/prototype.js
index 546f9fe44..06249a6ae 100644
--- a/public/javascripts/prototype.js
+++ b/public/javascripts/prototype.js
@@ -1,5 +1,5 @@
-/* Prototype JavaScript framework, version 1.6.0.1
- * (c) 2005-2007 Sam Stephenson
+/* Prototype JavaScript framework, version 1.7_rc2
+ * (c) 2005-2010 Sam Stephenson
*
* Prototype is freely distributable under the terms of an MIT-style license.
* For details, see the Prototype web site: http://www.prototypejs.org/
@@ -7,29 +7,53 @@
*--------------------------------------------------------------------------*/
var Prototype = {
- Version: '1.6.0.1',
- Browser: {
- IE: !!(window.attachEvent && !window.opera),
- Opera: !!window.opera,
- WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
- Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
- MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
- },
+ Version: '1.7_rc2',
+
+ Browser: (function(){
+ var ua = navigator.userAgent;
+ var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
+ return {
+ IE: !!window.attachEvent && !isOpera,
+ Opera: isOpera,
+ WebKit: ua.indexOf('AppleWebKit/') > -1,
+ Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
+ MobileSafari: /Apple.*Mobile/.test(ua)
+ }
+ })(),
BrowserFeatures: {
XPath: !!document.evaluate,
- ElementExtensions: !!window.HTMLElement,
- SpecificElementExtensions:
- document.createElement('div').__proto__ &&
- document.createElement('div').__proto__ !==
- document.createElement('form').__proto__
+
+ SelectorsAPI: !!document.querySelector,
+
+ ElementExtensions: (function() {
+ var constructor = window.Element || window.HTMLElement;
+ return !!(constructor && constructor.prototype);
+ })(),
+ SpecificElementExtensions: (function() {
+ if (typeof window.HTMLDivElement !== 'undefined')
+ return true;
+
+ var div = document.createElement('div'),
+ form = document.createElement('form'),
+ isSupported = false;
+
+ if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
+ isSupported = true;
+ }
+
+ div = form = null;
+
+ return isSupported;
+ })()
},
ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
emptyFunction: function() { },
+
K: function(x) { return x }
};
@@ -37,9 +61,38 @@ if (Prototype.Browser.MobileSafari)
Prototype.BrowserFeatures.SpecificElementExtensions = false;
+var Abstract = { };
+
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) { }
+ }
+
+ return returnValue;
+ }
+};
+
/* Based on Alex Arnell's inheritance implementation. */
-var Class = {
- create: function() {
+
+var Class = (function() {
+
+ var IS_DONTENUM_BUGGY = (function(){
+ for (var p in { toString: 1 }) {
+ if (p === 'toString') return false;
+ }
+ return true;
+ })();
+
+ function subclass() {};
+ function create() {
var parent = null, properties = $A(arguments);
if (Object.isFunction(properties[0]))
parent = properties.shift();
@@ -53,235 +106,374 @@ var Class = {
klass.subclasses = [];
if (parent) {
- var subclass = function() { };
subclass.prototype = parent.prototype;
klass.prototype = new subclass;
parent.subclasses.push(klass);
}
- for (var i = 0; i < properties.length; i++)
+ for (var i = 0, length = properties.length; i < length; i++)
klass.addMethods(properties[i]);
if (!klass.prototype.initialize)
klass.prototype.initialize = Prototype.emptyFunction;
klass.prototype.constructor = klass;
-
return klass;
}
-};
-Class.Methods = {
- addMethods: function(source) {
- var ancestor = this.superclass && this.superclass.prototype;
- var properties = Object.keys(source);
+ function addMethods(source) {
+ var ancestor = this.superclass && this.superclass.prototype,
+ properties = Object.keys(source);
- if (!Object.keys({ toString: true }).length)
- properties.push("toString", "valueOf");
+ if (IS_DONTENUM_BUGGY) {
+ if (source.toString != Object.prototype.toString)
+ properties.push("toString");
+ if (source.valueOf != Object.prototype.valueOf)
+ properties.push("valueOf");
+ }
for (var i = 0, length = properties.length; i < length; i++) {
var property = properties[i], value = source[property];
if (ancestor && Object.isFunction(value) &&
- value.argumentNames().first() == "$super") {
- var method = value, value = Object.extend((function(m) {
- return function() { return ancestor[m].apply(this, arguments) };
- })(property).wrap(method), {
- valueOf: function() { return method },
- toString: function() { return method.toString() }
- });
+ value.argumentNames()[0] == "$super") {
+ var method = value;
+ value = (function(m) {
+ return function() { return ancestor[m].apply(this, arguments); };
+ })(property).wrap(method);
+
+ value.valueOf = method.valueOf.bind(method);
+ value.toString = method.toString.bind(method);
}
this.prototype[property] = value;
}
return this;
}
-};
-var Abstract = { };
+ return {
+ create: create,
+ Methods: {
+ addMethods: addMethods
+ }
+ };
+})();
+(function() {
-Object.extend = function(destination, source) {
- for (var property in source)
- destination[property] = source[property];
- return destination;
-};
+ var _toString = Object.prototype.toString,
+ NULL_TYPE = 'Null',
+ UNDEFINED_TYPE = 'Undefined',
+ BOOLEAN_TYPE = 'Boolean',
+ NUMBER_TYPE = 'Number',
+ STRING_TYPE = 'String',
+ OBJECT_TYPE = 'Object',
+ BOOLEAN_CLASS = '[object Boolean]',
+ NUMBER_CLASS = '[object Number]',
+ STRING_CLASS = '[object String]',
+ ARRAY_CLASS = '[object Array]',
+ NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
+ typeof JSON.stringify === 'function' &&
+ JSON.stringify(0) === '0' &&
+ typeof JSON.stringify(Prototype.K) === 'undefined';
+
+ function Type(o) {
+ switch(o) {
+ case null: return NULL_TYPE;
+ case (void 0): return UNDEFINED_TYPE;
+ }
+ var type = typeof o;
+ switch(type) {
+ case 'boolean': return BOOLEAN_TYPE;
+ case 'number': return NUMBER_TYPE;
+ case 'string': return STRING_TYPE;
+ }
+ return OBJECT_TYPE;
+ }
-Object.extend(Object, {
- inspect: function(object) {
+ function extend(destination, source) {
+ for (var property in source)
+ destination[property] = source[property];
+ return destination;
+ }
+
+ function inspect(object) {
try {
- if (Object.isUndefined(object)) return 'undefined';
+ if (isUndefined(object)) return 'undefined';
if (object === null) return 'null';
- return object.inspect ? object.inspect() : object.toString();
+ return object.inspect ? object.inspect() : String(object);
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
- },
+ }
- toJSON: function(object) {
- var type = typeof object;
- switch (type) {
- case 'undefined':
- case 'function':
- case 'unknown': return;
- case 'boolean': return object.toString();
+ function toJSON(value) {
+ return Str('', { '': value }, []);
+ }
+
+ function Str(key, holder, stack) {
+ var value = holder[key],
+ type = typeof value;
+
+ if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
}
- if (object === null) return 'null';
- if (object.toJSON) return object.toJSON();
- if (Object.isElement(object)) return;
+ var _class = _toString.call(value);
- var results = [];
- for (var property in object) {
- var value = Object.toJSON(object[property]);
- if (!Object.isUndefined(value))
- results.push(property.toJSON() + ': ' + value);
+ switch (_class) {
+ case NUMBER_CLASS:
+ case BOOLEAN_CLASS:
+ case STRING_CLASS:
+ value = value.valueOf();
}
- return '{' + results.join(', ') + '}';
- },
+ switch (value) {
+ case null: return 'null';
+ case true: return 'true';
+ case false: return 'false';
+ }
+
+ type = typeof value;
+ switch (type) {
+ case 'string':
+ return value.inspect(true);
+ case 'number':
+ return isFinite(value) ? String(value) : 'null';
+ case 'object':
+
+ for (var i = 0, length = stack.length; i < length; i++) {
+ if (stack[i] === value) { throw new TypeError(); }
+ }
+ stack.push(value);
- toQueryString: function(object) {
+ var partial = [];
+ if (_class === ARRAY_CLASS) {
+ for (var i = 0, length = value.length; i < length; i++) {
+ var str = Str(i, value, stack);
+ partial.push(typeof str === 'undefined' ? 'null' : str);
+ }
+ partial = '[' + partial.join(',') + ']';
+ } else {
+ var keys = Object.keys(value);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ var key = keys[i], str = Str(key, value, stack);
+ if (typeof str !== "undefined") {
+ partial.push(key.inspect(true)+ ':' + str);
+ }
+ }
+ partial = '{' + partial.join(',') + '}';
+ }
+ stack.pop();
+ return partial;
+ }
+ }
+
+ function stringify(object) {
+ return JSON.stringify(object);
+ }
+
+ function toQueryString(object) {
return $H(object).toQueryString();
- },
+ }
- toHTML: function(object) {
+ function toHTML(object) {
return object && object.toHTML ? object.toHTML() : String.interpret(object);
- },
+ }
- keys: function(object) {
- var keys = [];
- for (var property in object)
- keys.push(property);
- return keys;
- },
+ function keys(object) {
+ if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
+ var results = [];
+ for (var property in object) {
+ if (object.hasOwnProperty(property)) {
+ results.push(property);
+ }
+ }
+ return results;
+ }
- values: function(object) {
- var values = [];
+ function values(object) {
+ var results = [];
for (var property in object)
- values.push(object[property]);
- return values;
- },
+ results.push(object[property]);
+ return results;
+ }
- clone: function(object) {
- return Object.extend({ }, object);
- },
+ function clone(object) {
+ return extend({ }, object);
+ }
- isElement: function(object) {
- return object && object.nodeType == 1;
- },
+ function isElement(object) {
+ return !!(object && object.nodeType == 1);
+ }
- isArray: function(object) {
- return object && object.constructor === Array;
- },
+ function isArray(object) {
+ return _toString.call(object) === ARRAY_CLASS;
+ }
+
+ var hasNativeIsArray = (typeof Array.isArray == 'function')
+ && Array.isArray([]) && !Array.isArray({});
- isHash: function(object) {
+ if (hasNativeIsArray) {
+ isArray = Array.isArray;
+ }
+
+ function isHash(object) {
return object instanceof Hash;
- },
+ }
- isFunction: function(object) {
- return typeof object == "function";
- },
+ function isFunction(object) {
+ return typeof object === "function";
+ }
- isString: function(object) {
- return typeof object == "string";
- },
+ function isString(object) {
+ return _toString.call(object) === STRING_CLASS;
+ }
- isNumber: function(object) {
- return typeof object == "number";
- },
+ function isNumber(object) {
+ return _toString.call(object) === NUMBER_CLASS;
+ }
- isUndefined: function(object) {
- return typeof object == "undefined";
+ function isUndefined(object) {
+ return typeof object === "undefined";
}
-});
-Object.extend(Function.prototype, {
- argumentNames: function() {
- var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+ extend(Object, {
+ extend: extend,
+ inspect: inspect,
+ toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
+ toQueryString: toQueryString,
+ toHTML: toHTML,
+ keys: Object.keys || keys,
+ values: values,
+ clone: clone,
+ isElement: isElement,
+ isArray: isArray,
+ isHash: isHash,
+ isFunction: isFunction,
+ isString: isString,
+ isNumber: isNumber,
+ isUndefined: isUndefined
+ });
+})();
+Object.extend(Function.prototype, (function() {
+ var slice = Array.prototype.slice;
+
+ function update(array, args) {
+ var arrayLength = array.length, length = args.length;
+ while (length--) array[arrayLength + length] = args[length];
+ return array;
+ }
+
+ function merge(array, args) {
+ array = slice.call(array, 0);
+ return update(array, args);
+ }
+
+ function argumentNames() {
+ var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
+ .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
+ .replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
- },
+ }
- bind: function() {
+ function bind(context) {
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
- var __method = this, args = $A(arguments), object = args.shift();
+ var __method = this, args = slice.call(arguments, 1);
return function() {
- return __method.apply(object, args.concat($A(arguments)));
+ var a = merge(args, arguments);
+ return __method.apply(context, a);
}
- },
+ }
- bindAsEventListener: function() {
- var __method = this, args = $A(arguments), object = args.shift();
+ function bindAsEventListener(context) {
+ var __method = this, args = slice.call(arguments, 1);
return function(event) {
- return __method.apply(object, [event || window.event].concat(args));
+ var a = update([event || window.event], args);
+ return __method.apply(context, a);
}
- },
+ }
- curry: function() {
+ function curry() {
if (!arguments.length) return this;
- var __method = this, args = $A(arguments);
+ var __method = this, args = slice.call(arguments, 0);
return function() {
- return __method.apply(this, args.concat($A(arguments)));
+ var a = merge(args, arguments);
+ return __method.apply(this, a);
}
- },
+ }
- delay: function() {
- var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+ function delay(timeout) {
+ var __method = this, args = slice.call(arguments, 1);
+ timeout = timeout * 1000;
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
- },
+ }
+
+ function defer() {
+ var args = update([0.01], arguments);
+ return this.delay.apply(this, args);
+ }
- wrap: function(wrapper) {
+ function wrap(wrapper) {
var __method = this;
return function() {
- return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+ var a = update([__method.bind(this)], arguments);
+ return wrapper.apply(this, a);
}
- },
+ }
- methodize: function() {
+ function methodize() {
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
- return __method.apply(null, [this].concat($A(arguments)));
+ var a = update([this], arguments);
+ return __method.apply(null, a);
};
}
-});
-Function.prototype.defer = Function.prototype.delay.curry(0.01);
+ return {
+ argumentNames: argumentNames,
+ bind: bind,
+ bindAsEventListener: bindAsEventListener,
+ curry: curry,
+ delay: delay,
+ defer: defer,
+ wrap: wrap,
+ methodize: methodize
+ }
+})());
-Date.prototype.toJSON = function() {
- return '"' + this.getUTCFullYear() + '-' +
- (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
- this.getUTCDate().toPaddedString(2) + 'T' +
- this.getUTCHours().toPaddedString(2) + ':' +
- this.getUTCMinutes().toPaddedString(2) + ':' +
- this.getUTCSeconds().toPaddedString(2) + 'Z"';
-};
-var Try = {
- these: function() {
- var returnValue;
- for (var i = 0, length = arguments.length; i < length; i++) {
- var lambda = arguments[i];
- try {
- returnValue = lambda();
- break;
- } catch (e) { }
- }
+(function(proto) {
- return returnValue;
+
+ function toISOString() {
+ return this.getUTCFullYear() + '-' +
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+ this.getUTCDate().toPaddedString(2) + 'T' +
+ this.getUTCHours().toPaddedString(2) + ':' +
+ this.getUTCMinutes().toPaddedString(2) + ':' +
+ this.getUTCSeconds().toPaddedString(2) + 'Z';
}
-};
+
+
+ function toJSON() {
+ return this.toISOString();
+ }
+
+ if (!proto.toISOString) proto.toISOString = toISOString;
+ if (!proto.toJSON) proto.toJSON = toJSON;
+
+})(Date.prototype);
+
RegExp.prototype.match = RegExp.prototype.test;
RegExp.escape = function(str) {
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
-
-/*--------------------------------------------------------------------------*/
-
var PeriodicalExecuter = Class.create({
initialize: function(callback, frequency) {
this.callback = callback;
@@ -310,8 +502,10 @@ var PeriodicalExecuter = Class.create({
try {
this.currentlyExecuting = true;
this.execute();
- } finally {
this.currentlyExecuting = false;
+ } catch(e) {
+ this.currentlyExecuting = false;
+ throw e;
}
}
}
@@ -330,10 +524,28 @@ Object.extend(String, {
}
});
-Object.extend(String.prototype, {
- gsub: function(pattern, replacement) {
+Object.extend(String.prototype, (function() {
+ var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
+ typeof JSON.parse === 'function' &&
+ JSON.parse('{"test": true}').test;
+
+ function prepareReplacement(replacement) {
+ if (Object.isFunction(replacement)) return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+ }
+
+ function gsub(pattern, replacement) {
var result = '', source = this, match;
- replacement = arguments.callee.prepareReplacement(replacement);
+ replacement = prepareReplacement(replacement);
+
+ if (Object.isString(pattern))
+ pattern = RegExp.escape(pattern);
+
+ if (!(pattern.length || pattern.source)) {
+ replacement = replacement('');
+ return replacement + source.split('').join(replacement) + replacement;
+ }
while (source.length > 0) {
if (match = source.match(pattern)) {
@@ -345,76 +557,72 @@ Object.extend(String.prototype, {
}
}
return result;
- },
+ }
- sub: function(pattern, replacement, count) {
- replacement = this.gsub.prepareReplacement(replacement);
+ function sub(pattern, replacement, count) {
+ replacement = prepareReplacement(replacement);
count = Object.isUndefined(count) ? 1 : count;
return this.gsub(pattern, function(match) {
if (--count < 0) return match[0];
return replacement(match);
});
- },
+ }
- scan: function(pattern, iterator) {
+ function scan(pattern, iterator) {
this.gsub(pattern, iterator);
return String(this);
- },
+ }
- truncate: function(length, truncation) {
+ function truncate(length, truncation) {
length = length || 30;
truncation = Object.isUndefined(truncation) ? '...' : truncation;
return this.length > length ?
this.slice(0, length - truncation.length) + truncation : String(this);
- },
+ }
- strip: function() {
+ function strip() {
return this.replace(/^\s+/, '').replace(/\s+$/, '');
- },
+ }
- stripTags: function() {
- return this.replace(/<\/?[^>]+>/gi, '');
- },
+ function stripTags() {
+ return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
+ }
- stripScripts: function() {
+ function stripScripts() {
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
- },
+ }
- extractScripts: function() {
- var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
- var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ function extractScripts() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
+ matchOne = new RegExp(Prototype.ScriptFragment, 'im');
return (this.match(matchAll) || []).map(function(scriptTag) {
return (scriptTag.match(matchOne) || ['', ''])[1];
});
- },
+ }
- evalScripts: function() {
+ function evalScripts() {
return this.extractScripts().map(function(script) { return eval(script) });
- },
+ }
- escapeHTML: function() {
- var self = arguments.callee;
- self.text.data = this;
- return self.div.innerHTML;
- },
+ function escapeHTML() {
+ return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+ }
+
+ function unescapeHTML() {
+ return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
+ }
- unescapeHTML: function() {
- var div = new Element('div');
- div.innerHTML = this.stripTags();
- return div.childNodes[0] ? (div.childNodes.length > 1 ?
- $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
- div.childNodes[0].nodeValue) : '';
- },
- toQueryParams: function(separator) {
+ function toQueryParams(separator) {
var match = this.strip().match(/([^?#]*)(#.*)?$/);
if (!match) return { };
return match[1].split(separator || '&').inject({ }, function(hash, pair) {
if ((pair = pair.split('='))[0]) {
- var key = decodeURIComponent(pair.shift());
- var value = pair.length > 1 ? pair.join('=') : pair[0];
+ var key = decodeURIComponent(pair.shift()),
+ value = pair.length > 1 ? pair.join('=') : pair[0];
+
if (value != undefined) value = decodeURIComponent(value);
if (key in hash) {
@@ -425,128 +633,144 @@ Object.extend(String.prototype, {
}
return hash;
});
- },
+ }
- toArray: function() {
+ function toArray() {
return this.split('');
- },
+ }
- succ: function() {
+ function succ() {
return this.slice(0, this.length - 1) +
String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
- },
+ }
- times: function(count) {
+ function times(count) {
return count < 1 ? '' : new Array(count + 1).join(this);
- },
-
- camelize: function() {
- var parts = this.split('-'), len = parts.length;
- if (len == 1) return parts[0];
-
- var camelized = this.charAt(0) == '-'
- ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
- : parts[0];
-
- for (var i = 1; i < len; i++)
- camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+ }
- return camelized;
- },
+ function camelize() {
+ return this.replace(/-+(.)?/g, function(match, chr) {
+ return chr ? chr.toUpperCase() : '';
+ });
+ }
- capitalize: function() {
+ function capitalize() {
return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
- },
+ }
- underscore: function() {
- return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
- },
+ function underscore() {
+ return this.replace(/::/g, '/')
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
+ .replace(/-/g, '_')
+ .toLowerCase();
+ }
- dasherize: function() {
- return this.gsub(/_/,'-');
- },
+ function dasherize() {
+ return this.replace(/_/g, '-');
+ }
- inspect: function(useDoubleQuotes) {
- var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
- var character = String.specialChar[match[0]];
- return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ function inspect(useDoubleQuotes) {
+ var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
+ if (character in String.specialChar) {
+ return String.specialChar[character];
+ }
+ return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
});
if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
return "'" + escapedString.replace(/'/g, '\\\'') + "'";
- },
-
- toJSON: function() {
- return this.inspect(true);
- },
+ }
- unfilterJSON: function(filter) {
- return this.sub(filter || Prototype.JSONFilter, '#{1}');
- },
+ function unfilterJSON(filter) {
+ return this.replace(filter || Prototype.JSONFilter, '$1');
+ }
- isJSON: function() {
+ function isJSON() {
var str = this;
if (str.blank()) return false;
- str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
- return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
- },
+ str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
+ str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
+ str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+ return (/^[\],:{}\s]*$/).test(str);
+ }
- evalJSON: function(sanitize) {
- var json = this.unfilterJSON();
+ function evalJSON(sanitize) {
+ var json = this.unfilterJSON(),
+ cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+ if (cx.test(json)) {
+ json = json.replace(cx, function (a) {
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
try {
if (!sanitize || json.isJSON()) return eval('(' + json + ')');
} catch (e) { }
throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
- },
+ }
- include: function(pattern) {
+ function parseJSON() {
+ var json = this.unfilterJSON();
+ return JSON.parse(json);
+ }
+
+ function include(pattern) {
return this.indexOf(pattern) > -1;
- },
+ }
- startsWith: function(pattern) {
- return this.indexOf(pattern) === 0;
- },
+ function startsWith(pattern) {
+ return this.lastIndexOf(pattern, 0) === 0;
+ }
- endsWith: function(pattern) {
+ function endsWith(pattern) {
var d = this.length - pattern.length;
- return d >= 0 && this.lastIndexOf(pattern) === d;
- },
+ return d >= 0 && this.indexOf(pattern, d) === d;
+ }
- empty: function() {
+ function empty() {
return this == '';
- },
+ }
- blank: function() {
+ function blank() {
return /^\s*$/.test(this);
- },
-
- interpolate: function(object, pattern) {
- return new Template(this, pattern).evaluate(object);
}
-});
-if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
- escapeHTML: function() {
- return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
- },
- unescapeHTML: function() {
- return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+ function interpolate(object, pattern) {
+ return new Template(this, pattern).evaluate(object);
}
-});
-
-String.prototype.gsub.prepareReplacement = function(replacement) {
- if (Object.isFunction(replacement)) return replacement;
- var template = new Template(replacement);
- return function(match) { return template.evaluate(match) };
-};
-String.prototype.parseQuery = String.prototype.toQueryParams;
-
-Object.extend(String.prototype.escapeHTML, {
- div: document.createElement('div'),
- text: document.createTextNode('')
-});
-
-with (String.prototype.escapeHTML) div.appendChild(text);
+ return {
+ gsub: gsub,
+ sub: sub,
+ scan: scan,
+ truncate: truncate,
+ strip: String.prototype.trim || strip,
+ stripTags: stripTags,
+ stripScripts: stripScripts,
+ extractScripts: extractScripts,
+ evalScripts: evalScripts,
+ escapeHTML: escapeHTML,
+ unescapeHTML: unescapeHTML,
+ toQueryParams: toQueryParams,
+ parseQuery: toQueryParams,
+ toArray: toArray,
+ succ: succ,
+ times: times,
+ camelize: camelize,
+ capitalize: capitalize,
+ underscore: underscore,
+ dasherize: dasherize,
+ inspect: inspect,
+ unfilterJSON: unfilterJSON,
+ isJSON: isJSON,
+ evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
+ include: include,
+ startsWith: startsWith,
+ endsWith: endsWith,
+ empty: empty,
+ blank: blank,
+ interpolate: interpolate
+ };
+})());
var Template = Class.create({
initialize: function(template, pattern) {
@@ -555,22 +779,23 @@ var Template = Class.create({
},
evaluate: function(object) {
- if (Object.isFunction(object.toTemplateReplacements))
+ if (object && Object.isFunction(object.toTemplateReplacements))
object = object.toTemplateReplacements();
return this.template.gsub(this.pattern, function(match) {
- if (object == null) return '';
+ if (object == null) return (match[1] + '');
var before = match[1] || '';
if (before == '\\') return match[2];
- var ctx = object, expr = match[3];
- var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ var ctx = object, expr = match[3],
+ pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+
match = pattern.exec(expr);
if (match == null) return before;
while (match != null) {
- var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+ var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
ctx = ctx[comp];
if (null == ctx || '' == match[3]) break;
expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
@@ -578,101 +803,98 @@ var Template = Class.create({
}
return before + String.interpret(ctx);
- }.bind(this));
+ });
}
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
var $break = { };
-var Enumerable = {
- each: function(iterator, context) {
+var Enumerable = (function() {
+ function each(iterator, context) {
var index = 0;
- iterator = iterator.bind(context);
try {
this._each(function(value) {
- iterator(value, index++);
+ iterator.call(context, value, index++);
});
} catch (e) {
if (e != $break) throw e;
}
return this;
- },
+ }
- eachSlice: function(number, iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ function eachSlice(number, iterator, context) {
var index = -number, slices = [], array = this.toArray();
+ if (number < 1) return array;
while ((index += number) < array.length)
slices.push(array.slice(index, index+number));
return slices.collect(iterator, context);
- },
+ }
- all: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ function all(iterator, context) {
+ iterator = iterator || Prototype.K;
var result = true;
this.each(function(value, index) {
- result = result && !!iterator(value, index);
+ result = result && !!iterator.call(context, value, index);
if (!result) throw $break;
});
return result;
- },
+ }
- any: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ function any(iterator, context) {
+ iterator = iterator || Prototype.K;
var result = false;
this.each(function(value, index) {
- if (result = !!iterator(value, index))
+ if (result = !!iterator.call(context, value, index))
throw $break;
});
return result;
- },
+ }
- collect: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ function collect(iterator, context) {
+ iterator = iterator || Prototype.K;
var results = [];
this.each(function(value, index) {
- results.push(iterator(value, index));
+ results.push(iterator.call(context, value, index));
});
return results;
- },
+ }
- detect: function(iterator, context) {
- iterator = iterator.bind(context);
+ function detect(iterator, context) {
var result;
this.each(function(value, index) {
- if (iterator(value, index)) {
+ if (iterator.call(context, value, index)) {
result = value;
throw $break;
}
});
return result;
- },
+ }
- findAll: function(iterator, context) {
- iterator = iterator.bind(context);
+ function findAll(iterator, context) {
var results = [];
this.each(function(value, index) {
- if (iterator(value, index))
+ if (iterator.call(context, value, index))
results.push(value);
});
return results;
- },
+ }
- grep: function(filter, iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ function grep(filter, iterator, context) {
+ iterator = iterator || Prototype.K;
var results = [];
if (Object.isString(filter))
- filter = new RegExp(filter);
+ filter = new RegExp(RegExp.escape(filter));
this.each(function(value, index) {
if (filter.match(value))
- results.push(iterator(value, index));
+ results.push(iterator.call(context, value, index));
});
return results;
- },
+ }
- include: function(object) {
+ function include(object) {
if (Object.isFunction(this.indexOf))
if (this.indexOf(object) != -1) return true;
@@ -684,96 +906,96 @@ var Enumerable = {
}
});
return found;
- },
+ }
- inGroupsOf: function(number, fillWith) {
+ function inGroupsOf(number, fillWith) {
fillWith = Object.isUndefined(fillWith) ? null : fillWith;
return this.eachSlice(number, function(slice) {
while(slice.length < number) slice.push(fillWith);
return slice;
});
- },
+ }
- inject: function(memo, iterator, context) {
- iterator = iterator.bind(context);
+ function inject(memo, iterator, context) {
this.each(function(value, index) {
- memo = iterator(memo, value, index);
+ memo = iterator.call(context, memo, value, index);
});
return memo;
- },
+ }
- invoke: function(method) {
+ function invoke(method) {
var args = $A(arguments).slice(1);
return this.map(function(value) {
return value[method].apply(value, args);
});
- },
+ }
- max: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ function max(iterator, context) {
+ iterator = iterator || Prototype.K;
var result;
this.each(function(value, index) {
- value = iterator(value, index);
+ value = iterator.call(context, value, index);
if (result == null || value >= result)
result = value;
});
return result;
- },
+ }
- min: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ function min(iterator, context) {
+ iterator = iterator || Prototype.K;
var result;
this.each(function(value, index) {
- value = iterator(value, index);
+ value = iterator.call(context, value, index);
if (result == null || value < result)
result = value;
});
return result;
- },
+ }
- partition: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ function partition(iterator, context) {
+ iterator = iterator || Prototype.K;
var trues = [], falses = [];
this.each(function(value, index) {
- (iterator(value, index) ?
+ (iterator.call(context, value, index) ?
trues : falses).push(value);
});
return [trues, falses];
- },
+ }
- pluck: function(property) {
+ function pluck(property) {
var results = [];
this.each(function(value) {
results.push(value[property]);
});
return results;
- },
+ }
- reject: function(iterator, context) {
- iterator = iterator.bind(context);
+ function reject(iterator, context) {
var results = [];
this.each(function(value, index) {
- if (!iterator(value, index))
+ if (!iterator.call(context, value, index))
results.push(value);
});
return results;
- },
+ }
- sortBy: function(iterator, context) {
- iterator = iterator.bind(context);
+ function sortBy(iterator, context) {
return this.map(function(value, index) {
- return {value: value, criteria: iterator(value, index)};
+ return {
+ value: value,
+ criteria: iterator.call(context, value, index)
+ };
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}).pluck('value');
- },
+ }
- toArray: function() {
+ function toArray() {
return this.map();
- },
+ }
- zip: function() {
+ function zip() {
var iterator = Prototype.K, args = $A(arguments);
if (Object.isFunction(args.last()))
iterator = args.pop();
@@ -782,330 +1004,409 @@ var Enumerable = {
return this.map(function(value, index) {
return iterator(collections.pluck(index));
});
- },
+ }
- size: function() {
+ function size() {
return this.toArray().length;
- },
+ }
- inspect: function() {
+ function inspect() {
return '#<Enumerable:' + this.toArray().inspect() + '>';
}
-};
-Object.extend(Enumerable, {
- map: Enumerable.collect,
- find: Enumerable.detect,
- select: Enumerable.findAll,
- filter: Enumerable.findAll,
- member: Enumerable.include,
- entries: Enumerable.toArray,
- every: Enumerable.all,
- some: Enumerable.any
-});
+
+
+
+
+
+
+
+
+ return {
+ each: each,
+ eachSlice: eachSlice,
+ all: all,
+ every: all,
+ any: any,
+ some: any,
+ collect: collect,
+ map: collect,
+ detect: detect,
+ findAll: findAll,
+ select: findAll,
+ filter: findAll,
+ grep: grep,
+ include: include,
+ member: include,
+ inGroupsOf: inGroupsOf,
+ inject: inject,
+ invoke: invoke,
+ max: max,
+ min: min,
+ partition: partition,
+ pluck: pluck,
+ reject: reject,
+ sortBy: sortBy,
+ toArray: toArray,
+ entries: toArray,
+ zip: zip,
+ size: size,
+ inspect: inspect,
+ find: detect
+ };
+})();
+
function $A(iterable) {
if (!iterable) return [];
- if (iterable.toArray) return iterable.toArray();
- var length = iterable.length, results = new Array(length);
+ if ('toArray' in Object(iterable)) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
}
-if (Prototype.Browser.WebKit) {
- function $A(iterable) {
- if (!iterable) return [];
- if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
- iterable.toArray) return iterable.toArray();
- var length = iterable.length, results = new Array(length);
- while (length--) results[length] = iterable[length];
- return results;
- }
+
+function $w(string) {
+ if (!Object.isString(string)) return [];
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
}
Array.from = $A;
-Object.extend(Array.prototype, Enumerable);
-if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+(function() {
+ var arrayProto = Array.prototype,
+ slice = arrayProto.slice,
+ _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
-Object.extend(Array.prototype, {
- _each: function(iterator) {
+ function each(iterator) {
for (var i = 0, length = this.length; i < length; i++)
iterator(this[i]);
- },
+ }
+ if (!_each) _each = each;
- clear: function() {
+ function clear() {
this.length = 0;
return this;
- },
+ }
- first: function() {
+ function first() {
return this[0];
- },
+ }
- last: function() {
+ function last() {
return this[this.length - 1];
- },
+ }
- compact: function() {
+ function compact() {
return this.select(function(value) {
return value != null;
});
- },
+ }
- flatten: function() {
+ function flatten() {
return this.inject([], function(array, value) {
- return array.concat(Object.isArray(value) ?
- value.flatten() : [value]);
+ if (Object.isArray(value))
+ return array.concat(value.flatten());
+ array.push(value);
+ return array;
});
- },
+ }
- without: function() {
- var values = $A(arguments);
+ function without() {
+ var values = slice.call(arguments, 0);
return this.select(function(value) {
return !values.include(value);
});
- },
-
- reverse: function(inline) {
- return (inline !== false ? this : this.toArray())._reverse();
- },
+ }
- reduce: function() {
- return this.length > 1 ? this : this[0];
- },
+ function reverse(inline) {
+ return (inline === false ? this.toArray() : this)._reverse();
+ }
- uniq: function(sorted) {
+ function uniq(sorted) {
return this.inject([], function(array, value, index) {
if (0 == index || (sorted ? array.last() != value : !array.include(value)))
array.push(value);
return array;
});
- },
+ }
- intersect: function(array) {
+ function intersect(array) {
return this.uniq().findAll(function(item) {
return array.detect(function(value) { return item === value });
});
- },
+ }
- clone: function() {
- return [].concat(this);
- },
- size: function() {
+ function clone() {
+ return slice.call(this, 0);
+ }
+
+ function size() {
return this.length;
- },
+ }
- inspect: function() {
+ function inspect() {
return '[' + this.map(Object.inspect).join(', ') + ']';
- },
-
- toJSON: function() {
- var results = [];
- this.each(function(object) {
- var value = Object.toJSON(object);
- if (!Object.isUndefined(value)) results.push(value);
- });
- return '[' + results.join(', ') + ']';
}
-});
-
-// use native browser JS 1.6 implementation if available
-if (Object.isFunction(Array.prototype.forEach))
- Array.prototype._each = Array.prototype.forEach;
-
-if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
- i || (i = 0);
- var length = this.length;
- if (i < 0) i = length + i;
- for (; i < length; i++)
- if (this[i] === item) return i;
- return -1;
-};
-
-if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
- i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
- var n = this.slice(0, i).reverse().indexOf(item);
- return (n < 0) ? n : i - n - 1;
-};
-Array.prototype.toArray = Array.prototype.clone;
+ function indexOf(item, i) {
+ i || (i = 0);
+ var length = this.length;
+ if (i < 0) i = length + i;
+ for (; i < length; i++)
+ if (this[i] === item) return i;
+ return -1;
+ }
-function $w(string) {
- if (!Object.isString(string)) return [];
- string = string.strip();
- return string ? string.split(/\s+/) : [];
-}
+ function lastIndexOf(item, i) {
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+ var n = this.slice(0, i).reverse().indexOf(item);
+ return (n < 0) ? n : i - n - 1;
+ }
-if (Prototype.Browser.Opera){
- Array.prototype.concat = function() {
- var array = [];
- for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ function concat() {
+ var array = slice.call(this, 0), item;
for (var i = 0, length = arguments.length; i < length; i++) {
- if (Object.isArray(arguments[i])) {
- for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
- array.push(arguments[i][j]);
+ item = arguments[i];
+ if (Object.isArray(item) && !('callee' in item)) {
+ for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
+ array.push(item[j]);
} else {
- array.push(arguments[i]);
+ array.push(item);
}
}
return array;
- };
-}
-Object.extend(Number.prototype, {
- toColorPart: function() {
- return this.toPaddedString(2, 16);
- },
-
- succ: function() {
- return this + 1;
- },
+ }
- times: function(iterator) {
- $R(0, this, true).each(iterator);
- return this;
- },
+ Object.extend(arrayProto, Enumerable);
+
+ if (!arrayProto._reverse)
+ arrayProto._reverse = arrayProto.reverse;
+
+ Object.extend(arrayProto, {
+ _each: _each,
+ clear: clear,
+ first: first,
+ last: last,
+ compact: compact,
+ flatten: flatten,
+ without: without,
+ reverse: reverse,
+ uniq: uniq,
+ intersect: intersect,
+ clone: clone,
+ toArray: clone,
+ size: size,
+ inspect: inspect
+ });
- toPaddedString: function(length, radix) {
- var string = this.toString(radix || 10);
- return '0'.times(length - string.length) + string;
- },
+ var CONCAT_ARGUMENTS_BUGGY = (function() {
+ return [].concat(arguments)[0][0] !== 1;
+ })(1,2)
- toJSON: function() {
- return isFinite(this) ? this.toString() : 'null';
- }
-});
+ if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
-$w('abs round ceil floor').each(function(method){
- Number.prototype[method] = Math[method].methodize();
-});
+ if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
+ if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
+})();
function $H(object) {
return new Hash(object);
};
var Hash = Class.create(Enumerable, (function() {
-
- function toQueryPair(key, value) {
- if (Object.isUndefined(value)) return key;
- return key + '=' + encodeURIComponent(String.interpret(value));
+ function initialize(object) {
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
}
- return {
- initialize: function(object) {
- this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
- },
- _each: function(iterator) {
- for (var key in this._object) {
- var value = this._object[key], pair = [key, value];
- pair.key = key;
- pair.value = value;
- iterator(pair);
- }
- },
+ function _each(iterator) {
+ for (var key in this._object) {
+ var value = this._object[key], pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ }
- set: function(key, value) {
- return this._object[key] = value;
- },
+ function set(key, value) {
+ return this._object[key] = value;
+ }
- get: function(key) {
+ function get(key) {
+ if (this._object[key] !== Object.prototype[key])
return this._object[key];
- },
+ }
- unset: function(key) {
- var value = this._object[key];
- delete this._object[key];
- return value;
- },
+ function unset(key) {
+ var value = this._object[key];
+ delete this._object[key];
+ return value;
+ }
- toObject: function() {
- return Object.clone(this._object);
- },
+ function toObject() {
+ return Object.clone(this._object);
+ }
- keys: function() {
- return this.pluck('key');
- },
- values: function() {
- return this.pluck('value');
- },
- index: function(value) {
- var match = this.detect(function(pair) {
- return pair.value === value;
- });
- return match && match.key;
- },
+ function keys() {
+ return this.pluck('key');
+ }
- merge: function(object) {
- return this.clone().update(object);
- },
+ function values() {
+ return this.pluck('value');
+ }
- update: function(object) {
- return new Hash(object).inject(this, function(result, pair) {
- result.set(pair.key, pair.value);
- return result;
- });
- },
+ function index(value) {
+ var match = this.detect(function(pair) {
+ return pair.value === value;
+ });
+ return match && match.key;
+ }
- toQueryString: function() {
- return this.map(function(pair) {
- var key = encodeURIComponent(pair.key), values = pair.value;
+ function merge(object) {
+ return this.clone().update(object);
+ }
- if (values && typeof values == 'object') {
- if (Object.isArray(values))
- return values.map(toQueryPair.curry(key)).join('&');
- }
- return toQueryPair(key, values);
- }).join('&');
- },
+ function update(object) {
+ return new Hash(object).inject(this, function(result, pair) {
+ result.set(pair.key, pair.value);
+ return result;
+ });
+ }
- inspect: function() {
- return '#<Hash:{' + this.map(function(pair) {
- return pair.map(Object.inspect).join(': ');
- }).join(', ') + '}>';
- },
+ function toQueryPair(key, value) {
+ if (Object.isUndefined(value)) return key;
+ return key + '=' + encodeURIComponent(String.interpret(value));
+ }
- toJSON: function() {
- return Object.toJSON(this.toObject());
- },
+ function toQueryString() {
+ return this.inject([], function(results, pair) {
+ var key = encodeURIComponent(pair.key), values = pair.value;
- clone: function() {
- return new Hash(this);
- }
+ if (values && typeof values == 'object') {
+ if (Object.isArray(values))
+ return results.concat(values.map(toQueryPair.curry(key)));
+ } else results.push(toQueryPair(key, values));
+ return results;
+ }).join('&');
+ }
+
+ function inspect() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ }
+
+ function clone() {
+ return new Hash(this);
}
+
+ return {
+ initialize: initialize,
+ _each: _each,
+ set: set,
+ get: get,
+ unset: unset,
+ toObject: toObject,
+ toTemplateReplacements: toObject,
+ keys: keys,
+ values: values,
+ index: index,
+ merge: merge,
+ update: update,
+ toQueryString: toQueryString,
+ inspect: inspect,
+ toJSON: toObject,
+ clone: clone
+ };
})());
-Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $H;
-var ObjectRange = Class.create(Enumerable, {
- initialize: function(start, end, exclusive) {
+Object.extend(Number.prototype, (function() {
+ function toColorPart() {
+ return this.toPaddedString(2, 16);
+ }
+
+ function succ() {
+ return this + 1;
+ }
+
+ function times(iterator, context) {
+ $R(0, this, true).each(iterator, context);
+ return this;
+ }
+
+ function toPaddedString(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ }
+
+ function abs() {
+ return Math.abs(this);
+ }
+
+ function round() {
+ return Math.round(this);
+ }
+
+ function ceil() {
+ return Math.ceil(this);
+ }
+
+ function floor() {
+ return Math.floor(this);
+ }
+
+ return {
+ toColorPart: toColorPart,
+ succ: succ,
+ times: times,
+ toPaddedString: toPaddedString,
+ abs: abs,
+ round: round,
+ ceil: ceil,
+ floor: floor
+ };
+})());
+
+function $R(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var ObjectRange = Class.create(Enumerable, (function() {
+ function initialize(start, end, exclusive) {
this.start = start;
this.end = end;
this.exclusive = exclusive;
- },
+ }
- _each: function(iterator) {
+ function _each(iterator) {
var value = this.start;
while (this.include(value)) {
iterator(value);
value = value.succ();
}
- },
+ }
- include: function(value) {
+ function include(value) {
if (value < this.start)
return false;
if (this.exclusive)
return value < this.end;
return value <= this.end;
}
-});
-var $R = function(start, end, exclusive) {
- return new ObjectRange(start, end, exclusive);
-};
+ return {
+ initialize: initialize,
+ _each: _each,
+ include: include
+ };
+})());
+
+
var Ajax = {
getTransport: function() {
@@ -1152,7 +1453,6 @@ Ajax.Responders.register({
onCreate: function() { Ajax.activeRequestCount++ },
onComplete: function() { Ajax.activeRequestCount-- }
});
-
Ajax.Base = Class.create({
initialize: function(options) {
this.options = {
@@ -1174,7 +1474,6 @@ Ajax.Base = Class.create({
this.options.parameters = this.options.parameters.toObject();
}
});
-
Ajax.Request = Class.create(Ajax.Base, {
_complete: false,
@@ -1190,7 +1489,6 @@ Ajax.Request = Class.create(Ajax.Base, {
var params = Object.clone(this.options.parameters);
if (!['get', 'post'].include(this.method)) {
- // simulate other verbs over post
params['_method'] = this.method;
this.method = 'post';
}
@@ -1198,7 +1496,6 @@ Ajax.Request = Class.create(Ajax.Base, {
this.parameters = params;
if (params = Object.toQueryString(params)) {
- // when GET, append parameters to URL
if (this.method == 'get')
this.url += (this.url.include('?') ? '&' : '?') + params;
else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
@@ -1257,7 +1554,6 @@ Ajax.Request = Class.create(Ajax.Base, {
headers['Connection'] = 'close';
}
- // user-defined headers
if (typeof this.options.requestHeaders == 'object') {
var extras = this.options.requestHeaders;
@@ -1298,7 +1594,7 @@ Ajax.Request = Class.create(Ajax.Base, {
var contentType = response.getHeader('Content-type');
if (this.options.evalJS == 'force'
- || (this.options.evalJS && contentType
+ || (this.options.evalJS && this.isSameOrigin() && contentType
&& contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
this.evalResponse();
}
@@ -1311,15 +1607,23 @@ Ajax.Request = Class.create(Ajax.Base, {
}
if (state == 'Complete') {
- // avoid memory leak in MSIE: clean up
this.transport.onreadystatechange = Prototype.emptyFunction;
}
},
+ isSameOrigin: function() {
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+ protocol: location.protocol,
+ domain: document.domain,
+ port: location.port ? ':' + location.port : ''
+ }));
+ },
+
getHeader: function(name) {
try {
- return this.transport.getResponseHeader(name);
- } catch (e) { return null }
+ return this.transport.getResponseHeader(name) || null;
+ } catch (e) { return null; }
},
evalResponse: function() {
@@ -1339,20 +1643,27 @@ Ajax.Request = Class.create(Ajax.Base, {
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+
+
+
+
+
+
Ajax.Response = Class.create({
initialize: function(request){
this.request = request;
var transport = this.transport = request.transport,
readyState = this.readyState = transport.readyState;
- if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+ if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
this.status = this.getStatus();
this.statusText = this.getStatusText();
this.responseText = String.interpret(transport.responseText);
this.headerJSON = this._getHeaderJSON();
}
- if(readyState == 4) {
+ if (readyState == 4) {
var xml = transport.responseXML;
this.responseXML = Object.isUndefined(xml) ? null : xml;
this.responseJSON = this._getResponseJSON();
@@ -1360,6 +1671,7 @@ Ajax.Response = Class.create({
},
status: 0,
+
statusText: '',
getStatus: Ajax.Request.prototype.getStatus,
@@ -1391,7 +1703,8 @@ Ajax.Response = Class.create({
if (!json) return null;
json = decodeURIComponent(escape(json));
try {
- return json.evalJSON(this.request.options.sanitizeJSON);
+ return json.evalJSON(this.request.options.sanitizeJSON ||
+ !this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
@@ -1404,7 +1717,8 @@ Ajax.Response = Class.create({
this.responseText.blank())
return null;
try {
- return this.responseText.evalJSON(options.sanitizeJSON);
+ return this.responseText.evalJSON(options.sanitizeJSON ||
+ !this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
@@ -1487,6 +1801,8 @@ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
this.updater = new Ajax.Updater(this.container, this.url, this.options);
}
});
+
+
function $(element) {
if (arguments.length > 1) {
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
@@ -1511,10 +1827,9 @@ if (Prototype.BrowserFeatures.XPath) {
/*--------------------------------------------------------------------------*/
-if (!window.Node) var Node = { };
+if (!Node) var Node = { };
if (!Node.ELEMENT_NODE) {
- // DOM level 2 ECMAScript Language Binding
Object.extend(Node, {
ELEMENT_NODE: 1,
ATTRIBUTE_NODE: 2,
@@ -1531,13 +1846,27 @@ if (!Node.ELEMENT_NODE) {
});
}
-(function() {
- var element = this.Element;
- this.Element = function(tagName, attributes) {
+
+
+(function(global) {
+
+ var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
+ try {
+ var el = document.createElement('<input name="x">');
+ return el.tagName.toLowerCase() === 'input' && el.name === 'x';
+ }
+ catch(err) {
+ return false;
+ }
+ })();
+
+ var element = global.Element;
+
+ global.Element = function(tagName, attributes) {
attributes = attributes || { };
tagName = tagName.toLowerCase();
var cache = Element.cache;
- if (Prototype.Browser.IE && attributes.name) {
+ if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
tagName = '<' + tagName + ' name="' + attributes.name + '">';
delete attributes.name;
return Element.writeAttribute(document.createElement(tagName), attributes);
@@ -1545,11 +1874,24 @@ if (!Node.ELEMENT_NODE) {
if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
};
- Object.extend(this.Element, element || { });
-}).call(window);
+ Object.extend(global.Element, element || { });
+ if (element) global.Element.prototype = element.prototype;
+
+})(this);
+
+Element.idCounter = 1;
Element.cache = { };
+function purgeElement(element) {
+ var uid = element._prototypeUID;
+ if (uid) {
+ Element.stopObserving(element);
+ element._prototypeUID = void 0;
+ delete Element.Storage[uid];
+ }
+}
+
Element.Methods = {
visible: function(element) {
return $(element).style.display != 'none';
@@ -1562,12 +1904,14 @@ Element.Methods = {
},
hide: function(element) {
- $(element).style.display = 'none';
+ element = $(element);
+ element.style.display = 'none';
return element;
},
show: function(element) {
- $(element).style.display = '';
+ element = $(element);
+ element.style.display = '';
return element;
},
@@ -1577,15 +1921,93 @@ Element.Methods = {
return element;
},
- update: function(element, content) {
- element = $(element);
- if (content && content.toElement) content = content.toElement();
- if (Object.isElement(content)) return element.update().insert(content);
- content = Object.toHTML(content);
- element.innerHTML = content.stripScripts();
- content.evalScripts.bind(content).defer();
- return element;
- },
+ update: (function(){
+
+ var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
+ var el = document.createElement("select"),
+ isBuggy = true;
+ el.innerHTML = "<option value=\"test\">test</option>";
+ if (el.options && el.options[0]) {
+ isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
+ }
+ el = null;
+ return isBuggy;
+ })();
+
+ var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
+ try {
+ var el = document.createElement("table");
+ if (el && el.tBodies) {
+ el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
+ var isBuggy = typeof el.tBodies[0] == "undefined";
+ el = null;
+ return isBuggy;
+ }
+ } catch (e) {
+ return true;
+ }
+ })();
+
+ var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
+ var s = document.createElement("script"),
+ isBuggy = false;
+ try {
+ s.appendChild(document.createTextNode(""));
+ isBuggy = !s.firstChild ||
+ s.firstChild && s.firstChild.nodeType !== 3;
+ } catch (e) {
+ isBuggy = true;
+ }
+ s = null;
+ return isBuggy;
+ })();
+
+ function update(element, content) {
+ element = $(element);
+
+ var descendants = element.getElementsByTagName('*'),
+ i = descendants.length;
+ while (i--) purgeElement(descendants[i]);
+
+ if (content && content.toElement)
+ content = content.toElement();
+
+ if (Object.isElement(content))
+ return element.update().insert(content);
+
+ content = Object.toHTML(content);
+
+ var tagName = element.tagName.toUpperCase();
+
+ if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
+ element.text = content;
+ return element;
+ }
+
+ if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {
+ if (tagName in Element._insertionTranslations.tags) {
+ while (element.firstChild) {
+ element.removeChild(element.firstChild);
+ }
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+ .each(function(node) {
+ element.appendChild(node)
+ });
+ }
+ else {
+ element.innerHTML = content.stripScripts();
+ }
+ }
+ else {
+ element.innerHTML = content.stripScripts();
+ }
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ }
+
+ return update;
+ })(),
replace: function(element, content) {
element = $(element);
@@ -1608,24 +2030,28 @@ Element.Methods = {
Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
insertions = {bottom:insertions};
- var content, t, range;
+ var content, insert, tagName, childNodes;
- for (position in insertions) {
+ for (var position in insertions) {
content = insertions[position];
position = position.toLowerCase();
- t = Element._insertionTranslations[position];
+ insert = Element._insertionTranslations[position];
if (content && content.toElement) content = content.toElement();
if (Object.isElement(content)) {
- t.insert(element, content);
+ insert(element, content);
continue;
}
content = Object.toHTML(content);
- range = element.ownerDocument.createRange();
- t.initializeRange(element, range);
- t.insert(element, range.createContextualFragment(content.stripScripts()));
+ tagName = ((position == 'before' || position == 'after')
+ ? element.parentNode : element).tagName.toUpperCase();
+
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+ if (position == 'top' || position == 'after') childNodes.reverse();
+ childNodes.each(insert.curry(element));
content.evalScripts.bind(content).defer();
}
@@ -1649,28 +2075,35 @@ Element.Methods = {
element = $(element);
var result = '<' + element.tagName.toLowerCase();
$H({'id': 'id', 'className': 'class'}).each(function(pair) {
- var property = pair.first(), attribute = pair.last();
- var value = (element[property] || '').toString();
+ var property = pair.first(),
+ attribute = pair.last(),
+ value = (element[property] || '').toString();
if (value) result += ' ' + attribute + '=' + value.inspect(true);
});
return result + '>';
},
- recursivelyCollect: function(element, property) {
+ recursivelyCollect: function(element, property, maximumLength) {
element = $(element);
+ maximumLength = maximumLength || -1;
var elements = [];
- while (element = element[property])
+
+ while (element = element[property]) {
if (element.nodeType == 1)
elements.push(Element.extend(element));
+ if (elements.length == maximumLength)
+ break;
+ }
+
return elements;
},
ancestors: function(element) {
- return $(element).recursivelyCollect('parentNode');
+ return Element.recursivelyCollect(element, 'parentNode');
},
descendants: function(element) {
- return $(element).getElementsBySelector("*");
+ return Element.select(element, "*");
},
firstDescendant: function(element) {
@@ -1680,79 +2113,96 @@ Element.Methods = {
},
immediateDescendants: function(element) {
- if (!(element = $(element).firstChild)) return [];
- while (element && element.nodeType != 1) element = element.nextSibling;
- if (element) return [element].concat($(element).nextSiblings());
- return [];
+ var results = [], child = $(element).firstChild;
+ while (child) {
+ if (child.nodeType === 1) {
+ results.push(Element.extend(child));
+ }
+ child = child.nextSibling;
+ }
+ return results;
},
- previousSiblings: function(element) {
- return $(element).recursivelyCollect('previousSibling');
+ previousSiblings: function(element, maximumLength) {
+ return Element.recursivelyCollect(element, 'previousSibling');
},
nextSiblings: function(element) {
- return $(element).recursivelyCollect('nextSibling');
+ return Element.recursivelyCollect(element, 'nextSibling');
},
siblings: function(element) {
element = $(element);
- return element.previousSiblings().reverse().concat(element.nextSiblings());
+ return Element.previousSiblings(element).reverse()
+ .concat(Element.nextSiblings(element));
},
match: function(element, selector) {
+ element = $(element);
if (Object.isString(selector))
- selector = new Selector(selector);
- return selector.match($(element));
+ return Prototype.Selector.match(element, selector);
+ return selector.match(element);
},
up: function(element, expression, index) {
element = $(element);
if (arguments.length == 1) return $(element.parentNode);
- var ancestors = element.ancestors();
- return expression ? Selector.findElement(ancestors, expression, index) :
- ancestors[index || 0];
+ var ancestors = Element.ancestors(element);
+ return Object.isNumber(expression) ? ancestors[expression] :
+ Prototype.Selector.find(ancestors, expression, index);
},
down: function(element, expression, index) {
element = $(element);
- if (arguments.length == 1) return element.firstDescendant();
- var descendants = element.descendants();
- return expression ? Selector.findElement(descendants, expression, index) :
- descendants[index || 0];
+ if (arguments.length == 1) return Element.firstDescendant(element);
+ return Object.isNumber(expression) ? Element.descendants(element)[expression] :
+ Element.select(element, expression)[index || 0];
},
previous: function(element, expression, index) {
element = $(element);
- if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
- var previousSiblings = element.previousSiblings();
- return expression ? Selector.findElement(previousSiblings, expression, index) :
- previousSiblings[index || 0];
+ if (Object.isNumber(expression)) index = expression, expression = false;
+ if (!Object.isNumber(index)) index = 0;
+
+ if (expression) {
+ return Prototype.Selector.find(element.previousSiblings(), expression, index);
+ } else {
+ return element.recursivelyCollect("previousSibling", index + 1)[index];
+ }
},
next: function(element, expression, index) {
element = $(element);
- if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
- var nextSiblings = element.nextSiblings();
- return expression ? Selector.findElement(nextSiblings, expression, index) :
- nextSiblings[index || 0];
+ if (Object.isNumber(expression)) index = expression, expression = false;
+ if (!Object.isNumber(index)) index = 0;
+
+ if (expression) {
+ return Prototype.Selector.find(element.nextSiblings(), expression, index);
+ } else {
+ var maximumLength = Object.isNumber(index) ? index + 1 : 1;
+ return element.recursivelyCollect("nextSibling", index + 1)[index];
+ }
},
- select: function() {
- var args = $A(arguments), element = $(args.shift());
- return Selector.findChildElements(element, args);
+
+ select: function(element) {
+ element = $(element);
+ var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
+ return Prototype.Selector.select(expressions, element);
},
- adjacent: function() {
- var args = $A(arguments), element = $(args.shift());
- return Selector.findChildElements(element.parentNode, args).without(element);
+ adjacent: function(element) {
+ element = $(element);
+ var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
+ return Prototype.Selector.select(expressions, element.parentNode).without(element);
},
identify: function(element) {
element = $(element);
- var id = element.readAttribute('id'), self = arguments.callee;
+ var id = Element.readAttribute(element, 'id');
if (id) return id;
- do { id = 'anonymous_element_' + self.counter++ } while ($(id));
- element.writeAttribute('id', id);
+ do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
+ Element.writeAttribute(element, 'id', id);
return id;
},
@@ -1791,11 +2241,11 @@ Element.Methods = {
},
getHeight: function(element) {
- return $(element).getDimensions().height;
+ return Element.getDimensions(element).height;
},
getWidth: function(element) {
- return $(element).getDimensions().width;
+ return Element.getDimensions(element).width;
},
classNames: function(element) {
@@ -1811,7 +2261,7 @@ Element.Methods = {
addClassName: function(element, className) {
if (!(element = $(element))) return;
- if (!element.hasClassName(className))
+ if (!Element.hasClassName(element, className))
element.className += (element.className ? ' ' : '') + className;
return element;
},
@@ -1825,11 +2275,10 @@ Element.Methods = {
toggleClassName: function(element, className) {
if (!(element = $(element))) return;
- return element[element.hasClassName(className) ?
- 'removeClassName' : 'addClassName'](className);
+ return Element[Element.hasClassName(element, className) ?
+ 'removeClassName' : 'addClassName'](element, className);
},
- // removes whitespace-only text node children
cleanWhitespace: function(element) {
element = $(element);
var node = element.firstChild;
@@ -1848,29 +2297,22 @@ Element.Methods = {
descendantOf: function(element, ancestor) {
element = $(element), ancestor = $(ancestor);
- var originalAncestor = ancestor;
if (element.compareDocumentPosition)
return (element.compareDocumentPosition(ancestor) & 8) === 8;
- if (element.sourceIndex && !Prototype.Browser.Opera) {
- var e = element.sourceIndex, a = ancestor.sourceIndex,
- nextAncestor = ancestor.nextSibling;
- if (!nextAncestor) {
- do { ancestor = ancestor.parentNode; }
- while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
- }
- if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
- }
+ if (ancestor.contains)
+ return ancestor.contains(element) && ancestor !== element;
while (element = element.parentNode)
- if (element == originalAncestor) return true;
+ if (element == ancestor) return true;
+
return false;
},
scrollTo: function(element) {
element = $(element);
- var pos = element.cumulativeOffset();
+ var pos = Element.cumulativeOffset(element);
window.scrollTo(pos[0], pos[1]);
return element;
},
@@ -1879,7 +2321,7 @@ Element.Methods = {
element = $(element);
style = style == 'float' ? 'cssFloat' : style.camelize();
var value = element.style[style];
- if (!value) {
+ if (!value || value == 'auto') {
var css = document.defaultView.getComputedStyle(element, null);
value = css ? css[style] : null;
}
@@ -1916,38 +2358,13 @@ Element.Methods = {
return element;
},
- getDimensions: function(element) {
- element = $(element);
- var display = $(element).getStyle('display');
- if (display != 'none' && display != null) // Safari bug
- return {width: element.offsetWidth, height: element.offsetHeight};
-
- // All *Width and *Height properties give 0 on elements with display none,
- // so enable the element temporarily
- var els = element.style;
- var originalVisibility = els.visibility;
- var originalPosition = els.position;
- var originalDisplay = els.display;
- els.visibility = 'hidden';
- els.position = 'absolute';
- els.display = 'block';
- var originalWidth = element.clientWidth;
- var originalHeight = element.clientHeight;
- els.display = originalDisplay;
- els.position = originalPosition;
- els.visibility = originalVisibility;
- return {width: originalWidth, height: originalHeight};
- },
-
makePositioned: function(element) {
element = $(element);
var pos = Element.getStyle(element, 'position');
if (pos == 'static' || !pos) {
element._madePositioned = true;
element.style.position = 'relative';
- // Opera returns the offset relative to the positioning context, when an
- // element is position relative but top and left have not been defined
- if (window.opera) {
+ if (Prototype.Browser.Opera) {
element.style.top = 0;
element.style.left = 0;
}
@@ -1987,11 +2404,13 @@ Element.Methods = {
cumulativeOffset: function(element) {
var valueT = 0, valueL = 0;
- do {
- valueT += element.offsetTop || 0;
- valueL += element.offsetLeft || 0;
- element = element.offsetParent;
- } while (element);
+ if (element.parentNode) {
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ }
return Element._returnOffset(valueL, valueT);
},
@@ -2002,9 +2421,9 @@ Element.Methods = {
valueL += element.offsetLeft || 0;
element = element.offsetParent;
if (element) {
- if (element.tagName == 'BODY') break;
+ if (element.tagName.toUpperCase() == 'BODY') break;
var p = Element.getStyle(element, 'position');
- if (p == 'relative' || p == 'absolute') break;
+ if (p !== 'static') break;
}
} while (element);
return Element._returnOffset(valueL, valueT);
@@ -2012,14 +2431,13 @@ Element.Methods = {
absolutize: function(element) {
element = $(element);
- if (element.getStyle('position') == 'absolute') return;
- // Position.prepare(); // To be done manually by Scripty when it needs it.
+ if (Element.getStyle(element, 'position') == 'absolute') return element;
- var offsets = element.positionedOffset();
- var top = offsets[1];
- var left = offsets[0];
- var width = element.clientWidth;
- var height = element.clientHeight;
+ var offsets = Element.positionedOffset(element),
+ top = offsets[1],
+ left = offsets[0],
+ width = element.clientWidth,
+ height = element.clientHeight;
element._originalLeft = left - parseFloat(element.style.left || 0);
element._originalTop = top - parseFloat(element.style.top || 0);
@@ -2036,12 +2454,11 @@ Element.Methods = {
relativize: function(element) {
element = $(element);
- if (element.getStyle('position') == 'relative') return;
- // Position.prepare(); // To be done manually by Scripty when it needs it.
+ if (Element.getStyle(element, 'position') == 'relative') return element;
element.style.position = 'relative';
- var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
- var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0),
+ left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
element.style.top = top + 'px';
element.style.left = left + 'px';
@@ -2072,14 +2489,14 @@ Element.Methods = {
},
viewportOffset: function(forElement) {
- var valueT = 0, valueL = 0;
+ var valueT = 0,
+ valueL = 0,
+ element = forElement;
- var element = forElement;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
- // Safari fix
if (element.offsetParent == document.body &&
Element.getStyle(element, 'position') == 'absolute') break;
@@ -2087,7 +2504,7 @@ Element.Methods = {
element = forElement;
do {
- if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+ if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
valueT -= element.scrollTop || 0;
valueL -= element.scrollLeft || 0;
}
@@ -2106,28 +2523,21 @@ Element.Methods = {
offsetLeft: 0
}, arguments[2] || { });
- // find page position of source
source = $(source);
- var p = source.viewportOffset();
+ var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
- // find coordinate system to use
element = $(element);
- var delta = [0, 0];
- var parent = null;
- // delta [0,0] will do fine with position: fixed elements,
- // position:absolute needs offsetParent deltas
+
if (Element.getStyle(element, 'position') == 'absolute') {
- parent = element.getOffsetParent();
- delta = parent.viewportOffset();
+ parent = Element.getOffsetParent(element);
+ delta = Element.viewportOffset(parent);
}
- // correct by body offsets (fixes Safari)
if (parent == document.body) {
delta[0] -= document.body.offsetLeft;
delta[1] -= document.body.offsetTop;
}
- // set position
if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
if (options.setWidth) element.style.width = source.offsetWidth + 'px';
@@ -2136,10 +2546,9 @@ Element.Methods = {
}
};
-Element.Methods.identify.counter = 1;
-
Object.extend(Element.Methods, {
getElementsBySelector: Element.Methods.select,
+
childElements: Element.Methods.immediateDescendants
});
@@ -2153,46 +2562,6 @@ Element._attributeTranslations = {
}
};
-
-if (!document.createRange || Prototype.Browser.Opera) {
- Element.Methods.insert = function(element, insertions) {
- element = $(element);
-
- if (Object.isString(insertions) || Object.isNumber(insertions) ||
- Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
- insertions = { bottom: insertions };
-
- var t = Element._insertionTranslations, content, position, pos, tagName;
-
- for (position in insertions) {
- content = insertions[position];
- position = position.toLowerCase();
- pos = t[position];
-
- if (content && content.toElement) content = content.toElement();
- if (Object.isElement(content)) {
- pos.insert(element, content);
- continue;
- }
-
- content = Object.toHTML(content);
- tagName = ((position == 'before' || position == 'after')
- ? element.parentNode : element).tagName.toUpperCase();
-
- if (t.tags[tagName]) {
- var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
- if (position == 'top' || position == 'after') fragments.reverse();
- fragments.each(pos.insert.curry(element));
- }
- else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
-
- content.evalScripts.bind(content).defer();
- }
-
- return element;
- };
-}
-
if (Prototype.Browser.Opera) {
Element.Methods.getStyle = Element.Methods.getStyle.wrap(
function(proceed, element, style) {
@@ -2200,11 +2569,8 @@ if (Prototype.Browser.Opera) {
case 'left': case 'top': case 'right': case 'bottom':
if (proceed(element, 'position') === 'static') return null;
case 'height': case 'width':
- // returns '0px' for hidden elements; we want it to return null
if (!Element.visible(element)) return null;
- // returns the border-box dimensions rather than the content-box
- // dimensions, so we subtract padding and borders from the value
var dim = parseInt(proceed(element, style), 10);
if (dim !== element['offset' + style.capitalize()])
@@ -2237,12 +2603,29 @@ if (Prototype.Browser.Opera) {
}
else if (Prototype.Browser.IE) {
- $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
+ Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+ function(proceed, element) {
+ element = $(element);
+ if (!element.parentNode) return $(document.body);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+
+ $w('positionedOffset viewportOffset').each(function(method) {
Element.Methods[method] = Element.Methods[method].wrap(
function(proceed, element) {
element = $(element);
+ if (!element.parentNode) return Element._returnOffset(0, 0);
var position = element.getStyle('position');
- if (position != 'static') return proceed(element);
+ if (position !== 'static') return proceed(element);
+ var offsetParent = element.getOffsetParent();
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+ offsetParent.setStyle({ zoom: 1 });
element.setStyle({ position: 'relative' });
var value = proceed(element);
element.setStyle({ position: position });
@@ -2292,39 +2675,96 @@ else if (Prototype.Browser.IE) {
return element;
};
- Element._attributeTranslations = {
- read: {
- names: {
- 'class': 'className',
- 'for': 'htmlFor'
- },
- values: {
- _getAttr: function(element, attribute) {
- return element.getAttribute(attribute, 2);
- },
- _getAttrNode: function(element, attribute) {
- var node = element.getAttributeNode(attribute);
- return node ? node.value : "";
- },
- _getEv: function(element, attribute) {
- attribute = element.getAttribute(attribute);
- return attribute ? attribute.toString().slice(23, -2) : null;
- },
- _flag: function(element, attribute) {
- return $(element).hasAttribute(attribute) ? attribute : null;
- },
- style: function(element) {
- return element.style.cssText.toLowerCase();
+ Element._attributeTranslations = (function(){
+
+ var classProp = 'className',
+ forProp = 'for',
+ el = document.createElement('div');
+
+ el.setAttribute(classProp, 'x');
+
+ if (el.className !== 'x') {
+ el.setAttribute('class', 'x');
+ if (el.className === 'x') {
+ classProp = 'class';
+ }
+ }
+ el = null;
+
+ el = document.createElement('label');
+ el.setAttribute(forProp, 'x');
+ if (el.htmlFor !== 'x') {
+ el.setAttribute('htmlFor', 'x');
+ if (el.htmlFor === 'x') {
+ forProp = 'htmlFor';
+ }
+ }
+ el = null;
+
+ return {
+ read: {
+ names: {
+ 'class': classProp,
+ 'className': classProp,
+ 'for': forProp,
+ 'htmlFor': forProp
},
- title: function(element) {
- return element.title;
+ values: {
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute);
+ },
+ _getAttr2: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+ _getAttrNode: function(element, attribute) {
+ var node = element.getAttributeNode(attribute);
+ return node ? node.value : "";
+ },
+ _getEv: (function(){
+
+ var el = document.createElement('div'), f;
+ el.onclick = Prototype.emptyFunction;
+ var value = el.getAttribute('onclick');
+
+ if (String(value).indexOf('{') > -1) {
+ f = function(element, attribute) {
+ attribute = element.getAttribute(attribute);
+ if (!attribute) return null;
+ attribute = attribute.toString();
+ attribute = attribute.split('{')[1];
+ attribute = attribute.split('}')[0];
+ return attribute.strip();
+ };
+ }
+ else if (value === '') {
+ f = function(element, attribute) {
+ attribute = element.getAttribute(attribute);
+ if (!attribute) return null;
+ return attribute.strip();
+ };
+ }
+ el = null;
+ return f;
+ })(),
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ },
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function(element) {
+ return element.title;
+ }
}
}
}
- };
+ })();
Element._attributeTranslations.write = {
- names: Object.clone(Element._attributeTranslations.read.names),
+ names: Object.extend({
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing'
+ }, Element._attributeTranslations.read.names),
values: {
checked: function(element, value) {
element.checked = !!value;
@@ -2339,15 +2779,15 @@ else if (Prototype.Browser.IE) {
Element._attributeTranslations.has = {};
$w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
- 'encType maxLength readOnly longDesc').each(function(attr) {
+ 'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
Element._attributeTranslations.has[attr.toLowerCase()] = attr;
});
(function(v) {
Object.extend(v, {
- href: v._getAttr,
- src: v._getAttr,
+ href: v._getAttr2,
+ src: v._getAttr2,
type: v._getAttr,
action: v._getAttrNode,
disabled: v._flag,
@@ -2374,6 +2814,26 @@ else if (Prototype.Browser.IE) {
onchange: v._getEv
});
})(Element._attributeTranslations.read.values);
+
+ if (Prototype.BrowserFeatures.ElementExtensions) {
+ (function() {
+ function _descendants(element) {
+ var nodes = element.getElementsByTagName('*'), results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName !== "!") // Filter out comment nodes.
+ results.push(node);
+ return results;
+ }
+
+ Element.Methods.down = function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return element.firstDescendant();
+ return Object.isNumber(expression) ? _descendants(element)[expression] :
+ Element.select(element, expression)[index || 0];
+ }
+ })();
+ }
+
}
else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
@@ -2392,7 +2852,7 @@ else if (Prototype.Browser.WebKit) {
(value < 0.00001) ? 0 : value;
if (value == 1)
- if(element.tagName == 'IMG' && element.width) {
+ if (element.tagName.toUpperCase() == 'IMG' && element.width) {
element.width++; element.width--;
} else try {
var n = document.createTextNode(' ');
@@ -2403,9 +2863,6 @@ else if (Prototype.Browser.WebKit) {
return element;
};
- // Safari returns margins on body which is incorrect if the child is absolutely
- // positioned. For performance reasons, redefine Element#cumulativeOffset for
- // KHTML/WebKit only.
Element.Methods.cumulativeOffset = function(element) {
var valueT = 0, valueL = 0;
do {
@@ -2421,30 +2878,7 @@ else if (Prototype.Browser.WebKit) {
};
}
-if (Prototype.Browser.IE || Prototype.Browser.Opera) {
- // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
- Element.Methods.update = function(element, content) {
- element = $(element);
-
- if (content && content.toElement) content = content.toElement();
- if (Object.isElement(content)) return element.update().insert(content);
-
- content = Object.toHTML(content);
- var tagName = element.tagName.toUpperCase();
-
- if (tagName in Element._insertionTranslations.tags) {
- $A(element.childNodes).each(function(node) { element.removeChild(node) });
- Element._getContentFromAnonymousElement(tagName, content.stripScripts())
- .each(function(node) { element.appendChild(node) });
- }
- else element.innerHTML = content.stripScripts();
-
- content.evalScripts.bind(content).defer();
- return element;
- };
-}
-
-if (document.createElement('div').outerHTML) {
+if ('outerHTML' in document.documentElement) {
Element.Methods.replace = function(element, content) {
element = $(element);
@@ -2458,8 +2892,8 @@ if (document.createElement('div').outerHTML) {
var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
if (Element._insertionTranslations.tags[tagName]) {
- var nextSibling = element.next();
- var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+ var nextSibling = element.next(),
+ fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
parent.removeChild(element);
if (nextSibling)
fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
@@ -2481,46 +2915,32 @@ Element._returnOffset = function(l, t) {
};
Element._getContentFromAnonymousElement = function(tagName, html) {
- var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
- div.innerHTML = t[0] + html + t[1];
- t[2].times(function() { div = div.firstChild });
+ var div = new Element('div'),
+ t = Element._insertionTranslations.tags[tagName];
+ if (t) {
+ div.innerHTML = t[0] + html + t[1];
+ for (var i = t[2]; i--; ) {
+ div = div.firstChild;
+ }
+ }
+ else {
+ div.innerHTML = html;
+ }
return $A(div.childNodes);
};
Element._insertionTranslations = {
- before: {
- adjacency: 'beforeBegin',
- insert: function(element, node) {
- element.parentNode.insertBefore(node, element);
- },
- initializeRange: function(element, range) {
- range.setStartBefore(element);
- }
+ before: function(element, node) {
+ element.parentNode.insertBefore(node, element);
},
- top: {
- adjacency: 'afterBegin',
- insert: function(element, node) {
- element.insertBefore(node, element.firstChild);
- },
- initializeRange: function(element, range) {
- range.selectNodeContents(element);
- range.collapse(true);
- }
+ top: function(element, node) {
+ element.insertBefore(node, element.firstChild);
},
- bottom: {
- adjacency: 'beforeEnd',
- insert: function(element, node) {
- element.appendChild(node);
- }
+ bottom: function(element, node) {
+ element.appendChild(node);
},
- after: {
- adjacency: 'afterEnd',
- insert: function(element, node) {
- element.parentNode.insertBefore(node, element.nextSibling);
- },
- initializeRange: function(element, range) {
- range.setStartAfter(element);
- }
+ after: function(element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
},
tags: {
TABLE: ['<table>', '</table>', 1],
@@ -2532,19 +2952,19 @@ Element._insertionTranslations = {
};
(function() {
- this.bottom.initializeRange = this.top.initializeRange;
- Object.extend(this.tags, {
- THEAD: this.tags.TBODY,
- TFOOT: this.tags.TBODY,
- TH: this.tags.TD
+ var tags = Element._insertionTranslations.tags;
+ Object.extend(tags, {
+ THEAD: tags.TBODY,
+ TFOOT: tags.TBODY,
+ TH: tags.TD
});
-}).call(Element._insertionTranslations);
+})();
Element.Methods.Simulated = {
hasAttribute: function(element, attribute) {
attribute = Element._attributeTranslations.has[attribute] || attribute;
var node = $(element).getAttributeNode(attribute);
- return node && node.specified;
+ return !!(node && node.specified);
}
};
@@ -2552,41 +2972,81 @@ Element.Methods.ByTag = { };
Object.extend(Element, Element.Methods);
-if (!Prototype.BrowserFeatures.ElementExtensions &&
- document.createElement('div').__proto__) {
- window.HTMLElement = { };
- window.HTMLElement.prototype = document.createElement('div').__proto__;
- Prototype.BrowserFeatures.ElementExtensions = true;
-}
+(function(div) {
+
+ if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
+ window.HTMLElement = { };
+ window.HTMLElement.prototype = div['__proto__'];
+ Prototype.BrowserFeatures.ElementExtensions = true;
+ }
+
+ div = null;
+
+})(document.createElement('div'));
Element.extend = (function() {
- if (Prototype.BrowserFeatures.SpecificElementExtensions)
+
+ function checkDeficiency(tagName) {
+ if (typeof window.Element != 'undefined') {
+ var proto = window.Element.prototype;
+ if (proto) {
+ var id = '_' + (Math.random()+'').slice(2),
+ el = document.createElement(tagName);
+ proto[id] = 'x';
+ var isBuggy = (el[id] !== 'x');
+ delete proto[id];
+ el = null;
+ return isBuggy;
+ }
+ }
+ return false;
+ }
+
+ function extendElementWith(element, methods) {
+ for (var property in methods) {
+ var value = methods[property];
+ if (Object.isFunction(value) && !(property in element))
+ element[property] = value.methodize();
+ }
+ }
+
+ var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
+
+ if (Prototype.BrowserFeatures.SpecificElementExtensions) {
+ if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
+ return function(element) {
+ if (element && typeof element._extendedByPrototype == 'undefined') {
+ var t = element.tagName;
+ if (t && (/^(?:object|applet|embed)$/i.test(t))) {
+ extendElementWith(element, Element.Methods);
+ extendElementWith(element, Element.Methods.Simulated);
+ extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
+ }
+ }
+ return element;
+ }
+ }
return Prototype.K;
+ }
var Methods = { }, ByTag = Element.Methods.ByTag;
var extend = Object.extend(function(element) {
- if (!element || element._extendedByPrototype ||
+ if (!element || typeof element._extendedByPrototype != 'undefined' ||
element.nodeType != 1 || element == window) return element;
var methods = Object.clone(Methods),
- tagName = element.tagName, property, value;
+ tagName = element.tagName.toUpperCase();
- // extend methods for specific tags
if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
- for (property in methods) {
- value = methods[property];
- if (Object.isFunction(value) && !(property in element))
- element[property] = value.methodize();
- }
+ extendElementWith(element, methods);
element._extendedByPrototype = Prototype.emptyFunction;
return element;
}, {
refresh: function() {
- // extend methods for all tags (Safari doesn't need this)
if (!Prototype.BrowserFeatures.ElementExtensions) {
Object.extend(Methods, Element.Methods);
Object.extend(Methods, Element.Methods.Simulated);
@@ -2598,10 +3058,14 @@ Element.extend = (function() {
return extend;
})();
-Element.hasAttribute = function(element, attribute) {
- if (element.hasAttribute) return element.hasAttribute(attribute);
- return Element.Methods.Simulated.hasAttribute(element, attribute);
-};
+if (document.documentElement.hasAttribute) {
+ Element.hasAttribute = function(element, attribute) {
+ return element.hasAttribute(attribute);
+ };
+}
+else {
+ Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
+}
Element.addMethods = function(methods) {
var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
@@ -2665,14 +3129,19 @@ Element.addMethods = function(methods) {
klass = 'HTML' + tagName.capitalize() + 'Element';
if (window[klass]) return window[klass];
- window[klass] = { };
- window[klass].prototype = document.createElement(tagName).__proto__;
- return window[klass];
+ var element = document.createElement(tagName),
+ proto = element['__proto__'] || element.constructor.prototype;
+
+ element = null;
+ return proto;
}
+ var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
+ Element.prototype;
+
if (F.ElementExtensions) {
- copy(Element.Methods, HTMLElement.prototype);
- copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ copy(Element.Methods, elementPrototype);
+ copy(Element.Methods.Simulated, elementPrototype, true);
}
if (F.SpecificElementExtensions) {
@@ -2690,697 +3159,1803 @@ Element.addMethods = function(methods) {
Element.cache = { };
};
-document.viewport = {
- getDimensions: function() {
- var dimensions = { };
- var B = Prototype.Browser;
- $w('width height').each(function(d) {
- var D = d.capitalize();
- dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
- (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
- });
- return dimensions;
- },
- getWidth: function() {
- return this.getDimensions().width;
- },
+document.viewport = {
- getHeight: function() {
- return this.getDimensions().height;
+ getDimensions: function() {
+ return { width: this.getWidth(), height: this.getHeight() };
},
getScrollOffsets: function() {
return Element._returnOffset(
window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
- window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
}
};
-/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
- * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
- * license. Please see http://www.yui-ext.com/ for more information. */
-var Selector = Class.create({
- initialize: function(expression) {
- this.expression = expression.strip();
- this.compileMatcher();
- },
+(function(viewport) {
+ var B = Prototype.Browser, doc = document, element, property = {};
- shouldUseXPath: function() {
- if (!Prototype.BrowserFeatures.XPath) return false;
+ function getRootElement() {
+ if (B.WebKit && !doc.evaluate)
+ return document;
- var e = this.expression;
+ if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
+ return document.body;
- // Safari 3 chokes on :*-of-type and :empty
- if (Prototype.Browser.WebKit &&
- (e.include("-of-type") || e.include(":empty")))
- return false;
+ return document.documentElement;
+ }
- // XPath can't do namespaced attributes, nor can it read
- // the "checked" property from DOM nodes
- if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
- return false;
+ function define(D) {
+ if (!element) element = getRootElement();
- return true;
- },
+ property[D] = 'client' + D;
- compileMatcher: function() {
- if (this.shouldUseXPath())
- return this.compileXPathMatcher();
+ viewport['get' + D] = function() { return element[property[D]] };
+ return viewport['get' + D]();
+ }
- var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
- c = Selector.criteria, le, p, m;
+ viewport.getWidth = define.curry('Width');
- if (Selector._cache[e]) {
- this.matcher = Selector._cache[e];
- return;
- }
+ viewport.getHeight = define.curry('Height');
+})(document.viewport);
- this.matcher = ["this.matcher = function(root) {",
- "var r = root, h = Selector.handlers, c = false, n;"];
- while (e && le != e && (/\S/).test(e)) {
- le = e;
- for (var i in ps) {
- p = ps[i];
- if (m = e.match(p)) {
- this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
- new Template(c[i]).evaluate(m));
- e = e.replace(m[0], '');
- break;
- }
- }
+Element.Storage = {
+ UID: 1
+};
+
+Element.addMethods({
+ getStorage: function(element) {
+ if (!(element = $(element))) return;
+
+ var uid;
+ if (element === window) {
+ uid = 0;
+ } else {
+ if (typeof element._prototypeUID === "undefined")
+ element._prototypeUID = Element.Storage.UID++;
+ uid = element._prototypeUID;
}
- this.matcher.push("return h.unique(n);\n}");
- eval(this.matcher.join('\n'));
- Selector._cache[this.expression] = this.matcher;
+ if (!Element.Storage[uid])
+ Element.Storage[uid] = $H();
+
+ return Element.Storage[uid];
},
- compileXPathMatcher: function() {
- var e = this.expression, ps = Selector.patterns,
- x = Selector.xpath, le, m;
+ store: function(element, key, value) {
+ if (!(element = $(element))) return;
- if (Selector._cache[e]) {
- this.xpath = Selector._cache[e]; return;
+ if (arguments.length === 2) {
+ Element.getStorage(element).update(key);
+ } else {
+ Element.getStorage(element).set(key, value);
}
- this.matcher = ['.//*'];
- while (e && le != e && (/\S/).test(e)) {
- le = e;
- for (var i in ps) {
- if (m = e.match(ps[i])) {
- this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
- new Template(x[i]).evaluate(m));
- e = e.replace(m[0], '');
- break;
- }
- }
+ return element;
+ },
+
+ retrieve: function(element, key, defaultValue) {
+ if (!(element = $(element))) return;
+ var hash = Element.getStorage(element), value = hash.get(key);
+
+ if (Object.isUndefined(value)) {
+ hash.set(key, defaultValue);
+ value = defaultValue;
}
- this.xpath = this.matcher.join('');
- Selector._cache[this.expression] = this.xpath;
+ return value;
},
- findElements: function(root) {
- root = root || document;
- if (this.xpath) return document._getElementsByXPath(this.xpath, root);
- return this.matcher(root);
+ clone: function(element, deep) {
+ if (!(element = $(element))) return;
+ var clone = element.cloneNode(deep);
+ clone._prototypeUID = void 0;
+ if (deep) {
+ var descendants = Element.select(clone, '*'),
+ i = descendants.length;
+ while (i--) {
+ descendants[i]._prototypeUID = void 0;
+ }
+ }
+ return Element.extend(clone);
},
- match: function(element) {
- this.tokens = [];
+ purge: function(element) {
+ if (!(element = $(element))) return;
+ purgeElement(element);
- var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
- var le, p, m;
+ var descendants = element.getElementsByTagName('*'),
+ i = descendants.length;
- while (e && le !== e && (/\S/).test(e)) {
- le = e;
- for (var i in ps) {
- p = ps[i];
- if (m = e.match(p)) {
- // use the Selector.assertions methods unless the selector
- // is too complex.
- if (as[i]) {
- this.tokens.push([i, Object.clone(m)]);
- e = e.replace(m[0], '');
- } else {
- // reluctantly do a document-wide search
- // and look for a match in the array
- return this.findElements(document).include(element);
- }
- }
- }
+ while (i--) purgeElement(descendants[i]);
+
+ return null;
+ }
+});
+
+(function() {
+
+ function toDecimal(pctString) {
+ var match = pctString.match(/^(\d+)%?$/i);
+ if (!match) return null;
+ return (Number(match[1]) / 100);
+ }
+
+ function getPixelValue(value, property) {
+ if (Object.isElement(value)) {
+ element = value;
+ value = element.getStyle(property);
+ }
+ if (value === null) {
+ return null;
+ }
+
+ if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
+ return window.parseFloat(value);
}
- var match = true, name, matches;
- for (var i = 0, token; token = this.tokens[i]; i++) {
- name = token[0], matches = token[1];
- if (!Selector.assertions[name](element, matches)) {
- match = false; break;
+ if (/\d/.test(value) && element.runtimeStyle) {
+ var style = element.style.left, rStyle = element.runtimeStyle.left;
+ element.runtimeStyle.left = element.currentStyle.left;
+ element.style.left = value || 0;
+ value = element.style.pixelLeft;
+ element.style.left = style;
+ element.runtimeStyle.left = rStyle;
+
+ return value;
+ }
+
+ if (value.include('%')) {
+ var decimal = toDecimal(value);
+ var whole;
+ if (property.include('left') || property.include('right') ||
+ property.include('width')) {
+ whole = $(element.parentNode).measure('width');
+ } else if (property.include('top') || property.include('bottom') ||
+ property.include('height')) {
+ whole = $(element.parentNode).measure('height');
}
+
+ return whole * decimal;
}
- return match;
- },
+ return 0;
+ }
- toString: function() {
- return this.expression;
- },
+ function toCSSPixels(number) {
+ if (Object.isString(number) && number.endsWith('px')) {
+ return number;
+ }
+ return number + 'px';
+ }
- inspect: function() {
- return "#<Selector:" + this.expression.inspect() + ">";
+ function isDisplayed(element) {
+ var originalElement = element;
+ while (element && element.parentNode) {
+ var display = element.getStyle('display');
+ if (display === 'none') {
+ return false;
+ }
+ element = $(element.parentNode);
+ }
+ return true;
}
-});
-Object.extend(Selector, {
- _cache: { },
-
- xpath: {
- descendant: "//*",
- child: "/*",
- adjacent: "/following-sibling::*[1]",
- laterSibling: '/following-sibling::*',
- tagName: function(m) {
- if (m[1] == '*') return '';
- return "[local-name()='" + m[1].toLowerCase() +
- "' or local-name()='" + m[1].toUpperCase() + "']";
+ var hasLayout = Prototype.K;
+ if ('currentStyle' in document.documentElement) {
+ hasLayout = function(element) {
+ if (!element.currentStyle.hasLayout) {
+ element.style.zoom = 1;
+ }
+ return element;
+ };
+ }
+
+ function cssNameFor(key) {
+ if (key.include('border')) key = key + '-width';
+ return key.camelize();
+ }
+
+ Element.Layout = Class.create(Hash, {
+ initialize: function($super, element, preCompute) {
+ $super();
+ this.element = $(element);
+
+ Element.Layout.PROPERTIES.each( function(property) {
+ this._set(property, null);
+ }, this);
+
+ if (preCompute) {
+ this._preComputing = true;
+ this._begin();
+ Element.Layout.PROPERTIES.each( this._compute, this );
+ this._end();
+ this._preComputing = false;
+ }
+ },
+
+ _set: function(property, value) {
+ return Hash.prototype.set.call(this, property, value);
+ },
+
+ set: function(property, value) {
+ throw "Properties of Element.Layout are read-only.";
+ },
+
+ get: function($super, property) {
+ var value = $super(property);
+ return value === null ? this._compute(property) : value;
+ },
+
+ _begin: function() {
+ if (this._prepared) return;
+
+ var element = this.element;
+ if (isDisplayed(element)) {
+ this._prepared = true;
+ return;
+ }
+
+ var originalStyles = {
+ position: element.style.position || '',
+ width: element.style.width || '',
+ visibility: element.style.visibility || '',
+ display: element.style.display || ''
+ };
+
+ element.store('prototype_original_styles', originalStyles);
+
+ var position = element.getStyle('position'),
+ width = element.getStyle('width');
+
+ element.setStyle({
+ position: 'absolute',
+ visibility: 'hidden',
+ display: 'block'
+ });
+
+ var positionedWidth = element.getStyle('width');
+
+ var newWidth;
+ if (width && (positionedWidth === width)) {
+ newWidth = getPixelValue(width);
+ } else if (width && (position === 'absolute' || position === 'fixed')) {
+ newWidth = getPixelValue(width);
+ } else {
+ var parent = element.parentNode, pLayout = $(parent).getLayout();
+
+ newWidth = pLayout.get('width') -
+ this.get('margin-left') -
+ this.get('border-left') -
+ this.get('padding-left') -
+ this.get('padding-right') -
+ this.get('border-right') -
+ this.get('margin-right');
+ }
+
+ element.setStyle({ width: newWidth + 'px' });
+
+ this._prepared = true;
},
- className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
- id: "[@id='#{1}']",
- attrPresence: function(m) {
- m[1] = m[1].toLowerCase();
- return new Template("[@#{1}]").evaluate(m);
+
+ _end: function() {
+ var element = this.element;
+ var originalStyles = element.retrieve('prototype_original_styles');
+ element.store('prototype_original_styles', null);
+ element.setStyle(originalStyles);
+ this._prepared = false;
},
- attr: function(m) {
- m[1] = m[1].toLowerCase();
- m[3] = m[5] || m[6];
- return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+
+ _compute: function(property) {
+ var COMPUTATIONS = Element.Layout.COMPUTATIONS;
+ if (!(property in COMPUTATIONS)) {
+ throw "Property not found.";
+ }
+ return this._set(property, COMPUTATIONS[property].call(this, this.element));
},
- pseudo: function(m) {
- var h = Selector.xpath.pseudos[m[1]];
- if (!h) return '';
- if (Object.isFunction(h)) return h(m);
- return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+
+ toObject: function() {
+ var args = $A(arguments);
+ var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
+ args.join(' ').split(' ');
+ var obj = {};
+ keys.each( function(key) {
+ if (!Element.Layout.PROPERTIES.include(key)) return;
+ var value = this.get(key);
+ if (value != null) obj[key] = value;
+ }, this);
+ return obj;
},
- operators: {
- '=': "[@#{1}='#{3}']",
- '!=': "[@#{1}!='#{3}']",
- '^=': "[starts-with(@#{1}, '#{3}')]",
- '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
- '*=': "[contains(@#{1}, '#{3}')]",
- '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
- '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+
+ toHash: function() {
+ var obj = this.toObject.apply(this, arguments);
+ return new Hash(obj);
},
- pseudos: {
- 'first-child': '[not(preceding-sibling::*)]',
- 'last-child': '[not(following-sibling::*)]',
- 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
- 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
- 'checked': "[@checked]",
- 'disabled': "[@disabled]",
- 'enabled': "[not(@disabled)]",
- 'not': function(m) {
- var e = m[6], p = Selector.patterns,
- x = Selector.xpath, le, v;
-
- var exclusion = [];
- while (e && le != e && (/\S/).test(e)) {
- le = e;
- for (var i in p) {
- if (m = e.match(p[i])) {
- v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
- exclusion.push("(" + v.substring(1, v.length - 1) + ")");
- e = e.replace(m[0], '');
- break;
- }
- }
- }
- return "[not(" + exclusion.join(" and ") + ")]";
+
+ toCSS: function() {
+ var args = $A(arguments);
+ var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
+ args.join(' ').split(' ');
+ var css = {};
+
+ keys.each( function(key) {
+ if (!Element.Layout.PROPERTIES.include(key)) return;
+ if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
+
+ var value = this.get(key);
+ if (value != null) css[cssNameFor(key)] = value + 'px';
+ }, this);
+ return css;
+ },
+
+ inspect: function() {
+ return "#<Element.Layout>";
+ }
+ });
+
+ Object.extend(Element.Layout, {
+ PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
+
+ COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
+
+ COMPUTATIONS: {
+ 'height': function(element) {
+ if (!this._preComputing) this._begin();
+
+ var bHeight = this.get('border-box-height');
+ if (bHeight <= 0) return 0;
+
+ var bTop = this.get('border-top'),
+ bBottom = this.get('border-bottom');
+
+ var pTop = this.get('padding-top'),
+ pBottom = this.get('padding-bottom');
+
+ if (!this._preComputing) this._end();
+
+ return bHeight - bTop - bBottom - pTop - pBottom;
},
- 'nth-child': function(m) {
- return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+
+ 'width': function(element) {
+ if (!this._preComputing) this._begin();
+
+ var bWidth = this.get('border-box-width');
+ if (bWidth <= 0) return 0;
+
+ var bLeft = this.get('border-left'),
+ bRight = this.get('border-right');
+
+ var pLeft = this.get('padding-left'),
+ pRight = this.get('padding-right');
+
+ if (!this._preComputing) this._end();
+
+ return bWidth - bLeft - bRight - pLeft - pRight;
},
- 'nth-last-child': function(m) {
- return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+
+ 'padding-box-height': function(element) {
+ var height = this.get('height'),
+ pTop = this.get('padding-top'),
+ pBottom = this.get('padding-bottom');
+
+ return height + pTop + pBottom;
},
- 'nth-of-type': function(m) {
- return Selector.xpath.pseudos.nth("position() ", m);
+
+ 'padding-box-width': function(element) {
+ var width = this.get('width'),
+ pLeft = this.get('padding-left'),
+ pRight = this.get('padding-right');
+
+ return width + pLeft + pRight;
},
- 'nth-last-of-type': function(m) {
- return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+
+ 'border-box-height': function(element) {
+ return element.offsetHeight;
},
- 'first-of-type': function(m) {
- m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+
+ 'border-box-width': function(element) {
+ return element.offsetWidth;
},
- 'last-of-type': function(m) {
- m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+
+ 'margin-box-height': function(element) {
+ var bHeight = this.get('border-box-height'),
+ mTop = this.get('margin-top'),
+ mBottom = this.get('margin-bottom');
+
+ if (bHeight <= 0) return 0;
+
+ return bHeight + mTop + mBottom;
},
- 'only-of-type': function(m) {
- var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+
+ 'margin-box-width': function(element) {
+ var bWidth = this.get('border-box-width'),
+ mLeft = this.get('margin-left'),
+ mRight = this.get('margin-right');
+
+ if (bWidth <= 0) return 0;
+
+ return bWidth + mLeft + mRight;
},
- nth: function(fragment, m) {
- var mm, formula = m[6], predicate;
- if (formula == 'even') formula = '2n+0';
- if (formula == 'odd') formula = '2n+1';
- if (mm = formula.match(/^(\d+)$/)) // digit only
- return '[' + fragment + "= " + mm[1] + ']';
- if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
- if (mm[1] == "-") mm[1] = -1;
- var a = mm[1] ? Number(mm[1]) : 1;
- var b = mm[2] ? Number(mm[2]) : 0;
- predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
- "((#{fragment} - #{b}) div #{a} >= 0)]";
- return new Template(predicate).evaluate({
- fragment: fragment, a: a, b: b });
- }
+
+ 'top': function(element) {
+ var offset = element.positionedOffset();
+ return offset.top;
+ },
+
+ 'bottom': function(element) {
+ var offset = element.positionedOffset(),
+ parent = element.getOffsetParent(),
+ pHeight = parent.measure('height');
+
+ var mHeight = this.get('border-box-height');
+
+ return pHeight - mHeight - offset.top;
+ },
+
+ 'left': function(element) {
+ var offset = element.positionedOffset();
+ return offset.left;
+ },
+
+ 'right': function(element) {
+ var offset = element.positionedOffset(),
+ parent = element.getOffsetParent(),
+ pWidth = parent.measure('width');
+
+ var mWidth = this.get('border-box-width');
+
+ return pWidth - mWidth - offset.left;
+ },
+
+ 'padding-top': function(element) {
+ return getPixelValue(element, 'paddingTop');
+ },
+
+ 'padding-bottom': function(element) {
+ return getPixelValue(element, 'paddingBottom');
+ },
+
+ 'padding-left': function(element) {
+ return getPixelValue(element, 'paddingLeft');
+ },
+
+ 'padding-right': function(element) {
+ return getPixelValue(element, 'paddingRight');
+ },
+
+ 'border-top': function(element) {
+ return Object.isNumber(element.clientTop) ? element.clientTop :
+ getPixelValue(element, 'borderTopWidth');
+ },
+
+ 'border-bottom': function(element) {
+ return Object.isNumber(element.clientBottom) ? element.clientBottom :
+ getPixelValue(element, 'borderBottomWidth');
+ },
+
+ 'border-left': function(element) {
+ return Object.isNumber(element.clientLeft) ? element.clientLeft :
+ getPixelValue(element, 'borderLeftWidth');
+ },
+
+ 'border-right': function(element) {
+ return Object.isNumber(element.clientRight) ? element.clientRight :
+ getPixelValue(element, 'borderRightWidth');
+ },
+
+ 'margin-top': function(element) {
+ return getPixelValue(element, 'marginTop');
+ },
+
+ 'margin-bottom': function(element) {
+ return getPixelValue(element, 'marginBottom');
+ },
+
+ 'margin-left': function(element) {
+ return getPixelValue(element, 'marginLeft');
+ },
+
+ 'margin-right': function(element) {
+ return getPixelValue(element, 'marginRight');
}
}
- },
+ });
- criteria: {
- tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
- className: 'n = h.className(n, r, "#{1}", c); c = false;',
- id: 'n = h.id(n, r, "#{1}", c); c = false;',
- attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
- attr: function(m) {
- m[3] = (m[5] || m[6]);
- return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
- },
- pseudo: function(m) {
- if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
- return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
- },
- descendant: 'c = "descendant";',
- child: 'c = "child";',
- adjacent: 'c = "adjacent";',
- laterSibling: 'c = "laterSibling";'
- },
-
- patterns: {
- // combinators must be listed first
- // (and descendant needs to be last combinator)
- laterSibling: /^\s*~\s*/,
- child: /^\s*>\s*/,
- adjacent: /^\s*\+\s*/,
- descendant: /^\s/,
-
- // selectors follow
- tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
- id: /^#([\w\-\*]+)(\b|$)/,
- className: /^\.([\w\-\*]+)(\b|$)/,
- pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
- attrPresence: /^\[([\w]+)\]/,
- attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
- },
-
- // for Selector.match and Element#match
- assertions: {
- tagName: function(element, matches) {
- return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ if ('getBoundingClientRect' in document.documentElement) {
+ Object.extend(Element.Layout.COMPUTATIONS, {
+ 'right': function(element) {
+ var parent = hasLayout(element.getOffsetParent());
+ var rect = element.getBoundingClientRect(),
+ pRect = parent.getBoundingClientRect();
+
+ return (pRect.right - rect.right).round();
+ },
+
+ 'bottom': function(element) {
+ var parent = hasLayout(element.getOffsetParent());
+ var rect = element.getBoundingClientRect(),
+ pRect = parent.getBoundingClientRect();
+
+ return (pRect.bottom - rect.bottom).round();
+ }
+ });
+ }
+
+ Element.Offset = Class.create({
+ initialize: function(left, top) {
+ this.left = left.round();
+ this.top = top.round();
+
+ this[0] = this.left;
+ this[1] = this.top;
},
- className: function(element, matches) {
- return Element.hasClassName(element, matches[1]);
+ relativeTo: function(offset) {
+ return new Element.Offset(
+ this.left - offset.left,
+ this.top - offset.top
+ );
},
- id: function(element, matches) {
- return element.id === matches[1];
+ inspect: function() {
+ return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
},
- attrPresence: function(element, matches) {
- return Element.hasAttribute(element, matches[1]);
+ toString: function() {
+ return "[#{left}, #{top}]".interpolate(this);
},
- attr: function(element, matches) {
- var nodeValue = Element.readAttribute(element, matches[1]);
- return Selector.operators[matches[2]](nodeValue, matches[3]);
+ toArray: function() {
+ return [this.left, this.top];
}
- },
+ });
- handlers: {
- // UTILITY FUNCTIONS
- // joins two collections
- concat: function(a, b) {
- for (var i = 0, node; node = b[i]; i++)
- a.push(node);
- return a;
- },
+ function getLayout(element, preCompute) {
+ return new Element.Layout(element, preCompute);
+ }
- // marks an array of nodes for counting
- mark: function(nodes) {
- for (var i = 0, node; node = nodes[i]; i++)
- node._counted = true;
- return nodes;
- },
+ function measure(element, property) {
+ return $(element).getLayout().get(property);
+ }
- unmark: function(nodes) {
- for (var i = 0, node; node = nodes[i]; i++)
- node._counted = undefined;
- return nodes;
- },
+ function getDimensions(element) {
+ var layout = $(element).getLayout();
+ return {
+ width: layout.get('width'),
+ height: layout.get('height')
+ };
+ }
- // mark each child node with its position (for nth calls)
- // "ofType" flag indicates whether we're indexing for nth-of-type
- // rather than nth-child
- index: function(parentNode, reverse, ofType) {
- parentNode._counted = true;
- if (reverse) {
- for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
- var node = nodes[i];
- if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
- }
- } else {
- for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
- if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+ function getOffsetParent(element) {
+ if (isDetached(element)) return $(document.body);
+
+ var isInline = (Element.getStyle(element, 'display') === 'inline');
+ if (!isInline && element.offsetParent) return $(element.offsetParent);
+ if (element === document.body) return $(element);
+
+ while ((element = element.parentNode) && element !== document.body) {
+ if (Element.getStyle(element, 'position') !== 'static') {
+ return (element.nodeName === 'HTML') ? $(document.body) : $(element);
}
- },
+ }
- // filters out duplicates and extends all nodes
- unique: function(nodes) {
- if (nodes.length == 0) return nodes;
- var results = [], n;
- for (var i = 0, l = nodes.length; i < l; i++)
- if (!(n = nodes[i])._counted) {
- n._counted = true;
- results.push(Element.extend(n));
- }
- return Selector.handlers.unmark(results);
- },
+ return $(document.body);
+ }
- // COMBINATOR FUNCTIONS
- descendant: function(nodes) {
- var h = Selector.handlers;
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- h.concat(results, node.getElementsByTagName('*'));
- return results;
- },
- child: function(nodes) {
- var h = Selector.handlers;
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- for (var j = 0, child; child = node.childNodes[j]; j++)
- if (child.nodeType == 1 && child.tagName != '!') results.push(child);
- }
- return results;
- },
+ function cumulativeOffset(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return new Element.Offset(valueL, valueT);
+ }
+
+ function positionedOffset(element) {
+ var layout = element.getLayout();
- adjacent: function(nodes) {
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- var next = this.nextElementSibling(node);
- if (next) results.push(next);
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if (isBody(element)) break;
+ var p = Element.getStyle(element, 'position');
+ if (p !== 'static') break;
}
- return results;
- },
+ } while (element);
- laterSibling: function(nodes) {
- var h = Selector.handlers;
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- h.concat(results, Element.nextSiblings(node));
- return results;
- },
+ valueL -= layout.get('margin-top');
+ valueT -= layout.get('margin-left');
- nextElementSibling: function(node) {
- while (node = node.nextSibling)
- if (node.nodeType == 1) return node;
- return null;
- },
+ return new Element.Offset(valueL, valueT);
+ }
- previousElementSibling: function(node) {
- while (node = node.previousSibling)
- if (node.nodeType == 1) return node;
- return null;
- },
+ function cumulativeScrollOffset(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return new Element.Offset(valueL, valueT);
+ }
- // TOKEN FUNCTIONS
- tagName: function(nodes, root, tagName, combinator) {
- tagName = tagName.toUpperCase();
- var results = [], h = Selector.handlers;
- if (nodes) {
- if (combinator) {
- // fastlane for ordinary descendant combinators
- if (combinator == "descendant") {
- for (var i = 0, node; node = nodes[i]; i++)
- h.concat(results, node.getElementsByTagName(tagName));
- return results;
- } else nodes = this[combinator](nodes);
- if (tagName == "*") return nodes;
- }
- for (var i = 0, node; node = nodes[i]; i++)
- if (node.tagName.toUpperCase() == tagName) results.push(node);
- return results;
- } else return root.getElementsByTagName(tagName);
- },
+ function viewportOffset(forElement) {
+ var valueT = 0, valueL = 0, docBody = document.body;
- id: function(nodes, root, id, combinator) {
- var targetNode = $(id), h = Selector.handlers;
- if (!targetNode) return [];
- if (!nodes && root == document) return [targetNode];
- if (nodes) {
- if (combinator) {
- if (combinator == 'child') {
- for (var i = 0, node; node = nodes[i]; i++)
- if (targetNode.parentNode == node) return [targetNode];
- } else if (combinator == 'descendant') {
- for (var i = 0, node; node = nodes[i]; i++)
- if (Element.descendantOf(targetNode, node)) return [targetNode];
- } else if (combinator == 'adjacent') {
- for (var i = 0, node; node = nodes[i]; i++)
- if (Selector.handlers.previousElementSibling(targetNode) == node)
- return [targetNode];
- } else nodes = h[combinator](nodes);
- }
- for (var i = 0, node; node = nodes[i]; i++)
- if (node == targetNode) return [targetNode];
- return [];
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == docBody &&
+ Element.getStyle(element, 'position') == 'absolute') break;
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ if (element != docBody) {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
}
- return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
- },
+ } while (element = element.parentNode);
+ return new Element.Offset(valueL, valueT);
+ }
- className: function(nodes, root, className, combinator) {
- if (nodes && combinator) nodes = this[combinator](nodes);
- return Selector.handlers.byClassName(nodes, root, className);
- },
+ function absolutize(element) {
+ element = $(element);
- byClassName: function(nodes, root, className) {
- if (!nodes) nodes = Selector.handlers.descendant([root]);
- var needle = ' ' + className + ' ';
- for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
- nodeClassName = node.className;
- if (nodeClassName.length == 0) continue;
- if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
- results.push(node);
- }
- return results;
- },
+ if (Element.getStyle(element, 'position') === 'absolute') {
+ return element;
+ }
- attrPresence: function(nodes, root, attr) {
- if (!nodes) nodes = root.getElementsByTagName("*");
- var results = [];
- for (var i = 0, node; node = nodes[i]; i++)
- if (Element.hasAttribute(node, attr)) results.push(node);
- return results;
- },
+ var offsetParent = getOffsetParent(element);
+ var eOffset = element.viewportOffset(),
+ pOffset = offsetParent.viewportOffset();
- attr: function(nodes, root, attr, value, operator) {
- if (!nodes) nodes = root.getElementsByTagName("*");
- var handler = Selector.operators[operator], results = [];
- for (var i = 0, node; node = nodes[i]; i++) {
- var nodeValue = Element.readAttribute(node, attr);
- if (nodeValue === null) continue;
- if (handler(nodeValue, value)) results.push(node);
- }
- return results;
- },
+ var offset = eOffset.relativeTo(pOffset);
+ var layout = element.getLayout();
- pseudo: function(nodes, name, value, root, combinator) {
- if (nodes && combinator) nodes = this[combinator](nodes);
- if (!nodes) nodes = root.getElementsByTagName("*");
- return Selector.pseudos[name](nodes, value, root);
+ element.store('prototype_absolutize_original_styles', {
+ left: element.getStyle('left'),
+ top: element.getStyle('top'),
+ width: element.getStyle('width'),
+ height: element.getStyle('height')
+ });
+
+ element.setStyle({
+ position: 'absolute',
+ top: offset.top + 'px',
+ left: offset.left + 'px',
+ width: layout.get('width') + 'px',
+ height: layout.get('height') + 'px'
+ });
+
+ return element;
+ }
+
+ function relativize(element) {
+ element = $(element);
+ if (Element.getStyle(element, 'position') === 'relative') {
+ return element;
}
- },
- pseudos: {
- 'first-child': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- if (Selector.handlers.previousElementSibling(node)) continue;
- results.push(node);
- }
- return results;
- },
- 'last-child': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- if (Selector.handlers.nextElementSibling(node)) continue;
- results.push(node);
- }
- return results;
- },
- 'only-child': function(nodes, value, root) {
- var h = Selector.handlers;
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
- results.push(node);
- return results;
- },
- 'nth-child': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, formula, root);
- },
- 'nth-last-child': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, formula, root, true);
- },
- 'nth-of-type': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, formula, root, false, true);
- },
- 'nth-last-of-type': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, formula, root, true, true);
- },
- 'first-of-type': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, "1", root, false, true);
- },
- 'last-of-type': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, "1", root, true, true);
- },
- 'only-of-type': function(nodes, formula, root) {
- var p = Selector.pseudos;
- return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
- },
+ var originalStyles =
+ element.retrieve('prototype_absolutize_original_styles');
- // handles the an+b logic
- getIndices: function(a, b, total) {
- if (a == 0) return b > 0 ? [b] : [];
- return $R(1, total).inject([], function(memo, i) {
- if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
- return memo;
- });
- },
+ if (originalStyles) element.setStyle(originalStyles);
+ return element;
+ }
- // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
- nth: function(nodes, formula, root, reverse, ofType) {
- if (nodes.length == 0) return [];
- if (formula == 'even') formula = '2n+0';
- if (formula == 'odd') formula = '2n+1';
- var h = Selector.handlers, results = [], indexed = [], m;
- h.mark(nodes);
- for (var i = 0, node; node = nodes[i]; i++) {
- if (!node.parentNode._counted) {
- h.index(node.parentNode, reverse, ofType);
- indexed.push(node.parentNode);
- }
- }
- if (formula.match(/^\d+$/)) { // just a number
- formula = Number(formula);
- for (var i = 0, node; node = nodes[i]; i++)
- if (node.nodeIndex == formula) results.push(node);
- } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
- if (m[1] == "-") m[1] = -1;
- var a = m[1] ? Number(m[1]) : 1;
- var b = m[2] ? Number(m[2]) : 0;
- var indices = Selector.pseudos.getIndices(a, b, nodes.length);
- for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
- for (var j = 0; j < l; j++)
- if (node.nodeIndex == indices[j]) results.push(node);
+ Element.addMethods({
+ getLayout: getLayout,
+ measure: measure,
+ getDimensions: getDimensions,
+ getOffsetParent: getOffsetParent,
+ cumulativeOffset: cumulativeOffset,
+ positionedOffset: positionedOffset,
+ cumulativeScrollOffset: cumulativeScrollOffset,
+ viewportOffset: viewportOffset,
+ absolutize: absolutize,
+ relativize: relativize
+ });
+
+ function isBody(element) {
+ return element.nodeName.toUpperCase() === 'BODY';
+ }
+
+ function isDetached(element) {
+ return element !== document.body &&
+ !Element.descendantOf(element, document.body);
+ }
+
+ if ('getBoundingClientRect' in document.documentElement) {
+ Element.addMethods({
+ viewportOffset: function(element) {
+ element = $(element);
+ if (isDetached(element)) return new Element.Offset(0, 0);
+
+ var rect = element.getBoundingClientRect(),
+ docEl = document.documentElement;
+ return new Element.Offset(rect.left - docEl.clientLeft,
+ rect.top - docEl.clientTop);
+ },
+
+ positionedOffset: function(element) {
+ element = $(element);
+ var parent = element.getOffsetParent();
+ if (isDetached(element)) return new Element.Offset(0, 0);
+
+ if (element.offsetParent &&
+ element.offsetParent.nodeName.toUpperCase() === 'HTML') {
+ return positionedOffset(element);
}
- }
- h.unmark(nodes);
- h.unmark(indexed);
- return results;
- },
- 'empty': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- // IE treats comments as element nodes
- if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
- results.push(node);
- }
- return results;
- },
+ var eOffset = element.viewportOffset(),
+ pOffset = isBody(parent) ? viewportOffset(parent) :
+ parent.viewportOffset();
+ var retOffset = eOffset.relativeTo(pOffset);
- 'not': function(nodes, selector, root) {
- var h = Selector.handlers, selectorType, m;
- var exclusions = new Selector(selector).findElements(root);
- h.mark(exclusions);
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (!node._counted) results.push(node);
- h.unmark(exclusions);
- return results;
- },
+ var layout = element.getLayout();
+ var top = retOffset.top - layout.get('margin-top');
+ var left = retOffset.left - layout.get('margin-left');
- 'enabled': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (!node.disabled) results.push(node);
- return results;
- },
+ return new Element.Offset(left, top);
+ }
+ });
+ }
+})();
+window.$$ = function() {
+ var expression = $A(arguments).join(', ');
+ return Prototype.Selector.select(expression, document);
+};
- 'disabled': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (node.disabled) results.push(node);
- return results;
- },
+Prototype.Selector = (function() {
- 'checked': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (node.checked) results.push(node);
- return results;
- }
- },
+ function select() {
+ throw new Error('Method "Prototype.Selector.select" must be defined.');
+ }
- operators: {
- '=': function(nv, v) { return nv == v; },
- '!=': function(nv, v) { return nv != v; },
- '^=': function(nv, v) { return nv.startsWith(v); },
- '$=': function(nv, v) { return nv.endsWith(v); },
- '*=': function(nv, v) { return nv.include(v); },
- '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
- '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
- },
+ function match() {
+ throw new Error('Method "Prototype.Selector.match" must be defined.');
+ }
- matchElements: function(elements, expression) {
- var matches = new Selector(expression).findElements(), h = Selector.handlers;
- h.mark(matches);
- for (var i = 0, results = [], element; element = elements[i]; i++)
- if (element._counted) results.push(element);
- h.unmark(matches);
- return results;
- },
+ function find(elements, expression, index) {
+ index = index || 0;
+ var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
- findElement: function(elements, expression, index) {
- if (Object.isNumber(expression)) {
- index = expression; expression = false;
+ for (i = 0; i < length; i++) {
+ if (match(elements[i], expression) && index == matchIndex++) {
+ return Element.extend(elements[i]);
+ }
}
- return Selector.matchElements(elements, expression || '*')[index || 0];
- },
+ }
- findChildElements: function(element, expressions) {
- var exprs = expressions.join(',');
- expressions = [];
- exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
- expressions.push(m[1].strip());
- });
- var results = [], h = Selector.handlers;
- for (var i = 0, l = expressions.length, selector; i < l; i++) {
- selector = new Selector(expressions[i].strip());
- h.concat(results, selector.findElements(element));
+ function extendElements(elements) {
+ for (var i = 0, length = elements.length; i < length; i++) {
+ Element.extend(elements[i]);
}
- return (l > 1) ? h.unique(results) : results;
+ return elements;
}
-});
-if (Prototype.Browser.IE) {
- // IE returns comment nodes on getElementsByTagName("*").
- // Filter them out.
- Selector.handlers.concat = function(a, b) {
- for (var i = 0, node; node = b[i]; i++)
- if (node.tagName !== "!") a.push(node);
- return a;
+
+ var K = Prototype.K;
+
+ return {
+ select: select,
+ match: match,
+ find: find,
+ extendElements: (Element.extend === K) ? K : extendElements,
+ extendElement: Element.extend
};
+})();
+Prototype._original_property = window.Sizzle;
+/*!
+ * 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;
+
+[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, check, mode, extra, prune = true, contextXML = isXML(context),
+ soFar = selector;
+
+ 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 {
+ 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 ) {
+ throw "Syntax error, unrecognized expression: " + (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.match[ type ].exec( expr )) != null ) {
+ var filter = Expr.filter[ type ], found, item;
+ anyFound = false;
+
+ 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;
+ }
+ }
+ }
+
+ if ( expr == old ) {
+ if ( anyFound == null ) {
+ throw "Syntax error, unrecognized expression: " + expr;
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+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\(\)]*)+)\2\))?/
+ },
+ leftMatch: {},
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+ attrHandle: {
+ href: function(elem){
+ return elem.getAttribute("href");
+ }
+ },
+ relative: {
+ "+": function(checkSet, part, isXML){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !/\W/.test(part),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag && !isXML ) {
+ part = part.toUpperCase();
+ }
+
+ 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 === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+ ">": function(checkSet, part, isXML){
+ var isPartStr = typeof part === "string";
+
+ if ( isPartStr && !/\W/.test(part) ) {
+ part = isXML ? part : part.toUpperCase();
+
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName === 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 ( !/\W/.test(part) ) {
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
+ 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 = isXML ? part : part.toUpperCase();
+ 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, isXML){
+ 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 + " ").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){
+ for ( var i = 0; curLoop[i] === false; i++ ){}
+ return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+ },
+ CHILD: function(match){
+ if ( match[1] == "nth" ) {
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+ match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+
+ 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 ( ( 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){
+ 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.toUpperCase() === "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 || "").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;
+ }
+ },
+ 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 === 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 );
+}
+
+var makeArray = function(array, results) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 );
+
+} 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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;
+ };
+}
+
+(function(){
+ var form = document.createElement("div"),
+ id = "script" + (new Date).getTime();
+ form.innerHTML = "<a name='" + id + "'/>";
+
+ var root = document.documentElement;
+ root.insertBefore( form, root.firstChild );
+
+ 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(){
+
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function(match, context){
+ var results = context.getElementsByTagName(match[1]);
+
+ 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;
+ };
+ }
+
+ div.innerHTML = "<a href='#'></a>";
+ 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 = "<p class='TEST'></p>";
+
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function(query, context, extra, seed){
+ context = context || document;
+
+ 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
+})();
+
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
+ var div = document.createElement("div");
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ if ( div.getElementsByClassName("e").length === 0 )
+ return;
+
+ 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 ) {
+ var sibDir = dir == "previousSibling" && !isXML;
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ 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 === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
}
-function $$() {
- return Selector.findChildElements(document, $A(arguments));
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ var sibDir = dir == "previousSibling" && !isXML;
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ 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){
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
+};
+
+var posProcess = function(selector, context){
+ var tmpSet = [], later = "", match,
+ root = context.nodeType ? [context] : context;
+
+ 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 );
+};
+
+
+window.Sizzle = Sizzle;
+
+})();
+
+;(function(engine) {
+ var extendElements = Prototype.Selector.extendElements;
+
+ function select(selector, scope) {
+ return extendElements(engine(selector, scope || document));
+ }
+
+ function match(element, selector) {
+ return engine.matches(selector, [element]).length == 1;
+ }
+
+ Prototype.Selector.engine = engine;
+ Prototype.Selector.select = select;
+ Prototype.Selector.match = match;
+})(Sizzle);
+
+window.Sizzle = Prototype._original_property;
+delete Prototype._original_property;
+
var Form = {
reset: function(form) {
- $(form).reset();
+ form = $(form);
+ form.reset();
return form;
},
@@ -3392,10 +4967,9 @@ var Form = {
var data = elements.inject({ }, function(result, element) {
if (!element.disabled && element.name) {
key = element.name; value = $(element).getValue();
- if (value != null && (element.type != 'submit' || (!submitted &&
+ if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
submit !== false && (!submit || key == submit) && (submitted = true)))) {
if (key in result) {
- // a key is already present; construct an array of values
if (!Object.isArray(result[key])) result[key] = [result[key]];
result[key].push(value);
}
@@ -3415,13 +4989,18 @@ Form.Methods = {
},
getElements: function(form) {
- return $A($(form).getElementsByTagName('*')).inject([],
- function(elements, child) {
- if (Form.Element.Serializers[child.tagName.toLowerCase()])
- elements.push(Element.extend(child));
- return elements;
- }
- );
+ var elements = $(form).getElementsByTagName('*'),
+ element,
+ arr = [ ],
+ serializers = Form.Element.Serializers;
+ for (var i = 0; element = elements[i]; i++) {
+ arr.push(element);
+ }
+ return arr.inject([], function(elements, child) {
+ if (serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ })
},
getInputs: function(form, typeName, name) {
@@ -3461,7 +5040,7 @@ Form.Methods = {
}).sortBy(function(element) { return element.tabIndex }).first();
return firstByIndex ? firstByIndex : elements.find(function(element) {
- return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ return /^(?:input|select|textarea)$/i.test(element.tagName);
});
},
@@ -3492,6 +5071,7 @@ Form.Methods = {
/*--------------------------------------------------------------------------*/
+
Form.Element = {
focus: function(element) {
$(element).focus();
@@ -3505,6 +5085,7 @@ Form.Element = {
};
Form.Element.Methods = {
+
serialize: function(element) {
element = $(element);
if (!element.disabled && element.name) {
@@ -3545,7 +5126,7 @@ Form.Element.Methods = {
try {
element.focus();
if (element.select && (element.tagName.toLowerCase() != 'input' ||
- !['button', 'reset', 'submit'].include(element.type)))
+ !(/^(?:button|reset|submit)$/i.test(element.type))))
element.select();
} catch (e) { }
return element;
@@ -3553,7 +5134,6 @@ Form.Element.Methods = {
disable: function(element) {
element = $(element);
- element.blur();
element.disabled = true;
return element;
},
@@ -3568,6 +5148,7 @@ Form.Element.Methods = {
/*--------------------------------------------------------------------------*/
var Field = Form.Element;
+
var $F = Form.Element.Methods.getValue;
/*--------------------------------------------------------------------------*/
@@ -3593,22 +5174,22 @@ Form.Element.Serializers = {
else element.value = value;
},
- select: function(element, index) {
- if (Object.isUndefined(index))
+ select: function(element, value) {
+ if (Object.isUndefined(value))
return this[element.type == 'select-one' ?
'selectOne' : 'selectMany'](element);
else {
- var opt, value, single = !Object.isArray(index);
+ var opt, currentValue, single = !Object.isArray(value);
for (var i = 0, length = element.length; i < length; i++) {
opt = element.options[i];
- value = this.optionValue(opt);
+ currentValue = this.optionValue(opt);
if (single) {
- if (value == index) {
+ if (currentValue == value) {
opt.selected = true;
return;
}
}
- else opt.selected = index.include(value);
+ else opt.selected = value.include(currentValue);
}
}
},
@@ -3630,13 +5211,13 @@ Form.Element.Serializers = {
},
optionValue: function(opt) {
- // extend element because hasAttribute may not be native
return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
}
};
/*--------------------------------------------------------------------------*/
+
Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
initialize: function($super, element, frequency, callback) {
$super(callback, frequency);
@@ -3718,323 +5299,475 @@ Form.EventObserver = Class.create(Abstract.EventObserver, {
return Form.serialize(this.element);
}
});
-if (!window.Event) var Event = { };
-
-Object.extend(Event, {
- KEY_BACKSPACE: 8,
- KEY_TAB: 9,
- KEY_RETURN: 13,
- KEY_ESC: 27,
- KEY_LEFT: 37,
- KEY_UP: 38,
- KEY_RIGHT: 39,
- KEY_DOWN: 40,
- KEY_DELETE: 46,
- KEY_HOME: 36,
- KEY_END: 35,
- KEY_PAGEUP: 33,
- KEY_PAGEDOWN: 34,
- KEY_INSERT: 45,
-
- cache: { },
-
- relatedTarget: function(event) {
- var element;
- switch(event.type) {
- case 'mouseover': element = event.fromElement; break;
- case 'mouseout': element = event.toElement; break;
- default: return null;
- }
- return Element.extend(element);
- }
-});
+(function() {
-Event.Methods = (function() {
- var isButton;
+ var Event = {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+ KEY_INSERT: 45,
+
+ cache: {}
+ };
+
+ var docEl = document.documentElement;
+ var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
+ && 'onmouseleave' in docEl;
+ var _isButton;
if (Prototype.Browser.IE) {
var buttonMap = { 0: 1, 1: 4, 2: 2 };
- isButton = function(event, code) {
- return event.button == buttonMap[code];
+ _isButton = function(event, code) {
+ return event.button === buttonMap[code];
};
-
} else if (Prototype.Browser.WebKit) {
- isButton = function(event, code) {
+ _isButton = function(event, code) {
switch (code) {
case 0: return event.which == 1 && !event.metaKey;
case 1: return event.which == 1 && event.metaKey;
default: return false;
}
};
-
} else {
- isButton = function(event, code) {
+ _isButton = function(event, code) {
return event.which ? (event.which === code + 1) : (event.button === code);
};
}
- return {
- isLeftClick: function(event) { return isButton(event, 0) },
- isMiddleClick: function(event) { return isButton(event, 1) },
- isRightClick: function(event) { return isButton(event, 2) },
+ function isLeftClick(event) { return _isButton(event, 0) }
- element: function(event) {
- var node = Event.extend(event).target;
- return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
- },
+ function isMiddleClick(event) { return _isButton(event, 1) }
- findElement: function(event, expression) {
- var element = Event.element(event);
- if (!expression) return element;
- var elements = [element].concat(element.ancestors());
- return Selector.findElement(elements, expression, 0);
- },
+ function isRightClick(event) { return _isButton(event, 2) }
- pointer: function(event) {
- return {
- x: event.pageX || (event.clientX +
- (document.documentElement.scrollLeft || document.body.scrollLeft)),
- y: event.pageY || (event.clientY +
- (document.documentElement.scrollTop || document.body.scrollTop))
- };
- },
+ function element(event) {
+ event = Event.extend(event);
+
+ var node = event.target, type = event.type,
+ currentTarget = event.currentTarget;
+
+ if (currentTarget && currentTarget.tagName) {
+ if (type === 'load' || type === 'error' ||
+ (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
+ && currentTarget.type === 'radio'))
+ node = currentTarget;
+ }
- pointerX: function(event) { return Event.pointer(event).x },
- pointerY: function(event) { return Event.pointer(event).y },
+ if (node.nodeType == Node.TEXT_NODE)
+ node = node.parentNode;
- stop: function(event) {
- Event.extend(event);
- event.preventDefault();
- event.stopPropagation();
- event.stopped = true;
+ return Element.extend(node);
+ }
+
+ function findElement(event, expression) {
+ var element = Event.element(event);
+ if (!expression) return element;
+ while (element) {
+ if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
+ return Element.extend(element);
+ }
+ element = element.parentNode;
}
+ }
+
+ function pointer(event) {
+ return { x: pointerX(event), y: pointerY(event) };
+ }
+
+ function pointerX(event) {
+ var docElement = document.documentElement,
+ body = document.body || { scrollLeft: 0 };
+
+ return event.pageX || (event.clientX +
+ (docElement.scrollLeft || body.scrollLeft) -
+ (docElement.clientLeft || 0));
+ }
+
+ function pointerY(event) {
+ var docElement = document.documentElement,
+ body = document.body || { scrollTop: 0 };
+
+ return event.pageY || (event.clientY +
+ (docElement.scrollTop || body.scrollTop) -
+ (docElement.clientTop || 0));
+ }
+
+
+ function stop(event) {
+ Event.extend(event);
+ event.preventDefault();
+ event.stopPropagation();
+
+ event.stopped = true;
+ }
+
+ Event.Methods = {
+ isLeftClick: isLeftClick,
+ isMiddleClick: isMiddleClick,
+ isRightClick: isRightClick,
+
+ element: element,
+ findElement: findElement,
+
+ pointer: pointer,
+ pointerX: pointerX,
+ pointerY: pointerY,
+
+ stop: stop
};
-})();
-Event.extend = (function() {
+
var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
m[name] = Event.Methods[name].methodize();
return m;
});
if (Prototype.Browser.IE) {
+ function _relatedTarget(event) {
+ var element;
+ switch (event.type) {
+ case 'mouseover': element = event.fromElement; break;
+ case 'mouseout': element = event.toElement; break;
+ default: return null;
+ }
+ return Element.extend(element);
+ }
+
Object.extend(methods, {
stopPropagation: function() { this.cancelBubble = true },
preventDefault: function() { this.returnValue = false },
- inspect: function() { return "[object Event]" }
+ inspect: function() { return '[object Event]' }
});
- return function(event) {
+ Event.extend = function(event, element) {
if (!event) return false;
if (event._extendedByPrototype) return event;
event._extendedByPrototype = Prototype.emptyFunction;
var pointer = Event.pointer(event);
+
Object.extend(event, {
- target: event.srcElement,
- relatedTarget: Event.relatedTarget(event),
+ target: event.srcElement || element,
+ relatedTarget: _relatedTarget(event),
pageX: pointer.x,
pageY: pointer.y
});
+
return Object.extend(event, methods);
};
-
} else {
- Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+ Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
Object.extend(Event.prototype, methods);
- return Prototype.K;
+ Event.extend = Prototype.K;
}
-})();
-Object.extend(Event, (function() {
- var cache = Event.cache;
+ function _createResponder(element, eventName, handler) {
+ var registry = Element.retrieve(element, 'prototype_event_registry');
- function getEventID(element) {
- if (element._eventID) return element._eventID;
- arguments.callee.id = arguments.callee.id || 1;
- return element._eventID = ++arguments.callee.id;
- }
-
- function getDOMEventName(eventName) {
- if (eventName && eventName.include(':')) return "dataavailable";
- return eventName;
- }
+ if (Object.isUndefined(registry)) {
+ CACHE.push(element);
+ registry = Element.retrieve(element, 'prototype_event_registry', $H());
+ }
- function getCacheForID(id) {
- return cache[id] = cache[id] || { };
- }
+ var respondersForEvent = registry.get(eventName);
+ if (Object.isUndefined(respondersForEvent)) {
+ respondersForEvent = [];
+ registry.set(eventName, respondersForEvent);
+ }
- function getWrappersForEventName(id, eventName) {
- var c = getCacheForID(id);
- return c[eventName] = c[eventName] || [];
- }
+ if (respondersForEvent.pluck('handler').include(handler)) return false;
- function createWrapper(element, eventName, handler) {
- var id = getEventID(element);
- var c = getWrappersForEventName(id, eventName);
- if (c.pluck("handler").include(handler)) return false;
+ var responder;
+ if (eventName.include(":")) {
+ responder = function(event) {
+ if (Object.isUndefined(event.eventName))
+ return false;
- var wrapper = function(event) {
- if (!Event || !Event.extend ||
- (event.eventName && event.eventName != eventName))
+ if (event.eventName !== eventName)
return false;
- Event.extend(event);
- handler.call(element, event)
- };
+ Event.extend(event, element);
+ handler.call(element, event);
+ };
+ } else {
+ if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
+ (eventName === "mouseenter" || eventName === "mouseleave")) {
+ if (eventName === "mouseenter" || eventName === "mouseleave") {
+ responder = function(event) {
+ Event.extend(event, element);
+
+ var parent = event.relatedTarget;
+ while (parent && parent !== element) {
+ try { parent = parent.parentNode; }
+ catch(e) { parent = element; }
+ }
- wrapper.handler = handler;
- c.push(wrapper);
- return wrapper;
- }
+ if (parent === element) return;
- function findWrapper(id, eventName, handler) {
- var c = getWrappersForEventName(id, eventName);
- return c.find(function(wrapper) { return wrapper.handler == handler });
- }
+ handler.call(element, event);
+ };
+ }
+ } else {
+ responder = function(event) {
+ Event.extend(event, element);
+ handler.call(element, event);
+ };
+ }
+ }
- function destroyWrapper(id, eventName, handler) {
- var c = getCacheForID(id);
- if (!c[eventName]) return false;
- c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+ responder.handler = handler;
+ respondersForEvent.push(responder);
+ return responder;
}
- function destroyCache() {
- for (var id in cache)
- for (var eventName in cache[id])
- cache[id][eventName] = null;
+ function _destroyCache() {
+ for (var i = 0, length = CACHE.length; i < length; i++) {
+ Event.stopObserving(CACHE[i]);
+ CACHE[i] = null;
+ }
}
- if (window.attachEvent) {
- window.attachEvent("onunload", destroyCache);
+ var CACHE = [];
+
+ if (Prototype.Browser.IE)
+ window.attachEvent('onunload', _destroyCache);
+
+ if (Prototype.Browser.WebKit)
+ window.addEventListener('unload', Prototype.emptyFunction, false);
+
+
+ var _getDOMEventName = Prototype.K,
+ translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
+
+ if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
+ _getDOMEventName = function(eventName) {
+ return (translations[eventName] || eventName);
+ };
}
- return {
- observe: function(element, eventName, handler) {
- element = $(element);
- var name = getDOMEventName(eventName);
+ function observe(element, eventName, handler) {
+ element = $(element);
- var wrapper = createWrapper(element, eventName, handler);
- if (!wrapper) return element;
+ var responder = _createResponder(element, eventName, handler);
- if (element.addEventListener) {
- element.addEventListener(name, wrapper, false);
- } else {
- element.attachEvent("on" + name, wrapper);
+ if (!responder) return element;
+
+ if (eventName.include(':')) {
+ if (element.addEventListener)
+ element.addEventListener("dataavailable", responder, false);
+ else {
+ element.attachEvent("ondataavailable", responder);
+ element.attachEvent("onfilterchange", responder);
}
+ } else {
+ var actualEventName = _getDOMEventName(eventName);
+
+ if (element.addEventListener)
+ element.addEventListener(actualEventName, responder, false);
+ else
+ element.attachEvent("on" + actualEventName, responder);
+ }
+
+ return element;
+ }
+
+ function stopObserving(element, eventName, handler) {
+ element = $(element);
+
+ var registry = Element.retrieve(element, 'prototype_event_registry');
+ if (!registry) return element;
+ if (!eventName) {
+ registry.each( function(pair) {
+ var eventName = pair.key;
+ stopObserving(element, eventName);
+ });
return element;
- },
+ }
- stopObserving: function(element, eventName, handler) {
- element = $(element);
- var id = getEventID(element), name = getDOMEventName(eventName);
+ var responders = registry.get(eventName);
+ if (!responders) return element;
- if (!handler && eventName) {
- getWrappersForEventName(id, eventName).each(function(wrapper) {
- element.stopObserving(eventName, wrapper.handler);
- });
- return element;
+ if (!handler) {
+ responders.each(function(r) {
+ stopObserving(element, eventName, r.handler);
+ });
+ return element;
+ }
- } else if (!eventName) {
- Object.keys(getCacheForID(id)).each(function(eventName) {
- element.stopObserving(eventName);
- });
- return element;
+ var responder = responders.find( function(r) { return r.handler === handler; });
+ if (!responder) return element;
+
+ if (eventName.include(':')) {
+ if (element.removeEventListener)
+ element.removeEventListener("dataavailable", responder, false);
+ else {
+ element.detachEvent("ondataavailable", responder);
+ element.detachEvent("onfilterchange", responder);
}
+ } else {
+ var actualEventName = _getDOMEventName(eventName);
+ if (element.removeEventListener)
+ element.removeEventListener(actualEventName, responder, false);
+ else
+ element.detachEvent('on' + actualEventName, responder);
+ }
- var wrapper = findWrapper(id, eventName, handler);
- if (!wrapper) return element;
+ registry.set(eventName, responders.without(responder));
- if (element.removeEventListener) {
- element.removeEventListener(name, wrapper, false);
- } else {
- element.detachEvent("on" + name, wrapper);
- }
+ return element;
+ }
- destroyWrapper(id, eventName, handler);
+ function fire(element, eventName, memo, bubble) {
+ element = $(element);
- return element;
- },
+ if (Object.isUndefined(bubble))
+ bubble = true;
- fire: function(element, eventName, memo) {
- element = $(element);
- if (element == document && document.createEvent && !element.dispatchEvent)
- element = document.documentElement;
+ if (element == document && document.createEvent && !element.dispatchEvent)
+ element = document.documentElement;
- if (document.createEvent) {
- var event = document.createEvent("HTMLEvents");
- event.initEvent("dataavailable", true, true);
- } else {
- var event = document.createEventObject();
- event.eventType = "ondataavailable";
- }
+ var event;
+ if (document.createEvent) {
+ event = document.createEvent('HTMLEvents');
+ event.initEvent('dataavailable', true, true);
+ } else {
+ event = document.createEventObject();
+ event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';
+ }
- event.eventName = eventName;
- event.memo = memo || { };
+ event.eventName = eventName;
+ event.memo = memo || { };
- if (document.createEvent) {
- element.dispatchEvent(event);
- } else {
- element.fireEvent(event.eventType, event);
- }
+ if (document.createEvent)
+ element.dispatchEvent(event);
+ else
+ element.fireEvent(event.eventType, event);
+
+ return Event.extend(event);
+ }
+
+ Event.Handler = Class.create({
+ initialize: function(element, eventName, selector, callback) {
+ this.element = $(element);
+ this.eventName = eventName;
+ this.selector = selector;
+ this.callback = callback;
+ this.handler = this.handleEvent.bind(this);
+ },
- return Event.extend(event);
+ start: function() {
+ Event.observe(this.element, this.eventName, this.handler);
+ return this;
+ },
+
+ stop: function() {
+ Event.stopObserving(this.element, this.eventName, this.handler);
+ return this;
+ },
+
+ handleEvent: function(event) {
+ var element = event.findElement(this.selector);
+ if (element) this.callback.call(this.element, event, element);
}
- };
-})());
+ });
-Object.extend(Event, Event.Methods);
+ function on(element, eventName, selector, callback) {
+ element = $(element);
+ if (Object.isFunction(selector) && Object.isUndefined(callback)) {
+ callback = selector, selector = null;
+ }
-Element.addMethods({
- fire: Event.fire,
- observe: Event.observe,
- stopObserving: Event.stopObserving
-});
+ return new Event.Handler(element, eventName, selector, callback).start();
+ }
-Object.extend(document, {
- fire: Element.Methods.fire.methodize(),
- observe: Element.Methods.observe.methodize(),
- stopObserving: Element.Methods.stopObserving.methodize()
-});
+ Object.extend(Event, Event.Methods);
+
+ Object.extend(Event, {
+ fire: fire,
+ observe: observe,
+ stopObserving: stopObserving,
+ on: on
+ });
+
+ Element.addMethods({
+ fire: fire,
+
+ observe: observe,
+
+ stopObserving: stopObserving,
+
+ on: on
+ });
+
+ Object.extend(document, {
+ fire: fire.methodize(),
+
+ observe: observe.methodize(),
+
+ stopObserving: stopObserving.methodize(),
+
+ on: on.methodize(),
+
+ loaded: false
+ });
+
+ if (window.Event) Object.extend(window.Event, Event);
+ else window.Event = Event;
+})();
(function() {
/* Support for the DOMContentLoaded event is based on work by Dan Webb,
- Matthias Miller, Dean Edwards and John Resig. */
+ Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
- var timer, fired = false;
+ var timer;
function fireContentLoadedEvent() {
- if (fired) return;
- if (timer) window.clearInterval(timer);
- document.fire("dom:loaded");
- fired = true;
+ if (document.loaded) return;
+ if (timer) window.clearTimeout(timer);
+ document.loaded = true;
+ document.fire('dom:loaded');
}
- if (document.addEventListener) {
- if (Prototype.Browser.WebKit) {
- timer = window.setInterval(function() {
- if (/loaded|complete/.test(document.readyState))
- fireContentLoadedEvent();
- }, 0);
-
- Event.observe(window, "load", fireContentLoadedEvent);
+ function checkReadyState() {
+ if (document.readyState === 'complete') {
+ document.stopObserving('readystatechange', checkReadyState);
+ fireContentLoadedEvent();
+ }
+ }
- } else {
- document.addEventListener("DOMContentLoaded",
- fireContentLoadedEvent, false);
+ function pollDoScroll() {
+ try { document.documentElement.doScroll('left'); }
+ catch(e) {
+ timer = pollDoScroll.defer();
+ return;
}
+ fireContentLoadedEvent();
+ }
+ if (document.addEventListener) {
+ document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
} else {
- document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
- $("__onDOMContentLoaded").onreadystatechange = function() {
- if (this.readyState == "complete") {
- this.onreadystatechange = null;
- fireContentLoadedEvent();
- }
- };
+ document.observe('readystatechange', checkReadyState);
+ if (window == top)
+ timer = pollDoScroll.defer();
}
+
+ Event.observe(window, 'load', fireContentLoadedEvent);
})();
+
+Element.addMethods();
+
/*------------------------------- DEPRECATED -------------------------------*/
Hash.toQueryString = Object.toQueryString;
@@ -4063,16 +5796,9 @@ var Insertion = {
var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
-// This should be moved to script.aculo.us; notice the deprecated methods
-// further below, that map to the newer Element methods.
var Position = {
- // set to true if needed, warning: firefox performance problems
- // NOT neeeded for page scrolling, only if draggable contained in
- // scrollable elements
includeScrollOffsets: false,
- // must be called before calling withinIncludingScrolloffset, every time the
- // page is scrolled
prepare: function() {
this.deltaX = window.pageXOffset
|| document.documentElement.scrollLeft
@@ -4084,7 +5810,6 @@ var Position = {
|| 0;
},
- // caches x/y coordinate pair to use with overlap
within: function(element, x, y) {
if (this.includeScrollOffsets)
return this.withinIncludingScrolloffsets(element, x, y);
@@ -4111,7 +5836,6 @@ var Position = {
this.xcomp < this.offset[0] + element.offsetWidth);
},
- // within must be called directly before
overlap: function(mode, element) {
if (!mode) return 0;
if (mode == 'vertical')
@@ -4122,7 +5846,6 @@ var Position = {
element.offsetWidth;
},
- // Deprecation layer -- use newer Element methods now (1.5.2).
cumulativeOffset: Element.Methods.cumulativeOffset,
@@ -4222,4 +5945,57 @@ Object.extend(Element.ClassNames.prototype, Enumerable);
/*--------------------------------------------------------------------------*/
-Element.addMethods(); \ No newline at end of file
+(function() {
+ window.Selector = Class.create({
+ initialize: function(expression) {
+ this.expression = expression.strip();
+ },
+
+ findElements: function(rootElement) {
+ return Prototype.Selector.select(this.expression, rootElement);
+ },
+
+ match: function(element) {
+ return Prototype.Selector.match(element, this.expression);
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector: " + this.expression + ">";
+ }
+ });
+
+ Object.extend(Selector, {
+ matchElements: function(elements, expression) {
+ var match = Prototype.Selector.match,
+ results = [];
+
+ for (var i = 0, length = elements.length; i < length; i++) {
+ var element = elements[i];
+ if (match(element, expression)) {
+ results.push(Element.extend(element));
+ }
+ }
+ return results;
+ },
+
+ findElement: function(elements, expression, index) {
+ index = index || 0;
+ var matchIndex = 0, element;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
+ return Element.extend(element);
+ }
+ }
+ },
+
+ findChildElements: function(element, expressions) {
+ var selector = expressions.toArray().join(', ');
+ return Prototype.Selector.select(selector, element || document);
+ }
+ });
+})();
diff --git a/public/javascripts/rails.js b/public/javascripts/rails.js
new file mode 100644
index 000000000..5cea13c15
--- /dev/null
+++ b/public/javascripts/rails.js
@@ -0,0 +1,202 @@
+(function() {
+ Ajax.Responders.register({
+ onCreate: function(request) {
+ var token = $$('meta[name=csrf-token]')[0];
+ if (token) {
+ if (!request.options.requestHeaders) request.options.requestHeaders = {};
+ request.options.requestHeaders['X-CSRF-Token'] = token.readAttribute('content');
+ }
+ }
+ });
+
+ // Technique from Juriy Zaytsev
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+ function isEventSupported(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;
+ }
+
+ function isForm(element) {
+ return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM';
+ }
+
+ function isInput(element) {
+ if (Object.isElement(element)) {
+ var name = element.nodeName.toUpperCase();
+ return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA';
+ }
+ else return false;
+ }
+
+ var submitBubbles = isEventSupported('submit'),
+ changeBubbles = isEventSupported('change');
+
+ if (!submitBubbles || !changeBubbles) {
+ // augment the Event.Handler class to observe custom events when needed
+ Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
+ function(init, element, eventName, selector, callback) {
+ init(element, eventName, selector, callback);
+ // is the handler being attached to an element that doesn't support this event?
+ if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
+ (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
+ // "submit" => "emulated:submit"
+ this.eventName = 'emulated:' + this.eventName;
+ }
+ }
+ );
+ }
+
+ if (!submitBubbles) {
+ // discover forms on the page by observing focus events which always bubble
+ document.on('focusin', 'form', function(focusEvent, form) {
+ // special handler for the real "submit" event (one-time operation)
+ if (!form.retrieve('emulated:submit')) {
+ form.on('submit', function(submitEvent) {
+ var emulated = form.fire('emulated:submit', submitEvent, true);
+ // if custom event received preventDefault, cancel the real one too
+ if (emulated.returnValue === false) submitEvent.preventDefault();
+ });
+ form.store('emulated:submit', true);
+ }
+ });
+ }
+
+ if (!changeBubbles) {
+ // discover form inputs on the page
+ document.on('focusin', 'input, select, textarea', function(focusEvent, input) {
+ // special handler for real "change" events
+ if (!input.retrieve('emulated:change')) {
+ input.on('change', function(changeEvent) {
+ input.fire('emulated:change', changeEvent, true);
+ });
+ input.store('emulated:change', true);
+ }
+ });
+ }
+
+ function handleRemote(element) {
+ var method, url, params;
+
+ var event = element.fire("ajax:before");
+ if (event.stopped) return false;
+
+ if (element.tagName.toLowerCase() === 'form') {
+ method = element.readAttribute('method') || 'post';
+ url = element.readAttribute('action');
+ // serialize the form with respect to the submit button that was pressed
+ params = element.serialize({ submit: element.retrieve('rails:submit-button') });
+ // clear the pressed submit button information
+ element.store('rails:submit-button', null);
+ } else {
+ method = element.readAttribute('data-method') || 'get';
+ url = element.readAttribute('href');
+ params = {};
+ }
+
+ new Ajax.Request(url, {
+ method: method,
+ parameters: params,
+ evalScripts: true,
+
+ onCreate: function(response) { element.fire("ajax:create", response); },
+ onComplete: function(response) { element.fire("ajax:complete", response); },
+ onSuccess: function(response) { element.fire("ajax:success", response); },
+ onFailure: function(response) { element.fire("ajax:failure", response); }
+ });
+
+ element.fire("ajax:after");
+ }
+
+ function insertHiddenField(form, name, value) {
+ form.insert(new Element('input', { type: 'hidden', name: name, value: value }));
+ }
+
+ function handleMethod(element) {
+ var method = element.readAttribute('data-method'),
+ url = element.readAttribute('href'),
+ csrf_param = $$('meta[name=csrf-param]')[0],
+ csrf_token = $$('meta[name=csrf-token]')[0];
+
+ var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
+ $(element.parentNode).insert(form);
+
+ if (method !== 'post') {
+ insertHiddenField(form, '_method', method);
+ }
+
+ if (csrf_param) {
+ insertHiddenField(form, csrf_param.readAttribute('content'), csrf_token.readAttribute('content'));
+ }
+
+ form.submit();
+ }
+
+ function disableFormElements(form) {
+ form.select('input[type=submit][data-disable-with]').each(function(input) {
+ input.store('rails:original-value', input.getValue());
+ input.setValue(input.readAttribute('data-disable-with')).disable();
+ });
+ }
+
+ function enableFormElements(form) {
+ form.select('input[type=submit][data-disable-with]').each(function(input) {
+ input.setValue(input.retrieve('rails:original-value')).enable();
+ });
+ }
+
+ function allowAction(element) {
+ var message = element.readAttribute('data-confirm');
+ return !message || confirm(message);
+ }
+
+ document.on('click', 'a[data-confirm], a[data-remote], a[data-method]', function(event, link) {
+ if (!allowAction(link)) {
+ event.stop();
+ return false;
+ }
+
+ if (link.readAttribute('data-remote')) {
+ handleRemote(link);
+ event.stop();
+ } else if (link.readAttribute('data-method')) {
+ handleMethod(link);
+ event.stop();
+ }
+ });
+
+ document.on("click", "form input[type=submit], form button[type=submit], form button:not([type])", function(event, button) {
+ // register the pressed submit button
+ event.findElement('form').store('rails:submit-button', button.name || false);
+ });
+
+ document.on("submit", function(event) {
+ var form = event.findElement();
+
+ if (!allowAction(form)) {
+ event.stop();
+ return false;
+ }
+
+ if (form.readAttribute('data-remote')) {
+ handleRemote(form);
+ event.stop();
+ } else {
+ disableFormElements(form);
+ }
+ });
+
+ document.on('ajax:create', 'form', function(event, form) {
+ if (form == event.findElement()) disableFormElements(form);
+ });
+
+ document.on('ajax:complete', 'form', function(event, form) {
+ if (form == event.findElement()) enableFormElements(form);
+ });
+})();
diff --git a/public/stylesheets/main.css b/public/stylesheets/main.css
index 23c4b32ae..0bf88b0f2 100644
--- a/public/stylesheets/main.css
+++ b/public/stylesheets/main.css
@@ -744,6 +744,10 @@ padding:4px;
margin:0.5em 0;
}
+#everypage .popup-close {
+ text-align: right;
+}
+
#game_sidebar {
float:right;
clear:none;
diff --git a/script/cache-incoming-emails b/script/cache-incoming-emails
index 0b3069bd7..0b3069bd7 100644..100755
--- a/script/cache-incoming-emails
+++ b/script/cache-incoming-emails
diff --git a/script/handle-mail-replies.rb b/script/handle-mail-replies.rb
index c05dca453..73fca33c3 100755
--- a/script/handle-mail-replies.rb
+++ b/script/handle-mail-replies.rb
@@ -20,9 +20,11 @@ $:.push(File.join($alaveteli_dir, "lib", "mail_handler"))
require "configuration"
MySociety::Config.set_file(File.join($alaveteli_dir, 'config', 'general'), true)
MySociety::Config.load_default
-
-require 'action_mailer'
require 'mail_handler'
+if RUBY_VERSION.to_f >= 1.9
+ # the default encoding for IO is utf-8, and we use utf-8 internally
+ Encoding.default_external = Encoding.default_internal = Encoding::UTF_8
+end
def main(in_test_mode)
Dir.chdir($alaveteli_dir) do
@@ -47,16 +49,17 @@ def main(in_test_mode)
return 1
end
+ content_type = MailHandler.get_content_type(message)
# If we are still here, there are no permanent failures,
# so if the message is a multipart/report then it must be
# reporting a temporary failure. In this case we discard it
- if message.content_type == "multipart/report"
+ if content_type == "multipart/report"
return 1
end
# Another style of temporary failure message
- subject = message.header_string("Subject")
- if message.content_type == "multipart/mixed" && subject == "Delivery Status Notification (Delay)"
+ subject = MailHandler.get_header_string("Subject", message)
+ if content_type == "multipart/mixed" && subject == "Delivery Status Notification (Delay)"
return 1
end
@@ -72,26 +75,26 @@ def main(in_test_mode)
end
def permanently_failed_addresses(message)
- if message.header_string("Return-Path") == "<>"
+ if MailHandler.empty_return_path?(message)
# Some sort of auto-response
# Check for Exim’s X-Failed-Recipients header
- failed_recipients = message.header_string("X-Failed-Recipients")
+ failed_recipients = MailHandler.get_header_string("X-Failed-Recipients", message)
if !failed_recipients.nil?
# The X-Failed-Recipients header contains the email address that failed
# Check for the words "This is a permanent error." in the body, to indicate
# a permanent failure
- if message.body =~ /This is a permanent error./
+ if MailHandler.get_part_body(message) =~ /This is a permanent error./
return failed_recipients.split(/,\s*/)
end
end
# Next, look for multipart/report
- if message.content_type == "multipart/report"
+ if MailHandler.get_content_type(message) == "multipart/report"
permanently_failed_recipients = []
message.parts.each do |part|
- if part.content_type == "message/delivery-status"
- sections = part.body.split(/\r?\n\r?\n/)
+ if MailHandler.get_content_type(part) == "message/delivery-status"
+ sections = MailHandler.get_part_body(part).split(/\r?\n\r?\n/)
# The first section is a generic header; subsequent sections
# represent a particular recipient. Since we
sections[1..-1].each do |section|
@@ -111,11 +114,11 @@ def permanently_failed_addresses(message)
end
end
- subject = message.header_string("Subject")
+ subject = MailHandler.get_header_string("Subject", message)
# Then look for the style we’ve seen in WebShield bounces
# (These do not have a return path of <> in the cases I have seen.)
if subject == "Returned Mail: Error During Delivery"
- if message.body =~ /^\s*---- Failed Recipients ----\s*((?:<[^>]+>\n)+)/
+ if MailHandler.get_part_body(message) =~ /^\s*---- Failed Recipients ----\s*((?:<[^>]+>\n)+)/
return $1.scan(/<([^>]+)>/).flatten
end
end
@@ -126,12 +129,12 @@ end
def is_oof?(message)
# Check for out-of-office
- if message.header_string("X-POST-MessageClass") == "9; Autoresponder"
+ if MailHandler.get_header_string("X-POST-MessageClass", message) == "9; Autoresponder"
return true
end
- subject = message.header_string("Subject").downcase
- if message.header_string("Return-Path") == "<>"
+ subject = MailHandler.get_header_string("Subject", message).downcase
+ if MailHandler.empty_return_path?(message)
if subject.start_with? "out of office: "
return true
end
@@ -140,7 +143,7 @@ def is_oof?(message)
end
end
- if message.header_string("Auto-Submitted") == "auto-generated"
+ if MailHandler.get_header_string("Auto-Submitted", message) == "auto-generated"
if subject =~ /out of( the)? office/
return true
end
@@ -169,8 +172,8 @@ def forward_on(raw_message)
end
def load_rails
- require File.join('config', 'boot')
- require File.join('config', 'environment')
+ require File.join($alaveteli_dir, 'config', 'boot')
+ require File.join($alaveteli_dir, 'config', 'environment')
end
def record_bounce(email_address, bounce_message)
diff --git a/script/rails b/script/rails
new file mode 100755
index 000000000..f8da2cffd
--- /dev/null
+++ b/script/rails
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
+
+APP_PATH = File.expand_path('../../config/application', __FILE__)
+require File.expand_path('../../config/boot', __FILE__)
+require 'rails/commands'
diff --git a/spec/controllers/admin_censor_rule_controller_spec.rb b/spec/controllers/admin_censor_rule_controller_spec.rb
index 8893a858b..c4860da3f 100644
--- a/spec/controllers/admin_censor_rule_controller_spec.rb
+++ b/spec/controllers/admin_censor_rule_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminCensorRuleController, "when making censor rules from the admin interface" do
- integrate_views
+ render_views
before { basic_auth_login @request }
it "should create a censor rule and purge the corresponding request from varnish" do
diff --git a/spec/controllers/admin_general_controller_spec.rb b/spec/controllers/admin_general_controller_spec.rb
index dc1eb0d97..b0b65857f 100644
--- a/spec/controllers/admin_general_controller_spec.rb
+++ b/spec/controllers/admin_general_controller_spec.rb
@@ -4,7 +4,7 @@ describe AdminGeneralController do
describe "when viewing front page of admin interface" do
- integrate_views
+ render_views
before { basic_auth_login @request }
it "should render the front page" do
diff --git a/spec/controllers/admin_public_body_controller_spec.rb b/spec/controllers/admin_public_body_controller_spec.rb
index 504ddc5cc..c1d3f5014 100644
--- a/spec/controllers/admin_public_body_controller_spec.rb
+++ b/spec/controllers/admin_public_body_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminPublicBodyController, "when administering public bodies" do
- integrate_views
+ render_views
before do
@old_filters = ActionController::Routing::Routes.filters
@@ -154,7 +154,7 @@ end
describe AdminPublicBodyController, "when administering public bodies and paying attention to authentication" do
- integrate_views
+ render_views
before do
config = MySociety::Config.load_default()
@@ -263,7 +263,7 @@ describe AdminPublicBodyController, "when administering public bodies and paying
end
describe AdminPublicBodyController, "when administering public bodies with i18n" do
- integrate_views
+ render_views
it "shows the index page" do
get :index
@@ -288,7 +288,7 @@ describe AdminPublicBodyController, "when administering public bodies with i18n"
end
it "saves edits to a public body" do
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
pb = PublicBody.find(id=3)
pb.name.should == "El Department for Humpadinking"
post :update, {
@@ -308,10 +308,10 @@ describe AdminPublicBodyController, "when administering public bodies with i18n"
end
pb = PublicBody.find(public_bodies(:humpadink_public_body).id)
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
pb.name.should == "Renamed"
end
- PublicBody.with_locale(:en) do
+ I18n.with_locale(:en) do
pb.name.should == "Department for Humpadinking"
end
end
@@ -325,7 +325,7 @@ describe AdminPublicBodyController, "when administering public bodies with i18n"
end
describe AdminPublicBodyController, "when creating public bodies with i18n" do
- integrate_views
+ render_views
before do
@old_filters = ActionController::Routing::Routes.filters
@@ -357,12 +357,12 @@ describe AdminPublicBodyController, "when creating public bodies with i18n" do
body = PublicBody.find_by_name("New Quango")
body.translations.map {|t| t.locale.to_s}.sort.should == ["en", "es"]
- PublicBody.with_locale(:en) do
+ I18n.with_locale(:en) do
body.name.should == "New Quango"
body.url_name.should == "new_quango"
body.first_letter.should == "N"
end
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
body.name.should == "Mi Nuevo Quango"
body.url_name.should == "mi_nuevo_quango"
body.first_letter.should == "M"
diff --git a/spec/controllers/admin_request_controller_spec.rb b/spec/controllers/admin_request_controller_spec.rb
index 8a3934685..0709f6b0b 100644
--- a/spec/controllers/admin_request_controller_spec.rb
+++ b/spec/controllers/admin_request_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminRequestController, "when administering requests" do
- integrate_views
+ render_views
before { basic_auth_login @request }
before(:each) do
@@ -82,7 +82,7 @@ describe AdminRequestController, "when administering requests" do
end
describe AdminRequestController, "when administering the holding pen" do
- integrate_views
+ render_views
before(:each) do
basic_auth_login @request
load_raw_emails_data
diff --git a/spec/controllers/admin_track_controller_spec.rb b/spec/controllers/admin_track_controller_spec.rb
index 728c79f1f..f2de6c0d3 100644
--- a/spec/controllers/admin_track_controller_spec.rb
+++ b/spec/controllers/admin_track_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminTrackController, "when administering tracks" do
- integrate_views
+ render_views
it "shows the list page" do
get :list
diff --git a/spec/controllers/admin_user_controller_spec.rb b/spec/controllers/admin_user_controller_spec.rb
index cf3665c9f..a6e5a0d7e 100644
--- a/spec/controllers/admin_user_controller_spec.rb
+++ b/spec/controllers/admin_user_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminUserController, "when administering users" do
- integrate_views
+ render_views
it "shows the index/list page" do
get :index
diff --git a/spec/controllers/api_controller_spec.rb b/spec/controllers/api_controller_spec.rb
index 8d8a39950..1c320f85c 100644
--- a/spec/controllers/api_controller_spec.rb
+++ b/spec/controllers/api_controller_spec.rb
@@ -2,7 +2,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
def normalise_whitespace(s)
- s = s.gsub(/^\s+|\s+$/, "")
+ s = s.gsub(/\A\s+|\s+\Z/, "")
s = s.gsub(/\s+/, " ")
return s
end
@@ -14,23 +14,36 @@ Spec::Matchers.define :be_equal_modulo_whitespace_to do |expected|
end
describe ApiController, "when using the API" do
- it "should check the API key" do
- request_data = {
+
+ describe 'checking API keys' do
+ before do
+ @number_of_requests = InfoRequest.count
+ @request_data = {
"title" => "Tell me about your chickens",
"body" => "Dear Sir,\n\nI should like to know about your chickens.\n\nYours in faith,\nBob\n",
-
+
"external_url" => "http://www.example.gov.uk/foi/chickens_23",
"external_user_name" => "Bob Smith",
}
-
- number_of_requests = InfoRequest.count
+ end
+
+ it 'should check that an API key is given as a param' do
expect {
- post :create_request, :k => "This is not really an API key", :request_json => request_data.to_json
+ post :create_request, :request_json => @request_data.to_json
}.to raise_error ApplicationController::PermissionDenied
-
- InfoRequest.count.should == number_of_requests
+ InfoRequest.count.should == @number_of_requests
+ end
+
+ it "should check the API key" do
+ expect {
+ post :create_request,
+ :k => "This is not really an API key",
+ :request_json => @request_data.to_json
+ }.to raise_error ApplicationController::PermissionDenied
+ InfoRequest.count.should == @number_of_requests
+ end
end
-
+
it "should create a new request from a POST" do
number_of_requests = InfoRequest.count(
:conditions => [
@@ -38,61 +51,61 @@ describe ApiController, "when using the API" do
public_bodies(:geraldine_public_body).id
]
)
-
+
request_data = {
"title" => "Tell me about your chickens",
"body" => "Dear Sir,\n\nI should like to know about your chickens.\n\nYours in faith,\nBob\n",
-
+
"external_url" => "http://www.example.gov.uk/foi/chickens_23",
"external_user_name" => "Bob Smith",
}
-
+
post :create_request, :k => public_bodies(:geraldine_public_body).api_key, :request_json => request_data.to_json
response.should be_success
response.content_type.should == "application/json"
-
+
response_body = ActiveSupport::JSON.decode(response.body)
response_body["errors"].should be_nil
response_body["url"].should =~ /^http/
-
+
InfoRequest.count(:conditions => [
"public_body_id = ?",
public_bodies(:geraldine_public_body).id]
).should == number_of_requests + 1
-
+
new_request = InfoRequest.find(response_body["id"])
new_request.user_id.should be_nil
new_request.external_user_name.should == request_data["external_user_name"]
new_request.external_url.should == request_data["external_url"]
-
+
new_request.title.should == request_data["title"]
new_request.last_event_forming_initial_request.outgoing_message.body.should == request_data["body"].strip
-
+
new_request.public_body_id.should == public_bodies(:geraldine_public_body).id
end
-
+
def _create_request
post :create_request,
:k => public_bodies(:geraldine_public_body).api_key,
:request_json => {
"title" => "Tell me about your chickens",
"body" => "Dear Sir,\n\nI should like to know about your chickens.\n\nYours in faith,\nBob\n",
-
+
"external_url" => "http://www.example.gov.uk/foi/chickens_23",
"external_user_name" => "Bob Smith",
}.to_json
response.content_type.should == "application/json"
return ActiveSupport::JSON.decode(response.body)["id"]
end
-
+
it "should add a response to a request" do
# First we need an external request
request_id = info_requests(:external_request).id
-
+
# Initially it has no incoming messages
IncomingMessage.count(:conditions => ["info_request_id = ?", request_id]).should == 0
-
+
# Now add one
sent_at = "2012-05-28T12:35:39+01:00"
response_body = "Thank you for your request for information, which we are handling in accordance with the Freedom of Information Act 2000. You will receive a response within 20 working days or before the next full moon, whichever is sooner.\n\nYours sincerely,\nJohn Gandermulch,\nExample Council FOI Officer\n"
@@ -104,13 +117,13 @@ describe ApiController, "when using the API" do
"sent_at" => sent_at,
"body" => response_body
}.to_json
-
+
# And make sure it worked
response.should be_success
incoming_messages = IncomingMessage.all(:conditions => ["info_request_id = ?", request_id])
incoming_messages.count.should == 1
incoming_message = incoming_messages[0]
-
+
incoming_message.sent_at.should == Time.iso8601(sent_at)
incoming_message.get_main_body_text_folded.should be_equal_modulo_whitespace_to(response_body)
end
@@ -118,10 +131,10 @@ describe ApiController, "when using the API" do
it "should add a followup to a request" do
# First we need an external request
request_id = info_requests(:external_request).id
-
+
# Initially it has one outgoing message
OutgoingMessage.count(:conditions => ["info_request_id = ?", request_id]).should == 1
-
+
# Add another, as a followup
sent_at = "2012-05-29T12:35:39+01:00"
followup_body = "Pls answer ASAP.\nkthxbye\n"
@@ -133,7 +146,7 @@ describe ApiController, "when using the API" do
"sent_at" => sent_at,
"body" => followup_body
}.to_json
-
+
# Make sure it worked
response.should be_success
followup_messages = OutgoingMessage.all(
@@ -141,15 +154,15 @@ describe ApiController, "when using the API" do
)
followup_messages.size.should == 1
followup_message = followup_messages[0]
-
+
followup_message.last_sent_at.should == Time.iso8601(sent_at)
followup_message.body.should == followup_body.strip
end
-
+
it "should not allow internal requests to be updated" do
n_incoming_messages = IncomingMessage.count
n_outgoing_messages = OutgoingMessage.count
-
+
request_id = info_requests(:naughty_chicken_request).id
post :add_correspondence,
:k => public_bodies(:geraldine_public_body).api_key,
@@ -159,20 +172,20 @@ describe ApiController, "when using the API" do
"sent_at" => Time.now.iso8601,
"body" => "xxx"
}.to_json
-
+
response.status.should == "500 Internal Server Error"
ActiveSupport::JSON.decode(response.body)["errors"].should == [
"Request #{request_id} cannot be updated using the API"]
-
+
IncomingMessage.count.should == n_incoming_messages
OutgoingMessage.count.should == n_outgoing_messages
end
-
+
it "should not allow other people's requests to be updated" do
request_id = _create_request
n_incoming_messages = IncomingMessage.count
n_outgoing_messages = OutgoingMessage.count
-
+
post :add_correspondence,
:k => public_bodies(:humpadink_public_body).api_key,
:id => request_id,
@@ -181,15 +194,15 @@ describe ApiController, "when using the API" do
"sent_at" => Time.now.iso8601,
"body" => "xxx"
}.to_json
-
+
response.status.should == "500 Internal Server Error"
ActiveSupport::JSON.decode(response.body)["errors"].should == [
"You do not own request #{request_id}"]
-
+
IncomingMessage.count.should == n_incoming_messages
OutgoingMessage.count.should == n_outgoing_messages
end
-
+
it "should not allow files to be attached to a followup" do
post :add_correspondence,
:k => public_bodies(:geraldine_public_body).api_key,
@@ -202,21 +215,21 @@ describe ApiController, "when using the API" do
:attachments => [
fixture_file_upload("files/tfl.pdf")
]
-
-
+
+
# Make sure it worked
response.status.to_i.should == 500
errors = ActiveSupport::JSON.decode(response.body)["errors"]
errors.should == ["You cannot attach files to messages in the 'request' direction"]
end
-
+
it "should allow files to be attached to a response" do
# First we need an external request
request_id = info_requests(:external_request).id
-
+
# Initially it has no incoming messages
IncomingMessage.count(:conditions => ["info_request_id = ?", request_id]).should == 0
-
+
# Now add one
sent_at = "2012-05-28T12:35:39+01:00"
response_body = "Thank you for your request for information, which we are handling in accordance with the Freedom of Information Act 2000. You will receive a response within 20 working days or before the next full moon, whichever is sooner.\n\nYours sincerely,\nJohn Gandermulch,\nExample Council FOI Officer\n"
@@ -231,34 +244,33 @@ describe ApiController, "when using the API" do
:attachments => [
fixture_file_upload("files/tfl.pdf")
]
-
+
# And make sure it worked
response.should be_success
incoming_messages = IncomingMessage.all(:conditions => ["info_request_id = ?", request_id])
incoming_messages.count.should == 1
incoming_message = incoming_messages[0]
-
+
incoming_message.sent_at.should == Time.iso8601(sent_at)
incoming_message.get_main_body_text_folded.should be_equal_modulo_whitespace_to(response_body)
-
+
# Get the attachment
attachments = incoming_message.get_attachments_for_display
attachments.size.should == 1
attachment = attachments[0]
-
attachment.filename.should == "tfl.pdf"
- attachment.body.should == load_file_fixture("tfl.pdf")
+ attachment.body.should == load_file_fixture("tfl.pdf", as_binary=true)
end
-
+
it "should show information about a request" do
info_request = info_requests(:naughty_chicken_request)
get :show_request,
:k => public_bodies(:geraldine_public_body).api_key,
:id => info_request.id
-
+
response.should be_success
assigns[:request].id.should == info_request.id
-
+
r = ActiveSupport::JSON.decode(response.body)
r["title"].should == info_request.title
# Let’s not test all the fields here, because it would
@@ -266,13 +278,13 @@ describe ApiController, "when using the API" do
# assigns them and changing assignment to an equality
# check, which does not really test anything at all.
end
-
+
it "should show an Atom feed of new request events" do
get :body_request_events,
:id => public_bodies(:geraldine_public_body).id,
:k => public_bodies(:geraldine_public_body).api_key,
:feed_type => "atom"
-
+
response.should be_success
response.should render_template("api/request_events.atom")
assigns[:events].size.should > 0
@@ -288,7 +300,7 @@ describe ApiController, "when using the API" do
:id => public_bodies(:geraldine_public_body).id,
:k => public_bodies(:geraldine_public_body).api_key,
:feed_type => "json"
-
+
response.should be_success
assigns[:events].size.should > 0
assigns[:events].each do |event|
@@ -296,13 +308,13 @@ describe ApiController, "when using the API" do
event.outgoing_message.should_not be_nil
event.event_type.should satisfy {|x| ['sent', 'followup_sent', 'resent', 'followup_resent'].include?(x)}
end
-
+
assigns[:event_data].size.should == assigns[:events].size
assigns[:event_data].each do |event_record|
event_record[:event_type].should satisfy {|x| ['sent', 'followup_sent', 'resent', 'followup_resent'].include?(x)}
end
end
-
+
it "should honour the since_event_id parameter" do
get :body_request_events,
:id => public_bodies(:geraldine_public_body).id,
@@ -311,7 +323,7 @@ describe ApiController, "when using the API" do
response.should be_success
first_event = assigns[:event_data][0]
second_event_id = assigns[:event_data][1][:event_id]
-
+
get :body_request_events,
:id => public_bodies(:geraldine_public_body).id,
:k => public_bodies(:geraldine_public_body).api_key,
@@ -320,14 +332,14 @@ describe ApiController, "when using the API" do
response.should be_success
assigns[:event_data].should == [first_event]
end
-
+
it "should honour the since_date parameter for the Atom feed" do
get :body_request_events,
:id => public_bodies(:humpadink_public_body).id,
:k => public_bodies(:humpadink_public_body).api_key,
:since_date => "2010-01-01",
:feed_type => "atom"
-
+
response.should be_success
response.should render_template("api/request_events.atom")
assigns[:events].size.should > 0
@@ -335,7 +347,7 @@ describe ApiController, "when using the API" do
event.created_at.should >= Date.new(2010, 1, 1)
end
end
-
+
it "should return a JSON 404 error for non-existent requests" do
request_id = 123459876 # Let's hope this doesn't exist!
sent_at = "2012-05-28T12:35:39+01:00"
@@ -351,7 +363,7 @@ describe ApiController, "when using the API" do
response.status.should == "404 Not Found"
ActiveSupport::JSON.decode(response.body)["errors"].should == ["Could not find request 123459876"]
end
-
+
it "should return a JSON 500 error if we try to add correspondence to a request we don't own" do
request_id = info_requests(:naughty_chicken_request).id
sent_at = "2012-05-28T12:35:39+01:00"
diff --git a/spec/controllers/comment_controller_spec.rb b/spec/controllers/comment_controller_spec.rb
index 4a7acee23..c03615ce2 100644
--- a/spec/controllers/comment_controller_spec.rb
+++ b/spec/controllers/comment_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe CommentController, "when commenting on a request" do
- integrate_views
+ render_views
it "should give an error and render 'new' template when body text is just some whitespace" do
post :new, :url_title => info_requests(:naughty_chicken_request).url_title,
diff --git a/spec/controllers/general_controller_spec.rb b/spec/controllers/general_controller_spec.rb
index 830486493..9684732a5 100644
--- a/spec/controllers/general_controller_spec.rb
+++ b/spec/controllers/general_controller_spec.rb
@@ -35,7 +35,7 @@ end
describe GeneralController, "when showing the frontpage" do
- integrate_views
+ render_views
before do
public_body = mock_model(PublicBody, :name => "Example Public Body",
@@ -97,8 +97,57 @@ describe GeneralController, "when showing the frontpage" do
response.should be_success
end
+ describe 'when there is more than one locale' do
+
+ describe 'when using the default locale' do
+
+ before do
+ @default_lang_home_link = /href=".*\/en\//
+ @other_lang_home_link = /href=".*\/es\//
+ @old_include_default_locale_in_urls = Configuration::include_default_locale_in_urls
+ end
+
+ def set_default_locale_in_urls(value)
+ Configuration.stub!(:include_default_locale_in_urls).and_return(value)
+ load Rails.root.join("config/initializers/fast_gettext.rb")
+ end
+
+ describe 'when the config value INCLUDE_DEFAULT_LOCALE_IN_URLS is false' do
+
+ before do
+ set_default_locale_in_urls(false)
+ end
+
+ it 'should generate URLs without a locale prepended' do
+ get :frontpage
+ response.should_not have_text(@default_lang_home_link)
+ end
+
+ it 'should render the front page in the default language when no locale param
+ is present and the session locale is not the default' do
+ get(:frontpage, {}, {:locale => 'es'})
+ response.should_not have_text(@other_lang_home_link)
+ end
+ end
+
+ it 'should generate URLs with a locale prepended when the config value
+ INCLUDE_DEFAULT_LOCALE_IN_URLS is true' do
+ set_default_locale_in_urls(true)
+ get :frontpage
+ response.should have_text(@default_lang_home_link)
+ end
+
+ after do
+ set_default_locale_in_urls(@old_include_default_locale_in_urls)
+ end
+
+ end
+ end
+
+
describe "when using different locale settings" do
home_link_regex = /href=".*\/en\//
+
it "should generate URLs with a locale prepended when there's more than one locale set" do
get :frontpage
response.should have_text(home_link_regex)
@@ -137,6 +186,7 @@ describe GeneralController, "when showing the frontpage" do
FastGettext.default_available_locales = old_fgt_available_locales
I18n.available_locales = old_i18n_available_locales
end
+
end
end
describe GeneralController, "when showing the front page with fixture data" do
@@ -185,7 +235,7 @@ end
describe GeneralController, 'when using xapian search' do
- integrate_views
+ render_views
# rebuild xapian index after fixtures loaded
before(:each) do
diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb
index 28fd08c80..0f6f75eb9 100644
--- a/spec/controllers/help_controller_spec.rb
+++ b/spec/controllers/help_controller_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe HelpController, "when using help" do
- integrate_views
+ render_views
it "shows the about page" do
get :about
diff --git a/spec/controllers/public_body_controller_spec.rb b/spec/controllers/public_body_controller_spec.rb
index 29ece18cb..a0841b621 100644
--- a/spec/controllers/public_body_controller_spec.rb
+++ b/spec/controllers/public_body_controller_spec.rb
@@ -2,7 +2,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe PublicBodyController, "when showing a body" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -44,14 +44,14 @@ describe PublicBodyController, "when showing a body" do
end
it "should assign the body using different locale from that used for url_name" do
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
get :show, {:url_name => "dfh", :view => 'all'}
assigns[:public_body].notes.should == "Baguette"
end
end
it "should assign the body using same locale as that used in url_name" do
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
get :show, {:url_name => "edfh", :view => 'all'}
assigns[:public_body].notes.should == "Baguette"
end
@@ -79,7 +79,7 @@ describe PublicBodyController, "when showing a body" do
end
describe PublicBodyController, "when listing bodies" do
- integrate_views
+ render_views
it "should be successful" do
get :list
@@ -87,7 +87,7 @@ describe PublicBodyController, "when listing bodies" do
end
it "should list all bodies from default locale, even when there are no translations for selected locale" do
- PublicBody.with_locale(:en) do
+ I18n.with_locale(:en) do
@english_only = PublicBody.new(:name => 'English only',
:short_name => 'EO',
:request_email => 'english@flourish.org',
@@ -95,7 +95,7 @@ describe PublicBodyController, "when listing bodies" do
:last_edit_comment => '')
@english_only.save
end
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
get :list
assigns[:public_bodies].include?(@english_only).should == true
end
@@ -193,7 +193,7 @@ end
describe PublicBodyController, "when doing type ahead searches" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb
index e898fb91b..9e14153a7 100644
--- a/spec/controllers/request_controller_spec.rb
+++ b/spec/controllers/request_controller_spec.rb
@@ -59,19 +59,6 @@ describe RequestController, "when listing recent requests" do
:conditions => "id in (select info_request_id from info_request_events where created_at between '2007-10-13'::date and '2007-11-01'::date)")
end
- it "should make a sane-sized cache tag" do
- get :list, :view => 'all', :request_date_after => '13/10/2007', :request_date_before => '01/11/2007'
- assigns[:cache_tag].size.should <= 32
- end
-
- it "should vary the cache tag with locale" do
- get :list, :view => 'all', :request_date_after => '13/10/2007', :request_date_before => '01/11/2007'
- en_tag = assigns[:cache_tag]
- session[:locale] = :es
- get :list, :view => 'all', :request_date_after => '13/10/2007', :request_date_before => '01/11/2007'
- assigns[:cache_tag].should_not == en_tag
- end
-
it "should list internal_review requests as unresolved ones" do
get :list, :view => 'awaiting'
@@ -134,7 +121,7 @@ end
describe RequestController, "when changing things that appear on the request page" do
- integrate_views
+ render_views
it "should purge the downstream cache when mail is received" do
ir = info_requests(:fancy_dog_request)
@@ -200,7 +187,7 @@ describe RequestController, "when changing things that appear on the request pag
end
describe RequestController, "when showing one request" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -458,7 +445,7 @@ describe RequestController, "when showing one request" do
describe 'when handling incoming mail' do
- integrate_views
+ render_views
it "should receive incoming messages, send email to creator, and show them" do
ir = info_requests(:fancy_dog_request)
@@ -757,7 +744,7 @@ describe RequestController, "when showing one request" do
assigns[:url_path].should_not == old_path
response.location.should have_text(/#{assigns[:url_path]}/)
zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", assigns[:url_path])) { |zipfile|
- zipfile.count.should == 5 # the message, two hello.txt, the unknown attachment, and its empty message
+ zipfile.count.should == 4 # the message, two hello.txt plus the unknown attachment
}
end
@@ -908,7 +895,7 @@ describe RequestController, "when searching for an authority" do
end
describe RequestController, "when creating a new request" do
- integrate_views
+ render_views
before do
@user = users(:bob_smith_user)
@@ -1144,7 +1131,7 @@ describe RequestController, "when making a new request" do
end
describe RequestController, "when viewing an individual response for reply/followup" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -1577,7 +1564,7 @@ describe RequestController, "when classifying an information request" do
end
describe RequestController, "when sending a followup message" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -1659,7 +1646,7 @@ end
# it can't check the URLs in the emails I don't think, ugh.
describe RequestController, "sending overdue request alerts" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -1804,7 +1791,7 @@ describe RequestController, "sending overdue request alerts" do
end
describe RequestController, "sending unclassified new response reminder alerts" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -1834,7 +1821,7 @@ describe RequestController, "sending unclassified new response reminder alerts"
end
describe RequestController, "clarification required alerts" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
end
@@ -1887,7 +1874,7 @@ describe RequestController, "clarification required alerts" do
end
describe RequestController, "comment alerts" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
end
@@ -1966,7 +1953,7 @@ describe RequestController, "comment alerts" do
end
describe RequestController, "when viewing comments" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
end
@@ -1989,7 +1976,7 @@ end
describe RequestController, "authority uploads a response from the web interface" do
- integrate_views
+ render_views
before(:each) do
# domain after the @ is used for authentication of FOI officers, so to test it
@@ -2099,7 +2086,7 @@ end
describe RequestController, "when doing type ahead searches" do
- integrate_views
+ render_views
it "should return nothing for the empty query string" do
get :search_typeahead, :q => ""
@@ -2157,7 +2144,7 @@ describe RequestController, "when doing type ahead searches" do
end
describe RequestController, "when showing similar requests" do
- integrate_views
+ render_views
it "should work" do
get :similar, :url_title => info_requests(:badger_request).url_title
@@ -2191,7 +2178,7 @@ describe RequestController, "when reporting a request when not logged in" do
end
describe RequestController, "when reporting a request (logged in)" do
- integrate_views
+ render_views
before do
@user = users(:robin_user)
diff --git a/spec/controllers/services_controller_spec.rb b/spec/controllers/services_controller_spec.rb
index a701ae247..d2d22826a 100644
--- a/spec/controllers/services_controller_spec.rb
+++ b/spec/controllers/services_controller_spec.rb
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe ServicesController, "when using web services" do
- integrate_views
+ render_views
# store and restore the locale in the context of the test suite to isolate
# changes made in these tests
@@ -40,4 +40,4 @@ describe ServicesController, "when using web services" do
FastGettext.set_locale(@old_locale)
end
-end \ No newline at end of file
+end
diff --git a/spec/controllers/track_controller_spec.rb b/spec/controllers/track_controller_spec.rb
index c785960b5..cd7932e90 100644
--- a/spec/controllers/track_controller_spec.rb
+++ b/spec/controllers/track_controller_spec.rb
@@ -56,7 +56,7 @@ describe TrackController, "when making a new track on a request" do
end
describe TrackController, "when sending alerts for a track" do
- integrate_views
+ render_views
include LinkToHelper # for main_url
before(:each) do
@@ -134,7 +134,7 @@ describe TrackController, "when sending alerts for a track" do
end
describe TrackController, "when viewing RSS feed for a track" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -164,7 +164,7 @@ end
describe TrackController, "when viewing JSON version of a track feed" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
@@ -206,7 +206,7 @@ end
describe TrackController, "when tracking a public body" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
diff --git a/spec/controllers/user_controller_spec.rb b/spec/controllers/user_controller_spec.rb
index 23006803b..db64bdcaf 100644
--- a/spec/controllers/user_controller_spec.rb
+++ b/spec/controllers/user_controller_spec.rb
@@ -5,7 +5,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
# http://rspec.rubyforge.org/rspec-rails/1.1.12/classes/Spec/Rails/Example/ControllerExampleGroup.html
describe UserController, "when showing a user" do
- integrate_views
+ render_views
before(:each) do
load_raw_emails_data
get_fixtures_xapian_index
@@ -64,7 +64,7 @@ describe UserController, "when showing a user" do
end
describe UserController, "when signing in" do
- integrate_views
+ render_views
def get_last_postredirect
post_redirects = PostRedirect.find_by_sql("select * from post_redirects order by id desc limit 1")
@@ -228,7 +228,7 @@ describe UserController, "when signing in" do
end
describe UserController, "when signing up" do
- integrate_views
+ render_views
it "should be an error if you type the password differently each time" do
post :signup, { :user_signup => { :email => 'new@localhost', :name => 'New Person',
@@ -285,7 +285,7 @@ describe UserController, "when signing up" do
end
describe UserController, "when signing out" do
- integrate_views
+ render_views
it "should log you out and redirect to the home page" do
session[:user_id] = users(:bob_smith_user).id
@@ -309,7 +309,7 @@ describe UserController, "when signing out" do
end
describe UserController, "when sending another user a message" do
- integrate_views
+ render_views
it "should redirect to signin page if you go to the contact form and aren't signed in" do
get :contact, :id => users(:silly_name_user)
@@ -346,7 +346,7 @@ describe UserController, "when sending another user a message" do
end
describe UserController, "when changing password" do
- integrate_views
+ render_views
it "should show the email form when not logged in" do
get :signchangepassword
@@ -416,7 +416,7 @@ describe UserController, "when changing password" do
end
describe UserController, "when changing email address" do
- integrate_views
+ render_views
it "should require login" do
get :signchangeemail
@@ -561,7 +561,7 @@ describe UserController, "when changing email address" do
end
describe UserController, "when using profile photos" do
- integrate_views
+ render_views
before do
@user = users(:bob_smith_user)
@@ -631,7 +631,7 @@ describe UserController, "when showing JSON version for API" do
end
describe UserController, "when viewing the wall" do
- integrate_views
+ render_views
before(:each) do
get_fixtures_xapian_index
diff --git a/spec/fixtures/files/dos-linebreaks.email b/spec/fixtures/files/dos-linebreaks.email
new file mode 100644
index 000000000..1f5f1473f
--- /dev/null
+++ b/spec/fixtures/files/dos-linebreaks.email
@@ -0,0 +1,31 @@
+From email@example.com Wed Mar 12 14:58:26 2008
+Return-path: email@example.com>
+Envelope-to: request-xxx-xxxxxx@whatdotheyknow.com
+Delivery-date: Wed, 12 Mar 2008 14:58:26 +0000
+Received: from example.com ([0.0.0.0]:1368 helo=example.com)
+ by tea.ukcod.org.uk with esmtp (Exim 4.50)
+ id 1JZSPS-0002yK-Rq
+ for request-60-3548031c@whatdotheyknow.com; Wed, 12 Mar 2008 14:58:26 +0000
+X-MimeOLE: Produced By Microsoft Exchange V0.0.0.0
+Content-class: urn:content-classes:message
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset="us-ascii"
+Content-Transfer-Encoding: quoted-printable
+Disposition-Notification-To: "A Person" email@example.com>
+Subject: RE: Freedom of Information request - Plans for the East Oxford Community Centre
+Date: Wed, 12 Mar 2008 14:59:04 -0000
+Message-ID: <3D8BEC617D49EF45A9E6D103A83FD30331BF84@local>
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator:
+Thread-Topic: Freedom of Information request
+Thread-Index: AciDziuIcYirFQ7GT36VyP2ABE14qgAg1c0w
+From: "A Person" email@example.com>
+To: FOI Person <EMAIL_TO>
+X-OriginalArrivalTime: 12 Mar 2008 14:59:04.0368 (UTC) FILETIME=[9D245300:01C88451]
+X-SEF-7853D99-ADF1-478E-8894-213D316B8FFA: 1
+X-SEF-Processed: 6_0_1_111__2008_03_12_14_59_05
+
+Thank you for your Freedom of Information request. I have forwarded it=0D=0A=
+to the relevant department for their reply.=0D=0A=0D=0A
+
diff --git a/spec/fixtures/files/humberside-police-odd-mime-type.email b/spec/fixtures/files/humberside-police-odd-mime-type.email
index 5514b29da..ae4ceeffe 100644
--- a/spec/fixtures/files/humberside-police-odd-mime-type.email
+++ b/spec/fixtures/files/humberside-police-odd-mime-type.email
@@ -3,19 +3,20 @@ Return-path: <>
Envelope-to: request-5335-xxxxxxxx@whatdotheyknow.com
Delivery-date: Thu, 01 Jan 2009 15:56:20 +0000
Received: from earth.karoo.kcom.com ([212.50.160.55]:62894)
- by sandwich.ukcod.org.uk with esmtp (Exim 4.63)
- id 1LIPuG-0004AJ-B3
- for request-5335-xxxxxxxx@whatdotheyknow.com; Thu, 01 Jan 2009 15:56:20 +0000
+ by sandwich.ukcod.org.uk with esmtp (Exim 4.63)
+ id 1LIPuG-0004AJ-B3
+ for request-5335-xxxxxxxx@whatdotheyknow.com; Thu, 01 Jan 2009 15:56:20 +0000
Received: from unknown (HELO smtp-in.karoo.kcom.com) ([10.102.8.11])
by earth.karoo.kcom.com with ESMTP; 01 Jan 2009 15:44:42 +0000
Received: from exim by smtp-in.karoo.kcom.comwith local (Exim 4.30)
- id 1LIPu0-0004fg-G6 server-id smtp-in4
- for request-5335-xxxxxxxx@whatdotheyknow.com; Thu, 01 Jan 2009 15:56:04 +0000
+ id 1LIPu0-0004fg-G6 server-id smtp-in4
+ for request-5335-xxxxxxxx@whatdotheyknow.com; Thu, 01 Jan 2009 15:56:04 +0000
X-Failed-Recipients: clerk@humberside-pa.karoo.co.uk
Reply-To: Postmaster <postmaster@karoo.kcom.com>
Auto-Submitted: auto-generated
From: Mail Delivery System <Mailer-Daemon@karoo.co.uk>
To: request-5335-xxxxxxxx@whatdotheyknow.com
+Cc: request-5335-xxxxxxxx@whatdotheyknow.com
Subject: Mail delivery failed : returning message to sender
X-Mailer: Karoo Mailcore [version 2.0-IB]
MIME-Version: 1.0
@@ -52,19 +53,19 @@ Content-Transfer-Encoding: 8bit
Return-path: <request-5335-xxxxxxxx@whatdotheyknow.com>
Received: from [212.50.160.60] (helo=venus.karoo.kcom.com)
- by smtp-in.karoo.kcom.comwith esmtp (Exim 4.30)
- id 1LIPu0-0004fc-FM server-id smtp-in4
- for clerk@humberside-pa.karoo.co.uk; Thu, 01 Jan 2009 15:56:04 +0000
+ by smtp-in.karoo.kcom.comwith esmtp (Exim 4.30)
+ id 1LIPu0-0004fc-FM server-id smtp-in4
+ for clerk@humberside-pa.karoo.co.uk; Thu, 01 Jan 2009 15:56:04 +0000
X-IronPort-Anti-Spam-Filtered: true
X-IronPort-Anti-Spam-Result: AnECAF9nXElSb+bUmWdsb2JhbACMZQGHFQEBAQEBCAsKBxG2eIVy
-X-IronPort-AV: E=Sophos;i="4.36,313,1228089600";
+X-IronPort-AV: E=Sophos;i="4.36,313,1228089600";
d="scan'208";a="465483300"
Received: from sandwich.ukcod.org.uk ([82.111.230.212])
by venus.karoo.kcom.com with ESMTP; 01 Jan 2009 15:46:44 +0000
Received: from foi by sandwich.ukcod.org.uk with local (Exim 4.63)
- (envelope-from <request-5335-xxxxxxxx@whatdotheyknow.com>)
- id 1LIPtz-0004AG-OC
- for clerk@humberside-pa.karoo.co.uk; Thu, 01 Jan 2009 15:56:03 +0000
+ (envelope-from <request-5335-xxxxxxxx@whatdotheyknow.com>)
+ id 1LIPtz-0004AG-OC
+ for clerk@humberside-pa.karoo.co.uk; Thu, 01 Jan 2009 15:56:03 +0000
From: John Jarman <request-5335-xxxxxxxx@whatdotheyknow.com>
To: FOI requests at Humberside Police Authority <clerk@humberside-pa.karoo.co.uk>
Subject: Freedom of Information request - Police Injury Award Pensions
diff --git a/spec/fixtures/files/incoming-request-attachment-headers.email b/spec/fixtures/files/incoming-request-attachment-headers.email
new file mode 100644
index 000000000..80e71556d
--- /dev/null
+++ b/spec/fixtures/files/incoming-request-attachment-headers.email
@@ -0,0 +1,50 @@
+From foi@example.com Mon Oct 06 13:45:38 2008
+Return-path: <foi@example.com>
+Envelope-to: foi@sandwich.ukcod.org.uk
+Delivery-date: Mon, 06 Oct 2008 13:45:38 +0100
+Message-Id: <s8ea1156.098@example.com>
+X-Mailer: Novell GroupWise Internet Agent 6.5.4
+Date: Mon, 06 Oct 2008 13:23:00 +0100
+From: "Steve Knight" <foi@example.com>
+To: <request-xxxx-xxxxx@whatdotheyknow.com>
+Subject: FOI request
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="=__PartFDD45234.0__="
+X-Proofpoint-Virus-Version: vendor=nai engine=5.2.00 definitions=5398 signatures=473327
+X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 classifier= adjust=0 reason=limit engine=3.1.0-0805090000 definitions=main-0810060054
+
+This is a MIME message. If you are reading this text, you may want to
+consider changing to a mail reader or gateway that understands how to
+properly handle MIME multipart messages.
+--=__PartFDD45234.0__=
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+Dear Mr Requester,
+
+Here is some information.
+
+--=__PartFDD45234.0__=
+Content-Type: message/rfc822
+
+Date: Fri, 23 May 2008 17:02:11 +0100
+From: "David Bishop" <foi2@example.com>
+To: "foi" <foi3@example.com>
+Subject: Foi request
+Mime-Version: 1.0
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+Extra info.
+
+--=__PartFDD45234.0__=
+Content-Type: image/jpeg; name="info.jpg"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="info.jpg"
+
+xxxx
+
+--=__PartFDD45234.0__=--
+
diff --git a/spec/fixtures/files/incoming-request-attachment-unknown-extension.email b/spec/fixtures/files/incoming-request-attachment-unknown-extension.email
index b3485ec2d..aecd9a52c 100644
--- a/spec/fixtures/files/incoming-request-attachment-unknown-extension.email
+++ b/spec/fixtures/files/incoming-request-attachment-unknown-extension.email
@@ -4,17 +4,16 @@ Subject: Same attachment twice
Content-Type: multipart/mixed; boundary="Q68bSM7Ycu6FN28Q"
Content-Disposition: inline
-
--Q68bSM7Ycu6FN28Q
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
-
--Q68bSM7Ycu6FN28Q
Content-Type: application/x-nonsense
Content-Disposition: attachment; filename="hello.qwglhm"
This is an unusual sort of file.
---Q68bSM7Ycu6FN28Q
+--Q68bSM7Ycu6FN28Q--
+
diff --git a/spec/fixtures/files/many-attachments-date-header.email b/spec/fixtures/files/many-attachments-date-header.email
new file mode 100644
index 000000000..a241e2456
--- /dev/null
+++ b/spec/fixtures/files/many-attachments-date-header.email
@@ -0,0 +1,451 @@
+From email@example.com Wed Apr 14 11:23:08 2010
+Return-path: <email@example.com>
+Envelope-to: email@example.com
+Delivery-date: Wed, 14 Apr 2010 11:23:08 +0100
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from example.com ([0.0.0.0]) by example.com ([0.0.0.0]) with ESMTP (TREND IMSS SMTP Service 7.0) id 1ec0f7ac0002a77f ; Wed, 14 Apr 2010 11:22:52 +0100
+Received: from GWGATE-MTA by example.com
+ with Novell_GroupWise; Wed, 14 Apr 2010 11:22:53 +0100
+Message-Id: <email@example.com>
+X-Mailer: Novell GroupWise Internet Agent 8.0.1
+Date: Wed, 14 Apr 2010 11:22:47 +0100
+From: "A Person" <email@example.com>
+To: <email@example.com>
+Cc: "FOI FOI" <email@example.com>,
+ "A Person" <email@example.com>
+Subject: Fwd: Re: Freedom of Information request
+References: <email@example.com>
+ <email@example.com>
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="=__Part163C9567.0__="
+
+This is a MIME message. If you are reading this text, you may want to
+consider changing to a mail reader or gateway that understands how to
+properly handle MIME multipart messages.
+
+--=__Part163C9567.0__=
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+Some information
+
+
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Date: Wed, 10 Mar 2010 14:17:52 +0000
+From: "A Person" <email@example.com>
+To: "A Person" <email@example.com>
+Subject: Re: xxx
+Mime-Version: 1.0
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+2
+
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Tue, 24 Nov 2009 10:45:58 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from example.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 00660acd00000f42 ; Tue, 24 Nov 2009 10:45:55 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob115.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Tue, 24 Nov 2009 10:45:56 UTC
+Received: from example.com ([::1]) by
+ example.com ([::1]) with mapi; Tue, 24 Nov 2009
+ 10:45:53 +0000
+From: A Person <email@example.com>
+To: email@example.com <email@example.com>
+Date: Tue, 24 Nov 2009 10:45:52 +0000
+Subject: example
+Thread-Topic: example
+Thread-Index: AcpnbI2i+XAmfHFVTFy0eGDpVJhXoQFhVeZw
+Message-ID: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach: yes
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: multipart/mixed;
+ boundary="_006_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_"
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17028.005
+X-TM-AS-Result: No--19.329-5.0-31-1
+X-imss-scan-details: No--19.329-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: multipart/related;
+ boundary="_005_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_";
+ type="multipart/alternative"
+
+--_005_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: multipart/alternative;
+ boundary="_000_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_"
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: text/plain; charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+
+3
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: text/html; charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+4
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_--
+
+--_005_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: image/gif; name="image001.gif"
+Content-Description: image001.gif
+Content-Disposition: inline; filename="image001.gif"; size=5445;
+ creation-date="Tue, 17 Nov 2009 09:58:46 GMT";
+ modification-date="Tue, 17 Nov 2009 09:58:46 GMT"
+Content-ID: <email@example.com>
+Content-Transfer-Encoding: base64
+
+5
+--_005_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_--
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: application/vnd.ms-excel;
+ name="particpant list.xls"
+Content-Description: particpant list.xls
+Content-Disposition: attachment;
+ filename="particpant list.xls"; size=21504;
+ creation-date="Mon, 02 Nov 2009 09:42:37 GMT";
+ modification-date="Tue, 24 Nov 2009 10:45:52 GMT"
+Content-Transfer-Encoding: base64
+
+6
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_--
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Thu, 03 Dec 2009 09:29:07 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog116.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 0ac1bf1b0001116e ; Thu, 3 Dec 2009 09:29:04 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob116.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Thu, 03 Dec 2009 09:29:06 UTC
+Received: from example.com ([::1]) by
+ example.com ([::1]) with mapi; Thu, 3 Dec 2009 09:29:03
+ +0000
+From: A Person <email@example.com>
+To: 'A Person' <email@example.com>
+Date: Thu, 3 Dec 2009 09:29:03 +0000
+Subject: RE: example
+Thread-Topic: example
+Thread-Index: AcpuoEyRvzM8fXw+THuj/617pjnvCgFWqZdQ
+Message-ID: <email@example.com>
+References: <email@example.com>
+ <email@example.com>
+In-Reply-To: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17046.004
+X-TM-AS-Result: No--16.791-5.0-31-1
+X-imss-scan-details: No--16.791-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+
+7
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Wed, 25 Nov 2009 22:26:23 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog105.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 034354c900007016 ; Wed, 25 Nov 2009 22:26:19 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob105.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Wed, 25 Nov 2009 22:26:21 UTC
+Received: from example.com ([::1]) by
+ example.com ([::1]) with mapi; Wed, 25 Nov 2009
+ 22:26:15 +0000
+From: A Person <email@example.com>
+To: email@example.com <email@example.com>
+CC: A Person <email@example.com>
+Date: Wed, 25 Nov 2009 22:26:12 +0000
+Subject: As promised - Masterclass info (example)
+Thread-Topic: As promised - Masterclass info (example)
+Thread-Index: AcpuHcJ4yrR8PBHZTVCU/RLGzwqsDAAACGwQ
+Message-ID: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach: yes
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: multipart/mixed;
+ boundary="_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_"
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17032.000
+X-TM-AS-Result: No--26.167-5.0-31-1
+X-imss-scan-details: No--26.167-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+
+--_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: multipart/related;
+ boundary="_006_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_";
+ type="multipart/alternative"
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: multipart/alternative;
+ boundary="_000_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_"
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+8
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: text/html; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+9
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_--
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: image/gif; name="image001.gif"
+Content-Description: image001.gif
+Content-Disposition: inline; filename="image001.gif"; size=5445;
+ creation-date="Wed, 25 Nov 2009 22:26:14 GMT";
+ modification-date="Wed, 25 Nov 2009 22:26:14 GMT"
+Content-ID: <email@example.com>
+Content-Transfer-Encoding: base64
+
+
+10
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_--
+
+--_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: application/msword;
+ name= "Participant List.doc"
+Content-Description: Participant List.doc
+Content-Disposition: attachment;
+ filename="Participant List.doc"; size=112640;
+ creation-date="Wed, 25 Nov 2009 22:17:24 GMT";
+ modification-date="Wed, 25 Nov 2009 11:43:48 GMT"
+Content-Transfer-Encoding: base64
+
+11
+--_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: application/msword; name="Information & Booking Form.doc"
+Content-Description: Information & Booking Form.doc
+Content-Disposition: attachment; filename="Information & Booking Form.doc"; size=84480;
+ creation-date="Wed, 25 Nov 2009 22:17:40 GMT";
+ modification-date="Wed, 04 Nov 2009 14:42:54 GMT"
+Content-Transfer-Encoding: base64
+
+12
+
+--_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_--
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Fri, 04 Dec 2009 10:00:05 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog109.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 100473260001a476 ; Fri, 4 Dec 2009 10:00:01 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob109.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Fri, 04 Dec 2009 10:00:04 UTC
+Received: from example.com ([::1]) by
+ example.com ([::1]) with mapi; Fri, 4 Dec 2009 10:00:01
+ +0000
+From: A Person <email@example.com>
+To: email@example.com <email@example.com>
+Date: Fri, 4 Dec 2009 10:00:01 +0000
+Subject: Re: As promised - info (example)
+Thread-Topic: As promised - info (example)
+Thread-Index: AcpzhLeBjBId8eZATYudOfBgN6CPXQBQ9Pok
+Message-ID: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17048.005
+X-TM-AS-Result: No--24.171-5.0-31-1
+X-imss-scan-details: No--24.171-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+13
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Sun, 21 Mar 2010 21:53:38 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog117.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 1e3611c1000d37df ; Sun, 21 Mar 2010 21:53:32 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob117.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Sun, 21 Mar 2010 21:53:37 UTC
+Received: from example.com ([::1]) by exchhub01
+ ([0.0.0.0]) with mapi; Sun, 21 Mar 2010 21:53:34 +0000
+From: A Person <email@example.com>
+To: email@example.com <email@example.com>
+CC: A Person <email@example.com>
+Date: Sun, 21 Mar 2010 21:53:32 +0000
+Subject: Thank you from example
+Thread-Topic: Thank you from example
+Thread-Index: AcrJQPL4xb9zjXMHRJGTjAxo3X/kfA==
+Message-ID: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach: yes
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: multipart/related;
+ boundary="_004_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_";
+ type="multipart/alternative"
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17266.002
+X-TM-AS-Result: No--26.373-5.0-31-1
+X-imss-scan-details: No--26.373-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+
+--_004_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_
+Content-Type: multipart/alternative;
+ boundary="_000_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_"
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: quoted-printable
+
+14
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_
+Content-Type: text/html; charset="us-ascii"
+Content-Transfer-Encoding: quoted-printable
+
+15
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_--
+
+--_004_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_
+Content-Type: image/gif; name="image001.gif"
+Content-Description: image001.gif
+Content-Disposition: inline; filename="image001.gif"; size=5445;
+ creation-date="Sun, 21 Mar 2010 21:53:33 GMT";
+ modification-date="Sun, 21 Mar 2010 21:53:33 GMT"
+Content-ID: <email@example.com>
+Content-Transfer-Encoding: base64
+
+16
+--_004_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_--
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Tue, 23 Feb 2010 15:33:48 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog112.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 96f54043000f2e72 ; Tue, 23 Feb 2010 15:33:48 +0000
+Received: from source ([0.0.0.0]) by eu1sys200aob112.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Tue, 23 Feb 2010 15:33:47 UTC
+Received: from gla-002561-lap ([0.0.0.0]) by example.com with Microsoft SMTPSVC(0.0.0.0);
+ Tue, 23 Feb 2010 15:33:46 +0000
+Reply-To: email@example.com
+From: email@example.com
+To: email@example.com
+Subject: example - Meeting - Tuesday 2nd March
+Date: 23 February 2010 15:33
+X-Mailer: Internet Professional v1.15
+Return-Path: email@example.com
+Message-ID: <email@example.com>
+X-OriginalArrivalTime: 23 Feb 2010 15:33:46.0648 (UTC) FILETIME=[96CEC980:01CAB49D]
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17212.000
+X-TM-AS-Result: No--16.146-5.0-31-1
+X-imss-scan-details: No--16.146-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+17
+
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Mon, 08 Mar 2010 09:21:42 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog117.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id d8931aff001580d6 ; Mon, 8 Mar 2010 09:21:40 +0000
+Received: from source ([0.0.0.0]) by eu1sys200aob117.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Mon, 08 Mar 2010 09:21:39 UTC
+Received: from gla-002561-lap ([0.0.0.0]) by example.com with Microsoft SMTPSVC(0.0.0.0);
+ Mon, 8 Mar 2010 09:21:36 +0000
+Reply-To: email@example.com
+From: email@example.com
+To: email@example.com
+Subject: example - Help needed
+Date: 08 March 2010 09:21
+X-Mailer: Internet Professional v1.15
+MIME-Version: 1.0
+Content-Type: multipart/mixed;boundary="_NextPart_00009D35-00000F3C-00271781-26DF"
+Return-Path: email@example.com
+Message-ID: <email@example.com>
+X-OriginalArrivalTime: 08 Mar 2010 09:21:36.0283 (UTC) FILETIME=[C03E3EB0:01CABEA0]
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17236.006
+X-TM-AS-Result: No--32.111-5.0-31-1
+X-imss-scan-details: No--32.111-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+This message is in MIME format. Since your mail reader does not
+understand this format, some or all of this message may not be legible.
+--_NextPart_00009D35-00000F3C-00271781-26DF
+Content-Type: text/plain
+Content-Transfer-Encoding: 7bit
+
+18
+
+--_NextPart_00009D35-00000F3C-00271781-26DF
+Content-Type: application/octet-stream;name="Information Pack.pdf"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;filename="Information Pack.pdf";size=106688
+
+19
+--_NextPart_00009D35-00000F3C-00271781-26DF--
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Date: Wed, 02 Dec 2009 19:21:27 +0000
+From: "A Person" <email@example.com>
+To: "A Person" <email@example.com>
+Subject: Re: As promised - info (example)
+Mime-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline
+
+20
+--=__Part163C9567.0__=--
+
diff --git a/spec/fixtures/files/rfc822-attachment.email b/spec/fixtures/files/rfc822-attachment.email
new file mode 100644
index 000000000..ae58a06af
--- /dev/null
+++ b/spec/fixtures/files/rfc822-attachment.email
@@ -0,0 +1,147 @@
+From foi.officer@example.com Fri Mar 14 08:39:57 2008
+Return-path: <foi.officer@example.com>
+Envelope-to: request-bounce-xx-xxxxx@whatdotheyknow.com
+Delivery-date: Fri, 14 Mar 2008 08:39:57 +0000
+Received: from service27.mimecast.com ([213.235.63.79]:55305)
+ by tea.ukcod.org.uk with smtp (Exim 4.50)
+ id 1Ja5SH-0005iP-Jm
+ for xxx@whatdotheyknow.com; Fri, 14 Mar 2008 08:39:57 +0000
+Received: from mailscan.ad.example.com (mailgate.example.com [194.70.143.2])
+ by service27.mimecast.com;
+ Fri, 14 Mar 2008 08:38:47 +0000
+Received: from exch2serv.ad.example.com ([201.234.62.4]) by mailscan.ad.example.com with InterScan Message Security Suite; Fri, 14 Mar 2008 08:38:47 -0000
+X-MIMEOLE: Produced By Microsoft Exchange V6.0.6603.0
+content-class: urn:content-classes:message
+MIME-Version: 1.0
+Subject:
+Date: Fri, 14 Mar 2008 08:38:46 -0000
+Message-ID: <0F3951EA9DCFB246827E1F6513F6C79D096DDADB@exch2serv.ad.example.com>
+X-MS-Has-Attach: yes
+X-MS-TNEF-Correlator:
+Thread-Index: AciFrtIeS9pyMOhuQdyxkm5305zs9g==
+From: "An FOI Officer" <foi.officer@example.com>
+To: "On" <request-bounce-xx-xxxxx@whatdotheyknow.com>
+X-MC-Unique: 108031408384702301
+Content-Type: multipart/mixed;
+ boundary="----_=_NextPart_001_01C885AE.D1BF23AC"
+
+This is a multi-part message in MIME format.
+
+------_=_NextPart_001_01C885AE.D1BF23AC
+Content-Type: multipart/alternative;
+ boundary="MCBoundary=_108031408384800401"
+
+--MCBoundary=_108031408384800401
+Content-Type: text/plain; charset=WINDOWS-1252
+Content-Transfer-Encoding: quoted-printable
+
+
+ <<Freedom of Information request >>=20
+
+
+e-mail: foi.officer@example.com=20
+
+
+
+
+***************************************************************************=
+********
+The information in this Email and any attachments is personal to the
+sender and the views of the author may not necessarily reflect those
+of Borough Council. The information is strictly confidential
+and is intended only for the named person or organisation to whom it is
+addressed as it may contain privileged and confidential information. If
+you are not the intended recipient do not copy, distribute or use this
+Email, and please notify the sender. Please note that we cannot
+guarantee that this message or any attachment is virus free or has not
+been intercepted and amended.
+***************************************************************************=
+********
+
+Disclaimer=20
+---------------------------------------------------------------------------=
+----------------
+This email message has been scanned for viruses by Mimecast.
+Mimecast delivers a complete managed email solution from a single web based=
+ platform.
+For more information please visit www.mimecast.com
+---------------------------------------------------------------------------=
+----------------
+--MCBoundary=_108031408384800401
+Content-Type: text/html; charset=WINDOWS-1252
+Content-Transfer-Encoding: quoted-printable
+
+<HTML><BODY> =20
+ <BR>
+ &lt;&lt;Freedom of Information request &gt;&gt; <BR>
+<BR>
+e-mail: <a href=3D"mailto:foi.officer@example.com">foi.officer@example.com</a> <BR>
+<BR>
+<BR>
+<BR>
+<BR>
+***************************************************************************=
+********<BR>
+The information in this Email and any attachments is personal to the<B=
+R>
+sender and the views of the author may not necessarily reflect those<BR=
+>
+of Borough Council. The information is strictly confidential<B=
+R>
+and is intended only for the named person or organisation to whom it is<BR>
+addressed as it may contain privileged and confidential information. If<B=
+R>
+you are not the intended recipient do not copy, distribute or use this=
+<BR>
+Email, and please notify the sender. Please note that we cannot<B=
+R>
+guarantee that this message or any attachment is virus free or has not<BR>
+been intercepted and amended.<BR>
+***************************************************************************=
+********<BR>
+
+ <BR>
+ <BR>
+ <span style=3D"font-family:Arial; Font-size:10.0pt">
+ Disclaimer <p>
+ <hr width=3D"100%">
+ This email message has been scanned for viruses by Mimecast.<BR>
+ Mimecast delivers a complete managed email solution from a single we=
+b based platform.<BR>
+ For more information please visit <a href=3D"http://www.mimecast.com=
+">http://www.mimecast.com</a>
+ <hr width=3D"100%">
+ </span>
+ </BODY></HTML>
+
+
+--MCBoundary=_108031408384800401--
+------_=_NextPart_001_01C885AE.D1BF23AC
+Content-Type: message/rfc822
+Content-Transfer-Encoding: 7bit
+
+X-MIMEOLE: Produced By Microsoft Exchange V6.0.6603.0
+content-class: urn:content-classes:message
+MIME-Version: 1.0
+Subject: Freedom of Information request
+Date: Thu, 13 Mar 2008 16:57:33 -0000
+Message-ID: <0F3951EA9DCFB246827E1F6513F6C79D098AC8C5@exch2serv.ad.example.com>
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator:
+Thread-Topic: Freedom of Information request
+Thread-Index: AciE9w0L7QnQlahDQS+Zjrz40mr8KAAD5KuQAADg+1AABjC9wAAAXtJgAAEHJvAAAHTxwA==
+X-Priority: 1
+Priority: Urgent
+Importance: high
+From: "An FOI Officer" <foi.officer@example.com>
+To: <request-bounce-xx-xxxxx@whatdotheyno.com>
+Content-Type: text/plain;
+ charset="utf-8"
+Content-Transfer-Encoding: base64
+
+c29tZSBleGFtcGxlIHRleHQ=
+
+------_=_NextPart_001_01C885AE.D1BF23AC--
+
+
+
diff --git a/spec/lib/i18n_interpolation.rb b/spec/lib/i18n_interpolation.rb
index 8c86413ad..6b745059c 100644
--- a/spec/lib/i18n_interpolation.rb
+++ b/spec/lib/i18n_interpolation.rb
@@ -1,6 +1,3 @@
-# This is a test of the set_content_type monkey patch in
-# lib/tmail_extensions.rb
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "when using i18n" do
diff --git a/spec/lib/mail_handler/mail_handler_spec.rb b/spec/lib/mail_handler/mail_handler_spec.rb
index a3fba0698..ae65210f2 100644
--- a/spec/lib/mail_handler/mail_handler_spec.rb
+++ b/spec/lib/mail_handler/mail_handler_spec.rb
@@ -1,6 +1,12 @@
# coding: utf-8
require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
+def create_message_from(from_field)
+ mail_data = load_file_fixture('incoming-request-plain.email')
+ mail_data.gsub!('EMAIL_FROM', from_field)
+ mail = MailHandler.mail_from_raw_email(mail_data)
+end
+
describe 'when creating a mail object from raw data' do
it 'should correctly parse a multipart email with a linebreak in the boundary' do
@@ -17,7 +23,361 @@ describe 'when creating a mail object from raw data' do
it 'should convert an iso8859 email to utf8' do
mail = get_fixture_mail('iso8859_2_raw_email.email')
mail.subject.should have_text(/gjatë/u)
- mail.body.is_utf8?.should == true
+ MailHandler.get_part_body(mail).is_utf8?.should == true
+ end
+
+end
+
+describe 'when asked for the from name' do
+
+ it 'should return nil if there is a blank "From" field' do
+ mail = create_message_from('')
+ MailHandler.get_from_name(mail).should == nil
+ end
+
+ it 'should correctly return an encoded name from the from field' do
+ mail = get_fixture_mail('quoted-subject-iso8859-1.email')
+ MailHandler.get_from_name(mail).should == 'Coordenação de Relacionamento, Pesquisa e Informação/CEDI'
+ end
+
+ it 'should get a name from a "From" field with a name and address' do
+ mail = get_fixture_mail('incoming-request-oft-attachments.email')
+ MailHandler.get_from_name(mail).should == 'Public Authority'
+ end
+
+ it 'should return nil from a "From" field that is just a name'do
+ mail = get_fixture_mail('track-response-webshield-bounce.email')
+ MailHandler.get_from_name(mail).should == nil
+ end
+
+end
+
+describe 'when asked for the from address' do
+
+ it 'should return nil if there is a blank "From" field' do
+ mail = create_message_from('')
+ MailHandler.get_from_address(mail).should == nil
+ end
+
+ it 'should correctly return an address from a mail that has an encoded name in the from field' do
+ mail = get_fixture_mail('quoted-subject-iso8859-1.email')
+ MailHandler.get_from_address(mail).should == 'geraldinequango@localhost'
+ end
+
+ it 'should return nil if there is no address in the "From" field' do
+ mail = get_fixture_mail('track-response-webshield-bounce.email')
+ MailHandler.get_from_address(mail).should == nil
+ end
+
+ it 'should return the "From" email address if there is one' do
+ mail = get_fixture_mail('track-response-abcmail-oof.email')
+ MailHandler.get_from_address(mail).should == 'Name.Removed@example.gov.uk'
+ end
+
+ it 'should get an address from a "From" field with a name and address' do
+ mail = get_fixture_mail('incoming-request-oft-attachments.email')
+ MailHandler.get_from_address(mail).should == 'public@authority.gov.uk'
+ end
+end
+
+describe 'when asked for all the addresses a mail has been sent to' do
+
+ it 'should return an array containing the envelope-to address and the to address, and the cc address if there is one' do
+ mail_data = load_file_fixture('humberside-police-odd-mime-type.email')
+ mail_data.gsub!('Envelope-to: request-5335-xxxxxxxx@whatdotheyknow.com',
+ 'Envelope-to: request-5555-xxxxxxxx@whatdotheyknow.com')
+ mail_data.gsub!('Cc: request-5335-xxxxxxxx@whatdotheyknow.com',
+ 'Cc: request-3333-xxxxxxxx@whatdotheyknow.com')
+ mail = MailHandler.mail_from_raw_email(mail_data)
+ MailHandler.get_all_addresses(mail).should == ['request-5335-xxxxxxxx@whatdotheyknow.com',
+ 'request-3333-xxxxxxxx@whatdotheyknow.com',
+ 'request-5555-xxxxxxxx@whatdotheyknow.com']
+ end
+
+ it 'should only return unique values' do
+ # envelope-to and to fields are the same
+ mail = get_fixture_mail('humberside-police-odd-mime-type.email')
+ MailHandler.get_all_addresses(mail).should == ['request-5335-xxxxxxxx@whatdotheyknow.com']
+ end
+
+ it 'should handle the absence of an envelope-to or cc field' do
+ mail_data = load_file_fixture('autoresponse-header.email')
+ mail_data.gsub!('To: FOI Person <EMAIL_TO>',
+ 'To: FOI Person <request-5555-xxxxxxxx@whatdotheyknow.com>')
+ mail = MailHandler.mail_from_raw_email(mail_data)
+ MailHandler.get_all_addresses(mail).should == ["request-5555-xxxxxxxx@whatdotheyknow.com"]
+ end
+end
+
+describe 'when asked for auto_submitted' do
+
+ it 'should return a string value for an email with an auto-submitted header' do
+ mail = get_fixture_mail('autoresponse-header.email')
+ MailHandler.get_auto_submitted(mail).should == 'auto-replied'
+ end
+
+ it 'should return a nil value for an email with no auto-submitted header' do
+ mail = get_fixture_mail('incoming-request-plain.email')
+ MailHandler.get_auto_submitted(mail).should == nil
+ end
+
+end
+
+describe 'when asked if there is an empty return path' do
+
+ it 'should return true if there is an empty return-path specified' do
+ mail = get_fixture_mail('empty-return-path.email')
+ MailHandler.empty_return_path?(mail).should == true
+ end
+
+ it 'should return false if there is no return-path header' do
+ mail = get_fixture_mail('incoming-request-attach-attachments.email')
+ MailHandler.empty_return_path?(mail).should == false
+ end
+
+ it 'should return false if there is a return path address' do
+ mail = get_fixture_mail('autoresponse-header.email')
+ MailHandler.empty_return_path?(mail).should == false
+ end
+end
+
+describe 'when deriving a name, email and formatted address from a message from a line' do
+
+ def should_render_from_address(from_line, expected_result)
+ mail = create_message_from(from_line)
+ name = MailHandler.get_from_name(mail)
+ email = MailHandler.get_from_address(mail)
+ address = MailHandler.address_from_name_and_email(name, email).to_s
+ [name, email, address].should == expected_result
+ end
+
+ it 'should correctly render a name with quoted commas' do
+ should_render_from_address('"Clare College, Cambridge" <test@test.test>',
+ ['Clare College, Cambridge',
+ 'test@test.test',
+ '"Clare College, Cambridge" <test@test.test>'])
end
+ it 'should correctly reproduce a simple name and email that does not need quotes' do
+ should_render_from_address('"FOI Person" <foiperson@localhost>',
+ ['FOI Person',
+ 'foiperson@localhost',
+ 'FOI Person <foiperson@localhost>'])
+ end
+
+ it 'should render an address with no name' do
+ should_render_from_address("foiperson@localhost",
+ [nil,
+ "foiperson@localhost",
+ "foiperson@localhost"])
+ end
+
+ it 'should quote a name with a square bracked in it' do
+ should_render_from_address('"FOI [ Person" <foiperson@localhost>',
+ ['FOI [ Person',
+ 'foiperson@localhost',
+ '"FOI [ Person" <foiperson@localhost>'])
+ end
+
+ it 'should quote a name with an @ in it' do
+ should_render_from_address('"FOI @ Person" <foiperson@localhost>',
+ ['FOI @ Person',
+ 'foiperson@localhost',
+ '"FOI @ Person" <foiperson@localhost>'])
+ end
+
+
+ it 'should quote a name with quotes in it' do
+ should_render_from_address('"FOI \" Person" <foiperson@localhost>',
+ ['FOI " Person',
+ 'foiperson@localhost',
+ '"FOI \" Person" <foiperson@localhost>'])
+ end
+
+end
+
+describe 'when getting the content type of a mail part' do
+
+ def expect_content_type(fixture_file, content_type)
+ mail = get_fixture_mail(fixture_file)
+ MailHandler.get_content_type(mail).should == content_type
+ end
+
+ it 'should correctly return a type of "multipart/report"' do
+ expect_content_type('track-response-multipart-report.email', 'multipart/report')
+ end
+
+ it 'should correctly return a type of "text/plain"' do
+ expect_content_type('track-response-abcmail-oof.email', 'text/plain')
+ end
+
+ it 'should correctly return a type of "multipart/mixed"' do
+ expect_content_type('track-response-messageclass-oof.email', 'multipart/mixed')
+ end
+
+ it 'should correctly return the types in an example bounce report' do
+ mail = get_fixture_mail('track-response-ms-bounce.email')
+ report = mail.parts.detect{ |part| MailHandler.get_content_type(part) == 'multipart/report'}
+ MailHandler.get_content_type(report.parts[0]).should == 'text/plain'
+ MailHandler.get_content_type(report.parts[1]).should == 'message/delivery-status'
+ MailHandler.get_content_type(report.parts[2]).should == 'message/rfc822'
+ end
+
+end
+
+describe 'when getting header strings' do
+
+ def expect_header_string(fixture_file, header, header_string)
+ mail = get_fixture_mail(fixture_file)
+ MailHandler.get_header_string(header, mail).should == header_string
+ end
+
+ it 'should return the contents of a "Subject" header' do
+ expect_header_string('track-response-ms-bounce.email',
+ 'Subject',
+ 'Delivery Status Notification (Delay)')
+ end
+
+ it 'should return the contents of an "X-Failed-Recipients" header' do
+ expect_header_string('autoresponse-header.email',
+ 'X-Failed-Recipients',
+ 'enquiries@cheese.com')
+ end
+
+ it 'should return the contents of an example "" header' do
+ expect_header_string('track-response-messageclass-oof.email',
+ 'X-POST-MessageClass',
+ '9; Autoresponder')
+ end
+
+end
+
+describe "when parsing HTML mail" do
+ it "should display UTF-8 characters in the plain text version correctly" do
+ html = "<html><b>foo</b> është"
+ plain_text = MailHandler.get_attachment_text_one_file('text/html', html)
+ plain_text.should match(/është/)
+ end
+
+end
+
+describe "when getting the attachment text" do
+ it "should not raise an error if the expansion of a zip file raises an error" do
+ mock_entry = mock('ZipFile entry', :file? => true)
+ mock_entries = [mock_entry]
+ mock_entries.stub!(:close)
+ mock_entry.stub!(:get_input_stream).and_raise("invalid distance too far back")
+ Zip::ZipFile.stub!(:open).and_return(mock_entries)
+ MailHandler.get_attachment_text_one_file('application/zip', "some string")
+ end
+
+end
+
+describe 'when getting attachment attributes' do
+
+ it 'should get two attachment parts from a multipart mail with text and html alternatives
+ and an image' do
+ mail = get_fixture_mail('quoted-subject-iso8859-1.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.size.should == 2
+ end
+
+ it 'should expand a mail attached as text' do
+ mail = get_fixture_mail('rfc822-attachment.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.size.should == 2
+ rfc_attachment = attributes[1]
+ rfc_attachment[:within_rfc822_subject].should == 'Freedom of Information request'
+ headers = ['Date: Thu, 13 Mar 2008 16:57:33 +0000',
+ 'Subject: Freedom of Information request',
+ 'From: An FOI Officer <foi.officer@example.com>',
+ 'To: request-bounce-xx-xxxxx@whatdotheyno.com']
+ rfc_attachment[:body].should == "#{headers.join("\n")}\n\nsome example text"
+ end
+
+ it 'should handle a mail which causes Tmail to generate a blank header value' do
+ mail = get_fixture_mail('many-attachments-date-header.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ end
+
+ it 'should produce a consistent set of url_part_numbers, content_types, within_rfc822_subjects
+ and filenames from an example mail with lots of attachments' do
+ mail = get_fixture_mail('many-attachments-date-header.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+
+ expected_attributes = [ { :content_type=>"text/plain",
+ :url_part_number=>1,
+ :within_rfc822_subject=>nil,
+ :filename=>nil},
+ { :content_type=>"text/plain",
+ :url_part_number=>2,
+ :within_rfc822_subject=>"Re: xxx",
+ :filename=>nil},
+ { :content_type=>"text/html",
+ :url_part_number=>4,
+ :within_rfc822_subject=>"example",
+ :filename=>nil},
+ { :content_type=>"image/gif", :url_part_number=>5,
+ :within_rfc822_subject=>"example",
+ :filename=>"image001.gif"},
+ { :content_type=>"application/vnd.ms-excel",
+ :url_part_number=>6,
+ :within_rfc822_subject=>"example",
+ :filename=>"particpant list.xls"},
+ { :content_type=>"text/plain",
+ :url_part_number=>7,
+ :within_rfc822_subject=>"RE: example",
+ :filename=>nil},
+ { :content_type=>"text/html",
+ :url_part_number=>9,
+ :within_rfc822_subject=>"As promised - Masterclass info (example)",
+ :filename=>nil},
+ { :content_type=>"image/gif",
+ :url_part_number=>10,
+ :within_rfc822_subject=>"As promised - Masterclass info (example)",
+ :filename=>"image001.gif"},
+ { :content_type=>"application/vnd.ms-word",
+ :url_part_number=>11,
+ :within_rfc822_subject=>"As promised - Masterclass info (example)",
+ :filename=>"Participant List.doc"},
+ { :content_type=>"application/vnd.ms-word",
+ :url_part_number=>12,
+ :within_rfc822_subject=>"As promised - Masterclass info (example)",
+ :filename=>"Information & Booking Form.doc"},
+ { :content_type=>"text/plain",
+ :url_part_number=>13,
+ :within_rfc822_subject=>"Re: As promised - info (example)",
+ :filename=>nil},
+ { :content_type=>"text/html",
+ :url_part_number=>15,
+ :within_rfc822_subject=>"Thank you from example",
+ :filename=>nil},
+ { :content_type=>"image/gif",
+ :url_part_number=>16,
+ :within_rfc822_subject=>"Thank you from example",
+ :filename=>"image001.gif"},
+ { :content_type=>"text/plain",
+ :url_part_number=>17,
+ :within_rfc822_subject=>"example - Meeting - Tuesday 2nd March",
+ :filename=>nil},
+ { :content_type=>"text/plain",
+ :url_part_number=>18,
+ :within_rfc822_subject=>"example - Help needed",
+ :filename=>nil},
+ { :content_type=>"application/pdf",
+ :url_part_number=>19,
+ :within_rfc822_subject=>"example - Help needed",
+ :filename=>"Information Pack.pdf"},
+ { :content_type=>"text/plain",
+ :url_part_number=>20,
+ :within_rfc822_subject=>"Re: As promised - info (example)",
+ :filename=>nil} ]
+
+ attributes.each_with_index do |attr, index|
+ attr.delete(:charset)
+ attr.delete(:body)
+ attr.delete(:hexdigest)
+ attr.should == expected_attributes[index]
+ end
+ end
end
diff --git a/spec/models/incoming_message_spec.rb b/spec/models/incoming_message_spec.rb
index fdbcd1e23..97bf78cb6 100644
--- a/spec/models/incoming_message_spec.rb
+++ b/spec/models/incoming_message_spec.rb
@@ -26,13 +26,8 @@ describe IncomingMessage, " when dealing with incoming mail" do
@im.sent_at.should == @im.mail.date
end
- it "should be able to parse emails with quoted commas in" do
- em = "\"Clare College, Cambridge\" <test@test.test>"
- TMail::Address.parse(em)
- end
-
it "should correctly fold various types of footer" do
- Dir.glob(File.join(Spec::Runner.configuration.fixture_path, "files", "email-folding-example-*.txt")).each do |file|
+ Dir.glob(File.join(RSpec.configuration.fixture_path, "files", "email-folding-example-*.txt")).each do |file|
message = File.read(file)
parsed = IncomingMessage.remove_quoted_sections(message)
expected = File.read("#{file}.expected")
@@ -73,6 +68,14 @@ describe IncomingMessage, " when dealing with incoming mail" do
message.get_main_body_text_internal.should include("The above text was badly encoded")
end
+ it 'should convert DOS-style linebreaks to Unix style' do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('dos-linebreaks.email', ir.incoming_email)
+ message = ir.incoming_messages[1]
+ message.parse_raw_email!
+ message.get_main_body_text_internal.should_not match(/\r\n/)
+ end
+
it "should fold multiline sections" do
{
"foo\n--------\nconfidential" => "foo\nFOLDED_QUOTED_SECTION\n", # basic test
@@ -107,27 +110,6 @@ describe IncomingMessage, " when dealing with incoming mail" do
end
-describe IncomingMessage, "when parsing HTML mail" do
- it "should display UTF-8 characters in the plain text version correctly" do
- html = "<html><b>foo</b> është"
- plain_text = IncomingMessage._get_attachment_text_internal_one_file('text/html', html)
- plain_text.should match(/është/)
- end
-
-end
-
-describe IncomingMessage, "when getting the attachment text" do
-
- it "should not raise an error if the expansion of a zip file raises an error" do
- mock_entry = mock('ZipFile entry', :file? => true)
- mock_entry.stub!(:get_input_stream).and_raise("invalid distance too far back")
- Zip::ZipFile.stub!(:open).and_return([mock_entry])
- IncomingMessage._get_attachment_text_internal_one_file('application/zip', "some string")
- end
-
-end
-
-
describe IncomingMessage, " display attachments" do
it "should not show slashes in filenames" do
@@ -143,7 +125,7 @@ describe IncomingMessage, " display attachments" do
# http://www.whatdotheyknow.com/request/post_commercial_manager_librarie#incoming-17233
foi_attachment.within_rfc822_subject = "FOI/09/066 RESPONSE TO FOI REQUEST RECEIVED 21st JANUARY 2009"
foi_attachment.content_type = 'text/plain'
- foi_attachment.ensure_filename!
+ foi_attachment.ensure_filename!
expected_display_filename = foi_attachment.within_rfc822_subject.gsub(/\//, " ") + ".txt"
foi_attachment.display_filename.should == expected_display_filename
end
@@ -194,59 +176,50 @@ describe IncomingMessage, " folding quoted parts of emails" do
end
describe IncomingMessage, " checking validity to reply to" do
- def test_email(result, email, return_path, autosubmitted = nil)
- @address = mock(TMail::Address)
- @address.stub!(:spec).and_return(email)
-
- @return_path = mock(TMail::ReturnPathHeader)
- @return_path.stub!(:addr).and_return(return_path)
- if !autosubmitted.nil?
- @autosubmitted = TMail::UnstructuredHeader.new("auto-submitted", autosubmitted)
- end
- @mail = mock(TMail::Mail)
- @mail.stub!(:from_addrs).and_return( [ @address ] )
- @mail.stub!(:[]).with("return-path").and_return(@return_path)
- @mail.stub!(:[]).with("auto-submitted").and_return(@autosubmitted)
-
+ def test_email(result, email, empty_return_path, autosubmitted = nil)
+ @mail = mock('mail')
+ MailHandler.stub!(:get_from_address).and_return(email)
+ MailHandler.stub!(:empty_return_path?).with(@mail).and_return(empty_return_path)
+ MailHandler.stub!(:get_auto_submitted).with(@mail).and_return(autosubmitted)
@incoming_message = IncomingMessage.new()
@incoming_message.stub!(:mail).and_return(@mail)
@incoming_message._calculate_valid_to_reply_to.should == result
end
it "says a valid email is fine" do
- test_email(true, "team@mysociety.org", nil)
+ test_email(true, "team@mysociety.org", false)
end
it "says postmaster email is bad" do
- test_email(false, "postmaster@mysociety.org", nil)
+ test_email(false, "postmaster@mysociety.org", false)
end
it "says Mailer-Daemon email is bad" do
- test_email(false, "Mailer-Daemon@mysociety.org", nil)
+ test_email(false, "Mailer-Daemon@mysociety.org", false)
end
it "says case mangled MaIler-DaemOn email is bad" do
- test_email(false, "MaIler-DaemOn@mysociety.org", nil)
+ test_email(false, "MaIler-DaemOn@mysociety.org", false)
end
it "says Auto_Reply email is bad" do
- test_email(false, "Auto_Reply@mysociety.org", nil)
+ test_email(false, "Auto_Reply@mysociety.org", false)
end
it "says DoNotReply email is bad" do
- test_email(false, "DoNotReply@tube.tfl.gov.uk", nil)
+ test_email(false, "DoNotReply@tube.tfl.gov.uk", false)
end
it "says a filled-out return-path is fine" do
- test_email(true, "team@mysociety.org", "Return-path: <foo@baz.com>")
+ test_email(true, "team@mysociety.org", false)
end
it "says an empty return-path is bad" do
- test_email(false, "team@mysociety.org", "<>")
+ test_email(false, "team@mysociety.org", true)
end
it "says an auto-submitted keyword is bad" do
- test_email(false, "team@mysociety.org", nil, "auto-replied")
+ test_email(false, "team@mysociety.org", false, "auto-replied")
end
end
@@ -340,12 +313,12 @@ describe IncomingMessage, " when censoring data" do
orig_pdf = load_file_fixture('tfl.pdf')
pdf = orig_pdf.dup
- orig_text = IncomingMessage._get_attachment_text_internal_one_file('application/pdf', pdf)
+ orig_text = MailHandler.get_attachment_text_one_file('application/pdf', pdf)
orig_text.should match(/foi@tfl.gov.uk/)
@im.binary_mask_stuff!(pdf, "application/pdf")
- masked_text = IncomingMessage._get_attachment_text_internal_one_file('application/pdf', pdf)
+ masked_text = MailHandler.get_attachment_text_one_file('application/pdf', pdf)
masked_text.should_not match(/foi@tfl.gov.uk/)
masked_text.should match(/xxx@xxx.xxx.xx/)
config['USE_GHOSTSCRIPT_COMPRESSION'] = previous
@@ -455,6 +428,24 @@ end
describe IncomingMessage, "when messages are attached to messages" do
+ it 'should expand an RFC822 attachment' do
+ mail_body = load_file_fixture('rfc822-attachment.email')
+ mail = MailHandler.mail_from_raw_email(mail_body)
+
+ im = incoming_messages(:useless_incoming_message)
+ im.stub!(:mail).and_return(mail)
+
+ attachments = im.get_attachments_for_display
+ attachments.size.should == 1
+ attachment = attachments.first
+
+ attachment.content_type.should == 'text/plain'
+ attachment.filename.should == "Freedom of Information request.txt"
+ attachment.charset.should == "utf-8"
+ attachment.within_rfc822_subject.should == "Freedom of Information request"
+ attachment.hexdigest.should == 'f10fe56e4f2287685a58b71329f09639'
+ end
+
it "should flatten all the attachments out" do
mail = get_fixture_mail('incoming-request-attach-attachments.email')
@@ -470,6 +461,19 @@ describe IncomingMessage, "when messages are attached to messages" do
'hello.txt',
]
end
+
+ it 'should add headers to attached plain text message bodies' do
+ mail_body = load_file_fixture('incoming-request-attachment-headers.email')
+ mail = MailHandler.mail_from_raw_email(mail_body)
+
+ im = incoming_messages(:useless_incoming_message)
+ im.stub!(:mail).and_return(mail)
+
+ attachments = im.get_attachments_for_display
+ attachments.size.should == 2
+ attachments[0].body.should match('Date: Fri, 23 May 2008')
+ end
+
end
describe IncomingMessage, "when Outlook messages are attached to messages" do
diff --git a/spec/models/info_request_event_spec.rb b/spec/models/info_request_event_spec.rb
index 796f8b840..842246fd8 100644
--- a/spec/models/info_request_event_spec.rb
+++ b/spec/models/info_request_event_spec.rb
@@ -21,7 +21,7 @@ describe InfoRequestEvent do
:event_type => 'sent',
:params => {})
event.should_receive(:xapian_mark_needs_index)
- event.run_callbacks(:after_save)
+ event.run_callbacks(:save)
end
end
diff --git a/spec/models/info_request_spec.rb b/spec/models/info_request_spec.rb
index 2aeac2fec..a1a6e6c27 100644
--- a/spec/models/info_request_spec.rb
+++ b/spec/models/info_request_spec.rb
@@ -2,6 +2,22 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe InfoRequest do
+ describe 'when generating a user name slug' do
+
+ before do
+ @public_body = mock_model(PublicBody, :url_name => 'example_body',
+ :eir_only? => false)
+ @info_request = InfoRequest.new(:external_url => 'http://www.example.com',
+ :external_user_name => 'Example User',
+ :public_body => @public_body)
+ end
+
+ it 'should generate a slug for an example user name' do
+ @info_request.user_name_slug.should == 'example_body_example_user'
+ end
+
+ end
+
describe "guessing a request from an email" do
before(:each) do
@@ -410,8 +426,8 @@ describe InfoRequest do
before do
Time.stub!(:now).and_return(Time.utc(2007, 11, 9, 23, 59))
- @mock_comment_event = safe_mock_model(InfoRequestEvent, :created_at => Time.now - 23.days, :event_type => 'comment')
- @mock_response_event = safe_mock_model(InfoRequestEvent, :created_at => Time.now - 22.days, :event_type => 'response')
+ @mock_comment_event = mock_model(InfoRequestEvent, :created_at => Time.now - 23.days, :event_type => 'comment')
+ @mock_response_event = mock_model(InfoRequestEvent, :created_at => Time.now - 22.days, :event_type => 'response')
@info_request = InfoRequest.new(:prominence => 'normal',
:awaiting_description => true,
:info_request_events => [@mock_response_event, @mock_comment_event])
@@ -441,16 +457,16 @@ describe InfoRequest do
describe 'when applying censor rules' do
before do
- @global_rule = safe_mock_model(CensorRule, :apply_to_text! => nil,
+ @global_rule = mock_model(CensorRule, :apply_to_text! => nil,
:apply_to_binary! => nil)
- @user_rule = safe_mock_model(CensorRule, :apply_to_text! => nil,
+ @user_rule = mock_model(CensorRule, :apply_to_text! => nil,
:apply_to_binary! => nil)
- @request_rule = safe_mock_model(CensorRule, :apply_to_text! => nil,
+ @request_rule = mock_model(CensorRule, :apply_to_text! => nil,
:apply_to_binary! => nil)
- @body_rule = safe_mock_model(CensorRule, :apply_to_text! => nil,
+ @body_rule = mock_model(CensorRule, :apply_to_text! => nil,
:apply_to_binary! => nil)
- @user = safe_mock_model(User, :censor_rules => [@user_rule])
- @body = safe_mock_model(PublicBody, :censor_rules => [@body_rule])
+ @user = mock_model(User, :censor_rules => [@user_rule])
+ @body = mock_model(PublicBody, :censor_rules => [@body_rule])
@info_request = InfoRequest.new(:prominence => 'normal',
:awaiting_description => true,
:title => 'title')
diff --git a/spec/models/public_body_spec.rb b/spec/models/public_body_spec.rb
index c2e0a6353..f247f561c 100644
--- a/spec/models/public_body_spec.rb
+++ b/spec/models/public_body_spec.rb
@@ -210,7 +210,7 @@ describe PublicBody, "when searching" do
end
it "should cope with same url_name across multiple locales" do
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
# use the unique spanish name to retrieve and edit
body = PublicBody.find_by_url_name_with_historic('etgq')
body.short_name = 'tgq' # Same as english version
@@ -231,7 +231,7 @@ end
describe PublicBody, " when dealing public body locales" do
it "shouldn't fail if it internal_admin_body was created in a locale other than the default" do
# first time, do it with the non-default locale
- PublicBody.with_locale(:es) do
+ I18n.with_locale(:es) do
PublicBody.internal_admin_body
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index e31c3f1b5..651ba4b65 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -152,10 +152,10 @@ end
describe User, "when reindexing referencing models" do
before do
- @request_event = safe_mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
- @request = safe_mock_model(InfoRequest, :info_request_events => [@request_event])
- @comment_event = safe_mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
- @comment = safe_mock_model(Comment, :info_request_events => [@comment_event])
+ @request_event = mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
+ @request = mock_model(InfoRequest, :info_request_events => [@request_event])
+ @comment_event = mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
+ @comment = mock_model(Comment, :info_request_events => [@comment_event])
@user = User.new(:comments => [@comment], :info_requests => [@request])
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index d4dad591d..3c170ed16 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,35 +1,26 @@
-# This file is copied to ~/spec when you run 'ruby script/generate rspec'
-# from the project root directory.
-ENV["RAILS_ENV"] = 'test'
-require File.expand_path(File.join('..', '..', 'config', 'environment'), __FILE__)
-require 'spec/autorun'
-require 'spec/rails'
+# This file is copied to spec/ when you run 'rails generate rspec:install'
+ENV["RAILS_ENV"] ||= 'test'
+require File.expand_path("../../config/environment", __FILE__)
+require 'rspec/rails'
+require 'rspec/autorun'
-# set a default username and password so we can test
-config = MySociety::Config.load_default()
-config['ADMIN_USERNAME'] = 'foo'
-config['ADMIN_PASSWORD'] = 'baz'
+# Requires supporting ruby files with custom matchers and macros, etc,
+# in spec/support/ and its subdirectories.
+Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
-# tests assume 20 days
-config['REPLY_LATE_AFTER_DAYS'] = 20
-
-# register a fake Varnish server
-require 'fakeweb'
-FakeWeb.register_uri(:purge, %r|varnish.localdomain|, :body => "OK")
-
-# Uncomment the next line to use webrat's matchers
-#require 'webrat/integrations/rspec-rails'
+RSpec.configure do |config|
+ # ## Mock Framework
+ #
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
+ #
+ # config.mock_with :mocha
+ # config.mock_with :flexmock
+ # config.mock_with :rr
-# Use test-specific translations
-FastGettext.add_text_domain 'app', :path => File.join(File.dirname(__FILE__), 'fixtures', 'locale'), :type => :po
-FastGettext.default_text_domain = 'app'
-Spec::Runner.configure do |config|
- # If you're not using ActiveRecord you should remove these
- # lines, delete config/database.yml and disable :active_record
- # in your config/boot.rb
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
- # fixture_path must end in a separator
- config.fixture_path = File.join(Rails.root, 'spec', 'fixtures') + File::SEPARATOR
+ # The order (!) of this is important thanks to foreign keys
config.global_fixtures = :users,
:public_bodies,
:public_body_translations,
@@ -46,213 +37,19 @@ Spec::Runner.configure do |config|
:holidays,
:track_things_sent_emails
-
- # == Fixtures
- #
- # You can declare fixtures for each example_group like this:
- # describe "...." do
- # fixtures :table_a, :table_b
- #
- # Alternatively, if you prefer to declare them only once, you can
- # do so right here. Just uncomment the next line and replace the fixture
- # names with your fixtures.
- #
- # config.global_fixtures = :table_a, :table_b
- #
- # If you declare global fixtures, be aware that they will be declared
- # for all of your examples, even those that don't use them.
- #
- # You can also declare which fixtures to use (for example fixtures for test/fixtures):
- #
- # config.fixture_path = Rails.root + '/spec/fixtures/'
- #
- # == Mock Framework
- #
- # RSpec uses its own mocking framework by default. If you prefer to
- # use mocha, flexmock or RR, uncomment the appropriate line:
- #
- # config.mock_with :mocha
- # config.mock_with :flexmock
- # config.mock_with :rr
- #
- # == Notes
- #
- # For more information take a look at Spec::Runner::Configuration and Spec::Runner
-end
-
-# XXX No idea what namespace/class/module to put this in
-def receive_incoming_mail(email_name, email_to, email_from = 'geraldinequango@localhost')
- email_name = file_fixture_name(email_name)
- content = File.read(email_name)
- content.gsub!('EMAIL_TO', email_to)
- content.gsub!('EMAIL_FROM', email_from)
- RequestMailer.receive(content)
-end
-
-def file_fixture_name(file_name)
- return File.join(Spec::Runner.configuration.fixture_path, "files", file_name)
-end
-
-def load_file_fixture(file_name)
- file_name = file_fixture_name(file_name)
- content = File.read(file_name)
- return content
-end
-
-def parse_all_incoming_messages
- IncomingMessage.find(:all).each{ |x| x.parse_raw_email! }
-end
-
-def load_raw_emails_data
- raw_emails_yml = File.join(Spec::Runner.configuration.fixture_path, "raw_emails.yml")
- for raw_email_id in YAML::load_file(raw_emails_yml).map{|k,v| v["id"]} do
- raw_email = RawEmail.find(raw_email_id)
- raw_email.data = load_file_fixture("raw_emails/%d.email" % [raw_email_id])
- end
-end
-
-# Rebuild the current xapian index
-def rebuild_xapian_index(terms = true, values = true, texts = true, dropfirst = true)
- if dropfirst
- begin
- ActsAsXapian.readable_init
- FileUtils.rm_r(ActsAsXapian.db_path)
- rescue RuntimeError
- end
- ActsAsXapian.writable_init
- ActsAsXapian.writable_db.close
- end
- parse_all_incoming_messages
- # safe_rebuild=true, which involves forking to avoid memory leaks, doesn't work well with rspec.
- # unsafe is significantly faster, and we can afford possible memory leaks while testing.
- models = [PublicBody, User, InfoRequestEvent]
- ActsAsXapian.rebuild_index(models, verbose=false, terms, values, texts, safe_rebuild=false)
-end
-
-# Create a clean xapian index based on the fixture files and the raw_email data.
-def create_fixtures_xapian_index
- load_raw_emails_data
- rebuild_xapian_index
-end
-
-def update_xapian_index
- ActsAsXapian.update_index(flush_to_disk=false, verbose=false)
-end
-
-# Copy the xapian index created in create_fixtures_xapian_index to a temporary
-# copy at the same level and point xapian at the copy
-def get_fixtures_xapian_index()
- # Create a base index for the fixtures if not already created
- $existing_xapian_db ||= create_fixtures_xapian_index
- # Store whatever the xapian db path is originally
- $original_xapian_path ||= ActsAsXapian.db_path
- path_array = $original_xapian_path.split(File::Separator)
- path_array.pop
- temp_path = File.join(path_array, 'test.temp')
- FileUtils.remove_entry_secure(temp_path, force=true)
- FileUtils.cp_r($original_xapian_path, temp_path)
- ActsAsXapian.db_path = temp_path
-end
-
-# Validate an entire HTML page
-def validate_html(html)
- $tempfilecount = $tempfilecount + 1
- tempfilename = File.join(Dir::tmpdir, "railshtmlvalidate."+$$.to_s+"."+$tempfilecount.to_s+".html")
- File.open(tempfilename, "w+") do |f|
- f.puts html
- end
- if not system($html_validation_script, *($html_validation_script_options +[tempfilename]))
- raise "HTML validation error in " + tempfilename + " HTTP status: " + @response.response_code.to_s
- end
- File.unlink(tempfilename)
- return true
-end
-
-# Validate HTML fragment by wrapping it as the <body> of a page
-def validate_as_body(html)
- validate_html('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' +
- "<html><head><title>Test</title></head><body>#{html}</body></html>")
-end
-
-def basic_auth_login(request, username = nil, password = nil)
- username = Configuration::admin_username if username.nil?
- password = Configuration::admin_password if password.nil?
- request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("#{username}:#{password}")
-end
-
-# Monkeypatch! Validate HTML in tests.
-$html_validation_script_found = false
-Configuration::utility_search_path.each do |d|
- $html_validation_script = File.join(d, "validate")
- $html_validation_script_options = ["--charset=utf-8"]
- if File.file? $html_validation_script and File.executable? $html_validation_script
- $html_validation_script_found = true
- break
- end
-end
-if $tempfilecount.nil?
- $tempfilecount = 0
- if $html_validation_script_found
- module ActionController
- module TestProcess
- # Hook into the process function, so can automatically get HTML after each request
- alias :original_process :process
- def is_fragment
- # XXX there must be a better way of doing this!
- return @request.query_parameters["action"] == "search_typeahead"
- end
- def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
- self.original_process(action, parameters, session, flash, http_method)
- # don't validate auto-generated HTML
- return if @request.query_parameters["action"] == "get_attachment_as_html"
- # XXX Is there a better way to check this than calling a private method?
- return unless @response.template.controller.instance_eval { integrate_views? }
- # And then if HTML, not a redirect (302, 301)
- if @response.content_type == "text/html" && ! [301,302,401].include?(@response.response_code)
- if !is_fragment
- validate_html(@response.body)
- else
- # it's a partial
- validate_as_body(@response.body)
- end
- end
- end
- end
- end
- else
- puts "WARNING: HTML validation script " + $html_validation_script + " not found"
- end
-end
-
-# to_ary differs in Ruby 1.8 and 1.9
-# @see http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
-def safe_mock_model(model, args = {})
- mock = mock_model(model, args)
- mock.should_receive(:to_ary).any_number_of_times
- mock
-end
-
-def get_fixture_mail(filename)
- MailHandler.mail_from_raw_email(load_file_fixture(filename))
-end
-
-def load_test_categories
- PublicBodyCategories.add(:en, [
- "Local and regional",
- [ "local_council", "Local councils", "a local council" ],
- "Miscellaneous",
- [ "other", "Miscellaneous", "miscellaneous" ],])
-end
-
-
-# Monkeypatch applicationcontroller because the `render_to_string`
-# method in the original breaks all the rspec test assertions such as
-# `should render_template('foo')`. Same problem as
-# http://stackoverflow.com/questions/8174415/is-it-possible-to-assert-template-or-render-template-against-the-same-partial-wi
-# - a bug in either Rails or Rspec I don't have the time to fix :(
-
-class ApplicationController < ActionController::Base
- def set_popup_banner
- @popup_banner = nil
- end
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
+ # examples within a transaction, remove the following line or assign false
+ # instead of true.
+ config.use_transactional_fixtures = true
+
+ # If true, the base class of anonymous controllers will be inferred
+ # automatically. This will be the default behavior in future versions of
+ # rspec-rails.
+ config.infer_base_class_for_anonymous_controllers = false
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ #config.order = "random"
end
diff --git a/spec/spec_helper.rb.rails2 b/spec/spec_helper.rb.rails2
new file mode 100644
index 000000000..3be11c0c7
--- /dev/null
+++ b/spec/spec_helper.rb.rails2
@@ -0,0 +1,152 @@
+# This file is copied to ~/spec when you run 'ruby script/generate rspec'
+# from the project root directory.
+ENV["RAILS_ENV"] = 'test'
+require File.expand_path(File.join('..', '..', 'config', 'environment'), __FILE__)
+require 'spec/autorun'
+require 'spec/rails'
+
+# set a default username and password so we can test
+config = MySociety::Config.load_default()
+config['ADMIN_USERNAME'] = 'foo'
+config['ADMIN_PASSWORD'] = 'baz'
+
+# tests assume 20 days
+config['REPLY_LATE_AFTER_DAYS'] = 20
+
+# register a fake Varnish server
+require 'fakeweb'
+FakeWeb.register_uri(:purge, %r|varnish.localdomain|, :body => "OK")
+
+# Uncomment the next line to use webrat's matchers
+#require 'webrat/integrations/rspec-rails'
+
+# Use test-specific translations
+FastGettext.add_text_domain 'app', :path => File.join(File.dirname(__FILE__), 'fixtures', 'locale'), :type => :po
+FastGettext.default_text_domain = 'app'
+Spec::Runner.configure do |config|
+ # If you're not using ActiveRecord you should remove these
+ # lines, delete config/database.yml and disable :active_record
+ # in your config/boot.rb
+
+ # fixture_path must end in a separator
+ config.fixture_path = File.join(Rails.root, 'spec', 'fixtures') + File::SEPARATOR
+ config.global_fixtures = :users,
+ :public_bodies,
+ :public_body_translations,
+ :public_body_versions,
+ :info_requests,
+ :raw_emails,
+ :incoming_messages,
+ :outgoing_messages,
+ :comments,
+ :info_request_events,
+ :track_things,
+ :foi_attachments,
+ :has_tag_string_tags,
+ :holidays,
+ :track_things_sent_emails
+
+ # This section makes the garbage collector run less often to speed up tests
+ last_gc_run = Time.now
+
+ config.before(:each) do
+ GC.disable
+ end
+
+ config.after(:each) do
+ if Time.now - last_gc_run > 4
+ GC.enable
+ GC.start
+ last_gc_run = Time.now
+ end
+ end
+
+ # == Fixtures
+ #
+ # You can declare fixtures for each example_group like this:
+ # describe "...." do
+ # fixtures :table_a, :table_b
+ #
+ # Alternatively, if you prefer to declare them only once, you can
+ # do so right here. Just uncomment the next line and replace the fixture
+ # names with your fixtures.
+ #
+ # config.global_fixtures = :table_a, :table_b
+ #
+ # If you declare global fixtures, be aware that they will be declared
+ # for all of your examples, even those that don't use them.
+ #
+ # You can also declare which fixtures to use (for example fixtures for test/fixtures):
+ #
+ # config.fixture_path = Rails.root + '/spec/fixtures/'
+ #
+ # == Mock Framework
+ #
+ # RSpec uses its own mocking framework by default. If you prefer to
+ # use mocha, flexmock or RR, uncomment the appropriate line:
+ #
+ # config.mock_with :mocha
+ # config.mock_with :flexmock
+ # config.mock_with :rr
+ #
+ # == Notes
+ #
+ # For more information take a look at Spec::Runner::Configuration and Spec::Runner
+end
+
+# XXX No idea what namespace/class/module to put this in
+# Create a clean xapian index based on the fixture files and the raw_email data.
+def create_fixtures_xapian_index
+ load_raw_emails_data
+ rebuild_xapian_index
+end
+
+# Copy the xapian index created in create_fixtures_xapian_index to a temporary
+# copy at the same level and point xapian at the copy
+def get_fixtures_xapian_index()
+ # Create a base index for the fixtures if not already created
+ $existing_xapian_db ||= create_fixtures_xapian_index
+ # Store whatever the xapian db path is originally
+ $original_xapian_path ||= ActsAsXapian.db_path
+ path_array = $original_xapian_path.split(File::Separator)
+ path_array.pop
+ temp_path = File.join(path_array, 'test.temp')
+ FileUtils.remove_entry_secure(temp_path, force=true)
+ FileUtils.cp_r($original_xapian_path, temp_path)
+ ActsAsXapian.db_path = temp_path
+end
+
+def basic_auth_login(request, username = nil, password = nil)
+ username = Configuration::admin_username if username.nil?
+ password = Configuration::admin_password if password.nil?
+ request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("#{username}:#{password}")
+end
+
+# to_ary differs in Ruby 1.8 and 1.9
+# @see http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
+def safe_mock_model(model, args = {})
+ mock = mock_model(model, args)
+ mock.should_receive(:to_ary).any_number_of_times
+ mock
+end
+
+def load_test_categories
+ PublicBodyCategories.add(:en, [
+ "Local and regional",
+ [ "local_council", "Local councils", "a local council" ],
+ "Miscellaneous",
+ [ "other", "Miscellaneous", "miscellaneous" ],])
+end
+
+
+# Monkeypatch applicationcontroller because the `render_to_string`
+# method in the original breaks all the rspec test assertions such as
+# `should render_template('foo')`. Same problem as
+# http://stackoverflow.com/questions/8174415/is-it-possible-to-assert-template-or-render-template-against-the-same-partial-wi
+# - a bug in either Rails or Rspec I don't have the time to fix :(
+
+class ApplicationController < ActionController::Base
+ def set_popup_banner
+ @popup_banner = nil
+ end
+end
diff --git a/spec/support/email_helpers.rb b/spec/support/email_helpers.rb
new file mode 100644
index 000000000..7e98c39f6
--- /dev/null
+++ b/spec/support/email_helpers.rb
@@ -0,0 +1,23 @@
+def load_raw_emails_data
+ raw_emails_yml = File.join(RSpec.configuration.fixture_path, "raw_emails.yml")
+ for raw_email_id in YAML::load_file(raw_emails_yml).map{|k,v| v["id"]} do
+ raw_email = RawEmail.find(raw_email_id)
+ raw_email.data = load_file_fixture("raw_emails/%d.email" % [raw_email_id])
+ end
+end
+
+def receive_incoming_mail(email_name, email_to, email_from = 'geraldinequango@localhost')
+ email_name = file_fixture_name(email_name)
+ content = File.read(email_name)
+ content.gsub!('EMAIL_TO', email_to)
+ content.gsub!('EMAIL_FROM', email_from)
+ RequestMailer.receive(content)
+end
+
+def get_fixture_mail(filename)
+ MailHandler.mail_from_raw_email(load_file_fixture(filename))
+end
+
+def parse_all_incoming_messages
+ IncomingMessage.find(:all).each{ |x| x.parse_raw_email! }
+end
diff --git a/spec/support/load_file_fixtures.rb b/spec/support/load_file_fixtures.rb
new file mode 100644
index 000000000..08079f654
--- /dev/null
+++ b/spec/support/load_file_fixtures.rb
@@ -0,0 +1,14 @@
+def file_fixture_name(file_name)
+ return File.join(RSpec.configuration.fixture_path, "files", file_name)
+end
+
+def load_file_fixture(file_name, as_binary=false)
+ file_name = file_fixture_name(file_name)
+ content = File.open(file_name, 'r') do |file|
+ if as_binary
+ file.set_encoding(Encoding::BINARY) if file.respond_to?(:set_encoding)
+ end
+ file.read
+ end
+ return content
+end
diff --git a/spec/support/validate_html.rb b/spec/support/validate_html.rb
new file mode 100644
index 000000000..ba40b395a
--- /dev/null
+++ b/spec/support/validate_html.rb
@@ -0,0 +1,65 @@
+# Validate an entire HTML page
+def validate_html(html)
+ $tempfilecount = $tempfilecount + 1
+ tempfilename = File.join(Dir::tmpdir, "railshtmlvalidate."+$$.to_s+"."+$tempfilecount.to_s+".html")
+ File.open(tempfilename, "w+") do |f|
+ f.puts html
+ end
+ if not system($html_validation_script, *($html_validation_script_options +[tempfilename]))
+ raise "HTML validation error in " + tempfilename + " HTTP status: " + @response.response_code.to_s
+ end
+ File.unlink(tempfilename)
+ return true
+end
+
+# Validate HTML fragment by wrapping it as the <body> of a page
+def validate_as_body(html)
+ validate_html('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' +
+ "<html><head><title>Test</title></head><body>#{html}</body></html>")
+end
+
+# Monkeypatch! Validate HTML in tests.
+$html_validation_script_found = false
+Configuration::utility_search_path.each do |d|
+ $html_validation_script = File.join(d, "validate")
+ $html_validation_script_options = ["--charset=utf-8"]
+ if File.file? $html_validation_script and File.executable? $html_validation_script
+ $html_validation_script_found = true
+ break
+ end
+end
+if $tempfilecount.nil?
+ $tempfilecount = 0
+ if $html_validation_script_found
+ module ActionController
+ class TestCase
+ module Behavior
+ # Hook into the process function, so can automatically get HTML after each request
+ alias :original_process :process
+ def is_fragment
+ # XXX there must be a better way of doing this!
+ return @request.query_parameters["action"] == "search_typeahead"
+ end
+ def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
+ self.original_process(action, parameters, session, flash, http_method)
+ # don't validate auto-generated HTML
+ return if @request.query_parameters["action"] == "get_attachment_as_html"
+ # XXX Is there a better way to check this than calling a private method?
+ return unless @response.template.controller.instance_eval { render_views? }
+ # And then if HTML, not a redirect (302, 301)
+ if @response.content_type == "text/html" && ! [301,302,401].include?(@response.response_code)
+ if !is_fragment
+ validate_html(@response.body)
+ else
+ # it's a partial
+ validate_as_body(@response.body)
+ end
+ end
+ end
+ end
+ end
+ end
+ else
+ puts "WARNING: HTML validation script " + $html_validation_script + " not found"
+ end
+end
diff --git a/spec/support/xapian_index.rb b/spec/support/xapian_index.rb
new file mode 100644
index 000000000..21f6192ef
--- /dev/null
+++ b/spec/support/xapian_index.rb
@@ -0,0 +1,21 @@
+# Rebuild the current xapian index
+def rebuild_xapian_index(terms = true, values = true, texts = true, dropfirst = true)
+ if dropfirst
+ begin
+ ActsAsXapian.readable_init
+ FileUtils.rm_r(ActsAsXapian.db_path)
+ rescue RuntimeError
+ end
+ ActsAsXapian.writable_init
+ ActsAsXapian.writable_db.close
+ end
+ parse_all_incoming_messages
+ # safe_rebuild=true, which involves forking to avoid memory leaks, doesn't work well with rspec.
+ # unsafe is significantly faster, and we can afford possible memory leaks while testing.
+ models = [PublicBody, User, InfoRequestEvent]
+ ActsAsXapian.rebuild_index(models, verbose=false, terms, values, texts, safe_rebuild=false)
+end
+
+def update_xapian_index
+ ActsAsXapian.update_index(flush_to_disk=false, verbose=false)
+end
diff --git a/spec/views/public_body/show.rhtml_spec.rb b/spec/views/public_body/show.rhtml_spec.rb
index b68b3f43b..1a972a661 100644
--- a/spec/views/public_body/show.rhtml_spec.rb
+++ b/spec/views/public_body/show.rhtml_spec.rb
@@ -1,6 +1,6 @@
require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
-describe "when viewing a body" do
+describe "public_body/show" do
before do
@pb = mock_model(PublicBody,
:name => 'Test Quango',
@@ -17,64 +17,62 @@ describe "when viewing a body" do
@pb.stub!(:is_requestable?).and_return(true)
@pb.stub!(:has_notes?).and_return(false)
@pb.stub!(:has_tag?).and_return(false)
- @xap = mock_model(ActsAsXapian::Search, :matches_estimated => 2)
+ @xap = mock(ActsAsXapian::Search, :matches_estimated => 2)
@xap.stub!(:results).and_return([
{ :model => mock_event },
{ :model => mock_event }
])
- assigns[:public_body] = @pb
- assigns[:track_thing] = mock_model(TrackThing,
- :track_type => 'public_body_updates', :public_body => @pb, :params => {})
- assigns[:xapian_requests] = @xap
- assigns[:page] = 1
- assigns[:per_page] = 10
- # work round a bug in ActionController::TestRequest; allows request.query_string to work in the template
- request.env["REQUEST_URI"] = ""
+ assign(:public_body, @pb)
+ assign(:track_thing, mock_model(TrackThing,
+ :track_type => 'public_body_updates', :public_body => @pb, :params => {}))
+ assign(:xapian_requests, @xap)
+ assign(:page, 1)
+ assign(:per_page, 10)
end
it "should be successful" do
- render "public_body/show"
- response.should be_success
+ render
+ controller.response.should be_success
end
it "should be valid HTML" do
- render "public_body/show"
+ render
validate_as_body response.body
end
it "should show the body's name" do
- render "public_body/show"
- response.should have_tag("h1", "Test Quango")
+ render
+ response.should have_selector('h1', :content => "Test Quango")
end
it "should tell total number of requests" do
- render "public_body/show"
- response.should include_text("4 Freedom of Information requests")
+ render
+ response.should match "4 Freedom of Information requests"
end
it "should cope with no results" do
@pb.stub!(:info_requests).and_return([])
- render "public_body/show"
- response.should have_tag("p", /Nobody has made any Freedom of Information requests/m)
+ render
+ response.should have_selector('p', :content => "Nobody has made any Freedom of Information requests")
end
it "should cope with Xapian being down" do
- assigns[:xapian_requests] = nil
- render "public_body/show"
- response.should have_tag("p", /The search index is currently offline/m)
+ assign(:xapian_requests, nil)
+ render
+ response.should match "The search index is currently offline"
end
it "should link to Charity Commission site if we have numbers to do so" do
@pb.stub!(:has_tag?).and_return(true)
@pb.stub!(:get_tag_values).and_return(['98765', '12345'])
- render "public_body/show"
- response.should have_tag("div#header_right") do
- with_tag("a[href*=?]", /charity-commission.gov.uk.*RegisteredCharityNumber=98765$/)
+ render
+ response.should have_selector("div#header_right") do
+ have_selector "a", :href => /charity-commission.gov.uk.*RegisteredCharityNumber=98765$/
end
- response.should have_tag("div#header_right") do
- with_tag("a[href*=?]", /charity-commission.gov.uk.*RegisteredCharityNumber=12345$/)
+ response.should have_selector("div#header_right") do
+ have_selector "a", :href => /www.charity-commission.gov.uk.*RegisteredCharityNumber=12345$/
end
end
@@ -82,17 +80,17 @@ describe "when viewing a body" do
@pb.stub!(:has_tag?).and_return(true)
@pb.stub!(:get_tag_values).and_return(['SC1234'])
- render "public_body/show"
- response.should have_tag("div#header_right") do
- with_tag("a[href*=?]", /www.oscr.org.uk.*id=SC1234$/)
+ render
+ response.should have_selector("div#header_right") do
+ have_selector "a", :href => /www.oscr.org.uk.*id=SC1234$/
end
end
it "should not link to Charity Commission site if we don't have number" do
- render "public_body/show"
- response.should have_tag("div#header_right") do
- without_tag("a[href*=?]", /charity-commission.gov.uk/)
+ render
+ response.should have_selector("div#header_right") do
+ have_selector "a", :href => /charity-commission.gov.uk/
end
end
diff --git a/spec/views/request/_after_actions.rhtml_spec.rb b/spec/views/request/_after_actions.rhtml_spec.rb
index 5b4734c52..54fef8cab 100644
--- a/spec/views/request/_after_actions.rhtml_spec.rb
+++ b/spec/views/request/_after_actions.rhtml_spec.rb
@@ -14,51 +14,27 @@ describe 'when displaying actions that can be taken with regard to a request' do
:public_body => @mock_body,
:comments_allowed? => true,
:url_title => 'test_request')
- assigns[:info_request] = @mock_request
- end
-
- def do_render
- render :partial => 'request/after_actions'
- end
-
- def expect_owner_div
- do_render
- response.should have_tag('div#owner_actions'){ yield }
- end
-
- def expect_anyone_div
- do_render
- response.should have_tag('div#anyone_actions'){ yield }
- end
-
- def expect_owner_link(text)
- expect_owner_div{ with_tag('a', :text => text) }
- end
-
- def expect_no_owner_link(text)
- expect_owner_div{ without_tag('a', :text => text) }
- end
-
- def expect_anyone_link(text)
- expect_anyone_div{ with_tag('a', :text => text) }
- end
-
- def expect_no_anyone_link(text)
- expect_anyone_div{ without_tag('a', :text => text) }
+ assign :info_request, @mock_request
end
describe 'if the request is old and unclassified' do
before do
- assigns[:old_unclassified] = true
+ assign :old_unclassified, true
end
it 'should not display a link for the request owner to update the status of the request' do
- expect_no_owner_link('Update the status of this request')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#owner_actions') do |div|
+ div.should_not have_selector('a', :content => 'Update the status of this request')
+ end
end
it 'should display a link for anyone to update the status of the request' do
- expect_anyone_link('Update the status of this request')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#anyone_actions') do |div|
+ div.should have_selector('a', :content => 'Update the status of this request')
+ end
end
end
@@ -66,21 +42,30 @@ describe 'when displaying actions that can be taken with regard to a request' do
describe 'if the request is not old and unclassified' do
before do
- assigns[:old_unclassified] = false
+ assign :old_unclassified, false
end
it 'should display a link for the request owner to update the status of the request' do
- expect_owner_link('Update the status of this request')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#owner_actions') do |div|
+ div.should have_selector('a', :content => 'Update the status of this request')
+ end
end
it 'should not display a link for anyone to update the status of the request' do
- expect_no_anyone_link('Update the status of this request')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#anyone_actions') do |div|
+ div.should_not have_selector('a', :content => 'Update the status of this request')
+ end
end
end
it 'should display a link for the request owner to request a review' do
- expect_owner_link('Request an internal review')
+ render :partial => 'request/after_actions'
+ response.should have_selector('div#owner_actions') do |div|
+ div.should have_selector('a', :content => 'Request an internal review')
+ end
end
end
diff --git a/spec/views/request/_describe_state.rhtml_spec.rb b/spec/views/request/_describe_state.rhtml_spec.rb
index 18778d5d2..41b308d75 100644
--- a/spec/views/request/_describe_state.rhtml_spec.rb
+++ b/spec/views/request/_describe_state.rhtml_spec.rb
@@ -4,12 +4,12 @@ describe 'when showing the form for describing the state of a request' do
def expect_radio_button(value)
do_render
- response.should have_tag("input[type=radio][value=#{value}]")
+ response.should have_selector('input', :type => 'radio', :value => value)
end
def expect_no_radio_button(value)
do_render
- response.should_not have_tag("input[type=radio][value=#{value}]")
+ response.should_not have_selector('input', :type => 'radio', :value => value)
end
def do_render
@@ -24,25 +24,25 @@ describe 'when showing the form for describing the state of a request' do
:user_name => @mock_user.name,
:is_external? => false
)
- assigns[:info_request] = @mock_request
+ assign :info_request, @mock_request
end
describe 'if the user is a regular user (not the request owner)' do
before do
- assigns[:is_owning_user] = false
+ assign :is_owning_user, false
end
describe 'if the request is not old and unclassified' do
it 'should not show the form' do
do_render
- response.should_not have_tag('h2', :text => 'What best describes the status of this request now?')
+ response.should_not have_selector('h2', :content => 'What best describes the status of this request now?')
end
it 'should give a link to login' do
do_render
- response.should have_tag('a', :text => 'sign in')
+ response.should have_selector('a', :content => 'sign in')
end
end
@@ -50,22 +50,22 @@ describe 'when showing the form for describing the state of a request' do
describe 'if the request is old and unclassified' do
before do
- assigns[:old_unclassified] = true
+ assign :old_unclassified, true
end
it 'should not show the form' do
do_render
- response.should_not have_tag('h2', :text => 'What best describes the status of this request now?')
+ response.should_not have_selector('h2', :content => 'What best describes the status of this request now?')
end
it 'should show the form for someone else to classify the request' do
do_render
- response.should have_tag('h2', :text => /We need your help/)
+ response.should have_selector('h2', :content => /We need your help/)
end
it 'should not give a link to login' do
do_render
- response.should_not have_tag('a', :text => 'sign in')
+ response.should_not have_selector('a', :content => 'sign in')
end
end
@@ -74,7 +74,7 @@ describe 'when showing the form for describing the state of a request' do
describe 'if showing the form to the user owning the request' do
before do
- assigns[:is_owning_user] = true
+ assign :is_owning_user, true
end
describe 'when the request is not in internal review' do
@@ -100,7 +100,7 @@ describe 'when showing the form for describing the state of a request' do
describe 'when the user has asked to update the status of the request' do
before do
- assigns[:update_status] = true
+ assign :update_status, true
end
it 'should show a radio button to set the status to "internal_review"' do
@@ -129,7 +129,7 @@ describe 'when showing the form for describing the state of a request' do
it 'should show the text "The review has finished and overall:"' do
do_render
- response.should have_tag('p', :text => 'The review has finished and overall:')
+ response.should have_selector('p', :content => 'The review has finished and overall:')
end
end
@@ -170,4 +170,4 @@ describe 'when showing the form for describing the state of a request' do
end
end
-end \ No newline at end of file
+end
diff --git a/spec/views/request/list.rhtml_spec.rb b/spec/views/request/list.rhtml_spec.rb
index 94ece5e76..8e34147b5 100644
--- a/spec/views/request/list.rhtml_spec.rb
+++ b/spec/views/request/list.rhtml_spec.rb
@@ -1,14 +1,10 @@
require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
-describe "when listing recent requests" do
+describe "request/list" do
before do
- assigns[:page] = 1
- assigns[:per_page] = 10
- # work round a bug in ActionController::TestRequest; allows request.query_string to work in the template
- request.env["REQUEST_URI"] = ""
- # we're not testing the interlock plugin's cache
- template.stub!(:view_cache).and_yield
+ assign :page, 1
+ assign :per_page, 10
end
def make_mock_event
@@ -31,22 +27,22 @@ describe "when listing recent requests" do
)
end
- it "should be successful" do
- assigns[:list_results] = [ make_mock_event, make_mock_event ]
- assigns[:matches_estimated] = 2
- assigns[:show_no_more_than] = 100
- render "request/list"
- response.should have_tag("div.request_listing")
- response.should_not have_tag("p", /No requests of this sort yet/m)
+ it "should be successful", :focus => true do
+ assign :list_results, [ make_mock_event, make_mock_event ]
+ assign :matches_estimated, 2
+ assign :show_no_more_than, 100
+ render
+ response.should have_selector("div.request_listing")
+ response.should_not have_selector("p", :content => "No requests of this sort yet")
end
it "should cope with no results" do
- assigns[:list_results] = [ ]
- assigns[:matches_estimated] = 0
- assigns[:show_no_more_than] = 0
- render "request/list"
- response.should have_tag("p", /No requests of this sort yet/m)
- response.should_not have_tag("div.request_listing")
+ assign :list_results, [ ]
+ assign :matches_estimated, 0
+ assign :show_no_more_than, 0
+ render
+ response.should have_selector("p", :content => "No requests of this sort yet")
+ response.should_not have_selector("div.request_listing")
end
end
diff --git a/spec/views/request/show.rhtml_spec.rb b/spec/views/request/show.rhtml_spec.rb
index 4429e9e58..6e36a7ab6 100644
--- a/spec/views/request/show.rhtml_spec.rb
+++ b/spec/views/request/show.rhtml_spec.rb
@@ -23,32 +23,32 @@ describe 'when viewing an information request' do
end
def request_page
- assigns[:info_request] = @mock_request
- assigns[:info_request_events] = []
- assigns[:status] = @mock_request.calculate_status
- template.stub!(:render_partial)
+ assign :info_request, @mock_request
+ assign :info_request_events, []
+ assign :status, @mock_request.calculate_status
+ view.stub!(:render_partial)
render 'request/show'
end
it 'should show the sidebar' do
- template.should_receive(:render_partial).with(:partial => 'sidebar', :locals => {})
+ view.should_receive(:render_partial).with(:partial => 'sidebar', :locals => {})
request_page
end
it 'should show the actions people can take' do
- template.should_receive(:render_partial).with(:partial => 'after_actions', :locals => {})
+ view.should_receive(:render_partial).with(:partial => 'after_actions', :locals => {})
request_page
end
describe 'when a status update has been requested' do
before do
- assigns[:update_status] = true
+ assign :update_status, true
end
it 'should show the first form for describing the state of the request' do
request_page
- response.should have_tag("div.describe_state_form#describe_state_form_1")
+ response.should have_selector("div.describe_state_form#describe_state_form_1")
end
end
@@ -61,12 +61,12 @@ describe 'when viewing an information request' do
it 'should show the first form for describing the state of the request' do
request_page
- response.should have_tag("div.describe_state_form#describe_state_form_1")
+ response.should have_selector("div.describe_state_form#describe_state_form_1")
end
it 'should show the second form for describing the state of the request' do
request_page
- response.should have_tag("div.describe_state_form#describe_state_form_2")
+ response.should have_selector("div.describe_state_form#describe_state_form_2")
end
end
@@ -74,7 +74,7 @@ describe 'when viewing an information request' do
describe 'when the user is the request owner' do
before do
- assigns[:is_owning_user] = true
+ assign :is_owning_user, true
end
describe 'when the request status is "waiting clarification"' do
@@ -99,7 +99,7 @@ describe 'when viewing an information request' do
it 'should show a link to follow up the last response with clarification' do
request_page
expected_url = "http://test.host/request/#{@mock_request.id}/response/#{@mock_response.id}#followup"
- response.should have_tag("a[href=#{expected_url}]", :text => 'send a follow up message')
+ response.should have_selector("a[href=#{expected_url}]", :content => 'send a follow up message')
end
end
@@ -119,7 +119,7 @@ describe 'when viewing an information request' do
it 'should show a link to follow up the request without reference to a specific response' do
request_page
expected_url = "http://test.host/request/#{@mock_request.id}/response#followup"
- response.should have_tag("a[href=#{expected_url}]", :text => 'send a follow up message')
+ response.should have_selector("a[href=#{expected_url}]", :content => 'send a follow up message')
end
end
end
diff --git a/spec/views/request_game/play.rhtml_spec.rb b/spec/views/request_game/play.rhtml_spec.rb
index 24fb6d75d..0353f25b5 100644
--- a/spec/views/request_game/play.rhtml_spec.rb
+++ b/spec/views/request_game/play.rhtml_spec.rb
@@ -22,10 +22,10 @@ describe 'when viewing the request game' do
:initial_request_text => 'hi there',
:display_status => 'Awaiting categorisation',
:created_at => Time.now)
- assigns[:league_table_28_days] = []
- assigns[:league_table_all_time] = []
- assigns[:requests] = [@mock_request]
- assigns[:play_urls] = true
+ assign :league_table_28_days, []
+ assign :league_table_all_time, []
+ assign :requests, [@mock_request]
+ assign :play_urls, true
end
it 'should show the correct url for a request' do
diff --git a/vendor/gems/fakeweb-1.3.0/.gitignore b/vendor/gems/fakeweb-1.3.0/.gitignore
deleted file mode 100644
index 63be0c5de..000000000
--- a/vendor/gems/fakeweb-1.3.0/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-/doc
-/rdoc
-/html
-/coverage
-/pkg
-/.idea
-*.rbc
diff --git a/vendor/gems/fakeweb-1.3.0/.specification b/vendor/gems/fakeweb-1.3.0/.specification
deleted file mode 100644
index b69e9d545..000000000
--- a/vendor/gems/fakeweb-1.3.0/.specification
+++ /dev/null
@@ -1,135 +0,0 @@
---- !ruby/object:Gem::Specification
-name: fakeweb
-version: !ruby/object:Gem::Version
- hash: 27
- prerelease:
- segments:
- - 1
- - 3
- - 0
- version: 1.3.0
-platform: ruby
-authors:
-- Chris Kampmeier
-- Blaine Cook
-autorequire:
-bindir: bin
-cert_chain: []
-
-date: 2010-08-22 00:00:00 +01:00
-default_executable:
-dependencies:
-- !ruby/object:Gem::Dependency
- name: mocha
- prerelease: false
- requirement: &id001 !ruby/object:Gem::Requirement
- none: false
- requirements:
- - - ">="
- - !ruby/object:Gem::Version
- hash: 49
- segments:
- - 0
- - 9
- - 5
- version: 0.9.5
- type: :development
- version_requirements: *id001
-description: FakeWeb is a helper for faking web requests in Ruby. It works at a global level, without modifying code or writing extensive stubs.
-email:
-- chris@kampers.net
-- romeda@gmail.com
-executables: []
-
-extensions: []
-
-extra_rdoc_files: []
-
-files:
-- test/test_allow_net_connect.rb
-- test/test_deprecations.rb
-- test/test_fake_authentication.rb
-- test/test_fake_web.rb
-- test/test_fake_web_open_uri.rb
-- test/test_helper.rb
-- test/test_last_request.rb
-- test/test_missing_open_uri.rb
-- test/test_missing_pathname.rb
-- test/test_other_net_http_libraries.rb
-- test/test_precedence.rb
-- test/test_query_string.rb
-- test/test_regexes.rb
-- test/test_response_headers.rb
-- test/test_trailing_slashes.rb
-- test/test_utility.rb
-- test/vendor/right_http_connection-1.2.4/lib/net_fix.rb
-- test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb
-- test/vendor/right_http_connection-1.2.4/setup.rb
-- test/vendor/samuel-0.2.1/lib/samuel/net_http.rb
-- test/vendor/samuel-0.2.1/lib/samuel/request.rb
-- test/vendor/samuel-0.2.1/lib/samuel.rb
-- test/vendor/samuel-0.2.1/test/request_test.rb
-- test/vendor/samuel-0.2.1/test/samuel_test.rb
-- test/vendor/samuel-0.2.1/test/test_helper.rb
-- test/vendor/samuel-0.2.1/test/thread_test.rb
-has_rdoc: true
-homepage: http://github.com/chrisk/fakeweb
-licenses: []
-
-post_install_message:
-rdoc_options: []
-
-require_paths:
-- lib
-required_ruby_version: !ruby/object:Gem::Requirement
- none: false
- requirements:
- - - ">="
- - !ruby/object:Gem::Version
- hash: 3
- segments:
- - 0
- version: "0"
-required_rubygems_version: !ruby/object:Gem::Requirement
- none: false
- requirements:
- - - ">="
- - !ruby/object:Gem::Version
- hash: 3
- segments:
- - 0
- version: "0"
-requirements: []
-
-rubyforge_project: fakeweb
-rubygems_version: 1.6.2
-signing_key:
-specification_version: 3
-summary: A tool for faking responses to HTTP requests
-test_files:
-- test/test_allow_net_connect.rb
-- test/test_deprecations.rb
-- test/test_fake_authentication.rb
-- test/test_fake_web.rb
-- test/test_fake_web_open_uri.rb
-- test/test_helper.rb
-- test/test_last_request.rb
-- test/test_missing_open_uri.rb
-- test/test_missing_pathname.rb
-- test/test_other_net_http_libraries.rb
-- test/test_precedence.rb
-- test/test_query_string.rb
-- test/test_regexes.rb
-- test/test_response_headers.rb
-- test/test_trailing_slashes.rb
-- test/test_utility.rb
-- test/vendor/right_http_connection-1.2.4/lib/net_fix.rb
-- test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb
-- test/vendor/right_http_connection-1.2.4/setup.rb
-- test/vendor/samuel-0.2.1/lib/samuel/net_http.rb
-- test/vendor/samuel-0.2.1/lib/samuel/request.rb
-- test/vendor/samuel-0.2.1/lib/samuel.rb
-- test/vendor/samuel-0.2.1/test/request_test.rb
-- test/vendor/samuel-0.2.1/test/samuel_test.rb
-- test/vendor/samuel-0.2.1/test/test_helper.rb
-- test/vendor/samuel-0.2.1/test/thread_test.rb
diff --git a/vendor/gems/fakeweb-1.3.0/CHANGELOG b/vendor/gems/fakeweb-1.3.0/CHANGELOG
deleted file mode 100644
index e626352f6..000000000
--- a/vendor/gems/fakeweb-1.3.0/CHANGELOG
+++ /dev/null
@@ -1,215 +0,0 @@
-fakeweb (1.3.0)
-
-* improve response header registration so you can pass an array to set a header
- more than once [Myron Marston]
-
-* fix an exception when the response's :body option was set to nil [Chris Zingel]
-
-* fix that stubbed requests weren't mutating the Net::HTTP request object to set
- the body and content-length, like real requests do [Chris Kampmeier]
-
-* add FakeWeb.last_request [Chris Kampmeier]
-
-* assigning a String or Regexp to FakeWeb.allow_net_connect= sets a whitelist
- for outbound requests [Dan Dofter, Tim Carey-Smith, Ben Woosley]
-
-
-fakeweb (1.2.8)
-
-* support Pathname objects where a filename is expected [Chris Kampmeier]
-
-* fix compatibility with Ruby 1.9.2 [Chris Kampmeier]
-
-* simplify storage of FakeWeb::VERSION [Josh Peek, Woody Peterson, Ben Woosley]
-
-
-fakeweb (1.2.7)
-
-* revert to sorting query params before matching requests against regexps,
- instead of the 1.2.6 behavior that tried every possible order combination;
- that was factorial-time, which made matching hang for requests with long query
- strings [Jason Wadsworth, David Dollar, Blaine Cook]
-
-* print a warning when FakeWeb is loaded before RightHttpConnection or after
- Samuel, other libs that patch Net::HTTP [Chris Kampmeier, Ben Brinckerhoff]
-
-
-fakeweb (1.2.6)
-
-* fix that query params in a regex would have to be sorted for it to ever match
- a request URI [Chris Kampmeier, Ben Hall]
-
-* improve regex handling so registration with an explicit port (like
- /example.com:80/) matches a request that uses an implied port
- (like "http://example.com/") [Chris Kampmeier, Dan Dofter]
-
-* refactor URI registry to reduce duplication; now about twice as fast at
- handling requests [Chris Kampmeier]
-
-* Add FakeWeb::VERSION so you can programmatically determine what version of
- FakeWeb is loaded without using RubyGems [Chris Kampmeier, Chris Wanstrath]
-
-
-fakeweb (1.2.5)
-
-* fix handling of userinfo strings that contain percent-encoded unsafe
- characters [Chris Kampmeier, Ken Mayer]
-
-* fix that exact matches against strings/URIs with the :any method had a lower
- precedence than regex matches using a real HTTP method (exact matches now
- always take precedence) [Chris Kampmeier]
-
-* change request handling to raise an exception when more than one registered
- regex matches a request URI [Chris Kampmeier]
-
-
-fakeweb (1.2.4)
-
-* add experimental support for matching URIs via regular expressions
- [Jacqui Maher, Tiago Albineli Motta, Peter Wagene]
-
-* fix an exception when registering with the :response option and a string that
- is the same as the name of a directory in the current path [Chris Kampmeier]
-
-* DEPRECATION: Calling FakeWeb.register_uri with a :string or :file option is
- now deprecated. Both options have been replaced with a unified :body option,
- since they supply the response body (as opposed to :response, which supplies
- the full response including headers) [Chris Kampmeier]
-
-* add support for specifying HTTP headers as options to FakeWeb.register_uri
- when using the :string or :file response types, since those methods only
- specify a response body [David Michael, Chris Kampmeier]
-
-* DEPRECATION: Calling FakeWeb.register_uri and FakeWeb.registered_uri? without
- an HTTP method as the first argument is now deprecated. To match against any
- HTTP method (the pre-1.2.0 behavior), use :any [Chris Kampmeier]
-
-
-fakeweb (1.2.3)
-
-* fix the #http_version of :file and :string responses, which was returning the
- request URI instead of something sensible like "1.0" [Chris Kampmeier]
-
-* add method aliases in the Net::HTTP patch to eliminate warnings when running
- with -w [Joshua Clingenpeel]
-
-* fix that removing the redefinition of OpenURI::HTTPError in 1.2.0 caused
- :exception responses to raise when OpenURI isn't available [Chris Kampmeier]
-
-* fix registering an :exception response with classes that require arguments for
- instantiation, like Interrupt's subclasses [Chris Kampmeier]
-
-
-fakeweb (1.2.2)
-
-* fix that HTTP Digest and OAuth requests could raise URI::InvalidURIErrors
- [Bill Kocik, Chris Kampmeier]
-
-
-fakeweb (1.2.1)
-
-* fix that query parameters are handled correctly when registering with a URI
- object [Anselmo Alves, Chris Kampmeier]
-
-* fix an exception when registering with the :response option and a string
- containing "\0" [Jonathan Baudanza, Chris Kampmeier]
-
-* fix that trailing slashes were considered significant for requests to the root
- of a domain [Chris Kampmeier]
-
-* add support for HTTP basic authentication via userinfo strings in URIs
- [Michael Bleigh]
-
-
-fakeweb (1.2.0)
-
-* add lib/fakeweb.rb so you can require "fakeweb" as well [Chris Kampmeier]
-
-* fix compatibility with Ruby 1.9.1 [Chris Kampmeier]
-
-* fix that newlines in file-based responses could be doubled in the response
- object's body [Mark Menard, Chris Kampmeier]
-
-* fix unnecessary munging of the transfer-encoding header, which improves
- compatibility with mechanize [Mark Menard]
-
-* fix a test and the RCov dependency to be compatible with JRuby [Mark Menard]
-
-* remove an unnecessary redefinition of OpenURI::HTTPError [Josh Nichols]
-
-* rearrange implementation code into separate files, one per class [Josh Nichols]
-
-* fix a bug where FakeWeb.response_for would raise if the request wasn't
- registered [Chris Kampmeier]
-
-* add HTTP method support, so FakeWeb takes both the URI and method into
- account for registration, requests, and responses. Backwards-compatible with
- the old method signatures, which didn't have a method param. [Chris Kampmeier]
-
-* start work on Ruby 1.9 compatibility [Chris Kampmeier]
-
-* add FakeWeb.allow_net_connect= to enable/disable the pass-through to
- Net::HTTP for unregistered URIs [Mislav Marohnić, Chris Kampmeier]
-
-* remove setup.rb, since most people use RubyGems [Mislav Marohnić]
-
-* fix that 'http://example.com/?' (empty query) matches a registered
- 'http://example.com/', and vice-versa [Mislav Marohnić]
-
-* improve the test suite to not rely on an internet connection [Chris Kampmeier]
-
-* use `rake test` instead of `rake tests` [Josh Nichols]
-
-* fix an incompatibility with Ruby 1.8.6 p36 where you'd get "Errno::EINTR:
- Interrupted system call" exceptions in Socket#sysread for any non-faked
- request [Chris Kampmeier]
-
-* response rotation: you can now optionally call FakeWeb.register_uri with an
- array of options hashes; these are used, in order, to respond to
- repeated requests (to repeat a response more than once before rotating, use
- the :times option). Once you run out of responses, further requests always
- receive the last response. [Michael Shapiro]
-
-* add support for Net::HTTP's undocumented full-URI request style (fixes
- URI::InvalidURIErrors that you might see in older libraries) [Chris Kampmeier]
-
-* sort query params before storing internally, so that
- http://example.com/?a=1&b=2 and http://example.com/?b=2&a=1 are considered the
- same URL (although this is technically incorrect, it's much more
- convenient--most web apps work that way, and Net::HTTP's use of a hash to pass
- query params means that the order in which FakeWeb stores them can be
- unpredictable) [Chris Kampmeier]
-
-* add support for ports in URLs, so that http://example.com/ and
- http://example.com:3000/ are not the same [Chris Kampmeier]
-
-* fix for non-faked SSL requests failing with "Unable to create local socket"
- [Chris Kampmeier]
-
-* update Rakefile to fix warning about deprecated code [Chris Kampmeier]
-
-
-fakeweb (1.1.2)
-
-* add required dependencies to GemSpec to ensure that tests pass in firebrigade
- (http://firebrigade.seattlerb.org/) [Blaine Cook]
-
-
-fakeweb (1.1.1)
-
-* fix for non-existence of :string method on File as presented by open-uri
- [Blaine Cook]
-
-* fix for curl example test - google redirects to ccTLDs for those outside US
- [Blaine Cook]
-
-
-fakeweb (1.1.0)
-
-* update code to correspond to ruby 1.8.4 (breaks compatibility with ruby 1.8.2)
- [Blaine Cook]
-
-
-fakeweb (1.0.0)
-
- * initial import [Blaine Cook]
diff --git a/vendor/gems/fakeweb-1.3.0/LICENSE.txt b/vendor/gems/fakeweb-1.3.0/LICENSE.txt
deleted file mode 100644
index ecae84703..000000000
--- a/vendor/gems/fakeweb-1.3.0/LICENSE.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright 2006-2010 Blaine Cook, Chris Kampmeier, and other contributors
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/vendor/gems/fakeweb-1.3.0/README.rdoc b/vendor/gems/fakeweb-1.3.0/README.rdoc
deleted file mode 100644
index 33115325b..000000000
--- a/vendor/gems/fakeweb-1.3.0/README.rdoc
+++ /dev/null
@@ -1,189 +0,0 @@
-= FakeWeb
-
-FakeWeb is a helper for faking web requests in Ruby. It works at a global
-level, without modifying code or writing extensive stubs.
-
-
-== Installation
-
- gem install fakeweb
-
-Note: the gem was previously available as +FakeWeb+ (capital letters), but now
-all versions are simply registered as +fakeweb+. If you have any old +FakeWeb+
-gems lying around, remove them: <tt>gem uninstall FakeWeb</tt>
-
-
-== Help and discussion
-
-RDocs for the current release are available at http://fakeweb.rubyforge.org.
-
-There's a mailing list for questions and discussion at
-http://groups.google.com/group/fakeweb-users.
-
-The main source repository is http://github.com/chrisk/fakeweb.
-
-== Examples
-
-Start by requiring FakeWeb:
-
- require 'fakeweb'
-
-=== Registering basic string responses
-
- FakeWeb.register_uri(:get, "http://example.com/test1", :body => "Hello World!")
-
- Net::HTTP.get(URI.parse("http://example.com/test1"))
- => "Hello World!"
-
- Net::HTTP.get(URI.parse("http://example.com/test2"))
- => FakeWeb is bypassed and the response from a real request is returned
-
-You can also call <tt>register_uri</tt> with a regular expression, to match
-more than one URI.
-
- FakeWeb.register_uri(:get, %r|http://example\.com/|, :body => "Hello World!")
-
- Net::HTTP.get(URI.parse("http://example.com/test3"))
- => "Hello World!"
-
-=== Replaying a recorded response
-
- page = `curl -is http://www.google.com/`
- FakeWeb.register_uri(:get, "http://www.google.com/", :response => page)
-
- Net::HTTP.get(URI.parse("http://www.google.com/"))
- # => Full response, including headers
-
-=== Adding a custom status to the response
-
- FakeWeb.register_uri(:get, "http://example.com/", :body => "Nothing to be found 'round here",
- :status => ["404", "Not Found"])
-
- Net::HTTP.start("example.com") do |req|
- response = req.get("/")
- response.code # => "404"
- response.message # => "Not Found"
- response.body # => "Nothing to be found 'round here"
- end
-
-=== Responding to any HTTP method
-
- FakeWeb.register_uri(:any, "http://example.com", :body => "response for any HTTP method")
-
-If you use the <tt>:any</tt> symbol, the URI you specify will be completely
-stubbed out (regardless of the HTTP method of the request). This can be useful
-for RPC-style services, where the HTTP method isn't significant. (Older
-versions of FakeWeb always behaved like this, and didn't accept the first
-+method+ argument above; this syntax is now deprecated.)
-
-=== Rotating responses
-
-You can optionally call <tt>FakeWeb.register_uri</tt> with an array of options
-hashes; these are used, in order, to respond to repeated requests. Once you run
-out of responses, further requests always receive the last response. (You can
-also send a response more than once before rotating, by specifying a
-<tt>:times</tt> option for that response.)
-
- FakeWeb.register_uri(:delete, "http://example.com/posts/1",
- [{:body => "Post 1 deleted.", :status => ["200", "OK"]},
- {:body => "Post not found", :status => ["404", "Not Found"]}])
-
- Net::HTTP.start("example.com") do |req|
- req.delete("/posts/1").body # => "Post 1 deleted"
- req.delete("/posts/1").body # => "Post not found"
- req.delete("/posts/1").body # => "Post not found"
- end
-
-=== Using HTTP basic authentication
-
-You can fake requests that use basic authentication by adding +userinfo+ strings
-to your URIs:
-
- FakeWeb.register_uri(:get, "http://example.com/secret", :body => "Unauthorized", :status => ["401", "Unauthorized"])
- FakeWeb.register_uri(:get, "http://user:pass@example.com/secret", :body => "Authorized")
-
- Net::HTTP.start("example.com") do |http|
- req = Net::HTTP::Get.new("/secret")
- http.request(req) # => "Unauthorized"
- req.basic_auth("user", "pass")
- http.request(req) # => "Authorized"
- end
-
-=== Clearing registered URIs
-
-The FakeWeb registry is a singleton that lasts for the duration of your program,
-maintaining every fake response you register. If needed, you can clean out the
-registry and remove all registered URIs:
-
- FakeWeb.clean_registry
-
-=== Blocking all real requests
-
-When you're using FakeWeb to replace _all_ of your requests, it's useful to
-catch when requests are made for unregistered URIs (unlike the default
-behavior, which is to pass those requests through to Net::HTTP as usual).
-
- FakeWeb.allow_net_connect = false
- Net::HTTP.get(URI.parse("http://example.com/"))
- => raises FakeWeb::NetConnectNotAllowedError
-
- FakeWeb.allow_net_connect = true
- Net::HTTP.get(URI.parse("http://example.com/"))
- => FakeWeb is bypassed and the response from a real request is returned
-
-It's recommended that you set <tt>FakeWeb.allow_net_connect = false</tt> in the
-setup for your tests.
-
-==== Allowing requests to a specific server
-
-If you want to prevent your tests from hitting the internet while allowing
-access to a specific server for integration testing, you can assign a URI or
-+Regexp+ to be used as a whitelist for outbound requests:
-
- FakeWeb.allow_net_connect = %r[^https?://localhost]
- Net::HTTP.get(URI.parse("http://localhost/path")) # => allowed
- Net::HTTP.get(URI.parse("http://example.com/")) # => raises FakeWeb::NetConnectNotAllowedError
-
-=== Specifying HTTP response headers
-
-When you register a response using the <tt>:body</tt> option, you're only
-setting the body of the response. If you want to add headers to these responses,
-simply add the header as an option to +register_uri+:
-
- FakeWeb.register_uri(:get, "http://example.com/hello.txt", :body => "Hello", :content_type => "text/plain")
-
-This sets the "Content-Type" header in the response.
-
-=== Checking the last request
-
-It's often useful to retrieve the last request made by your code, so you can
-write tests for its content. FakeWeb keeps track of the last request, whether it
-was stubbed or not:
-
- Net::HTTP.get(URI.parse("http://example.com"))
- FakeWeb.last_request # => Net::HTTP::Get request object
-
-== More info
-
-FakeWeb lets you decouple your test environment from live services without
-modifying code or writing extensive stubs.
-
-In addition to the conceptual advantage of having idempotent request
-behaviour, FakeWeb makes tests run faster than if they were made to remote (or
-even local) web servers. It also makes it possible to run tests without a
-network connection or in situations where the server is behind a firewall or
-has host-based access controls.
-
-FakeWeb works with anything based on Net::HTTP--both higher-level wrappers,
-like OpenURI, as well as a ton of libraries for popular web services.
-
-
-== Known Issues
-
-* Request bodies are ignored, including PUT and POST parameters. If you need
- different responses for different request bodies, you need to request
- different URLs, and register different responses for each. (Query strings are
- fully supported, though.) We're currently considering how the API should
- change to add support for request bodies in 1.3.0. Your input would be really
- helpful: see http://groups.google.com/group/fakeweb-users/browse_thread/thread/44d190a6b12e4273
- for a discussion of some different options. Thanks!
diff --git a/vendor/gems/fakeweb-1.3.0/Rakefile b/vendor/gems/fakeweb-1.3.0/Rakefile
deleted file mode 100644
index e3c5298c6..000000000
--- a/vendor/gems/fakeweb-1.3.0/Rakefile
+++ /dev/null
@@ -1,67 +0,0 @@
-require 'rubygems'
-require 'rake'
-
-version = '1.3.0'
-
-begin
- require 'jeweler'
- Jeweler::Tasks.new do |gem|
- gem.name = "fakeweb"
- gem.rubyforge_project = "fakeweb"
- gem.version = version
- gem.summary = "A tool for faking responses to HTTP requests"
- gem.description = "FakeWeb is a helper for faking web requests in Ruby. It works at a global level, without modifying code or writing extensive stubs."
- gem.email = ["chris@kampers.net", "romeda@gmail.com"]
- gem.authors = ["Chris Kampmeier", "Blaine Cook"]
- gem.homepage = "http://github.com/chrisk/fakeweb"
- gem.add_development_dependency "mocha", ">= 0.9.5"
- end
-rescue LoadError
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
-end
-
-
-require 'rake/testtask'
-Rake::TestTask.new(:test) do |test|
- test.test_files = FileList["test/**/*.rb"].exclude("test/test_helper.rb", "test/vendor")
- test.libs << "test"
- test.verbose = false
- test.warning = true
-end
-
-task :default => [:check_dependencies, :test]
-
-
-begin
- require 'rcov/rcovtask'
- Rcov::RcovTask.new do |t|
- t.test_files = FileList["test/**/*.rb"].exclude("test/test_helper.rb", "test/vendor")
- t.libs << "test"
- t.rcov_opts << "--sort coverage"
- t.rcov_opts << "--exclude gems"
- t.warning = true
- end
-rescue LoadError
- print "rcov support disabled "
- if RUBY_PLATFORM =~ /java/
- puts "(running under JRuby)"
- else
- puts "(install RCov to enable the `rcov` task)"
- end
-end
-
-
-begin
- require 'sdoc'
- require 'rdoc/task'
- Rake::RDocTask.new do |rdoc|
- rdoc.main = "README.rdoc"
- rdoc.rdoc_files.include("README.rdoc", "CHANGELOG", "LICENSE.txt", "lib/*.rb")
- rdoc.title = "FakeWeb #{version} API Documentation"
- rdoc.rdoc_dir = "doc"
- rdoc.template = "direct"
- rdoc.options << "--line-numbers" << "--show-hash" << "--charset=utf-8"
- end
-rescue LoadError
- puts "SDoc (or a dependency) not available. Install it with: gem install sdoc"
-end
diff --git a/vendor/gems/fakeweb-1.3.0/fakeweb.gemspec b/vendor/gems/fakeweb-1.3.0/fakeweb.gemspec
deleted file mode 100644
index 39b23f001..000000000
--- a/vendor/gems/fakeweb-1.3.0/fakeweb.gemspec
+++ /dev/null
@@ -1,126 +0,0 @@
-# Generated by jeweler
-# DO NOT EDIT THIS FILE DIRECTLY
-# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
-# -*- encoding: utf-8 -*-
-
-Gem::Specification.new do |s|
- s.name = %q{fakeweb}
- s.version = "1.3.0"
-
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.authors = ["Chris Kampmeier", "Blaine Cook"]
- s.date = %q{2010-08-22}
- s.description = %q{FakeWeb is a helper for faking web requests in Ruby. It works at a global level, without modifying code or writing extensive stubs.}
- s.email = ["chris@kampers.net", "romeda@gmail.com"]
- s.extra_rdoc_files = [
- "LICENSE.txt",
- "README.rdoc"
- ]
- s.files = [
- ".autotest",
- ".gitignore",
- "CHANGELOG",
- "LICENSE.txt",
- "README.rdoc",
- "Rakefile",
- "fakeweb.gemspec",
- "lib/fake_web.rb",
- "lib/fake_web/ext/net_http.rb",
- "lib/fake_web/registry.rb",
- "lib/fake_web/responder.rb",
- "lib/fake_web/response.rb",
- "lib/fake_web/stub_socket.rb",
- "lib/fake_web/utility.rb",
- "lib/fakeweb.rb",
- "test/fixtures/google_response_from_curl",
- "test/fixtures/google_response_with_transfer_encoding",
- "test/fixtures/google_response_without_transfer_encoding",
- "test/fixtures/test_example.txt",
- "test/fixtures/test_txt_file",
- "test/test_allow_net_connect.rb",
- "test/test_deprecations.rb",
- "test/test_fake_authentication.rb",
- "test/test_fake_web.rb",
- "test/test_fake_web_open_uri.rb",
- "test/test_helper.rb",
- "test/test_last_request.rb",
- "test/test_missing_open_uri.rb",
- "test/test_missing_pathname.rb",
- "test/test_other_net_http_libraries.rb",
- "test/test_precedence.rb",
- "test/test_query_string.rb",
- "test/test_regexes.rb",
- "test/test_response_headers.rb",
- "test/test_trailing_slashes.rb",
- "test/test_utility.rb",
- "test/vendor/right_http_connection-1.2.4/History.txt",
- "test/vendor/right_http_connection-1.2.4/Manifest.txt",
- "test/vendor/right_http_connection-1.2.4/README.txt",
- "test/vendor/right_http_connection-1.2.4/Rakefile",
- "test/vendor/right_http_connection-1.2.4/lib/net_fix.rb",
- "test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb",
- "test/vendor/right_http_connection-1.2.4/setup.rb",
- "test/vendor/samuel-0.2.1/.document",
- "test/vendor/samuel-0.2.1/.gitignore",
- "test/vendor/samuel-0.2.1/LICENSE",
- "test/vendor/samuel-0.2.1/README.rdoc",
- "test/vendor/samuel-0.2.1/Rakefile",
- "test/vendor/samuel-0.2.1/VERSION",
- "test/vendor/samuel-0.2.1/lib/samuel.rb",
- "test/vendor/samuel-0.2.1/lib/samuel/net_http.rb",
- "test/vendor/samuel-0.2.1/lib/samuel/request.rb",
- "test/vendor/samuel-0.2.1/samuel.gemspec",
- "test/vendor/samuel-0.2.1/test/request_test.rb",
- "test/vendor/samuel-0.2.1/test/samuel_test.rb",
- "test/vendor/samuel-0.2.1/test/test_helper.rb",
- "test/vendor/samuel-0.2.1/test/thread_test.rb"
- ]
- s.homepage = %q{http://github.com/chrisk/fakeweb}
- s.rdoc_options = ["--charset=UTF-8"]
- s.require_paths = ["lib"]
- s.rubyforge_project = %q{fakeweb}
- s.rubygems_version = %q{1.3.7}
- s.summary = %q{A tool for faking responses to HTTP requests}
- s.test_files = [
- "test/test_allow_net_connect.rb",
- "test/test_deprecations.rb",
- "test/test_fake_authentication.rb",
- "test/test_fake_web.rb",
- "test/test_fake_web_open_uri.rb",
- "test/test_helper.rb",
- "test/test_last_request.rb",
- "test/test_missing_open_uri.rb",
- "test/test_missing_pathname.rb",
- "test/test_other_net_http_libraries.rb",
- "test/test_precedence.rb",
- "test/test_query_string.rb",
- "test/test_regexes.rb",
- "test/test_response_headers.rb",
- "test/test_trailing_slashes.rb",
- "test/test_utility.rb",
- "test/vendor/right_http_connection-1.2.4/lib/net_fix.rb",
- "test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb",
- "test/vendor/right_http_connection-1.2.4/setup.rb",
- "test/vendor/samuel-0.2.1/lib/samuel/net_http.rb",
- "test/vendor/samuel-0.2.1/lib/samuel/request.rb",
- "test/vendor/samuel-0.2.1/lib/samuel.rb",
- "test/vendor/samuel-0.2.1/test/request_test.rb",
- "test/vendor/samuel-0.2.1/test/samuel_test.rb",
- "test/vendor/samuel-0.2.1/test/test_helper.rb",
- "test/vendor/samuel-0.2.1/test/thread_test.rb"
- ]
-
- if s.respond_to? :specification_version then
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
- s.specification_version = 3
-
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
- s.add_development_dependency(%q<mocha>, [">= 0.9.5"])
- else
- s.add_dependency(%q<mocha>, [">= 0.9.5"])
- end
- else
- s.add_dependency(%q<mocha>, [">= 0.9.5"])
- end
-end
-
diff --git a/vendor/gems/fakeweb-1.3.0/lib/fake_web.rb b/vendor/gems/fakeweb-1.3.0/lib/fake_web.rb
deleted file mode 100644
index 77fbdaec4..000000000
--- a/vendor/gems/fakeweb-1.3.0/lib/fake_web.rb
+++ /dev/null
@@ -1,215 +0,0 @@
-require 'singleton'
-
-require 'fake_web/ext/net_http'
-require 'fake_web/registry'
-require 'fake_web/response'
-require 'fake_web/responder'
-require 'fake_web/stub_socket'
-require 'fake_web/utility'
-
-FakeWeb::Utility.record_loaded_net_http_replacement_libs
-FakeWeb::Utility.puts_warning_for_net_http_around_advice_libs_if_needed
-
-module FakeWeb
-
- # Returns the version string for the copy of FakeWeb you have loaded.
- VERSION = '1.3.0'
-
- # Resets the FakeWeb Registry. This will force all subsequent web requests to
- # behave as real requests.
- def self.clean_registry
- Registry.instance.clean_registry
- end
-
- # Enables or disables real HTTP connections for requests that don't match
- # registered URIs.
- #
- # If you set <tt>FakeWeb.allow_net_connect = false</tt> and subsequently try
- # to make a request to a URI you haven't registered with #register_uri, a
- # NetConnectNotAllowedError will be raised. This is handy when you want to
- # make sure your tests are self-contained, or want to catch the scenario
- # when a URI is changed in implementation code without a corresponding test
- # change.
- #
- # When <tt>FakeWeb.allow_net_connect = true</tt> (the default), requests to
- # URIs not stubbed with FakeWeb are passed through to Net::HTTP.
- #
- # If you assign a +String+, +URI+, or +Regexp+ object, unstubbed requests
- # will be allowed if they match that value. This is useful when you want to
- # allow access to a local server for integration testing, while still
- # preventing your tests from using the internet.
- def self.allow_net_connect=(allowed)
- case allowed
- when String, URI, Regexp
- @allow_all_connections = false
- Registry.instance.register_passthrough_uri(allowed)
- else
- @allow_all_connections = allowed
- Registry.instance.remove_passthrough_uri
- end
- end
-
- # Enable pass-through to Net::HTTP by default.
- self.allow_net_connect = true
-
- # Returns +true+ if requests to URIs not registered with FakeWeb are passed
- # through to Net::HTTP for normal processing (the default). Returns +false+
- # if an exception is raised for these requests.
- #
- # If you've assigned a +String+, +URI+, or +Regexp+ to
- # <tt>FakeWeb.allow_net_connect=</tt>, you must supply a URI to check
- # against that filter. Otherwise, an ArgumentError will be raised.
- def self.allow_net_connect?(uri = nil)
- if Registry.instance.passthrough_uri_map.any?
- raise ArgumentError, "You must supply a URI to test" if uri.nil?
- Registry.instance.passthrough_uri_matches?(uri)
- else
- @allow_all_connections
- end
- end
-
- # This exception is raised if you set <tt>FakeWeb.allow_net_connect =
- # false</tt> and subsequently try to make a request to a URI you haven't
- # stubbed.
- class NetConnectNotAllowedError < StandardError; end;
-
- # This exception is raised if a Net::HTTP request matches more than one of
- # the stubs you've registered. To fix the problem, remove a duplicate
- # registration or disambiguate any regular expressions by making them more
- # specific.
- class MultipleMatchingURIsError < StandardError; end;
-
- # call-seq:
- # FakeWeb.register_uri(method, uri, options)
- #
- # Register requests using the HTTP method specified by the symbol +method+
- # for +uri+ to be handled according to +options+. If you specify the method
- # <tt>:any</tt>, the response will be reigstered for any request for +uri+.
- # +uri+ can be a +String+, +URI+, or +Regexp+ object. +options+ must be either
- # a +Hash+ or an +Array+ of +Hashes+ (see below), which must contain one of
- # these two keys:
- #
- # <tt>:body</tt>::
- # A string which is used as the body of the response. If the string refers
- # to a valid filesystem path, the contents of that file will be read and used
- # as the body of the response instead. (This used to be two options,
- # <tt>:string</tt> and <tt>:file</tt>, respectively. These are now deprecated.)
- # <tt>:response</tt>::
- # Either a <tt>Net::HTTPResponse</tt>, an +IO+, or a +String+ which is used
- # as the full response for the request.
- #
- # The easier way by far is to pass the <tt>:response</tt> option to
- # +register_uri+ as a +String+ or an (open for reads) +IO+ object which
- # will be used as the complete HTTP response, including headers and body.
- # If the string points to a readable file, this file will be used as the
- # content for the request.
- #
- # To obtain a complete response document, you can use the +curl+ command,
- # like so:
- #
- # curl -i http://example.com > response_from_example.com
- #
- # which can then be used in your test environment like so:
- #
- # FakeWeb.register_uri(:get, "http://example.com", :response => "response_from_example.com")
- #
- # See the <tt>Net::HTTPResponse</tt>
- # documentation[http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html]
- # for more information on creating custom response objects.
- #
- # +options+ may also be an +Array+ containing a list of the above-described
- # +Hash+. In this case, FakeWeb will rotate through each response. You can
- # optionally repeat a response more than once before rotating:
- #
- # <tt>:times</tt>::
- # The number of times this response will be used before moving on to the
- # next one. The last response will be repeated indefinitely, regardless of
- # its <tt>:times</tt> parameter.
- #
- # Two optional arguments are also accepted:
- #
- # <tt>:status</tt>::
- # Passing <tt>:status</tt> as a two-value array will set the response code
- # and message. The defaults are <tt>200</tt> and <tt>OK</tt>, respectively.
- # Example:
- # FakeWeb.register_uri(:get, "http://example.com", :body => "Go away!", :status => [404, "Not Found"])
- # <tt>:exception</tt>::
- # The argument passed via <tt>:exception</tt> will be raised when the
- # specified URL is requested. Any +Exception+ class is valid. Example:
- # FakeWeb.register_uri(:get, "http://example.com", :exception => Net::HTTPError)
- #
- # If you're using the <tt>:body</tt> response type, you can pass additional
- # options to specify the HTTP headers to be used in the response. Example:
- #
- # FakeWeb.register_uri(:get, "http://example.com/index.txt", :body => "Hello", :content_type => "text/plain")
- #
- # You can also pass an array of header values to include a header in the
- # response more than once:
- #
- # FakeWeb.register_uri(:get, "http://example.com", :set_cookie => ["name=value", "example=1"])
- def self.register_uri(*args)
- case args.length
- when 3
- Registry.instance.register_uri(*args)
- when 2
- print_missing_http_method_deprecation_warning(*args)
- Registry.instance.register_uri(:any, *args)
- else
- raise ArgumentError.new("wrong number of arguments (#{args.length} for 3)")
- end
- end
-
- # call-seq:
- # FakeWeb.response_for(method, uri)
- #
- # Returns the faked Net::HTTPResponse object associated with +method+ and +uri+.
- def self.response_for(*args, &block) #:nodoc: :yields: response
- case args.length
- when 2
- Registry.instance.response_for(*args, &block)
- when 1
- print_missing_http_method_deprecation_warning(*args)
- Registry.instance.response_for(:any, *args, &block)
- else
- raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
- end
- end
-
- # call-seq:
- # FakeWeb.registered_uri?(method, uri)
- #
- # Returns true if a +method+ request for +uri+ is registered with FakeWeb.
- # Specify a method of <tt>:any</tt> to check against all HTTP methods.
- def self.registered_uri?(*args)
- case args.length
- when 2
- Registry.instance.registered_uri?(*args)
- when 1
- print_missing_http_method_deprecation_warning(*args)
- Registry.instance.registered_uri?(:any, *args)
- else
- raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
- end
- end
-
- # Returns the request object from the last request made via Net::HTTP.
- def self.last_request
- @last_request
- end
-
- def self.last_request=(request) #:nodoc:
- @last_request = request
- end
-
- private
-
- def self.print_missing_http_method_deprecation_warning(*args)
- method = caller.first.match(/`(.*?)'/)[1]
- new_args = args.map { |a| a.inspect }.unshift(":any")
- new_args.last.gsub!(/^\{|\}$/, "").gsub!("=>", " => ") if args.last.is_a?(Hash)
- $stderr.puts
- $stderr.puts "Deprecation warning: FakeWeb requires an HTTP method argument (or use :any). Try this:"
- $stderr.puts " FakeWeb.#{method}(#{new_args.join(', ')})"
- $stderr.puts "Called at #{caller[1]}"
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/lib/fake_web/ext/net_http.rb b/vendor/gems/fakeweb-1.3.0/lib/fake_web/ext/net_http.rb
deleted file mode 100644
index 4ff3e9a10..000000000
--- a/vendor/gems/fakeweb-1.3.0/lib/fake_web/ext/net_http.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-require 'net/http'
-require 'net/https'
-require 'stringio'
-
-module Net #:nodoc: all
-
- class BufferedIO
- def initialize_with_fakeweb(io, debug_output = nil)
- @read_timeout = 60
- @rbuf = ''
- @debug_output = debug_output
-
- @io = case io
- when Socket, OpenSSL::SSL::SSLSocket, IO
- io
- when String
- if !io.include?("\0") && File.exists?(io) && !File.directory?(io)
- File.open(io, "r")
- else
- StringIO.new(io)
- end
- end
- raise "Unable to create local socket" unless @io
- end
- alias_method :initialize_without_fakeweb, :initialize
- alias_method :initialize, :initialize_with_fakeweb
- end
-
- class HTTP
- class << self
- def socket_type_with_fakeweb
- FakeWeb::StubSocket
- end
- alias_method :socket_type_without_fakeweb, :socket_type
- alias_method :socket_type, :socket_type_with_fakeweb
- end
-
- def request_with_fakeweb(request, body = nil, &block)
- FakeWeb.last_request = request
-
- uri = FakeWeb::Utility.request_uri_as_string(self, request)
- method = request.method.downcase.to_sym
-
- if FakeWeb.registered_uri?(method, uri)
- @socket = Net::HTTP.socket_type.new
- FakeWeb::Utility.produce_side_effects_of_net_http_request(request, body)
- FakeWeb.response_for(method, uri, &block)
- elsif FakeWeb.allow_net_connect?(uri)
- connect_without_fakeweb
- request_without_fakeweb(request, body, &block)
- else
- uri = FakeWeb::Utility.strip_default_port_from_uri(uri)
- raise FakeWeb::NetConnectNotAllowedError,
- "Real HTTP connections are disabled. Unregistered request: #{request.method} #{uri}"
- end
- end
- alias_method :request_without_fakeweb, :request
- alias_method :request, :request_with_fakeweb
-
-
- def connect_with_fakeweb
- unless @@alredy_checked_for_net_http_replacement_libs ||= false
- FakeWeb::Utility.puts_warning_for_net_http_replacement_libs_if_needed
- @@alredy_checked_for_net_http_replacement_libs = true
- end
- nil
- end
- alias_method :connect_without_fakeweb, :connect
- alias_method :connect, :connect_with_fakeweb
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/lib/fake_web/registry.rb b/vendor/gems/fakeweb-1.3.0/lib/fake_web/registry.rb
deleted file mode 100644
index 9a4a34e68..000000000
--- a/vendor/gems/fakeweb-1.3.0/lib/fake_web/registry.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-module FakeWeb
- class Registry #:nodoc:
- include Singleton
-
- attr_accessor :uri_map, :passthrough_uri_map
-
- def initialize
- clean_registry
- end
-
- def clean_registry
- self.uri_map = Hash.new { |hash, key| hash[key] = {} }
- end
-
- def register_uri(method, uri, options)
- uri_map[normalize_uri(uri)][method] = [*[options]].flatten.collect do |option|
- FakeWeb::Responder.new(method, uri, option, option[:times])
- end
- end
-
- def registered_uri?(method, uri)
- !responders_for(method, uri).empty?
- end
-
- def response_for(method, uri, &block)
- responders = responders_for(method, uri)
- return nil if responders.empty?
-
- next_responder = responders.last
- responders.each do |responder|
- if responder.times and responder.times > 0
- responder.times -= 1
- next_responder = responder
- break
- end
- end
-
- next_responder.response(&block)
- end
-
- def register_passthrough_uri(uri)
- self.passthrough_uri_map = {normalize_uri(uri) => {:any => true}}
- end
-
- def remove_passthrough_uri
- self.passthrough_uri_map = {}
- end
-
- def passthrough_uri_matches?(uri)
- uri = normalize_uri(uri)
- uri_map_matches(passthrough_uri_map, :any, uri, URI) ||
- uri_map_matches(passthrough_uri_map, :any, uri, Regexp)
- end
-
- private
-
- def responders_for(method, uri)
- uri = normalize_uri(uri)
-
- uri_map_matches(uri_map, method, uri, URI) ||
- uri_map_matches(uri_map, :any, uri, URI) ||
- uri_map_matches(uri_map, method, uri, Regexp) ||
- uri_map_matches(uri_map, :any, uri, Regexp) ||
- []
- end
-
- def uri_map_matches(map, method, uri, type_to_check = URI)
- uris_to_check = variations_of_uri_as_strings(uri)
-
- matches = map.select { |registered_uri, method_hash|
- registered_uri.is_a?(type_to_check) && method_hash.has_key?(method)
- }.select { |registered_uri, method_hash|
- if type_to_check == URI
- uris_to_check.include?(registered_uri.to_s)
- elsif type_to_check == Regexp
- uris_to_check.any? { |u| u.match(registered_uri) }
- end
- }
-
- if matches.size > 1
- raise MultipleMatchingURIsError,
- "More than one registered URI matched this request: #{method.to_s.upcase} #{uri}"
- end
-
- matches.map { |_, method_hash| method_hash[method] }.first
- end
-
-
- def variations_of_uri_as_strings(uri_object)
- normalized_uri = normalize_uri(uri_object.dup)
- normalized_uri_string = normalized_uri.to_s
-
- variations = [normalized_uri_string]
-
- # if the port is implied in the original, add a copy with an explicit port
- if normalized_uri.default_port == normalized_uri.port
- variations << normalized_uri_string.sub(
- /#{Regexp.escape(normalized_uri.request_uri)}$/,
- ":#{normalized_uri.port}#{normalized_uri.request_uri}")
- end
-
- variations
- end
-
- def normalize_uri(uri)
- return uri if uri.is_a?(Regexp)
- normalized_uri =
- case uri
- when URI then uri
- when String
- uri = 'http://' + uri unless uri.match('^https?://')
- URI.parse(uri)
- end
- normalized_uri.query = sort_query_params(normalized_uri.query)
- normalized_uri.normalize
- end
-
- def sort_query_params(query)
- if query.nil? || query.empty?
- nil
- else
- query.split('&').sort.join('&')
- end
- end
-
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/lib/fake_web/responder.rb b/vendor/gems/fakeweb-1.3.0/lib/fake_web/responder.rb
deleted file mode 100644
index 573fec3d4..000000000
--- a/vendor/gems/fakeweb-1.3.0/lib/fake_web/responder.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-module FakeWeb
- class Responder #:nodoc:
-
- attr_accessor :method, :uri, :options, :times
- KNOWN_OPTIONS = [:body, :exception, :response, :status].freeze
-
- def initialize(method, uri, options, times)
- self.method = method
- self.uri = uri
- self.options = options
- self.times = times ? times : 1
-
- if options.has_key?(:file) || options.has_key?(:string)
- print_file_string_options_deprecation_warning
- options[:body] = options.delete(:file) || options.delete(:string)
- end
- end
-
- def response(&block)
- if has_baked_response?
- response = baked_response
- else
- code, msg = meta_information
- response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
- response.instance_variable_set(:@body, body)
- headers_extracted_from_options.each do |name, value|
- if value.respond_to?(:each)
- value.each { |v| response.add_field(name, v) }
- else
- response[name] = value
- end
- end
- end
-
- response.instance_variable_set(:@read, true)
- response.extend FakeWeb::Response
-
- optionally_raise(response)
-
- yield response if block_given?
-
- response
- end
-
- private
-
- def headers_extracted_from_options
- options.reject {|name, _| KNOWN_OPTIONS.include?(name) }.map { |name, value|
- [name.to_s.split("_").map { |segment| segment.capitalize }.join("-"), value]
- }
- end
-
- def body
- return '' if options[:body].nil?
-
- options[:body] = options[:body].to_s if defined?(Pathname) && options[:body].is_a?(Pathname)
-
- if !options[:body].include?("\0") && File.exists?(options[:body]) && !File.directory?(options[:body])
- File.read(options[:body])
- else
- options[:body]
- end
- end
-
- def baked_response
- return options[:response] if options[:response].is_a?(Net::HTTPResponse)
-
- if options[:response].is_a?(String) || (defined?(Pathname) && options[:response].is_a?(Pathname))
- socket = Net::BufferedIO.new(options[:response].to_s)
- r = Net::HTTPResponse.read_new(socket)
-
- # Store the original transfer-encoding
- saved_transfer_encoding = r.instance_eval {
- @header['transfer-encoding'] if @header.key?('transfer-encoding')
- }
-
- # Read the body of response
- r.instance_eval { @header['transfer-encoding'] = nil }
- r.reading_body(socket, true) {}
-
- # Delete the transfer-encoding key from r.@header if there wasn't one;
- # otherwise, restore the saved_transfer_encoding
- if saved_transfer_encoding.nil?
- r.instance_eval { @header.delete('transfer-encoding') }
- else
- r.instance_eval { @header['transfer-encoding'] = saved_transfer_encoding }
- end
- r
- else
- raise StandardError, "Handler unimplemented for response #{options[:response]}"
- end
- end
-
- def has_baked_response?
- options.has_key?(:response)
- end
-
- def optionally_raise(response)
- return unless options.has_key?(:exception)
-
- case options[:exception].to_s
- when "Net::HTTPError", "OpenURI::HTTPError"
- raise options[:exception].new('Exception from FakeWeb', response)
- else
- raise options[:exception].new('Exception from FakeWeb')
- end
- end
-
- def meta_information
- options.has_key?(:status) ? options[:status] : [200, 'OK']
- end
-
- def print_file_string_options_deprecation_warning
- which = options.has_key?(:file) ? :file : :string
- $stderr.puts
- $stderr.puts "Deprecation warning: FakeWeb's :#{which} option has been renamed to :body."
- $stderr.puts "Just replace :#{which} with :body in your FakeWeb.register_uri calls."
- $stderr.puts "Called at #{caller[6]}"
- end
-
- end
-end \ No newline at end of file
diff --git a/vendor/gems/fakeweb-1.3.0/lib/fake_web/response.rb b/vendor/gems/fakeweb-1.3.0/lib/fake_web/response.rb
deleted file mode 100644
index 41ba2557b..000000000
--- a/vendor/gems/fakeweb-1.3.0/lib/fake_web/response.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module FakeWeb
- module Response #:nodoc:
-
- def read_body(*args, &block)
- yield @body if block_given?
- @body
- end
-
- end
-end \ No newline at end of file
diff --git a/vendor/gems/fakeweb-1.3.0/lib/fake_web/stub_socket.rb b/vendor/gems/fakeweb-1.3.0/lib/fake_web/stub_socket.rb
deleted file mode 100644
index 008681ca6..000000000
--- a/vendor/gems/fakeweb-1.3.0/lib/fake_web/stub_socket.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module FakeWeb
- class StubSocket #:nodoc:
-
- def initialize(*args)
- end
-
- def closed?
- @closed ||= true
- end
-
- def readuntil(*args)
- end
-
- end
-end \ No newline at end of file
diff --git a/vendor/gems/fakeweb-1.3.0/lib/fake_web/utility.rb b/vendor/gems/fakeweb-1.3.0/lib/fake_web/utility.rb
deleted file mode 100644
index bd5d7161c..000000000
--- a/vendor/gems/fakeweb-1.3.0/lib/fake_web/utility.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-module FakeWeb
- module Utility #:nodoc:
-
- def self.decode_userinfo_from_header(header)
- header.sub(/^Basic /, "").unpack("m").first
- end
-
- def self.encode_unsafe_chars_in_userinfo(userinfo)
- unsafe_in_userinfo = /[^#{URI::REGEXP::PATTERN::UNRESERVED};&=+$,]|^(#{URI::REGEXP::PATTERN::ESCAPED})/
- userinfo.split(":").map { |part| uri_escape(part, unsafe_in_userinfo) }.join(":")
- end
-
- def self.strip_default_port_from_uri(uri)
- case uri
- when %r{^http://} then uri.sub(%r{:80(/|$)}, '\1')
- when %r{^https://} then uri.sub(%r{:443(/|$)}, '\1')
- else uri
- end
- end
-
- # Returns a string with a normalized version of a Net::HTTP request's URI.
- def self.request_uri_as_string(net_http, request)
- protocol = net_http.use_ssl? ? "https" : "http"
-
- path = request.path
- path = URI.parse(request.path).request_uri if request.path =~ /^http/
-
- if request["authorization"] =~ /^Basic /
- userinfo = FakeWeb::Utility.decode_userinfo_from_header(request["authorization"])
- userinfo = FakeWeb::Utility.encode_unsafe_chars_in_userinfo(userinfo) + "@"
- else
- userinfo = ""
- end
-
- uri = "#{protocol}://#{userinfo}#{net_http.address}:#{net_http.port}#{path}"
- end
-
- # Wrapper for URI escaping that switches between URI::Parser#escape and
- # URI.escape for 1.9-compatibility
- def self.uri_escape(*args)
- if URI.const_defined?(:Parser)
- URI::Parser.new.escape(*args)
- else
- URI.escape(*args)
- end
- end
-
- def self.produce_side_effects_of_net_http_request(request, body)
- request.set_body_internal(body)
- request.content_length = request.body.length unless request.body.nil?
- end
-
- def self.puts_warning_for_net_http_around_advice_libs_if_needed
- libs = {"Samuel" => defined?(Samuel)}
- warnings = libs.select { |_, loaded| loaded }.map do |name, _|
- <<-TEXT.gsub(/ {10}/, '')
- \e[1mWarning: FakeWeb was loaded after #{name}\e[0m
- * #{name}'s code is being ignored when a request is handled by FakeWeb,
- because both libraries work by patching Net::HTTP.
- * To fix this, just reorder your requires so that FakeWeb is before #{name}.
- TEXT
- end
- $stderr.puts "\n" + warnings.join("\n") + "\n" if warnings.any?
- end
-
- def self.record_loaded_net_http_replacement_libs
- libs = {"RightHttpConnection" => defined?(RightHttpConnection)}
- @loaded_net_http_replacement_libs = libs.map { |name, loaded| name if loaded }.compact
- end
-
- def self.puts_warning_for_net_http_replacement_libs_if_needed
- libs = {"RightHttpConnection" => defined?(RightHttpConnection)}
- warnings = libs.select { |_, loaded| loaded }.
- reject { |name, _| @loaded_net_http_replacement_libs.include?(name) }.
- map do |name, _|
- <<-TEXT.gsub(/ {10}/, '')
- \e[1mWarning: #{name} was loaded after FakeWeb\e[0m
- * FakeWeb's code is being ignored, because #{name} replaces parts of
- Net::HTTP without deferring to other libraries. This will break Net::HTTP requests.
- * To fix this, just reorder your requires so that #{name} is before FakeWeb.
- TEXT
- end
- $stderr.puts "\n" + warnings.join("\n") + "\n" if warnings.any?
- end
-
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/lib/fakeweb.rb b/vendor/gems/fakeweb-1.3.0/lib/fakeweb.rb
deleted file mode 100644
index 6982966bf..000000000
--- a/vendor/gems/fakeweb-1.3.0/lib/fakeweb.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-# So you can require "fakeweb" instead of "fake_web"
-require "fake_web" \ No newline at end of file
diff --git a/vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_from_curl b/vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_from_curl
deleted file mode 100644
index fe2fe3945..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_from_curl
+++ /dev/null
@@ -1,12 +0,0 @@
-HTTP/1.1 200 OK
-Cache-Control: private, max-age=0
-Date: Sun, 01 Feb 2009 02:16:24 GMT
-Expires: -1
-Content-Type: text/html; charset=ISO-8859-1
-Set-Cookie: PREF=ID=a6d9b5f5a4056dfe:TM=1233454584:LM=1233454584:S=U9pSwSu4eQwOPenX; expires=Tue, 01-Feb-2011 02:16:24 GMT; path=/; domain=.google.com
-Server: gws
-Transfer-Encoding: chunked
-
-<html><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><title>Google</title><script>var _gjwl=location;function _gjuc(){var a=_gjwl.hash;if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var e=a.substring(d,b);if(e.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(e=="cad=h")return 0;c=b}_gjwl.href="search?"+a+"&cad=h";return 1}}return 0};
-window._gjuc && location.hash && _gjuc();</script><style>body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#36c;font-size:20px}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}#gbar{height:22px;padding-left:2px}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}#gbi,#gbs{background:#fff;left:0;position:absolute;top:24px;visibility:hidden;z-index:1000}#gbi{border:1px solid;border-color:#c9d7f1 #36c #36c #a2bae7;z-index:1001}#guser{padding-bottom:7px !important}#gbar,#guser{font-size:13px;padding-top:1px !important}@media all{.gb1,.gb3{height:22px;margin-right:.73em;vertical-align:top}#gbar{float:left}}.gb2{display:block;padding:.2em .5em}a.gb1,a.gb2,a.gb3{color:#00c !important}.gb2,.gb3{text-decoration:none}a.gb2:hover{background:#36c;color:#fff !important}</style><script>window.google={kEI:"-AWFSZ6qFYuUswO9j5HIDQ",kEXPI:"17259,19547",kHL:"en"};
-google.y={};google.x=function(e,g){google.y[e.id]=[e,g];return false};window.gbar={};(function(){var b=window.gbar,f,h;b.qs=function(a){var c=window.encodeURIComponent&&(document.forms[0].q||"").value;if(c)a.href=a.href.replace(/([?&])q=[^&]*|$/,function(i,g){return(g||"&")+"q="+encodeURIComponent(c)})};function j(a,c){a.visibility=h?"hidden":"visible";a.left=c+"px"}b.tg=function(a){a=a||window.event;var c=0,i,g=window.navExtra,d=document.getElementById("gbi"),e=a.target||a.srcElement;a.cancelBubble=true;if(!f){f=document.createElement(Array.every||window.createPopup?"iframe":"div");f.frameBorder="0";f.src="#";d.parentNode.appendChild(f).id="gbs";if(g)for(i in g)d.insertBefore(g[i],d.firstChild).className="gb2";document.onclick=b.close}if(e.className!="gb3")e=e.parentNode;do c+=e.offsetLeft;while(e=e.offsetParent);j(d.style,c);f.style.width=d.offsetWidth+"px";f.style.height=d.offsetHeight+"px";j(f.style,c);h=!h};b.close=function(a){h&&b.tg(a)}})();</script></head><body bgcolor=#ffffff text=#000000 link=#0000cc vlink=#551a8b alink=#ff0000 onload="document.f.q.focus();if(document.images)new Image().src='/images/nav_logo3.png'" topmargin=3 marginheight=3><div id=gbar><nobr><b class=gb1>Web</b> <a href="http://images.google.com/imghp?hl=en&tab=wi" onclick=gbar.qs(this) class=gb1>Images</a> <a href="http://maps.google.com/maps?hl=en&tab=wl" onclick=gbar.qs(this) class=gb1>Maps</a> <a href="http://news.google.com/nwshp?hl=en&tab=wn" onclick=gbar.qs(this) class=gb1>News</a> <a href="http://www.google.com/prdhp?hl=en&tab=wf" onclick=gbar.qs(this) class=gb1>Shopping</a> <a href="http://mail.google.com/mail/?hl=en&tab=wm" class=gb1>Gmail</a> <a href="http://www.google.com/intl/en/options/" onclick="this.blur();gbar.tg(event);return !1" class=gb3><u>more</u> <small>&#9660;</small></a><div id=gbi> <a href="http://video.google.com/?hl=en&tab=wv" onclick=gbar.qs(this) class=gb2>Video</a> <a href="http://groups.google.com/grphp?hl=en&tab=wg" onclick=gbar.qs(this) class=gb2>Groups</a> <a href="http://books.google.com/bkshp?hl=en&tab=wp" onclick=gbar.qs(this) class=gb2>Books</a> <a href="http://scholar.google.com/schhp?hl=en&tab=ws" onclick=gbar.qs(this) class=gb2>Scholar</a> <a href="http://finance.google.com/finance?hl=en&tab=we" onclick=gbar.qs(this) class=gb2>Finance</a> <a href="http://blogsearch.google.com/?hl=en&tab=wb" onclick=gbar.qs(this) class=gb2>Blogs</a> <div class=gb2><div class=gbd></div></div> <a href="http://www.youtube.com/?hl=en&tab=w1" onclick=gbar.qs(this) class=gb2>YouTube</a> <a href="http://www.google.com/calendar/render?hl=en&tab=wc" class=gb2>Calendar</a> <a href="http://picasaweb.google.com/home?hl=en&tab=wq" onclick=gbar.qs(this) class=gb2>Photos</a> <a href="http://docs.google.com/?hl=en&tab=wo" class=gb2>Documents</a> <a href="http://www.google.com/reader/view/?hl=en&tab=wy" class=gb2>Reader</a> <a href="http://sites.google.com/?hl=en&tab=w3" class=gb2>Sites</a> <div class=gb2><div class=gbd></div></div> <a href="http://www.google.com/intl/en/options/" class=gb2>even more &raquo;</a></div> </nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div><div align=right id=guser style="font-size:84%;padding:0 0 4px" width=100%><nobr><a href="/url?sa=p&pref=ig&pval=3&q=http://www.google.com/ig%3Fhl%3Den%26source%3Diglk&usg=AFQjCNFA18XPfgb7dKnXfKz7x7g1GDH1tg">iGoogle</a> | <a href="https://www.google.com/accounts/Login?continue=http://www.google.com/&hl=en">Sign in</a></nobr></div><center><br clear=all id=lgpd><img alt="Google" height=110 src="/intl/en_ALL/images/logo.gif" width=276><br><br><form action="/search" name=f><table cellpadding=0 cellspacing=0><tr valign=top><td width=25%>&nbsp;</td><td align=center nowrap><input name=hl type=hidden value=en><input type=hidden name=ie value="ISO-8859-1"><input autocomplete="off" maxlength=2048 name=q size=55 title="Google Search" value=""><br><input name=btnG type=submit value="Google Search"><input name=btnI type=submit value="I'm Feeling Lucky"></td><td nowrap width=25%><font size=-2>&nbsp;&nbsp;<a href=/advanced_search?hl=en>Advanced Search</a><br>&nbsp;&nbsp;<a href=/preferences?hl=en>Preferences</a><br>&nbsp;&nbsp;<a href=/language_tools?hl=en>Language Tools</a></font></td></tr></table></form><br><font size=-1>Share what you know. <a href="/aclk?sa=L&ai=CYhslHwSFSZH6LIHusAPEsc2eBfv77nqP3YC9CsHZnNkTEAEgwVRQypDftPn_____AWDJBqoECU_QbUVlfOdxZw&num=1&sig=AGiWqtwRgqw8y_kza6RGKxBrCstaXkDJ7A&q=http://knol.google.com">Write a Knol</a>.</font><br><br><br><font size=-1><a href="/intl/en/ads/">Advertising&nbsp;Programs</a> - <a href="/services/">Business Solutions</a> - <a href="/intl/en/about.html">About Google</a></font><p><font size=-2>&copy;2009 - <a href="/intl/en/privacy.html">Privacy</a></font></p></center></body><script>if(google.y)google.y.first=[];window.setTimeout(function(){var xjs=document.createElement('script');xjs.src='/extern_js/f/CgJlbhICdXMgACswCjgVLCswDjgELCswGDgDLA/L3N5xu59nDE.js';document.getElementsByTagName('head')[0].appendChild(xjs)},0);google.y.first.push(function(){google.ac.i(document.f,document.f.q,'','')})</script><script>function _gjp() {!(location.hash && _gjuc()) && setTimeout(_gjp, 500);}window._gjuc && _gjp();</script></html> \ No newline at end of file
diff --git a/vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_with_transfer_encoding b/vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_with_transfer_encoding
deleted file mode 100644
index 82025d36e..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_with_transfer_encoding
+++ /dev/null
@@ -1,17 +0,0 @@
-HTTP/1.1 200 OK
-Cache-Control: private, max-age=0
-Date: Sun, 01 Feb 2009 01:54:36 GMT
-Expires: -1
-Content-Type: text/html; charset=ISO-8859-1
-Set-Cookie: PREF=ID=4320bcaa30d097de:TM=1233453276:LM=1233453276:S=Eio39bg_nIabTxzL; expires=Tue, 01-Feb-2011 01:54:36 GMT; path=/; domain=.google.com
-Server: gws
-Transfer-Encoding: chunked
-
-fef
-<html><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><title>Google</title><script>var _gjwl=location;function _gjuc(){var a=_gjwl.hash;if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var e=a.substring(d,b);if(e.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(e=="cad=h")return 0;c=b}_gjwl.href="search?"+a+"&cad=h";return 1}}return 0};
-window._gjuc && location.hash && _gjuc();</script><style>body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#36c;font-size:20px}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}#gbar{height:22px;padding-left:2px}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}#gbi,#gbs{background:#fff;left:0;position:absolute;top:24px;visibility:hidden;z-index:1000}#gbi{border:1px solid;border-color:#c9d7f1 #36c #36c #a2bae7;z-index:1001}#guser{padding-bottom:7px !important}#gbar,#guser{font-size:13px;padding-top:1px !important}@media all{.gb1,.gb3{height:22px;margin-right:.73em;vertical-align:top}#gbar{float:left}}.gb2{display:block;padding:.2em .5em}a.gb1,a.gb2,a.gb3{color:#00c !important}.gb2,.gb3{text-decoration:none}a.gb2:hover{background:#36c;color:#fff !important}</style><script>window.google={kEI:"3ACFSYC6EKTcswOL4_nBDQ",kEXPI:"17259,19463",kHL:"en"};
-google.y={};google.x=function(e,g){google.y[e.id]=[e,g];return false};window.gbar={};(function(){var b=window.gbar,f,h;b.qs=function(a){var c=window.encodeURIComponent&&(document.forms[0].q||"").value;if(c)a.href=a.href.replace(/([?&])q=[^&]*|$/,function(i,g){return(g||"&")+"q="+encodeURIComponent(c)})};function j(a,c){a.visibility=h?"hidden":"visible";a.left=c+"px"}b.tg=function(a){a=a||window.event;var c=0,i,g=window.navExtra,d=document.getElementById("gbi"),e=a.target||a.srcElement;a.cancelBubble=true;if(!f){f=document.createElement(Array.every||window.createPopup?"iframe":"div");f.frameBorder="0";f.src="#";d.parentNode.appendChild(f).id="gbs";if(g)for(i in g)d.insertBefore(g[i],d.firstChild).className="gb2";document.onclick=b.close}if(e.className!="gb3")e=e.parentNode;do c+=e.offsetLeft;while(e=e.offsetParent);j(d.style,c);f.style.width=d.offsetWidth+"px";f.style.height=d.offsetHeight+"px";j(f.style,c);h=!h};b.close=function(a){h&&b.tg(a)}})();</script></head><body bgcolor=#ffffff text=#000000 link=#0000cc vlink=#551a8b alink=#ff0000 onload="document.f.q.focus();if(document.images)new Image().src='/images/nav_logo3.png'" topmargin=3 marginheight=3><div id=gbar><nobr><b class=gb1>Web</b> <a href="http://images.google.com/imghp?hl=en&tab=wi" onclick=gbar.qs(this) class=gb1>Images</a> <a href="http://maps.google.com/maps?hl=en&tab=wl" onclick=gbar.qs(this) class=gb1>Maps</a> <a href="http://news.google.com/nwshp?hl=en&tab=wn" onclick=gbar.qs(this) class=gb1>News</a> <a href="http://www.google.com/prdhp?hl=en&tab=wf" onclick=gbar.qs(this) class=gb1>Shopping</a> <a href="http://mail.google.com/mail/?hl=en&tab=wm" class=gb1>Gmail</a> <a href="http://www.google.com/intl/en/options/" onclick="this.blur();gbar.tg(event);return !1" class=gb3><u>more</u> <small>&#9660;</small></a><div id=gbi> <a href="http://video.google.com/?hl=en&tab=wv" onclick=gbar.qs(this) class=gb2>Video</a> <a href="http://groups.google.com/grphp?hl=en&tab=wg" onclick=gbar.qs(this) class=gb2>Groups</a> <a href="http://books.google.com/bkshp?hl=en&tab=wp" onclick=gbar.qs(this) class=gb2>Books</a> <a href="http://scholar.google.com/schhp?hl=en&tab=ws" onclick=gbar.qs(this) class=gb2>Scholar</a> <a href="http://finance.google.com/finance?hl=en&tab=we" onclick=gbar.qs(this) class=gb2>Finance</a> <a href="http://blogsearch.google.com/?hl=en&tab=wb" onclick=gbar.qs(this) class=gb2>Blogs</a> <div class=gb2><div class=gbd></div></div> <a href="http://www.youtube.com/?hl=en&tab=w1" onclick=gbar.qs(this) class=gb2>YouTube</a> <a href="http://www.google.com/calendar/render?hl=en&tab=wc" class=gb2>Calendar</a> <a href="http
-a27
-://picasaweb.google.com/home?hl=en&tab=wq" onclick=gbar.qs(this) class=gb2>Photos</a> <a href="http://docs.google.com/?hl=en&tab=wo" class=gb2>Documents</a> <a href="http://www.google.com/reader/view/?hl=en&tab=wy" class=gb2>Reader</a> <a href="http://sites.google.com/?hl=en&tab=w3" class=gb2>Sites</a> <div class=gb2><div class=gbd></div></div> <a href="http://www.google.com/intl/en/options/" class=gb2>even more &raquo;</a></div> </nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div><div align=right id=guser style="font-size:84%;padding:0 0 4px" width=100%><nobr><a href="/url?sa=p&pref=ig&pval=3&q=http://www.google.com/ig%3Fhl%3Den%26source%3Diglk&usg=AFQjCNFA18XPfgb7dKnXfKz7x7g1GDH1tg">iGoogle</a> | <a href="https://www.google.com/accounts/Login?continue=http://www.google.com/&hl=en">Sign in</a></nobr></div><center><br clear=all id=lgpd><img alt="Google" height=110 src="/intl/en_ALL/images/logo.gif" width=276><br><br><form action="/search" name=f><table cellpadding=0 cellspacing=0><tr valign=top><td width=25%>&nbsp;</td><td align=center nowrap><input name=hl type=hidden value=en><input type=hidden name=ie value="ISO-8859-1"><input autocomplete="off" maxlength=2048 name=q size=55 title="Google Search" value=""><br><input name=btnG type=submit value="Google Search"><input name=btnI type=submit value="I'm Feeling Lucky"></td><td nowrap width=25%><font size=-2>&nbsp;&nbsp;<a href=/advanced_search?hl=en>Advanced Search</a><br>&nbsp;&nbsp;<a href=/preferences?hl=en>Preferences</a><br>&nbsp;&nbsp;<a href=/language_tools?hl=en>Language Tools</a></font></td></tr></table></form><br><font size=-1>Share what you know. <a href="/aclk?sa=L&ai=CFL7HzwCFSZCnCJSwsQPm842HB_v77nqP3YC9CsHZnNkTEAEgwVRQypDftPn_____AWDJBqoECU_Q1sTewQNSbw&num=1&sig=AGiWqtyz-UiOD3EpsSp4k3n8A7zooeg48g&q=http://knol.google.com">Write a Knol</a>.</font><br><br><br><font size=-1><a href="/intl/en/ads/">Advertising&nbsp;Programs</a> - <a href="/services/">Business Solutions</a> - <a href="/intl/en/about.html">About Google</a></font><p><font size=-2>&copy;2009 - <a href="/intl/en/privacy.html">Privacy</a></font></p></center></body><script>if(google.y)google.y.first=[];window.setTimeout(function(){var xjs=document.createElement('script');xjs.src='/extern_js/f/CgJlbhICdXMgACswCjgVLCswDjgELCswGDgDLA/L3N5xu59nDE.js';document.getElementsByTagName('head')[0].appendChild(xjs)},0);google.y.first.push(function(){google.ac.i(document.f,document.f.q,'','')})</script><script>function _gjp() {!(location.hash && _gjuc()) && setTimeout(_gjp, 500);}window._gjuc && _gjp();</script></html>
-0
-
diff --git a/vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_without_transfer_encoding b/vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_without_transfer_encoding
deleted file mode 100644
index 51433c990..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/fixtures/google_response_without_transfer_encoding
+++ /dev/null
@@ -1,11 +0,0 @@
-HTTP/1.0 200 OK
-Cache-Control: private, max-age=0
-Date: Sun, 01 Feb 2009 01:55:33 GMT
-Expires: -1
-Content-Type: text/html; charset=ISO-8859-1
-Set-Cookie: PREF=ID=3c140c3eb4c4f516:TM=1233453333:LM=1233453333:S=OH7sElk2hOWkb9ot; expires=Tue, 01-Feb-2011 01:55:33 GMT; path=/; domain=.google.com
-Server: gws
-
-<html><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><title>Google</title><script>var _gjwl=location;function _gjuc(){var a=_gjwl.hash;if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var e=a.substring(d,b);if(e.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(e=="cad=h")return 0;c=b}_gjwl.href="search?"+a+"&cad=h";return 1}}return 0};
-window._gjuc && location.hash && _gjuc();</script><style>body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#36c;font-size:20px}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}#gbar{height:22px;padding-left:2px}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}#gbi,#gbs{background:#fff;left:0;position:absolute;top:24px;visibility:hidden;z-index:1000}#gbi{border:1px solid;border-color:#c9d7f1 #36c #36c #a2bae7;z-index:1001}#guser{padding-bottom:7px !important}#gbar,#guser{font-size:13px;padding-top:1px !important}@media all{.gb1,.gb3{height:22px;margin-right:.73em;vertical-align:top}#gbar{float:left}}.gb2{display:block;padding:.2em .5em}a.gb1,a.gb2,a.gb3{color:#00c !important}.gb2,.gb3{text-decoration:none}a.gb2:hover{background:#36c;color:#fff !important}</style><script>window.google={kEI:"FQGFSY2rG5eSswOKpsHeDQ",kEXPI:"17259",kHL:"en"};
-google.y={};google.x=function(e,g){google.y[e.id]=[e,g];return false};window.gbar={};(function(){var b=window.gbar,f,h;b.qs=function(a){var c=window.encodeURIComponent&&(document.forms[0].q||"").value;if(c)a.href=a.href.replace(/([?&])q=[^&]*|$/,function(i,g){return(g||"&")+"q="+encodeURIComponent(c)})};function j(a,c){a.visibility=h?"hidden":"visible";a.left=c+"px"}b.tg=function(a){a=a||window.event;var c=0,i,g=window.navExtra,d=document.getElementById("gbi"),e=a.target||a.srcElement;a.cancelBubble=true;if(!f){f=document.createElement(Array.every||window.createPopup?"iframe":"div");f.frameBorder="0";f.src="#";d.parentNode.appendChild(f).id="gbs";if(g)for(i in g)d.insertBefore(g[i],d.firstChild).className="gb2";document.onclick=b.close}if(e.className!="gb3")e=e.parentNode;do c+=e.offsetLeft;while(e=e.offsetParent);j(d.style,c);f.style.width=d.offsetWidth+"px";f.style.height=d.offsetHeight+"px";j(f.style,c);h=!h};b.close=function(a){h&&b.tg(a)}})();</script></head><body bgcolor=#ffffff text=#000000 link=#0000cc vlink=#551a8b alink=#ff0000 onload="document.f.q.focus();if(document.images)new Image().src='/images/nav_logo3.png'" topmargin=3 marginheight=3><div id=gbar><nobr><b class=gb1>Web</b> <a href="http://images.google.com/imghp?hl=en&tab=wi" onclick=gbar.qs(this) class=gb1>Images</a> <a href="http://maps.google.com/maps?hl=en&tab=wl" onclick=gbar.qs(this) class=gb1>Maps</a> <a href="http://news.google.com/nwshp?hl=en&tab=wn" onclick=gbar.qs(this) class=gb1>News</a> <a href="http://www.google.com/prdhp?hl=en&tab=wf" onclick=gbar.qs(this) class=gb1>Shopping</a> <a href="http://mail.google.com/mail/?hl=en&tab=wm" class=gb1>Gmail</a> <a href="http://www.google.com/intl/en/options/" onclick="this.blur();gbar.tg(event);return !1" class=gb3><u>more</u> <small>&#9660;</small></a><div id=gbi> <a href="http://video.google.com/?hl=en&tab=wv" onclick=gbar.qs(this) class=gb2>Video</a> <a href="http://groups.google.com/grphp?hl=en&tab=wg" onclick=gbar.qs(this) class=gb2>Groups</a> <a href="http://books.google.com/bkshp?hl=en&tab=wp" onclick=gbar.qs(this) class=gb2>Books</a> <a href="http://scholar.google.com/schhp?hl=en&tab=ws" onclick=gbar.qs(this) class=gb2>Scholar</a> <a href="http://finance.google.com/finance?hl=en&tab=we" onclick=gbar.qs(this) class=gb2>Finance</a> <a href="http://blogsearch.google.com/?hl=en&tab=wb" onclick=gbar.qs(this) class=gb2>Blogs</a> <div class=gb2><div class=gbd></div></div> <a href="http://www.youtube.com/?hl=en&tab=w1" onclick=gbar.qs(this) class=gb2>YouTube</a> <a href="http://www.google.com/calendar/render?hl=en&tab=wc" class=gb2>Calendar</a> <a href="http://picasaweb.google.com/home?hl=en&tab=wq" onclick=gbar.qs(this) class=gb2>Photos</a> <a href="http://docs.google.com/?hl=en&tab=wo" class=gb2>Documents</a> <a href="http://www.google.com/reader/view/?hl=en&tab=wy" class=gb2>Reader</a> <a href="http://sites.google.com/?hl=en&tab=w3" class=gb2>Sites</a> <div class=gb2><div class=gbd></div></div> <a href="http://www.google.com/intl/en/options/" class=gb2>even more &raquo;</a></div> </nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div><div align=right id=guser style="font-size:84%;padding:0 0 4px" width=100%><nobr><a href="/url?sa=p&pref=ig&pval=3&q=http://www.google.com/ig%3Fhl%3Den%26source%3Diglk&usg=AFQjCNFA18XPfgb7dKnXfKz7x7g1GDH1tg">iGoogle</a> | <a href="https://www.google.com/accounts/Login?continue=http://www.google.com/&hl=en">Sign in</a></nobr></div><center><br clear=all id=lgpd><img alt="Google" height=110 src="/intl/en_ALL/images/logo.gif" width=276><br><br><form action="/search" name=f><table cellpadding=0 cellspacing=0><tr valign=top><td width=25%>&nbsp;</td><td align=center nowrap><input name=hl type=hidden value=en><input type=hidden name=ie value="ISO-8859-1"><input autocomplete="off" maxlength=2048 name=q size=55 title="Google Search" value=""><br><input name=btnG type=submit value="Google Search"><input name=btnI type=submit value="I'm Feeling Lucky"></td><td nowrap width=25%><font size=-2>&nbsp;&nbsp;<a href=/advanced_search?hl=en>Advanced Search</a><br>&nbsp;&nbsp;<a href=/preferences?hl=en>Preferences</a><br>&nbsp;&nbsp;<a href=/language_tools?hl=en>Language Tools</a></font></td></tr></table></form><br><font size=-1>Share what you know. <a href="/aclk?sa=L&ai=ClyBp_v-EScTWD4W2tQOxoqSkB_v77nqP3YC9CsHZnNkTEAEgwVRQypDftPn_____AWDJBqoECU_QphTjHaZ5QA&num=1&sig=AGiWqtwtBqZ-zra3DJd_1chQKhKGf7lMVg&q=http://knol.google.com">Write a Knol</a>.</font><br><br><br><font size=-1><a href="/intl/en/ads/">Advertising&nbsp;Programs</a> - <a href="/services/">Business Solutions</a> - <a href="/intl/en/about.html">About Google</a></font><p><font size=-2>&copy;2009 - <a href="/intl/en/privacy.html">Privacy</a></font></p></center></body><script>if(google.y)google.y.first=[];window.setTimeout(function(){var xjs=document.createElement('script');xjs.src='/extern_js/f/CgJlbhICdXMgACswCjgNLCswDjgELCswGDgDLA/oTKXc0xdkmY.js';document.getElementsByTagName('head')[0].appendChild(xjs)},0);google.y.first.push(function(){google.ac.i(document.f,document.f.q,'','')})</script><script>function _gjp() {!(location.hash && _gjuc()) && setTimeout(_gjp, 500);}window._gjuc && _gjp();</script></html> \ No newline at end of file
diff --git a/vendor/gems/fakeweb-1.3.0/test/fixtures/test_example.txt b/vendor/gems/fakeweb-1.3.0/test/fixtures/test_example.txt
deleted file mode 100644
index 6310da9df..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/fixtures/test_example.txt
+++ /dev/null
@@ -1 +0,0 @@
-test example content \ No newline at end of file
diff --git a/vendor/gems/fakeweb-1.3.0/test/fixtures/test_txt_file b/vendor/gems/fakeweb-1.3.0/test/fixtures/test_txt_file
deleted file mode 100644
index 8cf2f17fe..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/fixtures/test_txt_file
+++ /dev/null
@@ -1,3 +0,0 @@
-line 1
-line 2
-line 3 \ No newline at end of file
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_allow_net_connect.rb b/vendor/gems/fakeweb-1.3.0/test/test_allow_net_connect.rb
deleted file mode 100644
index 25f4d3d64..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_allow_net_connect.rb
+++ /dev/null
@@ -1,168 +0,0 @@
-require 'test_helper'
-
-class TestFakeWebAllowNetConnect < Test::Unit::TestCase
- def test_unregistered_requests_are_passed_through_when_allow_net_connect_is_true
- FakeWeb.allow_net_connect = true
- setup_expectations_for_real_apple_hot_news_request
- Net::HTTP.get(URI.parse("http://images.apple.com/main/rss/hotnews/hotnews.rss"))
- end
-
- def test_raises_for_unregistered_requests_when_allow_net_connect_is_false
- FakeWeb.allow_net_connect = false
- assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.get(URI.parse("http://example.com/"))
- end
- end
-
- def test_unregistered_requests_are_passed_through_when_allow_net_connect_is_the_same_string
- FakeWeb.allow_net_connect = "http://images.apple.com/main/rss/hotnews/hotnews.rss"
- setup_expectations_for_real_apple_hot_news_request
- Net::HTTP.get(URI.parse("http://images.apple.com/main/rss/hotnews/hotnews.rss"))
- end
-
- def test_unregistered_requests_are_passed_through_when_allow_net_connect_is_the_same_string_with_default_port
- FakeWeb.allow_net_connect = "http://images.apple.com:80/main/rss/hotnews/hotnews.rss"
- setup_expectations_for_real_apple_hot_news_request
- Net::HTTP.get(URI.parse("http://images.apple.com/main/rss/hotnews/hotnews.rss"))
- end
-
- def test_unregistered_requests_are_passed_through_when_allow_net_connect_is_the_same_uri
- FakeWeb.allow_net_connect = URI.parse("http://images.apple.com/main/rss/hotnews/hotnews.rss")
- setup_expectations_for_real_apple_hot_news_request
- Net::HTTP.get(URI.parse("http://images.apple.com/main/rss/hotnews/hotnews.rss"))
- end
-
- def test_unregistered_requests_are_passed_through_when_allow_net_connect_is_a_matching_regexp
- FakeWeb.allow_net_connect = %r[^http://images\.apple\.com]
- setup_expectations_for_real_apple_hot_news_request
- Net::HTTP.get(URI.parse("http://images.apple.com/main/rss/hotnews/hotnews.rss"))
- end
-
- def test_raises_for_unregistered_requests_when_allow_net_connect_is_a_different_string
- FakeWeb.allow_net_connect = "http://example.com"
- assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.get(URI.parse("http://example.com/path"))
- end
- end
-
- def test_raises_for_unregistered_requests_when_allow_net_connect_is_a_different_uri
- FakeWeb.allow_net_connect = URI.parse("http://example.com")
- assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.get(URI.parse("http://example.com/path"))
- end
- end
-
- def test_raises_for_unregistered_requests_when_allow_net_connect_is_a_non_matching_regexp
- FakeWeb.allow_net_connect = %r[example\.net]
- assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.get(URI.parse("http://example.com"))
- end
- end
-
- def test_changing_allow_net_connect_from_string_to_false_corretly_removes_whitelist
- FakeWeb.allow_net_connect = "http://example.com"
- FakeWeb.allow_net_connect = false
- assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.get(URI.parse("http://example.com"))
- end
- end
-
- def test_changing_allow_net_connect_from_true_to_string_corretly_limits_connections
- FakeWeb.allow_net_connect = true
- FakeWeb.allow_net_connect = "http://example.com"
- assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.get(URI.parse("http://example.net"))
- end
- end
-
- def test_exception_message_includes_unregistered_request_method_and_uri_but_no_default_port
- FakeWeb.allow_net_connect = false
- exception = assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.get(URI.parse("http://example.com/"))
- end
- assert exception.message.include?("GET http://example.com/")
-
- exception = assert_raise FakeWeb::NetConnectNotAllowedError do
- http = Net::HTTP.new("example.com", 443)
- http.use_ssl = true
- http.get("/")
- end
- assert exception.message.include?("GET https://example.com/")
- end
-
- def test_exception_message_includes_unregistered_request_port_when_not_default
- FakeWeb.allow_net_connect = false
- exception = assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.start("example.com", 8000) { |http| http.get("/") }
- end
- assert exception.message.include?("GET http://example.com:8000/")
-
- exception = assert_raise FakeWeb::NetConnectNotAllowedError do
- http = Net::HTTP.new("example.com", 4433)
- http.use_ssl = true
- http.get("/")
- end
- assert exception.message.include?("GET https://example.com:4433/")
- end
-
- def test_exception_message_includes_unregistered_request_port_when_not_default_with_path
- FakeWeb.allow_net_connect = false
- exception = assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.start("example.com", 8000) { |http| http.get("/test") }
- end
- assert exception.message.include?("GET http://example.com:8000/test")
-
- exception = assert_raise FakeWeb::NetConnectNotAllowedError do
- http = Net::HTTP.new("example.com", 4433)
- http.use_ssl = true
- http.get("/test")
- end
- assert exception.message.include?("GET https://example.com:4433/test")
- end
-
- def test_question_mark_method_returns_true_after_setting_allow_net_connect_to_true
- FakeWeb.allow_net_connect = true
- assert FakeWeb.allow_net_connect?
- end
-
- def test_question_mark_method_returns_false_after_setting_allow_net_connect_to_false
- FakeWeb.allow_net_connect = false
- assert !FakeWeb.allow_net_connect?
- end
-
- def test_question_mark_method_raises_with_no_argument_when_allow_net_connect_is_a_whitelist
- FakeWeb.allow_net_connect = "http://example.com"
- exception = assert_raise ArgumentError do
- FakeWeb.allow_net_connect?
- end
- assert_equal "You must supply a URI to test", exception.message
- end
-
- def test_question_mark_method_returns_true_when_argument_is_same_uri_as_allow_net_connect_string
- FakeWeb.allow_net_connect = "http://example.com"
- assert FakeWeb.allow_net_connect?("http://example.com/")
- end
-
- def test_question_mark_method_returns_true_when_argument_matches_allow_net_connect_regexp
- FakeWeb.allow_net_connect = %r[^https?://example.com/]
- assert FakeWeb.allow_net_connect?("http://example.com/path")
- assert FakeWeb.allow_net_connect?("https://example.com:443/")
- end
-
- def test_question_mark_method_returns_false_when_argument_does_not_match_allow_net_connect_regexp
- FakeWeb.allow_net_connect = %r[^http://example.com/]
- assert !FakeWeb.allow_net_connect?("http://example.com:8080")
- end
-end
-
-
-class TestFakeWebAllowNetConnectWithCleanState < Test::Unit::TestCase
- # Our test_helper.rb sets allow_net_connect = false in an inherited #setup
- # method. Disable that here to test the default setting.
- def setup; end
- def teardown; end
-
- def test_allow_net_connect_is_true_by_default
- assert FakeWeb.allow_net_connect?
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_deprecations.rb b/vendor/gems/fakeweb-1.3.0/test/test_deprecations.rb
deleted file mode 100644
index e5b8953c5..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_deprecations.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-require 'test_helper'
-
-class TestDeprecations < Test::Unit::TestCase
-
- def test_register_uri_without_method_argument_prints_deprecation_warning
- warning = capture_stderr do
- FakeWeb.register_uri("http://example.com", :body => "test")
- end
- assert_match %r(deprecation warning: fakeweb)i, warning
- end
-
- def test_registered_uri_without_method_argument_prints_deprecation_warning
- warning = capture_stderr do
- FakeWeb.registered_uri?("http://example.com")
- end
- assert_match %r(deprecation warning: fakeweb)i, warning
- end
-
- def test_response_for_without_method_argument_prints_deprecation_warning
- warning = capture_stderr do
- FakeWeb.response_for("http://example.com")
- end
- assert_match %r(deprecation warning: fakeweb)i, warning
- end
-
- def test_register_uri_without_method_argument_prints_deprecation_warning_with_correct_caller
- warning = capture_stderr do
- FakeWeb.register_uri("http://example.com", :body => "test")
- end
- assert_match %r(Called at.*?test_deprecations\.rb)i, warning
- end
-
- def test_register_uri_with_string_option_prints_deprecation_warning
- warning = capture_stderr do
- FakeWeb.register_uri(:get, "http://example.com", :string => "test")
- end
- assert_match %r(deprecation warning: fakeweb's :string option)i, warning
- end
-
- def test_register_uri_with_file_option_prints_deprecation_warning
- warning = capture_stderr do
- FakeWeb.register_uri(:get, "http://example.com", :file => fixture_path("test_example.txt"))
- end
- assert_match %r(deprecation warning: fakeweb's :file option)i, warning
- end
-
- def test_register_uri_with_string_option_prints_deprecation_warning_with_correct_caller
- warning = capture_stderr do
- FakeWeb.register_uri(:get, "http://example.com", :string => "test")
- end
- assert_match %r(Called at.*?test_deprecations\.rb)i, warning
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_fake_authentication.rb b/vendor/gems/fakeweb-1.3.0/test/test_fake_authentication.rb
deleted file mode 100644
index cff276441..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_fake_authentication.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-require 'test_helper'
-
-class TestFakeAuthentication < Test::Unit::TestCase
-
- def test_register_uri_with_authentication
- FakeWeb.register_uri(:get, 'http://user:pass@mock/test_example.txt', :body => "example")
- assert FakeWeb.registered_uri?(:get, 'http://user:pass@mock/test_example.txt')
- end
-
- def test_register_uri_with_authentication_doesnt_trigger_without
- FakeWeb.register_uri(:get, 'http://user:pass@mock/test_example.txt', :body => "example")
- assert !FakeWeb.registered_uri?(:get, 'http://mock/test_example.txt')
- end
-
- def test_register_uri_with_authentication_doesnt_trigger_with_incorrect_credentials
- FakeWeb.register_uri(:get, 'http://user:pass@mock/test_example.txt', :body => "example")
- assert !FakeWeb.registered_uri?(:get, 'http://user:wrong@mock/test_example.txt')
- end
-
- def test_unauthenticated_request
- FakeWeb.register_uri(:get, 'http://mock/auth.txt', :body => 'unauthorized')
- http = Net::HTTP.new('mock', 80)
- req = Net::HTTP::Get.new('/auth.txt')
- assert_equal 'unauthorized', http.request(req).body
- end
-
- def test_authenticated_request
- FakeWeb.register_uri(:get, 'http://user:pass@mock/auth.txt', :body => 'authorized')
- http = Net::HTTP.new('mock',80)
- req = Net::HTTP::Get.new('/auth.txt')
- req.basic_auth 'user', 'pass'
- assert_equal 'authorized', http.request(req).body
- end
-
- def test_authenticated_request_where_only_userinfo_differs
- FakeWeb.register_uri(:get, 'http://user:pass@mock/auth.txt', :body => 'first user')
- FakeWeb.register_uri(:get, 'http://user2:pass@mock/auth.txt', :body => 'second user')
- http = Net::HTTP.new('mock')
- req = Net::HTTP::Get.new('/auth.txt')
- req.basic_auth 'user2', 'pass'
- assert_equal 'second user', http.request(req).body
- end
-
- def test_basic_auth_support_is_transparent_to_oauth
- FakeWeb.register_uri(:get, "http://sp.example.com/protected", :body => "secret")
-
- # from http://oauth.net/core/1.0/#auth_header
- auth_header = <<-HEADER
- OAuth realm="http://sp.example.com/",
- oauth_consumer_key="0685bd9184jfhq22",
- oauth_token="ad180jjd733klru7",
- oauth_signature_method="HMAC-SHA1",
- oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",
- oauth_timestamp="137131200",
- oauth_nonce="4572616e48616d6d65724c61686176",
- oauth_version="1.0"
- HEADER
- auth_header.gsub!(/\s+/, " ").strip!
-
- http = Net::HTTP.new("sp.example.com", 80)
- response = nil
- http.start do |request|
- response = request.get("/protected", {"authorization" => auth_header})
- end
- assert_equal "secret", response.body
- end
-
- def test_basic_auth_when_userinfo_contains_allowed_unencoded_characters
- FakeWeb.register_uri(:get, "http://roses&hel1o,(+$):so;longs=@example.com", :body => "authorized")
- http = Net::HTTP.new("example.com")
- request = Net::HTTP::Get.new("/")
- request.basic_auth("roses&hel1o,(+$)", "so;longs=")
- assert_equal "authorized", http.request(request).body
- end
-
- def test_basic_auth_when_userinfo_contains_encoded_at_sign
- FakeWeb.register_uri(:get, "http://user%40example.com:secret@example.com", :body => "authorized")
- http = Net::HTTP.new("example.com")
- request = Net::HTTP::Get.new("/")
- request.basic_auth("user@example.com", "secret")
- assert_equal "authorized", http.request(request).body
- end
-
- def test_basic_auth_when_userinfo_contains_allowed_encoded_characters
- FakeWeb.register_uri(:get, "http://us%20er:sec%20%2F%2Fret%3F@example.com", :body => "authorized")
- http = Net::HTTP.new("example.com")
- request = Net::HTTP::Get.new("/")
- request.basic_auth("us er", "sec //ret?")
- assert_equal "authorized", http.request(request).body
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_fake_web.rb b/vendor/gems/fakeweb-1.3.0/test/test_fake_web.rb
deleted file mode 100644
index c6e6b59eb..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_fake_web.rb
+++ /dev/null
@@ -1,590 +0,0 @@
-require 'test_helper'
-
-class TestFakeWeb < Test::Unit::TestCase
-
- def test_register_uri
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => "example")
- assert FakeWeb.registered_uri?(:get, 'http://mock/test_example.txt')
- end
-
- def test_register_uri_with_wrong_number_of_arguments
- assert_raises ArgumentError do
- FakeWeb.register_uri("http://example.com")
- end
- assert_raises ArgumentError do
- FakeWeb.register_uri(:get, "http://example.com", "/example", :body => "example")
- end
- end
-
- def test_registered_uri_with_wrong_number_of_arguments
- assert_raises ArgumentError do
- FakeWeb.registered_uri?
- end
- assert_raises ArgumentError do
- FakeWeb.registered_uri?(:get, "http://example.com", "/example")
- end
- end
-
- def test_response_for_with_wrong_number_of_arguments
- assert_raises ArgumentError do
- FakeWeb.response_for
- end
- assert_raises ArgumentError do
- FakeWeb.response_for(:get, "http://example.com", "/example")
- end
- end
-
- def test_register_uri_without_domain_name
- assert_raises URI::InvalidURIError do
- FakeWeb.register_uri(:get, 'test_example2.txt', fixture_path("test_example.txt"))
- end
- end
-
- def test_register_uri_with_port_and_check_with_port
- FakeWeb.register_uri(:get, 'http://example.com:3000/', :body => 'foo')
- assert FakeWeb.registered_uri?(:get, 'http://example.com:3000/')
- end
-
- def test_register_uri_with_port_and_check_without_port
- FakeWeb.register_uri(:get, 'http://example.com:3000/', :body => 'foo')
- assert !FakeWeb.registered_uri?(:get, 'http://example.com/')
- end
-
- def test_register_uri_with_default_port_for_http_and_check_without_port
- FakeWeb.register_uri(:get, 'http://example.com:80/', :body => 'foo')
- assert FakeWeb.registered_uri?(:get, 'http://example.com/')
- end
-
- def test_register_uri_with_default_port_for_https_and_check_without_port
- FakeWeb.register_uri(:get, 'https://example.com:443/', :body => 'foo')
- assert FakeWeb.registered_uri?(:get, 'https://example.com/')
- end
-
- def test_register_uri_with_no_port_for_http_and_check_with_default_port
- FakeWeb.register_uri(:get, 'http://example.com/', :body => 'foo')
- assert FakeWeb.registered_uri?(:get, 'http://example.com:80/')
- end
-
- def test_register_uri_with_no_port_for_https_and_check_with_default_port
- FakeWeb.register_uri(:get, 'https://example.com/', :body => 'foo')
- assert FakeWeb.registered_uri?(:get, 'https://example.com:443/')
- end
-
- def test_register_uri_with_no_port_for_https_and_check_with_443_on_http
- FakeWeb.register_uri(:get, 'https://example.com/', :body => 'foo')
- assert !FakeWeb.registered_uri?(:get, 'http://example.com:443/')
- end
-
- def test_register_uri_with_no_port_for_http_and_check_with_80_on_https
- FakeWeb.register_uri(:get, 'http://example.com/', :body => 'foo')
- assert !FakeWeb.registered_uri?(:get, 'https://example.com:80/')
- end
-
- def test_register_uri_for_any_method_explicitly
- FakeWeb.register_uri(:any, "http://example.com/rpc_endpoint", :body => "OK")
- assert FakeWeb.registered_uri?(:get, "http://example.com/rpc_endpoint")
- assert FakeWeb.registered_uri?(:post, "http://example.com/rpc_endpoint")
- assert FakeWeb.registered_uri?(:put, "http://example.com/rpc_endpoint")
- assert FakeWeb.registered_uri?(:delete, "http://example.com/rpc_endpoint")
- assert FakeWeb.registered_uri?(:any, "http://example.com/rpc_endpoint")
- capture_stderr do # silence deprecation warning
- assert FakeWeb.registered_uri?("http://example.com/rpc_endpoint")
- end
- end
-
- def test_register_uri_for_get_method_only
- FakeWeb.register_uri(:get, "http://example.com/users", :body => "User list")
- assert FakeWeb.registered_uri?(:get, "http://example.com/users")
- assert !FakeWeb.registered_uri?(:post, "http://example.com/users")
- assert !FakeWeb.registered_uri?(:put, "http://example.com/users")
- assert !FakeWeb.registered_uri?(:delete, "http://example.com/users")
- assert !FakeWeb.registered_uri?(:any, "http://example.com/users")
- capture_stderr do # silence deprecation warning
- assert !FakeWeb.registered_uri?("http://example.com/users")
- end
- end
-
- def test_clean_registry_affects_registered_uri
- FakeWeb.register_uri(:get, "http://example.com", :body => "registered")
- assert FakeWeb.registered_uri?(:get, "http://example.com")
- FakeWeb.clean_registry
- assert !FakeWeb.registered_uri?(:get, "http://example.com")
- end
-
- def test_clean_registry_affects_net_http_requests
- FakeWeb.register_uri(:get, "http://example.com", :body => "registered")
- response = Net::HTTP.start("example.com") { |query| query.get("/") }
- assert_equal "registered", response.body
- FakeWeb.clean_registry
- assert_raise FakeWeb::NetConnectNotAllowedError do
- Net::HTTP.start("example.com") { |query| query.get("/") }
- end
- end
-
- def test_response_for_with_registered_uri
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => fixture_path("test_example.txt"))
- assert_equal 'test example content', FakeWeb.response_for(:get, 'http://mock/test_example.txt').body
- end
-
- def test_response_for_with_unknown_uri
- assert_nil FakeWeb.response_for(:get, 'http://example.com/')
- end
-
- def test_response_for_with_put_method
- FakeWeb.register_uri(:put, "http://example.com", :body => "response")
- assert_equal 'response', FakeWeb.response_for(:put, "http://example.com").body
- end
-
- def test_response_for_with_any_method_explicitly
- FakeWeb.register_uri(:any, "http://example.com", :body => "response")
- assert_equal 'response', FakeWeb.response_for(:get, "http://example.com").body
- assert_equal 'response', FakeWeb.response_for(:any, "http://example.com").body
- end
-
- def test_content_for_registered_uri_with_port_and_request_with_port
- FakeWeb.register_uri(:get, 'http://example.com:3000/', :body => 'test example content')
- response = Net::HTTP.start('example.com', 3000) { |http| http.get('/') }
- assert_equal 'test example content', response.body
- end
-
- def test_content_for_registered_uri_with_default_port_for_http_and_request_without_port
- FakeWeb.register_uri(:get, 'http://example.com:80/', :body => 'test example content')
- response = Net::HTTP.start('example.com') { |http| http.get('/') }
- assert_equal 'test example content', response.body
- end
-
- def test_content_for_registered_uri_with_no_port_for_http_and_request_with_default_port
- FakeWeb.register_uri(:get, 'http://example.com/', :body => 'test example content')
- response = Net::HTTP.start('example.com', 80) { |http| http.get('/') }
- assert_equal 'test example content', response.body
- end
-
- def test_content_for_registered_uri_with_default_port_for_https_and_request_with_default_port
- FakeWeb.register_uri(:get, 'https://example.com:443/', :body => 'test example content')
- http = Net::HTTP.new('example.com', 443)
- http.use_ssl = true
- response = http.get('/')
- assert_equal 'test example content', response.body
- end
-
- def test_content_for_registered_uri_with_no_port_for_https_and_request_with_default_port
- FakeWeb.register_uri(:get, 'https://example.com/', :body => 'test example content')
- http = Net::HTTP.new('example.com', 443)
- http.use_ssl = true
- response = http.get('/')
- assert_equal 'test example content', response.body
- end
-
- def test_content_for_registered_uris_with_ports_on_same_domain_and_request_without_port
- FakeWeb.register_uri(:get, 'http://example.com:3000/', :body => 'port 3000')
- FakeWeb.register_uri(:get, 'http://example.com/', :body => 'port 80')
- response = Net::HTTP.start('example.com') { |http| http.get('/') }
- assert_equal 'port 80', response.body
- end
-
- def test_content_for_registered_uris_with_ports_on_same_domain_and_request_with_port
- FakeWeb.register_uri(:get, 'http://example.com:3000/', :body => 'port 3000')
- FakeWeb.register_uri(:get, 'http://example.com/', :body => 'port 80')
- response = Net::HTTP.start('example.com', 3000) { |http| http.get('/') }
- assert_equal 'port 3000', response.body
- end
-
- def test_content_for_registered_uri_with_get_method_only
- FakeWeb.allow_net_connect = false
- FakeWeb.register_uri(:get, "http://example.com/", :body => "test example content")
- http = Net::HTTP.new('example.com')
- assert_equal 'test example content', http.get('/').body
- assert_raises(FakeWeb::NetConnectNotAllowedError) { http.post('/', nil) }
- assert_raises(FakeWeb::NetConnectNotAllowedError) { http.put('/', nil) }
- assert_raises(FakeWeb::NetConnectNotAllowedError) { http.delete('/') }
- end
-
- def test_content_for_registered_uri_with_any_method_explicitly
- FakeWeb.allow_net_connect = false
- FakeWeb.register_uri(:any, "http://example.com/", :body => "test example content")
- http = Net::HTTP.new('example.com')
- assert_equal 'test example content', http.get('/').body
- assert_equal 'test example content', http.post('/', nil).body
- assert_equal 'test example content', http.put('/', nil).body
- assert_equal 'test example content', http.delete('/').body
- end
-
- def test_content_for_registered_uri_with_any_method_implicitly
- FakeWeb.allow_net_connect = false
- capture_stderr do # silence deprecation warning
- FakeWeb.register_uri("http://example.com/", :body => "test example content")
- end
-
- http = Net::HTTP.new('example.com')
- assert_equal 'test example content', http.get('/').body
- assert_equal 'test example content', http.post('/', nil).body
- assert_equal 'test example content', http.put('/', nil).body
- assert_equal 'test example content', http.delete('/').body
- end
-
- def test_mock_request_with_block
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => fixture_path("test_example.txt"))
- response = Net::HTTP.start('mock') { |http| http.get('/test_example.txt') }
- assert_equal 'test example content', response.body
- end
-
- def test_request_with_registered_body_yields_the_response_body_to_a_request_block
- FakeWeb.register_uri(:get, "http://example.com", :body => "content")
- body = nil
- Net::HTTP.start("example.com") do |http|
- http.get("/") do |response_body|
- body = response_body
- end
- end
- assert_equal "content", body
- end
-
- def test_request_with_registered_response_yields_the_response_body_to_a_request_block
- fake_response = Net::HTTPOK.new('1.1', '200', 'OK')
- fake_response.instance_variable_set(:@body, "content")
- FakeWeb.register_uri(:get, 'http://example.com', :response => fake_response)
- body = nil
- Net::HTTP.start("example.com") do |http|
- http.get("/") do |response_body|
- body = response_body
- end
- end
- assert_equal "content", body
- end
-
- def test_mock_request_with_undocumented_full_uri_argument_style
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => fixture_path("test_example.txt"))
- response = Net::HTTP.start('mock') { |query| query.get('http://mock/test_example.txt') }
- assert_equal 'test example content', response.body
- end
-
- def test_mock_request_with_undocumented_full_uri_argument_style_and_query
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt?a=b', :body => 'test query content')
- response = Net::HTTP.start('mock') { |query| query.get('http://mock/test_example.txt?a=b') }
- assert_equal 'test query content', response.body
- end
-
- def test_mock_post
- FakeWeb.register_uri(:post, 'http://mock/test_example.txt', :body => fixture_path("test_example.txt"))
- response = Net::HTTP.start('mock') { |query| query.post('/test_example.txt', '') }
- assert_equal 'test example content', response.body
- end
-
- def test_mock_post_with_string_as_registered_uri
- FakeWeb.register_uri(:post, 'http://mock/test_string.txt', :body => 'foo')
- response = Net::HTTP.start('mock') { |query| query.post('/test_string.txt', '') }
- assert_equal 'foo', response.body
- end
-
- def test_mock_post_with_body_sets_the_request_body
- FakeWeb.register_uri(:post, "http://example.com/posts", :status => [201, "Created"])
- http = Net::HTTP.new("example.com")
- request = Net::HTTP::Post.new("/posts")
- http.request(request, "title=Test")
- assert_equal "title=Test", request.body
- assert_equal 10, request.content_length
- end
-
- def test_mock_post_with_body_using_other_syntax_sets_the_request_body
- FakeWeb.register_uri(:post, "http://example.com/posts", :status => [201, "Created"])
- http = Net::HTTP.new("example.com")
- request = Net::HTTP::Post.new("/posts")
- request.body = "title=Test"
- http.request(request)
- assert_equal "title=Test", request.body
- assert_equal 10, request.content_length
- end
-
- def test_real_post_with_body_sets_the_request_body
- FakeWeb.allow_net_connect = true
- setup_expectations_for_real_apple_hot_news_request :method => "POST",
- :path => "/posts", :request_body => "title=Test"
- http = Net::HTTP.new("images.apple.com")
- request = Net::HTTP::Post.new("/posts")
- request["Content-Type"] = "application/x-www-form-urlencoded"
- http.request(request, "title=Test")
- assert_equal "title=Test", request.body
- assert_equal 10, request.content_length
- end
-
- def test_mock_get_with_request_as_registered_uri
- fake_response = Net::HTTPOK.new('1.1', '200', 'OK')
- FakeWeb.register_uri(:get, 'http://mock/test_response', :response => fake_response)
- response = Net::HTTP.start('mock') { |query| query.get('/test_response') }
- assert_equal fake_response, response
- end
-
- def test_mock_get_with_request_from_file_as_registered_uri
- FakeWeb.register_uri(:get, 'http://www.google.com/', :response => fixture_path("google_response_without_transfer_encoding"))
- response = Net::HTTP.start('www.google.com') { |query| query.get('/') }
- assert_equal '200', response.code
- assert response.body.include?('<title>Google</title>')
- end
-
- def test_mock_post_with_request_from_file_as_registered_uri
- FakeWeb.register_uri(:post, 'http://www.google.com/', :response => fixture_path("google_response_without_transfer_encoding"))
- response = Net::HTTP.start('www.google.com') { |query| query.post('/', '') }
- assert_equal "200", response.code
- assert response.body.include?('<title>Google</title>')
- end
-
- def test_proxy_request
- FakeWeb.register_uri(:get, 'http://www.example.com/', :body => "hello world")
- FakeWeb.register_uri(:get, 'http://your.proxy.host/', :body => "lala")
-
- response = nil
- Net::HTTP::Proxy('your.proxy.host', 8080).start('www.example.com') do |http|
- response = http.get('/')
- end
- assert_equal "hello world", response.body
- end
-
- def test_https_request
- FakeWeb.register_uri(:get, 'https://www.example.com/', :body => "Hello World")
- http = Net::HTTP.new('www.example.com', 443)
- http.use_ssl = true
- response = http.get('/')
- assert_equal "Hello World", response.body
- end
-
- def test_register_unimplemented_response
- FakeWeb.register_uri(:get, 'http://mock/unimplemented', :response => 1)
- assert_raises StandardError do
- Net::HTTP.start('mock') { |q| q.get('/unimplemented') }
- end
- end
-
- def test_specifying_nil_for_body
- FakeWeb.register_uri(:head, "http://example.com", :body => nil)
- response = Net::HTTP.start("example.com") { |query| query.head("/") }
- assert_equal "", response.body
- end
-
- def test_real_http_request
- FakeWeb.allow_net_connect = true
- setup_expectations_for_real_apple_hot_news_request
-
- resp = nil
- Net::HTTP.start('images.apple.com') do |query|
- resp = query.get('/main/rss/hotnews/hotnews.rss')
- end
- assert resp.body.include?('Apple')
- assert resp.body.include?('News')
- end
-
- def test_real_http_request_with_undocumented_full_uri_argument_style
- FakeWeb.allow_net_connect = true
- setup_expectations_for_real_apple_hot_news_request(:path => 'http://images.apple.com/main/rss/hotnews/hotnews.rss')
-
- resp = nil
- Net::HTTP.start('images.apple.com') do |query|
- resp = query.get('http://images.apple.com/main/rss/hotnews/hotnews.rss')
- end
- assert resp.body.include?('Apple')
- assert resp.body.include?('News')
- end
-
- def test_real_https_request
- FakeWeb.allow_net_connect = true
- setup_expectations_for_real_apple_hot_news_request(:port => 443)
-
- http = Net::HTTP.new('images.apple.com', 443)
- http.use_ssl = true
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE # silence certificate warning
- response = http.get('/main/rss/hotnews/hotnews.rss')
- assert response.body.include?('Apple')
- assert response.body.include?('News')
- end
-
- def test_real_request_on_same_domain_as_mock
- FakeWeb.allow_net_connect = true
- setup_expectations_for_real_apple_hot_news_request
-
- FakeWeb.register_uri(:get, 'http://images.apple.com/test_string.txt', :body => 'foo')
-
- resp = nil
- Net::HTTP.start('images.apple.com') do |query|
- resp = query.get('/main/rss/hotnews/hotnews.rss')
- end
- assert resp.body.include?('Apple')
- assert resp.body.include?('News')
- end
-
- def test_mock_request_on_real_domain
- FakeWeb.register_uri(:get, 'http://images.apple.com/test_string.txt', :body => 'foo')
- resp = nil
- Net::HTTP.start('images.apple.com') do |query|
- resp = query.get('/test_string.txt')
- end
- assert_equal 'foo', resp.body
- end
-
- def test_mock_post_that_raises_exception
- FakeWeb.register_uri(:post, 'http://mock/raising_exception.txt', :exception => StandardError)
- assert_raises(StandardError) do
- Net::HTTP.start('mock') do |query|
- query.post('/raising_exception.txt', 'some data')
- end
- end
- end
-
- def test_mock_post_that_raises_an_http_error
- FakeWeb.register_uri(:post, 'http://mock/raising_exception.txt', :exception => Net::HTTPError)
- assert_raises(Net::HTTPError) do
- Net::HTTP.start('mock') do |query|
- query.post('/raising_exception.txt', '')
- end
- end
- end
-
- def test_raising_an_exception_that_requires_an_argument_to_instantiate
- FakeWeb.register_uri(:get, "http://example.com/timeout.txt", :exception => Timeout::Error)
- assert_raises(Timeout::Error) do
- Net::HTTP.get(URI.parse("http://example.com/timeout.txt"))
- end
- end
-
- def test_mock_instance_syntax
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => fixture_path("test_example.txt"))
- response = nil
- uri = URI.parse('http://mock/test_example.txt')
- http = Net::HTTP.new(uri.host, uri.port)
- response = http.start do
- http.get(uri.path)
- end
-
- assert_equal 'test example content', response.body
- end
-
- def test_mock_via_nil_proxy
- response = nil
- proxy_address = nil
- proxy_port = nil
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => fixture_path("test_example.txt"))
- uri = URI.parse('http://mock/test_example.txt')
- http = Net::HTTP::Proxy(proxy_address, proxy_port).new(
- uri.host, (uri.port or 80))
- response = http.start do
- http.get(uri.path)
- end
-
- assert_equal 'test example content', response.body
- end
-
- def test_response_type
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => "test")
- response = Net::HTTP.start('mock') { |http| http.get('/test_example.txt') }
- assert_kind_of Net::HTTPSuccess, response
- end
-
- def test_mock_request_that_raises_an_http_error_with_a_specific_status
- FakeWeb.register_uri(:get, 'http://mock/raising_exception.txt', :exception => Net::HTTPError, :status => ['404', 'Not Found'])
- exception = assert_raises(Net::HTTPError) do
- Net::HTTP.start('mock') { |http| http.get('/raising_exception.txt') }
- end
- assert_equal '404', exception.response.code
- assert_equal 'Not Found', exception.response.msg
- end
-
- def test_mock_rotate_responses
- FakeWeb.register_uri(:get, 'http://mock/multiple_test_example.txt',
- [ {:body => fixture_path("test_example.txt"), :times => 2},
- {:body => "thrice", :times => 3},
- {:body => "ever_more"} ])
-
- uri = URI.parse('http://mock/multiple_test_example.txt')
- 2.times { assert_equal 'test example content', Net::HTTP.get(uri) }
- 3.times { assert_equal 'thrice', Net::HTTP.get(uri) }
- 4.times { assert_equal 'ever_more', Net::HTTP.get(uri) }
- end
-
- def test_mock_request_using_response_with_transfer_encoding_header_has_valid_transfer_encoding_header
- FakeWeb.register_uri(:get, 'http://www.google.com/', :response => fixture_path("google_response_with_transfer_encoding"))
- response = Net::HTTP.start('www.google.com') { |query| query.get('/') }
- assert_not_nil response['transfer-encoding']
- assert response['transfer-encoding'] == 'chunked'
- end
-
- def test_mock_request_using_response_without_transfer_encoding_header_does_not_have_a_transfer_encoding_header
- FakeWeb.register_uri(:get, 'http://www.google.com/', :response => fixture_path("google_response_without_transfer_encoding"))
- response = nil
- response = Net::HTTP.start('www.google.com') { |query| query.get('/') }
- assert !response.key?('transfer-encoding')
- end
-
- def test_mock_request_using_response_from_curl_has_original_transfer_encoding_header
- FakeWeb.register_uri(:get, 'http://www.google.com/', :response => fixture_path("google_response_from_curl"))
- response = Net::HTTP.start('www.google.com') { |query| query.get('/') }
- assert_not_nil response['transfer-encoding']
- assert response['transfer-encoding'] == 'chunked'
- end
-
- def test_txt_file_should_have_three_lines
- FakeWeb.register_uri(:get, 'http://www.google.com/', :body => fixture_path("test_txt_file"))
- response = Net::HTTP.start('www.google.com') { |query| query.get('/') }
- assert response.body.split(/\n/).size == 3, "response has #{response.body.split(/\n/).size} lines should have 3"
- end
-
- def test_requiring_fakeweb_instead_of_fake_web
- require "fakeweb"
- end
-
- def test_registering_with_string_containing_null_byte
- # Regression test for File.exists? raising an ArgumentError ("string
- # contains null byte") since :response first tries to find by filename.
- # The string should be treated as a response body, instead, and an
- # EOFError is raised when the byte is encountered.
- FakeWeb.register_uri(:get, "http://example.com", :response => "test\0test")
- assert_raise EOFError do
- Net::HTTP.get(URI.parse("http://example.com"))
- end
-
- FakeWeb.register_uri(:get, "http://example.com", :body => "test\0test")
- body = Net::HTTP.get(URI.parse("http://example.com"))
- assert_equal "test\0test", body
- end
-
- def test_registering_with_string_that_is_a_directory_name
- # Similar to above, but for Errno::EISDIR being raised since File.exists?
- # returns true for directories
- FakeWeb.register_uri(:get, "http://example.com", :response => File.dirname(__FILE__))
- assert_raise EOFError do
- body = Net::HTTP.get(URI.parse("http://example.com"))
- end
-
- FakeWeb.register_uri(:get, "http://example.com", :body => File.dirname(__FILE__))
- body = Net::HTTP.get(URI.parse("http://example.com"))
- assert_equal File.dirname(__FILE__), body
- end
-
- def test_registering_with_a_body_pointing_to_a_pathname
- path = Pathname.new(fixture_path("test_example.txt"))
- FakeWeb.register_uri(:get, "http://example.com", :body => path)
- response = Net::HTTP.start("example.com") { |http| http.get("/") }
- assert_equal "test example content", response.body
- end
-
- def test_registering_with_a_response_pointing_to_a_pathname
- path = Pathname.new(fixture_path("google_response_without_transfer_encoding"))
- FakeWeb.register_uri(:get, "http://google.com", :response => path)
- response = Net::HTTP.start("google.com") { |http| http.get("/") }
- assert response.body.include?("<title>Google</title>")
- end
-
- def test_http_version_from_string_response
- FakeWeb.register_uri(:get, "http://example.com", :body => "example")
- response = Net::HTTP.start("example.com") { |http| http.get("/") }
- assert_equal "1.0", response.http_version
- end
-
- def test_http_version_from_file_response
- FakeWeb.register_uri(:get, "http://example.com", :body => fixture_path("test_example.txt"))
- response = Net::HTTP.start("example.com") { |http| http.get("/") }
- assert_equal "1.0", response.http_version
- end
-
- def test_version
- assert_equal "1.3.0", FakeWeb::VERSION
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_fake_web_open_uri.rb b/vendor/gems/fakeweb-1.3.0/test/test_fake_web_open_uri.rb
deleted file mode 100644
index 699a64762..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_fake_web_open_uri.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require 'test_helper'
-
-class TestFakeWebOpenURI < Test::Unit::TestCase
-
- def test_content_for_registered_uri
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => fixture_path("test_example.txt"))
- assert_equal 'test example content', FakeWeb.response_for(:get, 'http://mock/test_example.txt').body
- end
-
- def test_mock_open
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => fixture_path("test_example.txt"))
- assert_equal 'test example content', open('http://mock/test_example.txt').read
- end
-
- def test_mock_open_with_string_as_registered_uri
- FakeWeb.register_uri(:get, 'http://mock/test_string.txt', :body => 'foo')
- assert_equal 'foo', open('http://mock/test_string.txt').string
- end
-
- def test_real_open
- FakeWeb.allow_net_connect = true
- setup_expectations_for_real_apple_hot_news_request
- resp = open('http://images.apple.com/main/rss/hotnews/hotnews.rss')
- assert_equal "200", resp.status.first
- body = resp.read
- assert body.include?('Apple')
- assert body.include?('News')
- end
-
- def test_mock_open_that_raises_exception
- FakeWeb.register_uri(:get, 'http://mock/raising_exception.txt', :exception => StandardError)
- assert_raises(StandardError) do
- open('http://mock/raising_exception.txt')
- end
- end
-
- def test_mock_open_that_raises_an_http_error
- FakeWeb.register_uri(:get, 'http://mock/raising_exception.txt', :exception => OpenURI::HTTPError)
- assert_raises(OpenURI::HTTPError) do
- open('http://mock/raising_exception.txt')
- end
- end
-
- def test_mock_open_that_raises_an_http_error_with_a_specific_status
- FakeWeb.register_uri(:get, 'http://mock/raising_exception.txt', :exception => OpenURI::HTTPError, :status => ['123', 'jodel'])
- exception = assert_raises(OpenURI::HTTPError) do
- open('http://mock/raising_exception.txt')
- end
- assert_equal '123', exception.io.code
- assert_equal 'jodel', exception.io.message
- end
-
- def test_mock_open_with_block
- FakeWeb.register_uri(:get, 'http://mock/test_example.txt', :body => fixture_path("test_example.txt"))
- body = open('http://mock/test_example.txt') { |f| f.readlines }
- assert_equal 'test example content', body.first
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_helper.rb b/vendor/gems/fakeweb-1.3.0/test/test_helper.rb
deleted file mode 100644
index b181391b1..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_helper.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-require 'test/unit'
-require 'open-uri'
-require 'pathname'
-require 'fake_web'
-require 'rbconfig'
-require 'rubygems'
-require 'mocha'
-
-
-# Give all tests a common setup and teardown that prevents shared state
-class Test::Unit::TestCase
- alias setup_without_fakeweb setup
- def setup
- FakeWeb.clean_registry
- @original_allow_net_connect = FakeWeb.allow_net_connect?
- FakeWeb.allow_net_connect = false
- end
-
- alias teardown_without_fakeweb teardown
- def teardown
- FakeWeb.allow_net_connect = @original_allow_net_connect
- end
-end
-
-
-module FakeWebTestHelper
-
- def fixture_path(basename)
- "test/fixtures/#{basename}"
- end
-
- def capture_stderr
- $stderr = StringIO.new
- yield
- $stderr.rewind && $stderr.read
- ensure
- $stderr = STDERR
- end
-
- # The path to the current ruby interpreter. Adapted from Rake's FileUtils.
- def ruby_path
- ext = ((RbConfig::CONFIG['ruby_install_name'] =~ /\.(com|cmd|exe|bat|rb|sh)$/) ? "" : RbConfig::CONFIG['EXEEXT'])
- File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'] + ext).sub(/.*\s.*/m, '"\&"')
- end
-
- # Sets several expectations (using Mocha) that a real HTTP request makes it
- # past FakeWeb to the socket layer. You can use this when you need to check
- # that a request isn't handled by FakeWeb.
- def setup_expectations_for_real_request(options = {})
- # Socket handling
- if options[:port] == 443
- socket = mock("SSLSocket")
- OpenSSL::SSL::SSLSocket.expects(:===).with(socket).returns(true).at_least_once
- OpenSSL::SSL::SSLSocket.expects(:new).with(socket, instance_of(OpenSSL::SSL::SSLContext)).returns(socket).at_least_once
- socket.stubs(:sync_close=).returns(true)
- socket.expects(:connect).with().at_least_once
- else
- socket = mock("TCPSocket")
- Socket.expects(:===).with(socket).at_least_once.returns(true)
- end
-
- TCPSocket.expects(:open).with(options[:host], options[:port]).returns(socket).at_least_once
- socket.stubs(:closed?).returns(false)
- socket.stubs(:close).returns(true)
-
- # Request/response handling
- request_parts = ["#{options[:method]} #{options[:path]} HTTP/1.1", "Host: #{options[:host]}"]
- socket.expects(:write).with(all_of(includes(request_parts[0]), includes(request_parts[1]))).returns(100)
- if !options[:request_body].nil?
- socket.expects(:write).with(options[:request_body]).returns(100)
- end
-
- read_method = RUBY_VERSION >= "1.9.2" ? :read_nonblock : :sysread
- socket.expects(read_method).at_least_once.returns("HTTP/1.1 #{options[:response_code]} #{options[:response_message]}\nContent-Length: #{options[:response_body].length}\n\n#{options[:response_body]}").then.raises(EOFError)
- end
-
-
- # A helper that calls #setup_expectations_for_real_request for you, using
- # defaults for our commonly used test request to images.apple.com.
- def setup_expectations_for_real_apple_hot_news_request(options = {})
- defaults = { :host => "images.apple.com", :port => 80, :method => "GET",
- :path => "/main/rss/hotnews/hotnews.rss",
- :response_code => 200, :response_message => "OK",
- :response_body => "<title>Apple Hot News</title>" }
- setup_expectations_for_real_request(defaults.merge(options))
- end
-
-end
-
-Test::Unit::TestCase.send(:include, FakeWebTestHelper)
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_last_request.rb b/vendor/gems/fakeweb-1.3.0/test/test_last_request.rb
deleted file mode 100644
index 7868c83a1..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_last_request.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'test_helper'
-
-class TestLastRequest < Test::Unit::TestCase
-
- def test_last_request_returns_correct_net_http_request_class
- FakeWeb.register_uri(:get, "http://example.com", :status => [200, "OK"])
- Net::HTTP.start("example.com") { |http| http.get("/") }
- assert_instance_of Net::HTTP::Get, FakeWeb.last_request
- end
-
- def test_last_request_has_correct_method_path_and_body_for_get
- FakeWeb.register_uri(:get, "http://example.com", :status => [200, "OK"])
- Net::HTTP.start("example.com") { |http| http.get("/") }
- assert_equal "GET", FakeWeb.last_request.method
- assert_equal "/", FakeWeb.last_request.path
- assert_nil FakeWeb.last_request.body
- assert_nil FakeWeb.last_request.content_length
- end
-
- def test_last_request_has_correct_method_path_and_body_for_post
- FakeWeb.register_uri(:post, "http://example.com/posts", :status => [201, "Created"])
- Net::HTTP.start("example.com") { |http| http.post("/posts", "title=Test") }
- assert_equal "POST", FakeWeb.last_request.method
- assert_equal "/posts", FakeWeb.last_request.path
- assert_equal "title=Test", FakeWeb.last_request.body
- assert_equal 10, FakeWeb.last_request.content_length
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_missing_open_uri.rb b/vendor/gems/fakeweb-1.3.0/test/test_missing_open_uri.rb
deleted file mode 100644
index 029ba1cab..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_missing_open_uri.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require 'test_helper'
-
-class TestMissingOpenURI < Test::Unit::TestCase
-
- def setup
- super
- @saved_open_uri = OpenURI
- Object.send(:remove_const, :OpenURI)
- end
-
- def teardown
- super
- Object.const_set(:OpenURI, @saved_open_uri)
- end
-
-
- def test_register_using_exception_without_open_uri
- # regression test for Responder needing OpenURI::HTTPError to be defined
- FakeWeb.register_uri(:get, "http://example.com/", :exception => StandardError)
- assert_raises(StandardError) do
- Net::HTTP.start("example.com") { |http| http.get("/") }
- end
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_missing_pathname.rb b/vendor/gems/fakeweb-1.3.0/test/test_missing_pathname.rb
deleted file mode 100644
index ee16a0d7b..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_missing_pathname.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require 'test_helper'
-
-class TestMissingPathname < Test::Unit::TestCase
-
- def setup
- super
- @saved_pathname = Pathname
- Object.send(:remove_const, :Pathname)
- end
-
- def teardown
- super
- Object.const_set(:Pathname, @saved_pathname)
- end
-
- # FakeWeb supports using Pathname objects where filenames are expected, but
- # Pathname isn't required to use FakeWeb. Make sure everything still works
- # when Pathname isn't in use.
-
- def test_register_using_body_without_pathname
- FakeWeb.register_uri(:get, "http://example.com/", :body => fixture_path("test_example.txt"))
- Net::HTTP.start("example.com") { |http| http.get("/") }
- end
-
- def test_register_using_response_without_pathname
- FakeWeb.register_uri(:get, "http://example.com/", :response => fixture_path("google_response_without_transfer_encoding"))
- Net::HTTP.start("example.com") { |http| http.get("/") }
- end
-
- def test_register_using_unsupported_response_without_pathname
- FakeWeb.register_uri(:get, "http://example.com/", :response => 1)
- assert_raise StandardError do
- Net::HTTP.start("example.com") { |http| http.get("/") }
- end
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_other_net_http_libraries.rb b/vendor/gems/fakeweb-1.3.0/test/test_other_net_http_libraries.rb
deleted file mode 100644
index af7e5e276..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_other_net_http_libraries.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require 'test_helper'
-
-class TestOtherNetHttpLibraries < Test::Unit::TestCase
-
- def capture_output_from_requiring(libs, additional_code = "")
- requires = libs.map { |lib| "require '#{lib}'" }.join("; ")
- fakeweb_dir = "#{File.dirname(__FILE__)}/../lib"
- vendor_dirs = Dir["#{File.dirname(__FILE__)}/vendor/*/lib"]
- load_path_opts = vendor_dirs.unshift(fakeweb_dir).map { |dir| "-I#{dir}" }.join(" ")
-
- `#{ruby_path} #{load_path_opts} -e "#{requires}; #{additional_code}" 2>&1`
- end
-
- def test_requiring_samuel_before_fakeweb_prints_warning
- output = capture_output_from_requiring %w(samuel fakeweb)
- assert_match %r(Warning: FakeWeb was loaded after Samuel), output
- end
-
- def test_requiring_samuel_after_fakeweb_does_not_print_warning
- output = capture_output_from_requiring %w(fakeweb samuel)
- assert output.empty?
- end
-
- def test_requiring_right_http_connection_before_fakeweb_and_then_connecting_does_not_print_warning
- additional_code = "Net::HTTP.start('example.com')"
- output = capture_output_from_requiring %w(right_http_connection fakeweb), additional_code
- assert output.empty?
- end
-
- def test_requiring_right_http_connection_after_fakeweb_and_then_connecting_prints_warning
- additional_code = "Net::HTTP.start('example.com')"
- output = capture_output_from_requiring %w(fakeweb right_http_connection), additional_code
- assert_match %r(Warning: RightHttpConnection was loaded after FakeWeb), output
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_precedence.rb b/vendor/gems/fakeweb-1.3.0/test/test_precedence.rb
deleted file mode 100644
index 388b9f8a1..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_precedence.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-require 'test_helper'
-
-class TestPrecedence < Test::Unit::TestCase
-
- def test_matching_get_strings_have_precedence_over_matching_get_regexes
- FakeWeb.register_uri(:get, "http://example.com/test", :body => "string")
- FakeWeb.register_uri(:get, %r|http://example\.com/test|, :body => "regex")
- response = Net::HTTP.start("example.com") { |query| query.get('/test') }
- assert_equal "string", response.body
- end
-
- def test_matching_any_strings_have_precedence_over_matching_any_regexes
- FakeWeb.register_uri(:any, "http://example.com/test", :body => "string")
- FakeWeb.register_uri(:any, %r|http://example\.com/test|, :body => "regex")
- response = Net::HTTP.start("example.com") { |query| query.get('/test') }
- assert_equal "string", response.body
- end
-
- def test_matching_get_strings_have_precedence_over_matching_any_strings
- FakeWeb.register_uri(:get, "http://example.com/test", :body => "get method")
- FakeWeb.register_uri(:any, "http://example.com/test", :body => "any method")
- response = Net::HTTP.start("example.com") { |query| query.get('/test') }
- assert_equal "get method", response.body
-
- # registration order should not matter
- FakeWeb.register_uri(:any, "http://example.com/test2", :body => "any method")
- FakeWeb.register_uri(:get, "http://example.com/test2", :body => "get method")
- response = Net::HTTP.start("example.com") { |query| query.get('/test2') }
- assert_equal "get method", response.body
- end
-
- def test_matching_any_strings_have_precedence_over_matching_get_regexes
- FakeWeb.register_uri(:any, "http://example.com/test", :body => "any string")
- FakeWeb.register_uri(:get, %r|http://example\.com/test|, :body => "get regex")
- response = Net::HTTP.start("example.com") { |query| query.get('/test') }
- assert_equal "any string", response.body
- end
-
- def test_registered_strings_and_uris_are_equivalent_so_second_takes_precedence
- FakeWeb.register_uri(:get, "http://example.com/test", :body => "string")
- FakeWeb.register_uri(:get, URI.parse("http://example.com/test"), :body => "uri")
- response = Net::HTTP.start("example.com") { |query| query.get('/test') }
- assert_equal "uri", response.body
-
- FakeWeb.register_uri(:get, URI.parse("http://example.com/test2"), :body => "uri")
- FakeWeb.register_uri(:get, "http://example.com/test2", :body => "string")
- response = Net::HTTP.start("example.com") { |query| query.get('/test2') }
- assert_equal "string", response.body
- end
-
- def test_identical_registration_replaces_previous_registration
- FakeWeb.register_uri(:get, "http://example.com/test", :body => "first")
- FakeWeb.register_uri(:get, "http://example.com/test", :body => "second")
- response = Net::HTTP.start("example.com") { |query| query.get('/test') }
- assert_equal "second", response.body
- end
-
- def test_identical_registration_replaces_previous_registration_accounting_for_normalization
- FakeWeb.register_uri(:get, "http://example.com/test?", :body => "first")
- FakeWeb.register_uri(:get, "http://example.com:80/test", :body => "second")
- response = Net::HTTP.start("example.com") { |query| query.get('/test') }
- assert_equal "second", response.body
- end
-
- def test_identical_registration_replaces_previous_registration_accounting_for_query_params
- FakeWeb.register_uri(:get, "http://example.com/test?a=1&b=2", :body => "first")
- FakeWeb.register_uri(:get, "http://example.com/test?b=2&a=1", :body => "second")
- response = Net::HTTP.start("example.com") { |query| query.get('/test?a=1&b=2') }
- assert_equal "second", response.body
- end
-
- def test_identical_registration_replaces_previous_registration_with_regexes
- FakeWeb.register_uri(:get, /test/, :body => "first")
- FakeWeb.register_uri(:get, /test/, :body => "second")
- response = Net::HTTP.start("example.com") { |query| query.get('/test') }
- assert_equal "second", response.body
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_query_string.rb b/vendor/gems/fakeweb-1.3.0/test/test_query_string.rb
deleted file mode 100644
index 11a211848..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_query_string.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-require 'test_helper'
-
-class TestFakeWebQueryString < Test::Unit::TestCase
-
- def test_register_uri_string_with_query_params
- FakeWeb.register_uri(:get, 'http://example.com/?a=1&b=1', :body => 'foo')
- assert FakeWeb.registered_uri?(:get, 'http://example.com/?a=1&b=1')
-
- FakeWeb.register_uri(:post, URI.parse("http://example.org/?a=1&b=1"), :body => "foo")
- assert FakeWeb.registered_uri?(:post, "http://example.org/?a=1&b=1")
- end
-
- def test_register_uri_with_query_params_and_check_in_different_order
- FakeWeb.register_uri(:get, 'http://example.com/?a=1&b=1', :body => 'foo')
- assert FakeWeb.registered_uri?(:get, 'http://example.com/?b=1&a=1')
-
- FakeWeb.register_uri(:post, URI.parse('http://example.org/?a=1&b=1'), :body => 'foo')
- assert FakeWeb.registered_uri?(:post, 'http://example.org/?b=1&a=1')
- end
-
- def test_registered_uri_gets_recognized_with_empty_query_params
- FakeWeb.register_uri(:get, 'http://example.com/', :body => 'foo')
- assert FakeWeb.registered_uri?(:get, 'http://example.com/?')
-
- FakeWeb.register_uri(:post, URI.parse('http://example.org/'), :body => 'foo')
- assert FakeWeb.registered_uri?(:post, 'http://example.org/?')
- end
-
- def test_register_uri_with_empty_query_params_and_check_with_none
- FakeWeb.register_uri(:get, 'http://example.com/?', :body => 'foo')
- assert FakeWeb.registered_uri?(:get, 'http://example.com/')
-
- FakeWeb.register_uri(:post, URI.parse('http://example.org/?'), :body => 'foo')
- assert FakeWeb.registered_uri?(:post, 'http://example.org/')
- end
-
- def test_registry_sort_query_params
- assert_equal "a=1&b=2", FakeWeb::Registry.instance.send(:sort_query_params, "b=2&a=1")
- end
-
- def test_registry_sort_query_params_sorts_by_value_if_keys_collide
- assert_equal "a=1&a=2&b=2", FakeWeb::Registry.instance.send(:sort_query_params, "a=2&b=2&a=1")
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_regexes.rb b/vendor/gems/fakeweb-1.3.0/test/test_regexes.rb
deleted file mode 100644
index e2eba1db8..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_regexes.rb
+++ /dev/null
@@ -1,157 +0,0 @@
-require 'test_helper'
-
-class TestRegexes < Test::Unit::TestCase
-
- def test_registered_uri_with_pattern
- FakeWeb.register_uri(:get, %r|http://example.com/test_example/\d+|, :body => "example")
- assert FakeWeb.registered_uri?(:get, "http://example.com/test_example/25")
- assert !FakeWeb.registered_uri?(:get, "http://example.com/test_example/abc")
- end
-
- def test_response_for_with_matching_registered_uri
- FakeWeb.register_uri(:get, %r|http://www.google.com|, :body => "Welcome to Google!")
- assert_equal "Welcome to Google!", FakeWeb.response_for(:get, "http://www.google.com").body
- end
-
- def test_response_for_with_matching_registered_uri_and_get_method_matching_to_any_method
- FakeWeb.register_uri(:any, %r|http://www.example.com|, :body => "example")
- assert_equal "example", FakeWeb.response_for(:get, "http://www.example.com").body
- end
-
- def test_registered_uri_with_authentication_and_pattern
- FakeWeb.register_uri(:get, %r|http://user:pass@mock/example\.\w+|i, :body => "example")
- assert FakeWeb.registered_uri?(:get, 'http://user:pass@mock/example.txt')
- end
-
- def test_registered_uri_with_authentication_and_pattern_handles_case_insensitivity
- FakeWeb.register_uri(:get, %r|http://user:pass@mock/example\.\w+|i, :body => "example")
- assert FakeWeb.registered_uri?(:get, 'http://uSeR:PAss@mock/example.txt')
- end
-
- def test_request_with_authentication_and_pattern_handles_case_insensitivity
- FakeWeb.register_uri(:get, %r|http://user:pass@mock/example\.\w+|i, :body => "example")
- http = Net::HTTP.new('mock', 80)
- req = Net::HTTP::Get.new('/example.txt')
- req.basic_auth 'uSeR', 'PAss'
- assert_equal "example", http.request(req).body
- end
-
- def test_requesting_a_uri_that_matches_two_registered_regexes_raises_an_error
- FakeWeb.register_uri(:get, %r|http://example\.com/|, :body => "first")
- FakeWeb.register_uri(:get, %r|http://example\.com/a|, :body => "second")
- assert_raise FakeWeb::MultipleMatchingURIsError do
- Net::HTTP.start("example.com") { |query| query.get('/a') }
- end
- end
-
- def test_requesting_a_uri_that_matches_two_registered_regexes_raises_an_error_including_request_info
- FakeWeb.register_uri(:get, %r|http://example\.com/|, :body => "first")
- FakeWeb.register_uri(:get, %r|http://example\.com/a|, :body => "second")
- begin
- Net::HTTP.start("example.com") { |query| query.get('/a') }
- rescue FakeWeb::MultipleMatchingURIsError => exception
- end
- assert exception.message.include?("GET http://example.com/a")
- end
-
- def test_registry_does_not_find_using_mismatched_protocols_or_ports_when_registered_with_both
- FakeWeb.register_uri(:get, %r|http://www.example.com:80|, :body => "example")
- assert !FakeWeb.registered_uri?(:get, "https://www.example.com:80")
- assert !FakeWeb.registered_uri?(:get, "http://www.example.com:443")
- end
-
- def test_registry_finds_using_non_default_port
- FakeWeb.register_uri(:get, %r|example\.com:8080|, :body => "example")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:8080/path")
- assert FakeWeb.registered_uri?(:get, "https://www.example.com:8080/path")
- end
-
- def test_registry_finds_using_default_port_and_http_when_registered_with_explicit_port_80
- FakeWeb.register_uri(:get, %r|example\.com:80|, :body => "example")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com/path")
-
- # check other permutations, too
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:80/path")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:8080/path")
- assert FakeWeb.registered_uri?(:get, "https://www.example.com:80/path")
- assert FakeWeb.registered_uri?(:get, "https://www.example.com:8080/path")
- assert !FakeWeb.registered_uri?(:get, "https://www.example.com/path")
- end
-
- def test_registry_finds_using_default_port_and_https_when_registered_with_explicit_port_443
- FakeWeb.register_uri(:get, %r|example\.com:443|, :body => "example")
- assert FakeWeb.registered_uri?(:get, "https://www.example.com/path")
-
- # check other permutations, too
- assert FakeWeb.registered_uri?(:get, "https://www.example.com:443/path")
- assert FakeWeb.registered_uri?(:get, "https://www.example.com:44321/path")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:443/path")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:44321/path")
- assert !FakeWeb.registered_uri?(:get, "http://www.example.com/path")
- end
-
- def test_registry_only_finds_using_default_port_when_registered_without_if_protocol_matches
- FakeWeb.register_uri(:get, %r|http://www.example.com/test|, :body => "example")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:80/test")
- assert !FakeWeb.registered_uri?(:get, "http://www.example.com:443/test")
- assert !FakeWeb.registered_uri?(:get, "https://www.example.com:443/test")
- FakeWeb.register_uri(:get, %r|https://www.example.org/test|, :body => "example")
- assert FakeWeb.registered_uri?(:get, "https://www.example.org:443/test")
- assert !FakeWeb.registered_uri?(:get, "https://www.example.org:80/test")
- assert !FakeWeb.registered_uri?(:get, "http://www.example.org:80/test")
- end
-
- def test_registry_matches_using_mismatched_port_when_registered_without
- FakeWeb.register_uri(:get, %r|http://www.example.com|, :body => "example")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:80")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:443")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:12345")
- assert !FakeWeb.registered_uri?(:get, "https://www.example.com:443")
- assert !FakeWeb.registered_uri?(:get, "https://www.example.com")
- end
-
- def test_registry_matches_using_default_port_for_protocol_when_registered_without_protocol_or_port
- FakeWeb.register_uri(:get, %r|www.example.com/home|, :body => "example")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com/home")
- assert FakeWeb.registered_uri?(:get, "https://www.example.com/home")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com:80/home")
- assert FakeWeb.registered_uri?(:get, "https://www.example.com:443/home")
- assert !FakeWeb.registered_uri?(:get, "https://www.example.com:80/home")
- assert !FakeWeb.registered_uri?(:get, "http://www.example.com:443/home")
- end
-
- def test_registry_matches_with_query_params
- FakeWeb.register_uri(:get, %r[example.com/list\?(.*&|)important=1], :body => "example")
- assert FakeWeb.registered_uri?(:get, "http://example.com/list?hash=123&important=1&unimportant=2")
- assert FakeWeb.registered_uri?(:get, "http://example.com/list?hash=123&important=12&unimportant=2")
- assert FakeWeb.registered_uri?(:get, "http://example.com/list?important=1&unimportant=2")
- assert !FakeWeb.registered_uri?(:get, "http://example.com/list?important=2")
- assert !FakeWeb.registered_uri?(:get, "http://example.com/list?important=2&unimportant=1")
- assert !FakeWeb.registered_uri?(:get, "http://example.com/list?hash=123&important=2&unimportant=1")
- assert !FakeWeb.registered_uri?(:get, "http://example.com/list?notimportant=1&unimportant=1")
- end
-
- def test_registry_does_not_match_when_regex_has_unsorted_query_params
- FakeWeb.register_uri(:get, %r[example\.com/list\?b=2&a=1], :body => "example")
- assert !FakeWeb.registered_uri?(:get, "http://example.com/list?b=2&a=1")
- assert !FakeWeb.registered_uri?(:get, "http://example.com/list?a=1&b=2")
- assert !FakeWeb.registered_uri?(:get, "https://example.com:443/list?b=2&a=1")
- end
-
- def test_registry_matches_when_regex_has_sorted_query_params
- FakeWeb.register_uri(:get, %r[example\.com/list\?a=1&b=2], :body => "example")
- assert FakeWeb.registered_uri?(:get, "http://example.com/list?b=2&a=1")
- assert FakeWeb.registered_uri?(:get, "http://example.com/list?a=1&b=2")
- assert FakeWeb.registered_uri?(:get, "https://example.com:443/list?b=2&a=1")
- end
-
- def test_registry_matches_quickly_with_lots_of_query_params
- # regression test for code that tried to calculate the permutations of the
- # query params, which hangs with a large number of params
- FakeWeb.register_uri(:get, %r[example.com], :body => "example")
- Timeout::timeout(1) do
- FakeWeb.registered_uri?(:get, "http://example.com/?a=1&b=2&c=3&d=4&e=5&f=6&g=7&h=8")
- end
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_response_headers.rb b/vendor/gems/fakeweb-1.3.0/test/test_response_headers.rb
deleted file mode 100644
index 45f3f5a99..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_response_headers.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-require 'test_helper'
-
-class TestResponseHeaders < Test::Unit::TestCase
- def test_content_type_when_registering_with_string_and_content_type_header_as_symbol_option
- FakeWeb.register_uri(:get, "http://example.com/users.json", :body => '[{"username": "chrisk"}]', :content_type => "application/json")
- response = Net::HTTP.start("example.com") { |query| query.get("/users.json") }
- assert_equal '[{"username": "chrisk"}]', response.body
- assert_equal "application/json", response['Content-Type']
- end
-
- def test_content_type_when_registering_with_string_and_content_type_header_as_string_option
- FakeWeb.register_uri(:get, "http://example.com/users.json", :body => '[{"username": "chrisk"}]', 'Content-Type' => "application/json")
- response = Net::HTTP.start("example.com") { |query| query.get("/users.json") }
- assert_equal "application/json", response['Content-Type']
- end
-
- def test_content_type_when_registering_with_string_only
- FakeWeb.register_uri(:get, "http://example.com/users.json", :body => '[{"username": "chrisk"}]')
- response = Net::HTTP.start("example.com") { |query| query.get("/users.json") }
- assert_equal '[{"username": "chrisk"}]', response.body
- assert_nil response['Content-Type']
- end
-
- def test_cookies_when_registering_with_file_and_set_cookie_header
- FakeWeb.register_uri(:get, "http://example.com/", :body => fixture_path("test_example.txt"),
- :set_cookie => "user_id=1; example=yes")
- response = Net::HTTP.start("example.com") { |query| query.get("/") }
- assert_equal "test example content", response.body
- assert_equal "user_id=1; example=yes", response['Set-Cookie']
- end
-
- def test_multiple_set_cookie_headers
- FakeWeb.register_uri(:get, "http://example.com", :set_cookie => ["user_id=1", "example=yes"])
- response = Net::HTTP.start("example.com") { |query| query.get("/") }
- assert_equal ["user_id=1", "example=yes"], response.get_fields('Set-Cookie')
- assert_equal "user_id=1, example=yes", response['Set-Cookie']
- end
-
- def test_registering_with_baked_response_ignores_header_options
- fake_response = Net::HTTPOK.new('1.1', '200', 'OK')
- fake_response["Server"] = "Apache/1.3.27 (Unix)"
- FakeWeb.register_uri(:get, "http://example.com/", :response => fake_response,
- :server => "FakeWeb/1.2.3 (Ruby)")
- response = Net::HTTP.start("example.com") { |query| query.get("/") }
- assert_equal "200", response.code
- assert_equal "OK", response.message
- assert_equal "Apache/1.3.27 (Unix)", response["Server"]
- end
-
- def test_headers_are_rotated_when_registering_with_response_rotation
- FakeWeb.register_uri(:get, "http://example.com",
- [{:body => 'test1', :expires => "Thu, 14 Jun 2009 16:00:00 GMT",
- :content_type => "text/plain"},
- {:body => 'test2', :expires => "Thu, 14 Jun 2009 16:00:01 GMT"}])
-
- first_response = second_response = nil
- Net::HTTP.start("example.com") do |query|
- first_response = query.get("/")
- second_response = query.get("/")
- end
- assert_equal 'test1', first_response.body
- assert_equal "Thu, 14 Jun 2009 16:00:00 GMT", first_response['Expires']
- assert_equal "text/plain", first_response['Content-Type']
- assert_equal 'test2', second_response.body
- assert_equal "Thu, 14 Jun 2009 16:00:01 GMT", second_response['Expires']
- assert_nil second_response['Content-Type']
- end
-
- def test_registering_with_status_option_and_response_headers
- FakeWeb.register_uri(:get, "http://example.com", :status => ["301", "Moved Permanently"],
- :location => "http://www.example.com")
-
- response = Net::HTTP.start("example.com") { |query| query.get("/") }
- assert_equal "301", response.code
- assert_equal "Moved Permanently", response.message
- assert_equal "http://www.example.com", response["Location"]
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_trailing_slashes.rb b/vendor/gems/fakeweb-1.3.0/test/test_trailing_slashes.rb
deleted file mode 100644
index 564d807dc..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_trailing_slashes.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-require 'test_helper'
-
-class TestFakeWebTrailingSlashes < Test::Unit::TestCase
-
- def test_registering_root_without_slash_and_ask_predicate_method_with_slash
- FakeWeb.register_uri(:get, "http://www.example.com", :body => "root")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com/")
- end
-
- def test_registering_root_without_slash_and_request
- FakeWeb.register_uri(:get, "http://www.example.com", :body => "root")
- response = Net::HTTP.start("www.example.com") { |query| query.get('/') }
- assert_equal "root", response.body
- end
-
- def test_registering_root_with_slash_and_ask_predicate_method_without_slash
- FakeWeb.register_uri(:get, "http://www.example.com/", :body => "root")
- assert FakeWeb.registered_uri?(:get, "http://www.example.com")
- end
-
- def test_registering_root_with_slash_and_request
- FakeWeb.register_uri(:get, "http://www.example.com/", :body => "root")
- response = Net::HTTP.start("www.example.com") { |query| query.get('/') }
- assert_equal "root", response.body
- end
-
- def test_registering_path_without_slash_and_ask_predicate_method_with_slash
- FakeWeb.register_uri(:get, "http://www.example.com/users", :body => "User list")
- assert !FakeWeb.registered_uri?(:get, "http://www.example.com/users/")
- end
-
- def test_registering_path_without_slash_and_request_with_slash
- FakeWeb.allow_net_connect = false
- FakeWeb.register_uri(:get, "http://www.example.com/users", :body => "User list")
- assert_raise FakeWeb::NetConnectNotAllowedError do
- response = Net::HTTP.start("www.example.com") { |query| query.get('/users/') }
- end
- end
-
- def test_registering_path_with_slash_and_ask_predicate_method_without_slash
- FakeWeb.register_uri(:get, "http://www.example.com/users/", :body => "User list")
- assert !FakeWeb.registered_uri?(:get, "http://www.example.com/users")
- end
-
- def test_registering_path_with_slash_and_request_without_slash
- FakeWeb.allow_net_connect = false
- FakeWeb.register_uri(:get, "http://www.example.com/users/", :body => "User list")
- assert_raise FakeWeb::NetConnectNotAllowedError do
- response = Net::HTTP.start("www.example.com") { |query| query.get('/users') }
- end
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/test_utility.rb b/vendor/gems/fakeweb-1.3.0/test/test_utility.rb
deleted file mode 100644
index 891de875b..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/test_utility.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-require 'test_helper'
-
-class TestUtility < Test::Unit::TestCase
-
- def test_decode_userinfo_from_header_handles_basic_auth
- authorization_header = "Basic dXNlcm5hbWU6c2VjcmV0"
- userinfo = FakeWeb::Utility.decode_userinfo_from_header(authorization_header)
- assert_equal "username:secret", userinfo
- end
-
- def test_encode_unsafe_chars_in_userinfo_does_not_encode_userinfo_safe_punctuation
- userinfo = "user;&=+$,:secret"
- assert_equal userinfo, FakeWeb::Utility.encode_unsafe_chars_in_userinfo(userinfo)
- end
-
- def test_encode_unsafe_chars_in_userinfo_does_not_encode_rfc_3986_unreserved_characters
- userinfo = "-_.!~*'()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:secret"
- assert_equal userinfo, FakeWeb::Utility.encode_unsafe_chars_in_userinfo(userinfo)
- end
-
- def test_encode_unsafe_chars_in_userinfo_does_encode_other_characters
- userinfo, safe_userinfo = 'us#rn@me:sec//ret?"', 'us%23rn%40me:sec%2F%2Fret%3F%22'
- assert_equal safe_userinfo, FakeWeb::Utility.encode_unsafe_chars_in_userinfo(userinfo)
- end
-
- def test_strip_default_port_from_uri_strips_80_from_http_with_path
- uri = "http://example.com:80/foo/bar"
- stripped_uri = FakeWeb::Utility.strip_default_port_from_uri(uri)
- assert_equal "http://example.com/foo/bar", stripped_uri
- end
-
- def test_strip_default_port_from_uri_strips_80_from_http_without_path
- uri = "http://example.com:80"
- stripped_uri = FakeWeb::Utility.strip_default_port_from_uri(uri)
- assert_equal "http://example.com", stripped_uri
- end
-
- def test_strip_default_port_from_uri_strips_443_from_https_without_path
- uri = "https://example.com:443"
- stripped_uri = FakeWeb::Utility.strip_default_port_from_uri(uri)
- assert_equal "https://example.com", stripped_uri
- end
-
- def test_strip_default_port_from_uri_strips_443_from_https
- uri = "https://example.com:443/foo/bar"
- stripped_uri = FakeWeb::Utility.strip_default_port_from_uri(uri)
- assert_equal "https://example.com/foo/bar", stripped_uri
- end
-
- def test_strip_default_port_from_uri_does_not_strip_8080_from_http
- uri = "http://example.com:8080/foo/bar"
- assert_equal uri, FakeWeb::Utility.strip_default_port_from_uri(uri)
- end
-
- def test_strip_default_port_from_uri_does_not_strip_443_from_http
- uri = "http://example.com:443/foo/bar"
- assert_equal uri, FakeWeb::Utility.strip_default_port_from_uri(uri)
- end
-
- def test_strip_default_port_from_uri_does_not_strip_80_from_query_string
- uri = "http://example.com/?a=:80&b=c"
- assert_equal uri, FakeWeb::Utility.strip_default_port_from_uri(uri)
- end
-
- def test_strip_default_port_from_uri_does_not_modify_strings_that_do_not_start_with_http_or_https
- uri = "httpz://example.com:80/"
- assert_equal uri, FakeWeb::Utility.strip_default_port_from_uri(uri)
- end
-
- def test_request_uri_as_string
- http = Net::HTTP.new("www.example.com", 80)
- request = Net::HTTP::Get.new("/index.html")
- expected = "http://www.example.com:80/index.html"
- assert_equal expected, FakeWeb::Utility.request_uri_as_string(http, request)
- end
-
- def test_uri_escape_delegates_to_uri_parser_when_available
- parsing_object = URI.const_defined?(:Parser) ? URI::Parser.any_instance : URI
- parsing_object.expects(:escape).with("string", /unsafe/).returns("escaped")
- assert_equal "escaped", FakeWeb::Utility.uri_escape("string", /unsafe/)
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/History.txt b/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/History.txt
deleted file mode 100644
index e06bcd0a2..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/History.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-== 0.0.1 2007-05-15
-* 1 major enhancement:
- * Initial release
-
-== 0.1.2 2007-06-27
-
-* No major changes.
-
-== 0.1.3 2007-07-09
-
-* No change.
-
-== 0.1.4 2007-08-10
-
-* r1442, todd, 2007-08-07 15:45:24
- * # 373, Add support in right_http_connection for bailing out to a block while
- reading the HTTP response (to support GET streaming...)
-
-* r1411, todd, 2007-08-03 15:14:45
- * # 373, Stream uploads (PUTs) if the source is a file, stream, or anything
- read()-able
-
-== 1.1.0 2007-08-15
-Initial public release
-
-== 1.2.0 2007-10-05
-
-* r1867, konstantin, 2007-10-05 06:19:45
- * # 220, (re)open connection to server if none exists or connection params
- have changed
-
-== 1.2.1
-
-* r2648, konstantin, 01-24-08 11:12:00
- * net_fix.rb moved from right_aws gem to fix the problem with uploading the streamable
- objects to S3
-
-* r2764, konstantin, 02-08-08 00:05:00 +03:00
- * "RightAws: incompatible Net::HTTP monkey-patch" exception is raised if our net_fix
- patch was overriden (by attachment_fu for example, to avoid this load attachment_fu
- before loading the right_http_connection gem).
-
-== 1.2.2
-
-* r3524, konstantin, 2008-04-17 11:35:42 +0400
- * Fixed a problem with incorrect error handling (connection retries always failed).
-
-== 1.2.3
-
-- Added support for setting retry & timeout parameters in the constructor
-- Improve handling of data streams during upload: if there is a failure and a retry, reset
- the seek pointer for the subsequent re-request
-
-== 1.2.4
-
-* r4984, konstantin, 2008-08-11 14:49:18 +0400
- * fixed a bug: <NoMethodError: You have a nil object when you didn't expect it!
- The error occurred while evaluating nil.body_stream>
-
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/Manifest.txt b/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/Manifest.txt
deleted file mode 100644
index 20f193b21..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/Manifest.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-History.txt
-Manifest.txt
-README.txt
-Rakefile
-lib/net_fix.rb
-lib/right_http_connection.rb
-setup.rb
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/README.txt b/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/README.txt
deleted file mode 100644
index 46c97e57a..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/README.txt
+++ /dev/null
@@ -1,54 +0,0 @@
-RightScale::HttpConnection
- by RightScale, Inc.
- www.RightScale.com
-
-== DESCRIPTION:
-
-Rightscale::HttpConnection is a robust HTTP/S library. It implements a retry
-algorithm for low-level network errors.
-
-== FEATURES:
-
-- provides put/get streaming
-- does configurable retries on connect and read timeouts, DNS failures, etc.
-- HTTPS certificate checking
-
-== SYNOPSIS:
-
-
-== REQUIREMENTS:
-
-- 2/11/08: If you use RightScale::HttpConnection in conjunction with attachment_fu, the
- HttpConnection gem must be included (using the require statement) AFTER
- attachment_fu.
- This is due to a conflict between the HttpConnection gem and another
- gem required by attachment_fu.
-
-
-
-== INSTALL:
-
-sudo gem install right_http_connection
-
-== LICENSE:
-
-Copyright (c) 2007-2008 RightScale, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-'Software'), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/Rakefile b/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/Rakefile
deleted file mode 100644
index 0ae50977c..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/Rakefile
+++ /dev/null
@@ -1,103 +0,0 @@
-require 'rubygems'
-require 'rake'
-require 'rake/clean'
-require 'rake/testtask'
-require 'rake/packagetask'
-require 'rake/gempackagetask'
-require 'rake/rdoctask'
-require 'rake/contrib/rubyforgepublisher'
-require 'fileutils'
-require 'hoe'
-include FileUtils
-require File.join(File.dirname(__FILE__), 'lib', 'right_http_connection')
-
-AUTHOR = 'RightScale' # can also be an array of Authors
-EMAIL = "rubygems@rightscale.com"
-DESCRIPTION = "RightScale's robust HTTP/S connection module"
-GEM_NAME = 'right_http_connection' # what ppl will type to install your gem
-RUBYFORGE_PROJECT = 'rightscale' # The unix name for your project
-HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
-DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
-
-NAME = "right_http_connection"
-REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
-VERS = RightHttpConnection::VERSION::STRING + (REV ? ".#{REV}" : "")
-CLEAN.include ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
-RDOC_OPTS = ['--quiet', '--title', 'right_http_connection documentation',
- "--opname", "index.html",
- "--line-numbers",
- "--main", "README",
- "--inline-source"]
-
-# Suppress Hoe's self-inclusion as a dependency for our Gem. This also keeps
-# Rake & rubyforge out of the dependency list. Users must manually install
-# these gems to run tests, etc.
-# TRB 2/19/09: also do this for the extra_dev_deps array present in newer hoes.
-# Older versions of RubyGems will try to install developer-dependencies as
-# required runtime dependencies....
-class Hoe
- def extra_deps
- @extra_deps.reject do |x|
- Array(x).first == 'hoe'
- end
- end
- def extra_dev_deps
- @extra_dev_deps.reject do |x|
- Array(x).first == 'hoe'
- end
- end
-end
-
-# Generate all the Rake tasks
-# Run 'rake -T' to see list of generated tasks (from gem root directory)
-hoe = Hoe.new(GEM_NAME, VERS) do |p|
- p.author = AUTHOR
- p.description = DESCRIPTION
- p.email = EMAIL
- p.summary = DESCRIPTION
- p.url = HOMEPATH
- p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
- p.test_globs = ["test/**/test_*.rb"]
- p.clean_globs = CLEAN #An array of file patterns to delete on clean.
- p.remote_rdoc_dir = "right_http_gem_doc"
-
- # == Optional
- p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
- #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
- #p.spec_extras = {} # A hash of extra values to set in the gemspec.
-end
-
-
-desc 'Generate website files'
-task :website_generate do
- Dir['website/**/*.txt'].each do |txt|
- sh %{ ruby scripts/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
- end
-end
-
-desc 'Upload website files to rubyforge'
-task :website_upload do
- config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
- host = "#{config["username"]}@rubyforge.org"
- remote_dir = "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}/"
- # remote_dir = "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
- local_dir = 'website'
- sh %{rsync -av #{local_dir}/ #{host}:#{remote_dir}}
-end
-
-desc 'Generate and upload website files'
-task :website => [:website_generate, :website_upload]
-
-desc 'Release the website and new gem version'
-task :deploy => [:check_version, :website, :release]
-
-task :check_version do
- unless ENV['VERSION']
- puts 'Must pass a VERSION=x.y.z release version'
- exit
- end
- unless ENV['VERSION'] == VERS
- puts "Please update your version.rb to match the release version, currently #{VERS}"
- exit
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/lib/net_fix.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/lib/net_fix.rb
deleted file mode 100644
index ad54f8a2a..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/lib/net_fix.rb
+++ /dev/null
@@ -1,160 +0,0 @@
-#
-# Copyright (c) 2008 RightScale Inc
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-#
-
-# Net::HTTP and Net::HTTPGenericRequest fixes to support 100-continue on
-# POST and PUT. The request must have 'expect' field set to '100-continue'.
-
-
-module Net
-
- class BufferedIO #:nodoc:
- # Monkey-patch Net::BufferedIO to read > 1024 bytes from the socket at a time
-
- # Default size (in bytes) of the max read from a socket into the user space read buffers for socket IO
- DEFAULT_SOCKET_READ_SIZE = 16*1024
-
- @@socket_read_size = DEFAULT_SOCKET_READ_SIZE
-
- def self.socket_read_size=(readsize)
- if(readsize <= 0)
- return
- end
- @@socket_read_size = readsize
- end
-
- def self.socket_read_size?()
- @@socket_read_size
- end
-
- def rbuf_fill
- timeout(@read_timeout) {
- @rbuf << @io.sysread(@@socket_read_size)
- }
- end
- end
-
-
- #-- Net::HTTPGenericRequest --
-
- class HTTPGenericRequest
- # Monkey-patch Net::HTTPGenericRequest to read > 1024 bytes from the local data
- # source at a time (used in streaming PUTs)
-
- # Default size (in bytes) of the max read from a local source (File, String,
- # etc.) to the user space write buffers for socket IO.
- DEFAULT_LOCAL_READ_SIZE = 16*1024
-
- @@local_read_size = DEFAULT_LOCAL_READ_SIZE
-
- def self.local_read_size=(readsize)
- if(readsize <= 0)
- return
- end
- @@local_read_size = readsize
- end
-
- def self.local_read_size?()
- @@local_read_size
- end
-
- def exec(sock, ver, path, send_only=nil) #:nodoc: internal use only
- if @body
- send_request_with_body sock, ver, path, @body, send_only
- elsif @body_stream
- send_request_with_body_stream sock, ver, path, @body_stream, send_only
- else
- write_header(sock, ver, path)
- end
- end
-
- private
-
- def send_request_with_body(sock, ver, path, body, send_only=nil)
- self.content_length = body.length
- delete 'Transfer-Encoding'
- supply_default_content_type
- write_header(sock, ver, path) unless send_only == :body
- sock.write(body) unless send_only == :header
- end
-
- def send_request_with_body_stream(sock, ver, path, f, send_only=nil)
- unless content_length() or chunked?
- raise ArgumentError,
- "Content-Length not given and Transfer-Encoding is not `chunked'"
- end
- supply_default_content_type
- write_header(sock, ver, path) unless send_only == :body
- unless send_only == :header
- if chunked?
- while s = f.read(@@local_read_size)
- sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
- end
- sock.write "0\r\n\r\n"
- else
- while s = f.read(@@local_read_size)
- sock.write s
- end
- end
- end
- end
- end
-
-
- #-- Net::HTTP --
-
- class HTTP
- def request(req, body = nil, &block) # :yield: +response+
- unless started?
- start {
- req['connection'] ||= 'close'
- return request(req, body, &block)
- }
- end
- if proxy_user()
- unless use_ssl?
- req.proxy_basic_auth proxy_user(), proxy_pass()
- end
- end
- # set body
- req.set_body_internal body
- begin_transport req
- # if we expect 100-continue then send a header first
- send_only = ((req.is_a?(Post)||req.is_a?(Put)) && (req['expect']=='100-continue')) ? :header : nil
- req.exec @socket, @curr_http_version, edit_path(req.path), send_only
- begin
- res = HTTPResponse.read_new(@socket)
- # if we expected 100-continue then send a body
- if res.is_a?(HTTPContinue) && send_only && req['content-length'].to_i > 0
- req.exec @socket, @curr_http_version, edit_path(req.path), :body
- end
- end while res.kind_of?(HTTPContinue)
- res.reading_body(@socket, req.response_body_permitted?) {
- yield res if block_given?
- }
- end_transport req, res
- res
- end
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb
deleted file mode 100644
index 0151ae685..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb
+++ /dev/null
@@ -1,435 +0,0 @@
-#
-# Copyright (c) 2007-2008 RightScale Inc
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-require "net/https"
-require "uri"
-require "time"
-require "logger"
-
-$:.unshift(File.dirname(__FILE__))
-require "net_fix"
-
-
-module RightHttpConnection #:nodoc:
- module VERSION #:nodoc:
- MAJOR = 1
- MINOR = 2
- TINY = 4
-
- STRING = [MAJOR, MINOR, TINY].join('.')
- end
-end
-
-
-module Rightscale
-
-=begin rdoc
-HttpConnection maintains a persistent HTTP connection to a remote
-server. Each instance maintains its own unique connection to the
-HTTP server. HttpConnection makes a best effort to receive a proper
-HTTP response from the server, although it does not guarantee that
-this response contains a HTTP Success code.
-
-On low-level errors (TCP/IP errors) HttpConnection invokes a reconnect
-and retry algorithm. Note that although each HttpConnection object
-has its own connection to the HTTP server, error handling is shared
-across all connections to a server. For example, if there are three
-connections to www.somehttpserver.com, a timeout error on one of those
-connections will cause all three connections to break and reconnect.
-A connection will not break and reconnect, however, unless a request
-becomes active on it within a certain amount of time after the error
-(as specified by HTTP_CONNECTION_RETRY_DELAY). An idle connection will not
-break even if other connections to the same server experience errors.
-
-A HttpConnection will retry a request a certain number of times (as
-defined by HTTP_CONNNECTION_RETRY_COUNT). If all the retries fail,
-an exception is thrown and all HttpConnections associated with a
-server enter a probationary period defined by HTTP_CONNECTION_RETRY_DELAY.
-If the user makes a new request subsequent to entering probation,
-the request will fail immediately with the same exception thrown
-on probation entry. This is so that if the HTTP server has gone
-down, not every subsequent request must wait for a connect timeout
-before failing. After the probation period expires, the internal
-state of the HttpConnection is reset and subsequent requests have
-the full number of potential reconnects and retries available to
-them.
-=end
-
- class HttpConnection
-
- # Number of times to retry the request after encountering the first error
- HTTP_CONNECTION_RETRY_COUNT = 3
- # Throw a Timeout::Error if a connection isn't established within this number of seconds
- HTTP_CONNECTION_OPEN_TIMEOUT = 5
- # Throw a Timeout::Error if no data have been read on this connnection within this number of seconds
- HTTP_CONNECTION_READ_TIMEOUT = 120
- # Length of the post-error probationary period during which all requests will fail
- HTTP_CONNECTION_RETRY_DELAY = 15
-
- #--------------------
- # class methods
- #--------------------
- #
- @@params = {}
- @@params[:http_connection_retry_count] = HTTP_CONNECTION_RETRY_COUNT
- @@params[:http_connection_open_timeout] = HTTP_CONNECTION_OPEN_TIMEOUT
- @@params[:http_connection_read_timeout] = HTTP_CONNECTION_READ_TIMEOUT
- @@params[:http_connection_retry_delay] = HTTP_CONNECTION_RETRY_DELAY
-
- # Query the global (class-level) parameters:
- #
- # :user_agent => 'www.HostName.com' # String to report as HTTP User agent
- # :ca_file => 'path_to_file' # Path to a CA certification file in PEM format. The file can contain several CA certificates. If this parameter isn't set, HTTPS certs won't be verified.
- # :logger => Logger object # If omitted, HttpConnection logs to STDOUT
- # :exception => Exception to raise # The type of exception to raise
- # # if a request repeatedly fails. RuntimeError is raised if this parameter is omitted.
- # :http_connection_retry_count # by default == Rightscale::HttpConnection::HTTP_CONNECTION_RETRY_COUNT
- # :http_connection_open_timeout # by default == Rightscale::HttpConnection::HTTP_CONNECTION_OPEN_TIMEOUT
- # :http_connection_read_timeout # by default == Rightscale::HttpConnection::HTTP_CONNECTION_READ_TIMEOUT
- # :http_connection_retry_delay # by default == Rightscale::HttpConnection::HTTP_CONNECTION_RETRY_DELAY
- def self.params
- @@params
- end
-
- # Set the global (class-level) parameters
- def self.params=(params)
- @@params = params
- end
-
- #------------------
- # instance methods
- #------------------
- attr_accessor :http
- attr_accessor :server
- attr_accessor :params # see @@params
- attr_accessor :logger
-
- # Params hash:
- # :user_agent => 'www.HostName.com' # String to report as HTTP User agent
- # :ca_file => 'path_to_file' # A path of a CA certification file in PEM format. The file can contain several CA certificates.
- # :logger => Logger object # If omitted, HttpConnection logs to STDOUT
- # :exception => Exception to raise # The type of exception to raise if a request repeatedly fails. RuntimeError is raised if this parameter is omitted.
- # :http_connection_retry_count # by default == Rightscale::HttpConnection.params[:http_connection_retry_count]
- # :http_connection_open_timeout # by default == Rightscale::HttpConnection.params[:http_connection_open_timeout]
- # :http_connection_read_timeout # by default == Rightscale::HttpConnection.params[:http_connection_read_timeout]
- # :http_connection_retry_delay # by default == Rightscale::HttpConnection.params[:http_connection_retry_delay]
- #
- def initialize(params={})
- @params = params
- @params[:http_connection_retry_count] ||= @@params[:http_connection_retry_count]
- @params[:http_connection_open_timeout] ||= @@params[:http_connection_open_timeout]
- @params[:http_connection_read_timeout] ||= @@params[:http_connection_read_timeout]
- @params[:http_connection_retry_delay] ||= @@params[:http_connection_retry_delay]
- @http = nil
- @server = nil
- @logger = get_param(:logger) ||
- (RAILS_DEFAULT_LOGGER if defined?(RAILS_DEFAULT_LOGGER)) ||
- Logger.new(STDOUT)
- end
-
- def get_param(name)
- @params[name] || @@params[name]
- end
-
- # Query for the maximum size (in bytes) of a single read from the underlying
- # socket. For bulk transfer, especially over fast links, this is value is
- # critical to performance.
- def socket_read_size?
- Net::BufferedIO.socket_read_size?
- end
-
- # Set the maximum size (in bytes) of a single read from the underlying
- # socket. For bulk transfer, especially over fast links, this is value is
- # critical to performance.
- def socket_read_size=(newsize)
- Net::BufferedIO.socket_read_size=(newsize)
- end
-
- # Query for the maximum size (in bytes) of a single read from local data
- # sources like files. This is important, for example, in a streaming PUT of a
- # large buffer.
- def local_read_size?
- Net::HTTPGenericRequest.local_read_size?
- end
-
- # Set the maximum size (in bytes) of a single read from local data
- # sources like files. This can be used to tune the performance of, for example, a streaming PUT of a
- # large buffer.
- def local_read_size=(newsize)
- Net::HTTPGenericRequest.local_read_size=(newsize)
- end
-
- private
- #--------------
- # Retry state - Keep track of errors on a per-server basis
- #--------------
- @@state = {} # retry state indexed by server: consecutive error count, error time, and error
- @@eof = {}
-
- # number of consecutive errors seen for server, 0 all is ok
- def error_count
- @@state[@server] ? @@state[@server][:count] : 0
- end
-
- # time of last error for server, nil if all is ok
- def error_time
- @@state[@server] && @@state[@server][:time]
- end
-
- # message for last error for server, "" if all is ok
- def error_message
- @@state[@server] ? @@state[@server][:message] : ""
- end
-
- # add an error for a server
- def error_add(message)
- @@state[@server] = { :count => error_count+1, :time => Time.now, :message => message }
- end
-
- # reset the error state for a server (i.e. a request succeeded)
- def error_reset
- @@state.delete(@server)
- end
-
- # Error message stuff...
- def banana_message
- return "#{@server} temporarily unavailable: (#{error_message})"
- end
-
- def err_header
- return "#{self.class.name} :"
- end
-
- # Adds new EOF timestamp.
- # Returns the number of seconds to wait before new conection retry:
- # 0.5, 1, 2, 4, 8
- def add_eof
- (@@eof[@server] ||= []).unshift Time.now
- 0.25 * 2 ** @@eof[@server].size
- end
-
- # Returns first EOF timestamp or nul if have no EOFs being tracked.
- def eof_time
- @@eof[@server] && @@eof[@server].last
- end
-
- # Returns true if we are receiving EOFs during last @params[:http_connection_retry_delay] seconds
- # and there were no successful response from server
- def raise_on_eof_exception?
- @@eof[@server].blank? ? false : ( (Time.now.to_i-@params[:http_connection_retry_delay]) > @@eof[@server].last.to_i )
- end
-
- # Reset a list of EOFs for this server.
- # This is being called when we have got an successful response from server.
- def eof_reset
- @@eof.delete(@server)
- end
-
- # Detects if an object is 'streamable' - can we read from it, and can we know the size?
- def setup_streaming(request)
- if(request.body && request.body.respond_to?(:read))
- body = request.body
- request.content_length = body.respond_to?(:lstat) ? body.lstat.size : body.size
- request.body_stream = request.body
- true
- end
- end
-
- def get_fileptr_offset(request_params)
- request_params[:request].body.pos
- rescue Exception => e
- # Probably caught this because the body doesn't support the pos() method, like if it is a socket.
- # Just return 0 and get on with life.
- 0
- end
-
- def reset_fileptr_offset(request, offset = 0)
- if(request.body_stream && request.body_stream.respond_to?(:pos))
- begin
- request.body_stream.pos = offset
- rescue Exception => e
- @logger.warn("Failed file pointer reset; aborting HTTP retries." +
- " -- #{err_header} #{e.inspect}")
- raise e
- end
- end
- end
-
- # Start a fresh connection. The object closes any existing connection and
- # opens a new one.
- def start(request_params)
- # close the previous if exists
- finish
- # create new connection
- @server = request_params[:server]
- @port = request_params[:port]
- @protocol = request_params[:protocol]
-
- @logger.info("Opening new #{@protocol.upcase} connection to #@server:#@port")
- @http = Net::HTTP.new(@server, @port)
- @http.open_timeout = @params[:http_connection_open_timeout]
- @http.read_timeout = @params[:http_connection_read_timeout]
-
- if @protocol == 'https'
- verifyCallbackProc = Proc.new{ |ok, x509_store_ctx|
- code = x509_store_ctx.error
- msg = x509_store_ctx.error_string
- #debugger
- @logger.warn("##### #{@server} certificate verify failed: #{msg}") unless code == 0
- true
- }
- @http.use_ssl = true
- ca_file = get_param(:ca_file)
- if ca_file
- @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
- @http.verify_callback = verifyCallbackProc
- @http.ca_file = ca_file
- end
- end
- # open connection
- @http.start
- end
-
- public
-
-=begin rdoc
- Send HTTP request to server
-
- request_params hash:
- :server => 'www.HostName.com' # Hostname or IP address of HTTP server
- :port => '80' # Port of HTTP server
- :protocol => 'https' # http and https are supported on any port
- :request => 'requeststring' # Fully-formed HTTP request to make
-
- Raises RuntimeError, Interrupt, and params[:exception] (if specified in new).
-
-=end
- def request(request_params, &block)
- # We save the offset here so that if we need to retry, we can return the file pointer to its initial position
- mypos = get_fileptr_offset(request_params)
- loop do
- # if we are inside a delay between retries: no requests this time!
- if error_count > @params[:http_connection_retry_count] &&
- error_time + @params[:http_connection_retry_delay] > Time.now
- # store the message (otherwise it will be lost after error_reset and
- # we will raise an exception with an empty text)
- banana_message_text = banana_message
- @logger.warn("#{err_header} re-raising same error: #{banana_message_text} " +
- "-- error count: #{error_count}, error age: #{Time.now.to_i - error_time.to_i}")
- exception = get_param(:exception) || RuntimeError
- raise exception.new(banana_message_text)
- end
-
- # try to connect server(if connection does not exist) and get response data
- begin
- request_params[:protocol] ||= (request_params[:port] == 443 ? 'https' : 'http')
-
- request = request_params[:request]
- request['User-Agent'] = get_param(:user_agent) || ''
-
- # (re)open connection to server if none exists or params has changed
- unless @http &&
- @http.started? &&
- @server == request_params[:server] &&
- @port == request_params[:port] &&
- @protocol == request_params[:protocol]
- start(request_params)
- end
-
- # Detect if the body is a streamable object like a file or socket. If so, stream that
- # bad boy.
- setup_streaming(request)
- response = @http.request(request, &block)
-
- error_reset
- eof_reset
- return response
-
- # We treat EOF errors and the timeout/network errors differently. Both
- # are tracked in different statistics blocks. Note below that EOF
- # errors will sleep for a certain (exponentially increasing) period.
- # Other errors don't sleep because there is already an inherent delay
- # in them; connect and read timeouts (for example) have already
- # 'slept'. It is still not clear which way we should treat errors
- # like RST and resolution failures. For now, there is no additional
- # delay for these errors although this may change in the future.
-
- # EOFError means the server closed the connection on us.
- rescue EOFError => e
- @logger.debug("#{err_header} server #{@server} closed connection")
- @http = nil
-
- # if we have waited long enough - raise an exception...
- if raise_on_eof_exception?
- exception = get_param(:exception) || RuntimeError
- @logger.warn("#{err_header} raising #{exception} due to permanent EOF being received from #{@server}, error age: #{Time.now.to_i - eof_time.to_i}")
- raise exception.new("Permanent EOF is being received from #{@server}.")
- else
- # ... else just sleep a bit before new retry
- sleep(add_eof)
- # We will be retrying the request, so reset the file pointer
- reset_fileptr_offset(request, mypos)
- end
- rescue Exception => e # See comment at bottom for the list of errors seen...
- @http = nil
- # if ctrl+c is pressed - we have to reraise exception to terminate proggy
- if e.is_a?(Interrupt) && !( e.is_a?(Errno::ETIMEDOUT) || e.is_a?(Timeout::Error))
- @logger.debug( "#{err_header} request to server #{@server} interrupted by ctrl-c")
- raise
- elsif e.is_a?(ArgumentError) && e.message.include?('wrong number of arguments (5 for 4)')
- # seems our net_fix patch was overriden...
- exception = get_param(:exception) || RuntimeError
- raise exception.new('incompatible Net::HTTP monkey-patch')
- end
- # oops - we got a banana: log it
- error_add(e.message)
- @logger.warn("#{err_header} request failure count: #{error_count}, exception: #{e.inspect}")
-
- # We will be retrying the request, so reset the file pointer
- reset_fileptr_offset(request, mypos)
-
- end
- end
- end
-
- def finish(reason = '')
- if @http && @http.started?
- reason = ", reason: '#{reason}'" unless reason.blank?
- @logger.info("Closing #{@http.use_ssl? ? 'HTTPS' : 'HTTP'} connection to #{@http.address}:#{@http.port}#{reason}")
- @http.finish
- end
- end
-
- # Errors received during testing:
- #
- # #<Timeout::Error: execution expired>
- # #<Errno::ETIMEDOUT: Connection timed out - connect(2)>
- # #<SocketError: getaddrinfo: Name or service not known>
- # #<SocketError: getaddrinfo: Temporary failure in name resolution>
- # #<EOFError: end of file reached>
- # #<Errno::ECONNRESET: Connection reset by peer>
- # #<OpenSSL::SSL::SSLError: SSL_write:: bad write retry>
- end
-
-end
-
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/setup.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/setup.rb
deleted file mode 100644
index 424a5f37c..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/right_http_connection-1.2.4/setup.rb
+++ /dev/null
@@ -1,1585 +0,0 @@
-#
-# setup.rb
-#
-# Copyright (c) 2000-2005 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the terms of
-# the GNU LGPL, Lesser General Public License version 2.1.
-#
-
-unless Enumerable.method_defined?(:map) # Ruby 1.4.6
- module Enumerable
- alias map collect
- end
-end
-
-unless File.respond_to?(:read) # Ruby 1.6
- def File.read(fname)
- open(fname) {|f|
- return f.read
- }
- end
-end
-
-unless Errno.const_defined?(:ENOTEMPTY) # Windows?
- module Errno
- class ENOTEMPTY
- # We do not raise this exception, implementation is not needed.
- end
- end
-end
-
-def File.binread(fname)
- open(fname, 'rb') {|f|
- return f.read
- }
-end
-
-# for corrupted Windows' stat(2)
-def File.dir?(path)
- File.directory?((path[-1,1] == '/') ? path : path + '/')
-end
-
-
-class ConfigTable
-
- include Enumerable
-
- def initialize(rbconfig)
- @rbconfig = rbconfig
- @items = []
- @table = {}
- # options
- @install_prefix = nil
- @config_opt = nil
- @verbose = true
- @no_harm = false
- end
-
- attr_accessor :install_prefix
- attr_accessor :config_opt
-
- attr_writer :verbose
-
- def verbose?
- @verbose
- end
-
- attr_writer :no_harm
-
- def no_harm?
- @no_harm
- end
-
- def [](key)
- lookup(key).resolve(self)
- end
-
- def []=(key, val)
- lookup(key).set val
- end
-
- def names
- @items.map {|i| i.name }
- end
-
- def each(&block)
- @items.each(&block)
- end
-
- def key?(name)
- @table.key?(name)
- end
-
- def lookup(name)
- @table[name] or setup_rb_error "no such config item: #{name}"
- end
-
- def add(item)
- @items.push item
- @table[item.name] = item
- end
-
- def remove(name)
- item = lookup(name)
- @items.delete_if {|i| i.name == name }
- @table.delete_if {|name, i| i.name == name }
- item
- end
-
- def load_script(path, inst = nil)
- if File.file?(path)
- MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
- end
- end
-
- def savefile
- '.config'
- end
-
- def load_savefile
- begin
- File.foreach(savefile()) do |line|
- k, v = *line.split(/=/, 2)
- self[k] = v.strip
- end
- rescue Errno::ENOENT
- setup_rb_error $!.message + "\n#{File.basename($0)} config first"
- end
- end
-
- def save
- @items.each {|i| i.value }
- File.open(savefile(), 'w') {|f|
- @items.each do |i|
- f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
- end
- }
- end
-
- def load_standard_entries
- standard_entries(@rbconfig).each do |ent|
- add ent
- end
- end
-
- def standard_entries(rbconfig)
- c = rbconfig
-
- rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
-
- major = c['MAJOR'].to_i
- minor = c['MINOR'].to_i
- teeny = c['TEENY'].to_i
- version = "#{major}.#{minor}"
-
- # ruby ver. >= 1.4.4?
- newpath_p = ((major >= 2) or
- ((major == 1) and
- ((minor >= 5) or
- ((minor == 4) and (teeny >= 4)))))
-
- if c['rubylibdir']
- # V > 1.6.3
- libruby = "#{c['prefix']}/lib/ruby"
- librubyver = c['rubylibdir']
- librubyverarch = c['archdir']
- siteruby = c['sitedir']
- siterubyver = c['sitelibdir']
- siterubyverarch = c['sitearchdir']
- elsif newpath_p
- # 1.4.4 <= V <= 1.6.3
- libruby = "#{c['prefix']}/lib/ruby"
- librubyver = "#{c['prefix']}/lib/ruby/#{version}"
- librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
- siteruby = c['sitedir']
- siterubyver = "$siteruby/#{version}"
- siterubyverarch = "$siterubyver/#{c['arch']}"
- else
- # V < 1.4.4
- libruby = "#{c['prefix']}/lib/ruby"
- librubyver = "#{c['prefix']}/lib/ruby/#{version}"
- librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
- siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
- siterubyver = siteruby
- siterubyverarch = "$siterubyver/#{c['arch']}"
- end
- parameterize = lambda {|path|
- path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
- }
-
- if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
- makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
- else
- makeprog = 'make'
- end
-
- [
- ExecItem.new('installdirs', 'std/site/home',
- 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
- {|val, table|
- case val
- when 'std'
- table['rbdir'] = '$librubyver'
- table['sodir'] = '$librubyverarch'
- when 'site'
- table['rbdir'] = '$siterubyver'
- table['sodir'] = '$siterubyverarch'
- when 'home'
- setup_rb_error '$HOME was not set' unless ENV['HOME']
- table['prefix'] = ENV['HOME']
- table['rbdir'] = '$libdir/ruby'
- table['sodir'] = '$libdir/ruby'
- end
- },
- PathItem.new('prefix', 'path', c['prefix'],
- 'path prefix of target environment'),
- PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
- 'the directory for commands'),
- PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
- 'the directory for libraries'),
- PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
- 'the directory for shared data'),
- PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
- 'the directory for man pages'),
- PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
- 'the directory for system configuration files'),
- PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
- 'the directory for local state data'),
- PathItem.new('libruby', 'path', libruby,
- 'the directory for ruby libraries'),
- PathItem.new('librubyver', 'path', librubyver,
- 'the directory for standard ruby libraries'),
- PathItem.new('librubyverarch', 'path', librubyverarch,
- 'the directory for standard ruby extensions'),
- PathItem.new('siteruby', 'path', siteruby,
- 'the directory for version-independent aux ruby libraries'),
- PathItem.new('siterubyver', 'path', siterubyver,
- 'the directory for aux ruby libraries'),
- PathItem.new('siterubyverarch', 'path', siterubyverarch,
- 'the directory for aux ruby binaries'),
- PathItem.new('rbdir', 'path', '$siterubyver',
- 'the directory for ruby scripts'),
- PathItem.new('sodir', 'path', '$siterubyverarch',
- 'the directory for ruby extentions'),
- PathItem.new('rubypath', 'path', rubypath,
- 'the path to set to #! line'),
- ProgramItem.new('rubyprog', 'name', rubypath,
- 'the ruby program using for installation'),
- ProgramItem.new('makeprog', 'name', makeprog,
- 'the make program to compile ruby extentions'),
- SelectItem.new('shebang', 'all/ruby/never', 'ruby',
- 'shebang line (#!) editing mode'),
- BoolItem.new('without-ext', 'yes/no', 'no',
- 'does not compile/install ruby extentions')
- ]
- end
- private :standard_entries
-
- def load_multipackage_entries
- multipackage_entries().each do |ent|
- add ent
- end
- end
-
- def multipackage_entries
- [
- PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
- 'package names that you want to install'),
- PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
- 'package names that you do not want to install')
- ]
- end
- private :multipackage_entries
-
- ALIASES = {
- 'std-ruby' => 'librubyver',
- 'stdruby' => 'librubyver',
- 'rubylibdir' => 'librubyver',
- 'archdir' => 'librubyverarch',
- 'site-ruby-common' => 'siteruby', # For backward compatibility
- 'site-ruby' => 'siterubyver', # For backward compatibility
- 'bin-dir' => 'bindir',
- 'bin-dir' => 'bindir',
- 'rb-dir' => 'rbdir',
- 'so-dir' => 'sodir',
- 'data-dir' => 'datadir',
- 'ruby-path' => 'rubypath',
- 'ruby-prog' => 'rubyprog',
- 'ruby' => 'rubyprog',
- 'make-prog' => 'makeprog',
- 'make' => 'makeprog'
- }
-
- def fixup
- ALIASES.each do |ali, name|
- @table[ali] = @table[name]
- end
- @items.freeze
- @table.freeze
- @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
- end
-
- def parse_opt(opt)
- m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
- m.to_a[1,2]
- end
-
- def dllext
- @rbconfig['DLEXT']
- end
-
- def value_config?(name)
- lookup(name).value?
- end
-
- class Item
- def initialize(name, template, default, desc)
- @name = name.freeze
- @template = template
- @value = default
- @default = default
- @description = desc
- end
-
- attr_reader :name
- attr_reader :description
-
- attr_accessor :default
- alias help_default default
-
- def help_opt
- "--#{@name}=#{@template}"
- end
-
- def value?
- true
- end
-
- def value
- @value
- end
-
- def resolve(table)
- @value.gsub(%r<\$([^/]+)>) { table[$1] }
- end
-
- def set(val)
- @value = check(val)
- end
-
- private
-
- def check(val)
- setup_rb_error "config: --#{name} requires argument" unless val
- val
- end
- end
-
- class BoolItem < Item
- def config_type
- 'bool'
- end
-
- def help_opt
- "--#{@name}"
- end
-
- private
-
- def check(val)
- return 'yes' unless val
- case val
- when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
- when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
- else
- setup_rb_error "config: --#{@name} accepts only yes/no for argument"
- end
- end
- end
-
- class PathItem < Item
- def config_type
- 'path'
- end
-
- private
-
- def check(path)
- setup_rb_error "config: --#{@name} requires argument" unless path
- path[0,1] == '$' ? path : File.expand_path(path)
- end
- end
-
- class ProgramItem < Item
- def config_type
- 'program'
- end
- end
-
- class SelectItem < Item
- def initialize(name, selection, default, desc)
- super
- @ok = selection.split('/')
- end
-
- def config_type
- 'select'
- end
-
- private
-
- def check(val)
- unless @ok.include?(val.strip)
- setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
- end
- val.strip
- end
- end
-
- class ExecItem < Item
- def initialize(name, selection, desc, &block)
- super name, selection, nil, desc
- @ok = selection.split('/')
- @action = block
- end
-
- def config_type
- 'exec'
- end
-
- def value?
- false
- end
-
- def resolve(table)
- setup_rb_error "$#{name()} wrongly used as option value"
- end
-
- undef set
-
- def evaluate(val, table)
- v = val.strip.downcase
- unless @ok.include?(v)
- setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
- end
- @action.call v, table
- end
- end
-
- class PackageSelectionItem < Item
- def initialize(name, template, default, help_default, desc)
- super name, template, default, desc
- @help_default = help_default
- end
-
- attr_reader :help_default
-
- def config_type
- 'package'
- end
-
- private
-
- def check(val)
- unless File.dir?("packages/#{val}")
- setup_rb_error "config: no such package: #{val}"
- end
- val
- end
- end
-
- class MetaConfigEnvironment
- def initialize(config, installer)
- @config = config
- @installer = installer
- end
-
- def config_names
- @config.names
- end
-
- def config?(name)
- @config.key?(name)
- end
-
- def bool_config?(name)
- @config.lookup(name).config_type == 'bool'
- end
-
- def path_config?(name)
- @config.lookup(name).config_type == 'path'
- end
-
- def value_config?(name)
- @config.lookup(name).config_type != 'exec'
- end
-
- def add_config(item)
- @config.add item
- end
-
- def add_bool_config(name, default, desc)
- @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
- end
-
- def add_path_config(name, default, desc)
- @config.add PathItem.new(name, 'path', default, desc)
- end
-
- def set_config_default(name, default)
- @config.lookup(name).default = default
- end
-
- def remove_config(name)
- @config.remove(name)
- end
-
- # For only multipackage
- def packages
- raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
- @installer.packages
- end
-
- # For only multipackage
- def declare_packages(list)
- raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
- @installer.packages = list
- end
- end
-
-end # class ConfigTable
-
-
-# This module requires: #verbose?, #no_harm?
-module FileOperations
-
- def mkdir_p(dirname, prefix = nil)
- dirname = prefix + File.expand_path(dirname) if prefix
- $stderr.puts "mkdir -p #{dirname}" if verbose?
- return if no_harm?
-
- # Does not check '/', it's too abnormal.
- dirs = File.expand_path(dirname).split(%r<(?=/)>)
- if /\A[a-z]:\z/i =~ dirs[0]
- disk = dirs.shift
- dirs[0] = disk + dirs[0]
- end
- dirs.each_index do |idx|
- path = dirs[0..idx].join('')
- Dir.mkdir path unless File.dir?(path)
- end
- end
-
- def rm_f(path)
- $stderr.puts "rm -f #{path}" if verbose?
- return if no_harm?
- force_remove_file path
- end
-
- def rm_rf(path)
- $stderr.puts "rm -rf #{path}" if verbose?
- return if no_harm?
- remove_tree path
- end
-
- def remove_tree(path)
- if File.symlink?(path)
- remove_file path
- elsif File.dir?(path)
- remove_tree0 path
- else
- force_remove_file path
- end
- end
-
- def remove_tree0(path)
- Dir.foreach(path) do |ent|
- next if ent == '.'
- next if ent == '..'
- entpath = "#{path}/#{ent}"
- if File.symlink?(entpath)
- remove_file entpath
- elsif File.dir?(entpath)
- remove_tree0 entpath
- else
- force_remove_file entpath
- end
- end
- begin
- Dir.rmdir path
- rescue Errno::ENOTEMPTY
- # directory may not be empty
- end
- end
-
- def move_file(src, dest)
- force_remove_file dest
- begin
- File.rename src, dest
- rescue
- File.open(dest, 'wb') {|f|
- f.write File.binread(src)
- }
- File.chmod File.stat(src).mode, dest
- File.unlink src
- end
- end
-
- def force_remove_file(path)
- begin
- remove_file path
- rescue
- end
- end
-
- def remove_file(path)
- File.chmod 0777, path
- File.unlink path
- end
-
- def install(from, dest, mode, prefix = nil)
- $stderr.puts "install #{from} #{dest}" if verbose?
- return if no_harm?
-
- realdest = prefix ? prefix + File.expand_path(dest) : dest
- realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
- str = File.binread(from)
- if diff?(str, realdest)
- verbose_off {
- rm_f realdest if File.exist?(realdest)
- }
- File.open(realdest, 'wb') {|f|
- f.write str
- }
- File.chmod mode, realdest
-
- File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
- if prefix
- f.puts realdest.sub(prefix, '')
- else
- f.puts realdest
- end
- }
- end
- end
-
- def diff?(new_content, path)
- return true unless File.exist?(path)
- new_content != File.binread(path)
- end
-
- def command(*args)
- $stderr.puts args.join(' ') if verbose?
- system(*args) or raise RuntimeError,
- "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
- end
-
- def ruby(*args)
- command config('rubyprog'), *args
- end
-
- def make(task = nil)
- command(*[config('makeprog'), task].compact)
- end
-
- def extdir?(dir)
- File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
- end
-
- def files_of(dir)
- Dir.open(dir) {|d|
- return d.select {|ent| File.file?("#{dir}/#{ent}") }
- }
- end
-
- DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
-
- def directories_of(dir)
- Dir.open(dir) {|d|
- return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
- }
- end
-
-end
-
-
-# This module requires: #srcdir_root, #objdir_root, #relpath
-module HookScriptAPI
-
- def get_config(key)
- @config[key]
- end
-
- alias config get_config
-
- # obsolete: use metaconfig to change configuration
- def set_config(key, val)
- @config[key] = val
- end
-
- #
- # srcdir/objdir (works only in the package directory)
- #
-
- def curr_srcdir
- "#{srcdir_root()}/#{relpath()}"
- end
-
- def curr_objdir
- "#{objdir_root()}/#{relpath()}"
- end
-
- def srcfile(path)
- "#{curr_srcdir()}/#{path}"
- end
-
- def srcexist?(path)
- File.exist?(srcfile(path))
- end
-
- def srcdirectory?(path)
- File.dir?(srcfile(path))
- end
-
- def srcfile?(path)
- File.file?(srcfile(path))
- end
-
- def srcentries(path = '.')
- Dir.open("#{curr_srcdir()}/#{path}") {|d|
- return d.to_a - %w(. ..)
- }
- end
-
- def srcfiles(path = '.')
- srcentries(path).select {|fname|
- File.file?(File.join(curr_srcdir(), path, fname))
- }
- end
-
- def srcdirectories(path = '.')
- srcentries(path).select {|fname|
- File.dir?(File.join(curr_srcdir(), path, fname))
- }
- end
-
-end
-
-
-class ToplevelInstaller
-
- Version = '3.4.1'
- Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
-
- TASKS = [
- [ 'all', 'do config, setup, then install' ],
- [ 'config', 'saves your configurations' ],
- [ 'show', 'shows current configuration' ],
- [ 'setup', 'compiles ruby extentions and others' ],
- [ 'install', 'installs files' ],
- [ 'test', 'run all tests in test/' ],
- [ 'clean', "does `make clean' for each extention" ],
- [ 'distclean',"does `make distclean' for each extention" ]
- ]
-
- def ToplevelInstaller.invoke
- config = ConfigTable.new(load_rbconfig())
- config.load_standard_entries
- config.load_multipackage_entries if multipackage?
- config.fixup
- klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
- klass.new(File.dirname($0), config).invoke
- end
-
- def ToplevelInstaller.multipackage?
- File.dir?(File.dirname($0) + '/packages')
- end
-
- def ToplevelInstaller.load_rbconfig
- if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
- ARGV.delete(arg)
- load File.expand_path(arg.split(/=/, 2)[1])
- $".push 'rbconfig.rb'
- else
- require 'rbconfig'
- end
- ::Config::CONFIG
- end
-
- def initialize(ardir_root, config)
- @ardir = File.expand_path(ardir_root)
- @config = config
- # cache
- @valid_task_re = nil
- end
-
- def config(key)
- @config[key]
- end
-
- def inspect
- "#<#{self.class} #{__id__()}>"
- end
-
- def invoke
- run_metaconfigs
- case task = parsearg_global()
- when nil, 'all'
- parsearg_config
- init_installers
- exec_config
- exec_setup
- exec_install
- else
- case task
- when 'config', 'test'
- ;
- when 'clean', 'distclean'
- @config.load_savefile if File.exist?(@config.savefile)
- else
- @config.load_savefile
- end
- __send__ "parsearg_#{task}"
- init_installers
- __send__ "exec_#{task}"
- end
- end
-
- def run_metaconfigs
- @config.load_script "#{@ardir}/metaconfig"
- end
-
- def init_installers
- @installer = Installer.new(@config, @ardir, File.expand_path('.'))
- end
-
- #
- # Hook Script API bases
- #
-
- def srcdir_root
- @ardir
- end
-
- def objdir_root
- '.'
- end
-
- def relpath
- '.'
- end
-
- #
- # Option Parsing
- #
-
- def parsearg_global
- while arg = ARGV.shift
- case arg
- when /\A\w+\z/
- setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
- return arg
- when '-q', '--quiet'
- @config.verbose = false
- when '--verbose'
- @config.verbose = true
- when '--help'
- print_usage $stdout
- exit 0
- when '--version'
- puts "#{File.basename($0)} version #{Version}"
- exit 0
- when '--copyright'
- puts Copyright
- exit 0
- else
- setup_rb_error "unknown global option '#{arg}'"
- end
- end
- nil
- end
-
- def valid_task?(t)
- valid_task_re() =~ t
- end
-
- def valid_task_re
- @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
- end
-
- def parsearg_no_options
- unless ARGV.empty?
- task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
- setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
- end
- end
-
- alias parsearg_show parsearg_no_options
- alias parsearg_setup parsearg_no_options
- alias parsearg_test parsearg_no_options
- alias parsearg_clean parsearg_no_options
- alias parsearg_distclean parsearg_no_options
-
- def parsearg_config
- evalopt = []
- set = []
- @config.config_opt = []
- while i = ARGV.shift
- if /\A--?\z/ =~ i
- @config.config_opt = ARGV.dup
- break
- end
- name, value = *@config.parse_opt(i)
- if @config.value_config?(name)
- @config[name] = value
- else
- evalopt.push [name, value]
- end
- set.push name
- end
- evalopt.each do |name, value|
- @config.lookup(name).evaluate value, @config
- end
- # Check if configuration is valid
- set.each do |n|
- @config[n] if @config.value_config?(n)
- end
- end
-
- def parsearg_install
- @config.no_harm = false
- @config.install_prefix = ''
- while a = ARGV.shift
- case a
- when '--no-harm'
- @config.no_harm = true
- when /\A--prefix=/
- path = a.split(/=/, 2)[1]
- path = File.expand_path(path) unless path[0,1] == '/'
- @config.install_prefix = path
- else
- setup_rb_error "install: unknown option #{a}"
- end
- end
- end
-
- def print_usage(out)
- out.puts 'Typical Installation Procedure:'
- out.puts " $ ruby #{File.basename $0} config"
- out.puts " $ ruby #{File.basename $0} setup"
- out.puts " # ruby #{File.basename $0} install (may require root privilege)"
- out.puts
- out.puts 'Detailed Usage:'
- out.puts " ruby #{File.basename $0} <global option>"
- out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
-
- fmt = " %-24s %s\n"
- out.puts
- out.puts 'Global options:'
- out.printf fmt, '-q,--quiet', 'suppress message outputs'
- out.printf fmt, ' --verbose', 'output messages verbosely'
- out.printf fmt, ' --help', 'print this message'
- out.printf fmt, ' --version', 'print version and quit'
- out.printf fmt, ' --copyright', 'print copyright and quit'
- out.puts
- out.puts 'Tasks:'
- TASKS.each do |name, desc|
- out.printf fmt, name, desc
- end
-
- fmt = " %-24s %s [%s]\n"
- out.puts
- out.puts 'Options for CONFIG or ALL:'
- @config.each do |item|
- out.printf fmt, item.help_opt, item.description, item.help_default
- end
- out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
- out.puts
- out.puts 'Options for INSTALL:'
- out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
- out.printf fmt, '--prefix=path', 'install path prefix', ''
- out.puts
- end
-
- #
- # Task Handlers
- #
-
- def exec_config
- @installer.exec_config
- @config.save # must be final
- end
-
- def exec_setup
- @installer.exec_setup
- end
-
- def exec_install
- @installer.exec_install
- end
-
- def exec_test
- @installer.exec_test
- end
-
- def exec_show
- @config.each do |i|
- printf "%-20s %s\n", i.name, i.value if i.value?
- end
- end
-
- def exec_clean
- @installer.exec_clean
- end
-
- def exec_distclean
- @installer.exec_distclean
- end
-
-end # class ToplevelInstaller
-
-
-class ToplevelInstallerMulti < ToplevelInstaller
-
- include FileOperations
-
- def initialize(ardir_root, config)
- super
- @packages = directories_of("#{@ardir}/packages")
- raise 'no package exists' if @packages.empty?
- @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
- end
-
- def run_metaconfigs
- @config.load_script "#{@ardir}/metaconfig", self
- @packages.each do |name|
- @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
- end
- end
-
- attr_reader :packages
-
- def packages=(list)
- raise 'package list is empty' if list.empty?
- list.each do |name|
- raise "directory packages/#{name} does not exist"\
- unless File.dir?("#{@ardir}/packages/#{name}")
- end
- @packages = list
- end
-
- def init_installers
- @installers = {}
- @packages.each do |pack|
- @installers[pack] = Installer.new(@config,
- "#{@ardir}/packages/#{pack}",
- "packages/#{pack}")
- end
- with = extract_selection(config('with'))
- without = extract_selection(config('without'))
- @selected = @installers.keys.select {|name|
- (with.empty? or with.include?(name)) \
- and not without.include?(name)
- }
- end
-
- def extract_selection(list)
- a = list.split(/,/)
- a.each do |name|
- setup_rb_error "no such package: #{name}" unless @installers.key?(name)
- end
- a
- end
-
- def print_usage(f)
- super
- f.puts 'Inluded packages:'
- f.puts ' ' + @packages.sort.join(' ')
- f.puts
- end
-
- #
- # Task Handlers
- #
-
- def exec_config
- run_hook 'pre-config'
- each_selected_installers {|inst| inst.exec_config }
- run_hook 'post-config'
- @config.save # must be final
- end
-
- def exec_setup
- run_hook 'pre-setup'
- each_selected_installers {|inst| inst.exec_setup }
- run_hook 'post-setup'
- end
-
- def exec_install
- run_hook 'pre-install'
- each_selected_installers {|inst| inst.exec_install }
- run_hook 'post-install'
- end
-
- def exec_test
- run_hook 'pre-test'
- each_selected_installers {|inst| inst.exec_test }
- run_hook 'post-test'
- end
-
- def exec_clean
- rm_f @config.savefile
- run_hook 'pre-clean'
- each_selected_installers {|inst| inst.exec_clean }
- run_hook 'post-clean'
- end
-
- def exec_distclean
- rm_f @config.savefile
- run_hook 'pre-distclean'
- each_selected_installers {|inst| inst.exec_distclean }
- run_hook 'post-distclean'
- end
-
- #
- # lib
- #
-
- def each_selected_installers
- Dir.mkdir 'packages' unless File.dir?('packages')
- @selected.each do |pack|
- $stderr.puts "Processing the package `#{pack}' ..." if verbose?
- Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
- Dir.chdir "packages/#{pack}"
- yield @installers[pack]
- Dir.chdir '../..'
- end
- end
-
- def run_hook(id)
- @root_installer.run_hook id
- end
-
- # module FileOperations requires this
- def verbose?
- @config.verbose?
- end
-
- # module FileOperations requires this
- def no_harm?
- @config.no_harm?
- end
-
-end # class ToplevelInstallerMulti
-
-
-class Installer
-
- FILETYPES = %w( bin lib ext data conf man )
-
- include FileOperations
- include HookScriptAPI
-
- def initialize(config, srcroot, objroot)
- @config = config
- @srcdir = File.expand_path(srcroot)
- @objdir = File.expand_path(objroot)
- @currdir = '.'
- end
-
- def inspect
- "#<#{self.class} #{File.basename(@srcdir)}>"
- end
-
- def noop(rel)
- end
-
- #
- # Hook Script API base methods
- #
-
- def srcdir_root
- @srcdir
- end
-
- def objdir_root
- @objdir
- end
-
- def relpath
- @currdir
- end
-
- #
- # Config Access
- #
-
- # module FileOperations requires this
- def verbose?
- @config.verbose?
- end
-
- # module FileOperations requires this
- def no_harm?
- @config.no_harm?
- end
-
- def verbose_off
- begin
- save, @config.verbose = @config.verbose?, false
- yield
- ensure
- @config.verbose = save
- end
- end
-
- #
- # TASK config
- #
-
- def exec_config
- exec_task_traverse 'config'
- end
-
- alias config_dir_bin noop
- alias config_dir_lib noop
-
- def config_dir_ext(rel)
- extconf if extdir?(curr_srcdir())
- end
-
- alias config_dir_data noop
- alias config_dir_conf noop
- alias config_dir_man noop
-
- def extconf
- ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
- end
-
- #
- # TASK setup
- #
-
- def exec_setup
- exec_task_traverse 'setup'
- end
-
- def setup_dir_bin(rel)
- files_of(curr_srcdir()).each do |fname|
- update_shebang_line "#{curr_srcdir()}/#{fname}"
- end
- end
-
- alias setup_dir_lib noop
-
- def setup_dir_ext(rel)
- make if extdir?(curr_srcdir())
- end
-
- alias setup_dir_data noop
- alias setup_dir_conf noop
- alias setup_dir_man noop
-
- def update_shebang_line(path)
- return if no_harm?
- return if config('shebang') == 'never'
- old = Shebang.load(path)
- if old
- $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1
- new = new_shebang(old)
- return if new.to_s == old.to_s
- else
- return unless config('shebang') == 'all'
- new = Shebang.new(config('rubypath'))
- end
- $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
- open_atomic_writer(path) {|output|
- File.open(path, 'rb') {|f|
- f.gets if old # discard
- output.puts new.to_s
- output.print f.read
- }
- }
- end
-
- def new_shebang(old)
- if /\Aruby/ =~ File.basename(old.cmd)
- Shebang.new(config('rubypath'), old.args)
- elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
- Shebang.new(config('rubypath'), old.args[1..-1])
- else
- return old unless config('shebang') == 'all'
- Shebang.new(config('rubypath'))
- end
- end
-
- def open_atomic_writer(path, &block)
- tmpfile = File.basename(path) + '.tmp'
- begin
- File.open(tmpfile, 'wb', &block)
- File.rename tmpfile, File.basename(path)
- ensure
- File.unlink tmpfile if File.exist?(tmpfile)
- end
- end
-
- class Shebang
- def Shebang.load(path)
- line = nil
- File.open(path) {|f|
- line = f.gets
- }
- return nil unless /\A#!/ =~ line
- parse(line)
- end
-
- def Shebang.parse(line)
- cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
- new(cmd, args)
- end
-
- def initialize(cmd, args = [])
- @cmd = cmd
- @args = args
- end
-
- attr_reader :cmd
- attr_reader :args
-
- def to_s
- "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
- end
- end
-
- #
- # TASK install
- #
-
- def exec_install
- rm_f 'InstalledFiles'
- exec_task_traverse 'install'
- end
-
- def install_dir_bin(rel)
- install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
- end
-
- def install_dir_lib(rel)
- install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
- end
-
- def install_dir_ext(rel)
- return unless extdir?(curr_srcdir())
- install_files rubyextentions('.'),
- "#{config('sodir')}/#{File.dirname(rel)}",
- 0555
- end
-
- def install_dir_data(rel)
- install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
- end
-
- def install_dir_conf(rel)
- # FIXME: should not remove current config files
- # (rename previous file to .old/.org)
- install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
- end
-
- def install_dir_man(rel)
- install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
- end
-
- def install_files(list, dest, mode)
- mkdir_p dest, @config.install_prefix
- list.each do |fname|
- install fname, dest, mode, @config.install_prefix
- end
- end
-
- def libfiles
- glob_reject(%w(*.y *.output), targetfiles())
- end
-
- def rubyextentions(dir)
- ents = glob_select("*.#{@config.dllext}", targetfiles())
- if ents.empty?
- setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
- end
- ents
- end
-
- def targetfiles
- mapdir(existfiles() - hookfiles())
- end
-
- def mapdir(ents)
- ents.map {|ent|
- if File.exist?(ent)
- then ent # objdir
- else "#{curr_srcdir()}/#{ent}" # srcdir
- end
- }
- end
-
- # picked up many entries from cvs-1.11.1/src/ignore.c
- JUNK_FILES = %w(
- core RCSLOG tags TAGS .make.state
- .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
- *~ *.old *.bak *.BAK *.orig *.rej _$* *$
-
- *.org *.in .*
- )
-
- def existfiles
- glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
- end
-
- def hookfiles
- %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
- %w( config setup install clean ).map {|t| sprintf(fmt, t) }
- }.flatten
- end
-
- def glob_select(pat, ents)
- re = globs2re([pat])
- ents.select {|ent| re =~ ent }
- end
-
- def glob_reject(pats, ents)
- re = globs2re(pats)
- ents.reject {|ent| re =~ ent }
- end
-
- GLOB2REGEX = {
- '.' => '\.',
- '$' => '\$',
- '#' => '\#',
- '*' => '.*'
- }
-
- def globs2re(pats)
- /\A(?:#{
- pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
- })\z/
- end
-
- #
- # TASK test
- #
-
- TESTDIR = 'test'
-
- def exec_test
- unless File.directory?('test')
- $stderr.puts 'no test in this package' if verbose?
- return
- end
- $stderr.puts 'Running tests...' if verbose?
- begin
- require 'test/unit'
- rescue LoadError
- setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
- end
- runner = Test::Unit::AutoRunner.new(true)
- runner.to_run << TESTDIR
- runner.run
- end
-
- #
- # TASK clean
- #
-
- def exec_clean
- exec_task_traverse 'clean'
- rm_f @config.savefile
- rm_f 'InstalledFiles'
- end
-
- alias clean_dir_bin noop
- alias clean_dir_lib noop
- alias clean_dir_data noop
- alias clean_dir_conf noop
- alias clean_dir_man noop
-
- def clean_dir_ext(rel)
- return unless extdir?(curr_srcdir())
- make 'clean' if File.file?('Makefile')
- end
-
- #
- # TASK distclean
- #
-
- def exec_distclean
- exec_task_traverse 'distclean'
- rm_f @config.savefile
- rm_f 'InstalledFiles'
- end
-
- alias distclean_dir_bin noop
- alias distclean_dir_lib noop
-
- def distclean_dir_ext(rel)
- return unless extdir?(curr_srcdir())
- make 'distclean' if File.file?('Makefile')
- end
-
- alias distclean_dir_data noop
- alias distclean_dir_conf noop
- alias distclean_dir_man noop
-
- #
- # Traversing
- #
-
- def exec_task_traverse(task)
- run_hook "pre-#{task}"
- FILETYPES.each do |type|
- if type == 'ext' and config('without-ext') == 'yes'
- $stderr.puts 'skipping ext/* by user option' if verbose?
- next
- end
- traverse task, type, "#{task}_dir_#{type}"
- end
- run_hook "post-#{task}"
- end
-
- def traverse(task, rel, mid)
- dive_into(rel) {
- run_hook "pre-#{task}"
- __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
- directories_of(curr_srcdir()).each do |d|
- traverse task, "#{rel}/#{d}", mid
- end
- run_hook "post-#{task}"
- }
- end
-
- def dive_into(rel)
- return unless File.dir?("#{@srcdir}/#{rel}")
-
- dir = File.basename(rel)
- Dir.mkdir dir unless File.dir?(dir)
- prevdir = Dir.pwd
- Dir.chdir dir
- $stderr.puts '---> ' + rel if verbose?
- @currdir = rel
- yield
- Dir.chdir prevdir
- $stderr.puts '<--- ' + rel if verbose?
- @currdir = File.dirname(rel)
- end
-
- def run_hook(id)
- path = [ "#{curr_srcdir()}/#{id}",
- "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
- return unless path
- begin
- instance_eval File.read(path), path, 1
- rescue
- raise if $DEBUG
- setup_rb_error "hook #{path} failed:\n" + $!.message
- end
- end
-
-end # class Installer
-
-
-class SetupError < StandardError; end
-
-def setup_rb_error(msg)
- raise SetupError, msg
-end
-
-if $0 == __FILE__
- begin
- ToplevelInstaller.invoke
- rescue SetupError
- raise if $DEBUG
- $stderr.puts $!.message
- $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
- exit 1
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/.document b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/.document
deleted file mode 100644
index ecf367319..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/.document
+++ /dev/null
@@ -1,5 +0,0 @@
-README.rdoc
-lib/**/*.rb
-bin/*
-features/**/*.feature
-LICENSE
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/.gitignore b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/.gitignore
deleted file mode 100644
index 482f92bf7..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-.DS_Store
-.yardoc
-/coverage
-/doc
-/pkg
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/LICENSE b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/LICENSE
deleted file mode 100644
index 590bcb6fa..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright 2009 Chris Kampmeier
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/README.rdoc b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/README.rdoc
deleted file mode 100644
index cfab5434f..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/README.rdoc
+++ /dev/null
@@ -1,70 +0,0 @@
-= Samuel
-
-Samuel is a gem for automatic logging of your Net::HTTP requests. It's named for
-the serial diarist Mr. Pepys, who was known to reliably record events both
-quotidian and remarkable.
-
-Should a Great Plague, Fire, or Whale befall an important external web service
-you use, you'll be sure to have a tidy record of it.
-
-== Usage:
-
-When Rails is loaded, Samuel configures a few things automatically. So all you
-need to do is this:
-
- # config/environment.rb
- config.gem "samuel"
-
-And Samuel will automatically use Rails's logger and an ActiveRecord-like format.
-
-For non-Rails projects, you'll have to manually configure logging, like this:
-
- require 'samuel'
- Samuel.logger = Logger.new('http_requests.log')
-
-If you don't assign a logger, Samuel will configure a default logger on +STDOUT+.
-
-== Configuration
-
-There are two ways to specify configuration options for Samuel: global and
-inline. Global configs look like this:
-
- Samuel.config[:labels] = {"example.com" => "Example API"}
- Samuel.config[:filtered_params] = :password
-
-You should put global configuration somewhere early-on in your program. If
-you're using Rails, <tt>config/initializers/samuel.rb</tt> will do the trick.
-
-Alternatively, an inline configuration block temporarily overrides any global
-configuration for a set of HTTP requests:
-
- Samuel.with_config :label => "Twitter API" do
- Net::HTTP.start("twitter.com") { |http| http.get("/help/test") }
- end
-
-Right now, there are three configuration changes you can make in either style:
-
-* +:labels+ - This is a hash with domain substrings as keys and log labels as
- values. If a request domain includes one of the domain substrings, the
- corresponding label will be used for the first part of that log entry. By
- default this is set to <tt>\{"" => "HTTP"}</tt>, so that all requests are
- labeled with <tt>"HTTP Request"</tt>.
-* +:label+ - As an alternative to the +:labels+ hash, this is simply a string.
- If set, it takes precedence over any +:labels+ (by default, it's not set). It
- gets <tt>"Request"</tt> appended to it as well -- so if you want your log to
- always say +Twitter API Request+ instead of the default +HTTP Request+, you
- can set this to <tt>"Twitter API"</tt>. I'd recommend using this setting
- globally if you're only making requests to one service, or inline if you just
- need to temporarily override the global +:labels+.
-* +:filtered_params+ - This works just like Rails's +filter_parameter_logging+
- method. Set it to a symbol, string, or array of them, and Samuel will filter
- the value of query parameters that have any of these patterns as a substring
- by replacing the value with <tt>[FILTERED]</tt> in your logs. By default, no
- filtering is enabled.
-
-Samuel logs successful HTTP requests at the +INFO+ level; Failed requests log at
-the +WARN+ level. This isn't currently configurable, but it's on the list.
-
-== License
-
-Copyright 2009 Chris Kampmeier. See +LICENSE+ for details.
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/Rakefile b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/Rakefile
deleted file mode 100644
index ffbe60384..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/Rakefile
+++ /dev/null
@@ -1,62 +0,0 @@
-require 'rubygems'
-require 'rake'
-
-begin
- require 'jeweler'
- Jeweler::Tasks.new do |gem|
- gem.name = "samuel"
- gem.summary = %Q{An automatic logger for HTTP requests in Ruby}
- gem.description = %Q{An automatic logger for HTTP requests in Ruby. Adds Net::HTTP request logging to your Rails logs, and more.}
- gem.email = "chris@kampers.net"
- gem.homepage = "http://github.com/chrisk/samuel"
- gem.authors = ["Chris Kampmeier"]
- gem.rubyforge_project = "samuel"
- gem.add_development_dependency "thoughtbot-shoulda"
- gem.add_development_dependency "yard"
- gem.add_development_dependency "mocha"
- gem.add_development_dependency "fakeweb"
- end
- Jeweler::GemcutterTasks.new
- Jeweler::RubyforgeTasks.new do |rubyforge|
- rubyforge.doc_task = "yardoc"
- end
-rescue LoadError
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
-end
-
-require 'rake/testtask'
-Rake::TestTask.new(:test) do |test|
- test.libs << 'lib' << 'test'
- test.pattern = 'test/**/*_test.rb'
- test.verbose = false
- test.warning = true
-end
-
-begin
- require 'rcov/rcovtask'
- Rcov::RcovTask.new do |test|
- test.libs << 'test'
- test.pattern = 'test/**/*_test.rb'
- test.rcov_opts << "--sort coverage"
- test.rcov_opts << "--exclude gems"
- test.verbose = false
- test.warning = true
- end
-rescue LoadError
- task :rcov do
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
- end
-end
-
-task :test => :check_dependencies
-
-task :default => :test
-
-begin
- require 'yard'
- YARD::Rake::YardocTask.new
-rescue LoadError
- task :yardoc do
- abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/VERSION b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/VERSION
deleted file mode 100644
index 0c62199f1..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-0.2.1
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel.rb
deleted file mode 100644
index 5c8fed6f6..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require "logger"
-require "net/http"
-require "net/https"
-require "benchmark"
-
-require "samuel/net_http"
-require "samuel/request"
-
-
-module Samuel
- extend self
-
- attr_writer :config, :logger
-
- def logger
- @logger = nil if !defined?(@logger)
- return @logger if !@logger.nil?
-
- if defined?(RAILS_DEFAULT_LOGGER)
- @logger = RAILS_DEFAULT_LOGGER
- else
- @logger = Logger.new(STDOUT)
- end
- end
-
- def config
- Thread.current[:__samuel_config] ? Thread.current[:__samuel_config] : @config
- end
-
- def log_request(http, request, &block)
- request = Request.new(http, request, block)
- request.perform_and_log!
- request.response
- end
-
- def with_config(options = {})
- original_config = config.dup
- nested = !Thread.current[:__samuel_config].nil?
-
- Thread.current[:__samuel_config] = original_config.merge(options)
- yield
- Thread.current[:__samuel_config] = nested ? original_config : nil
- end
-
- def reset_config
- Thread.current[:__samuel_config] = nil
- @config = {:label => nil, :labels => {"" => "HTTP"}, :filtered_params => []}
- end
-
-end
-
-Samuel.reset_config
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel/net_http.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel/net_http.rb
deleted file mode 100644
index 2ffadf220..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel/net_http.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class Net::HTTP
-
- alias request_without_samuel request
- def request(req, body = nil, &block)
- Samuel.log_request(self, req) do
- request_without_samuel(req, body, &block)
- end
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel/request.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel/request.rb
deleted file mode 100644
index e10ecb44e..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/lib/samuel/request.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-module Samuel
- class Request
-
- attr_accessor :response
-
- def initialize(http, request, proc)
- @http, @request, @proc = http, request, proc
- end
-
- def perform_and_log!
- # If an exception is raised in the Benchmark block, it'll interrupt the
- # benchmark. Instead, use an inner block to record it as the "response"
- # for raising after the benchmark (and logging) is done.
- @seconds = Benchmark.realtime do
- begin; @response = @proc.call; rescue Exception => @response; end
- end
- Samuel.logger.add(log_level, log_message)
- raise @response if @response.is_a?(Exception)
- end
-
- private
-
- def log_message
- bold = "\e[1m"
- blue = "\e[34m"
- underline = "\e[4m"
- reset = "\e[0m"
- " #{bold}#{blue}#{underline}#{label} request (#{milliseconds}ms) " +
- "#{response_summary}#{reset} #{method} #{uri}"
- end
-
- def milliseconds
- (@seconds * 1000).round
- end
-
- def uri
- "#{scheme}://#{@http.address}#{port_if_not_default}#{filtered_path}"
- end
-
- def filtered_path
- path_without_query, query = @request.path.split("?")
- if query
- patterns = [Samuel.config[:filtered_params]].flatten
- patterns.map { |pattern|
- pattern_for_regex = Regexp.escape(pattern.to_s)
- [/([^&]*#{pattern_for_regex}[^&=]*)=(?:[^&]+)/, '\1=[FILTERED]']
- }.each { |filter| query.gsub!(*filter) }
- "#{path_without_query}?#{query}"
- else
- @request.path
- end
- end
-
- def scheme
- @http.use_ssl? ? "https" : "http"
- end
-
- def port_if_not_default
- ssl, port = @http.use_ssl?, @http.port
- if (!ssl && port == 80) || (ssl && port == 443)
- ""
- else
- ":#{port}"
- end
- end
-
- def method
- @request.method.to_s.upcase
- end
-
- def label
- return Samuel.config[:label] if Samuel.config[:label]
-
- pair = Samuel.config[:labels].detect { |domain, label| @http.address.include?(domain) }
- pair[1] if pair
- end
-
- def response_summary
- if response.is_a?(Exception)
- response.class
- else
- "[#{response.code} #{response.message}]"
- end
- end
-
- def log_level
- error_classes = [Exception, Net::HTTPClientError, Net::HTTPServerError]
- if error_classes.any? { |klass| response.is_a?(klass) }
- level = Logger::WARN
- else
- level = Logger::INFO
- end
- end
-
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/samuel.gemspec b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/samuel.gemspec
deleted file mode 100644
index 3a3719bf8..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/samuel.gemspec
+++ /dev/null
@@ -1,69 +0,0 @@
-# Generated by jeweler
-# DO NOT EDIT THIS FILE
-# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
-# -*- encoding: utf-8 -*-
-
-Gem::Specification.new do |s|
- s.name = %q{samuel}
- s.version = "0.2.1"
-
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.authors = ["Chris Kampmeier"]
- s.date = %q{2009-09-15}
- s.description = %q{An automatic logger for HTTP requests in Ruby. Adds Net::HTTP request logging to your Rails logs, and more.}
- s.email = %q{chris@kampers.net}
- s.extra_rdoc_files = [
- "LICENSE",
- "README.rdoc"
- ]
- s.files = [
- ".document",
- ".gitignore",
- "LICENSE",
- "README.rdoc",
- "Rakefile",
- "VERSION",
- "lib/samuel.rb",
- "lib/samuel/net_http.rb",
- "lib/samuel/request.rb",
- "samuel.gemspec",
- "test/request_test.rb",
- "test/samuel_test.rb",
- "test/test_helper.rb",
- "test/thread_test.rb"
- ]
- s.homepage = %q{http://github.com/chrisk/samuel}
- s.rdoc_options = ["--charset=UTF-8"]
- s.require_paths = ["lib"]
- s.rubyforge_project = %q{samuel}
- s.rubygems_version = %q{1.3.5}
- s.summary = %q{An automatic logger for HTTP requests in Ruby}
- s.test_files = [
- "test/request_test.rb",
- "test/samuel_test.rb",
- "test/test_helper.rb",
- "test/thread_test.rb"
- ]
-
- if s.respond_to? :specification_version then
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
- s.specification_version = 3
-
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
- s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
- s.add_development_dependency(%q<yard>, [">= 0"])
- s.add_development_dependency(%q<mocha>, [">= 0"])
- s.add_development_dependency(%q<fakeweb>, [">= 0"])
- else
- s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
- s.add_dependency(%q<yard>, [">= 0"])
- s.add_dependency(%q<mocha>, [">= 0"])
- s.add_dependency(%q<fakeweb>, [">= 0"])
- end
- else
- s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
- s.add_dependency(%q<yard>, [">= 0"])
- s.add_dependency(%q<mocha>, [">= 0"])
- s.add_dependency(%q<fakeweb>, [">= 0"])
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/request_test.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/request_test.rb
deleted file mode 100644
index 4e905d1ec..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/request_test.rb
+++ /dev/null
@@ -1,193 +0,0 @@
-require 'test_helper'
-
-class RequestTest < Test::Unit::TestCase
-
- context "making an HTTP request" do
- setup { setup_test_logger
- FakeWeb.clean_registry
- Samuel.reset_config }
- teardown { teardown_test_logger }
-
- context "to GET http://example.com/test, responding with a 200 in 53ms" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"])
- Benchmark.stubs(:realtime).yields.returns(0.053)
- open "http://example.com/test"
- end
-
- should_log_lines 1
- should_log_at_level :info
- should_log_including "HTTP request"
- should_log_including "(53ms)"
- should_log_including "[200 OK]"
- should_log_including "GET http://example.com/test"
- end
-
- context "on a non-standard port" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com:8080/test", :status => [200, "OK"])
- open "http://example.com:8080/test"
- end
-
- should_log_including "GET http://example.com:8080/test"
- end
-
- context "with SSL" do
- setup do
- FakeWeb.register_uri(:get, "https://example.com/test", :status => [200, "OK"])
- open "https://example.com/test"
- end
-
- should_log_including "HTTP request"
- should_log_including "GET https://example.com/test"
- end
-
- context "with SSL on a non-standard port" do
- setup do
- FakeWeb.register_uri(:get, "https://example.com:80/test", :status => [200, "OK"])
- open "https://example.com:80/test"
- end
-
- should_log_including "HTTP request"
- should_log_including "GET https://example.com:80/test"
- end
-
- context "that raises" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :exception => Errno::ECONNREFUSED)
- begin
- Net::HTTP.start("example.com") { |http| http.get("/test") }
- rescue Errno::ECONNREFUSED => @exception
- end
- end
-
- should_log_at_level :warn
- should_log_including "HTTP request"
- should_log_including "GET http://example.com/test"
- should_log_including "Errno::ECONNREFUSED"
- should_log_including %r|\d+ms|
- should_raise_exception Errno::ECONNREFUSED
- end
-
- context "that responds with a 500-level code" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :status => [502, "Bad Gateway"])
- Net::HTTP.start("example.com") { |http| http.get("/test") }
- end
-
- should_log_at_level :warn
- end
-
- context "that responds with a 400-level code" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :status => [404, "Not Found"])
- Net::HTTP.start("example.com") { |http| http.get("/test") }
- end
-
- should_log_at_level :warn
- end
-
- context "inside a configuration block with :label => 'Example'" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"])
- Samuel.with_config :label => "Example" do
- open "http://example.com/test"
- end
- end
-
- should_log_including "Example request"
- should_have_config_afterwards_including :labels => {"" => "HTTP"},
- :label => nil
- end
-
- context "inside a configuration block with :filter_params" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test?password=secret&username=chrisk",
- :status => [200, "OK"])
- @uri = "http://example.com/test?password=secret&username=chrisk"
- end
-
- context "=> :password" do
- setup { Samuel.with_config(:filtered_params => :password) { open @uri } }
- should_log_including "http://example.com/test?password=[FILTERED]&username=chrisk"
- end
-
- context "=> :as" do
- setup { Samuel.with_config(:filtered_params => :ass) { open @uri } }
- should_log_including "http://example.com/test?password=[FILTERED]&username=chrisk"
- end
-
- context "=> ['pass', 'name']" do
- setup { Samuel.with_config(:filtered_params => %w(pass name)) { open @uri } }
- should_log_including "http://example.com/test?password=[FILTERED]&username=[FILTERED]"
- end
- end
-
- context "with a global config including :label => 'Example'" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"])
- Samuel.config[:label] = "Example"
- open "http://example.com/test"
- end
-
- should_log_including "Example request"
- should_have_config_afterwards_including :labels => {"" => "HTTP"},
- :label => "Example"
- end
-
- context "with a global config including :label => 'Example' but inside config block that changes it to 'Example 2'" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"])
- Samuel.config[:label] = "Example"
- Samuel.with_config(:label => "Example 2") { open "http://example.com/test" }
- end
-
- should_log_including "Example 2 request"
- should_have_config_afterwards_including :labels => {"" => "HTTP"},
- :label => "Example"
- end
-
- context "inside a config block of :label => 'Example 2' nested inside a config block of :label => 'Example'" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"])
- Samuel.with_config :label => "Example" do
- Samuel.with_config :label => "Example 2" do
- open "http://example.com/test"
- end
- end
- end
-
- should_log_including "Example 2 request"
- should_have_config_afterwards_including :labels => {"" => "HTTP"},
- :label => nil
- end
-
- context "wth a global config including :labels => {'example.com' => 'Example'} but inside a config block of :label => 'Example 3' nested inside a config block of :label => 'Example 2'" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"])
- Samuel.config[:labels] = {'example.com' => 'Example'}
- Samuel.with_config :label => "Example 2" do
- Samuel.with_config :label => "Example 3" do
- open "http://example.com/test"
- end
- end
- end
-
- should_log_including "Example 3 request"
- should_have_config_afterwards_including :labels => {'example.com' => 'Example'},
- :label => nil
- end
-
- context "with a global config including :labels => {'example.com' => 'Example API'}" do
- setup do
- FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"])
- Samuel.config[:labels] = {'example.com' => 'Example API'}
- open "http://example.com/test"
- end
-
- should_log_including "Example API request"
- end
-
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/samuel_test.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/samuel_test.rb
deleted file mode 100644
index 4a3665fa6..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/samuel_test.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'test_helper'
-
-class SamuelTest < Test::Unit::TestCase
-
- context "logger configuration" do
- setup do
- Samuel.logger = nil
- if Object.const_defined?(:RAILS_DEFAULT_LOGGER)
- Object.send(:remove_const, :RAILS_DEFAULT_LOGGER)
- end
- end
-
- teardown do
- Samuel.logger = nil
- end
-
- context "when Rails's logger is available" do
- setup { Object.const_set(:RAILS_DEFAULT_LOGGER, :mock_logger) }
-
- should "use the same logger" do
- assert_equal :mock_logger, Samuel.logger
- end
- end
-
- context "when Rails's logger is not available" do
- should "use a new Logger instance pointed to STDOUT" do
- assert_instance_of Logger, Samuel.logger
- assert_equal STDOUT, Samuel.logger.instance_variable_get(:"@logdev").dev
- end
- end
- end
-
-
- context ".reset_config" do
- should "reset the config to default vaules" do
- Samuel.config = {:foo => "bar"}
- Samuel.reset_config
- assert_equal({:label => nil, :labels => {"" => "HTTP"}, :filtered_params => []}, Samuel.config)
- end
- end
-
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/test_helper.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/test_helper.rb
deleted file mode 100644
index 2862051b9..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/test_helper.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-require 'rubygems'
-require 'test/unit'
-require 'shoulda'
-require 'mocha'
-require 'open-uri'
-require 'fakeweb'
-
-FakeWeb.allow_net_connect = false
-
-$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
-$LOAD_PATH.unshift(File.dirname(__FILE__))
-require 'samuel'
-
-class Test::Unit::TestCase
- TEST_LOG_PATH = File.join(File.dirname(__FILE__), 'test.log')
-
- def self.should_log_lines(expected_count)
- should "log #{expected_count} line#{'s' unless expected_count == 1}" do
- lines = File.readlines(TEST_LOG_PATH)
- assert_equal expected_count, lines.length
- end
- end
-
- def self.should_log_including(what)
- should "log a line including #{what.inspect}" do
- contents = File.read(TEST_LOG_PATH)
- if what.is_a?(Regexp)
- assert_match what, contents
- else
- assert contents.include?(what),
- "Expected #{contents.inspect} to include #{what.inspect}"
- end
- end
- end
-
- def self.should_log_at_level(level)
- level = level.to_s.upcase
- should "log at the #{level} level" do
- assert File.read(TEST_LOG_PATH).include?(" #{level} -- :")
- end
- end
-
- def self.should_raise_exception(klass)
- should "raise an #{klass} exception" do
- assert @exception.is_a?(klass)
- end
- end
-
- def self.should_have_config_afterwards_including(config)
- config.each_pair do |key, value|
- should "continue afterwards with Samuel.config[#{key.inspect}] set to #{value.inspect}" do
- assert_equal value, Samuel.config[key]
- end
- end
- end
-
- def setup_test_logger
- FileUtils.rm_rf TEST_LOG_PATH
- FileUtils.touch TEST_LOG_PATH
- Samuel.logger = Logger.new(TEST_LOG_PATH)
- end
-
- def teardown_test_logger
- FileUtils.rm_rf TEST_LOG_PATH
- end
-end
diff --git a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/thread_test.rb b/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/thread_test.rb
deleted file mode 100644
index d030cb973..000000000
--- a/vendor/gems/fakeweb-1.3.0/test/vendor/samuel-0.2.1/test/thread_test.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require 'test_helper'
-
-class ThreadTest < Test::Unit::TestCase
-
- context "when logging multiple requests at once" do
- setup do
- @log = StringIO.new
- Samuel.logger = Logger.new(@log)
- FakeWeb.register_uri(:get, /example\.com/, :status => [200, "OK"])
- threads = []
- 5.times do |i|
- threads << Thread.new(i) do |n|
- Samuel.with_config :label => "Example #{n}" do
- Thread.pass
- open "http://example.com/#{n}"
- end
- end
- end
- threads.each { |t| t.join }
- @log.rewind
- end
-
- should "not let configuration blocks interfere with eachother" do
- @log.each_line do |line|
- matches = %r|Example (\d+).*example\.com/(\d+)|.match(line)
- assert_not_nil matches
- assert_equal matches[1], matches[2]
- end
- end
- end
-
-end
diff --git a/vendor/plugins/active_record_base_without_table/lib/active_record/base_without_table.rb b/vendor/plugins/active_record_base_without_table/lib/active_record/base_without_table.rb
index 12cb05e02..14e9f935d 100644
--- a/vendor/plugins/active_record_base_without_table/lib/active_record/base_without_table.rb
+++ b/vendor/plugins/active_record_base_without_table/lib/active_record/base_without_table.rb
@@ -13,7 +13,8 @@ module ActiveRecord
def column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
- reset_column_information
+ # FIXME: Disabled temporarily during Rails 3 upgrade
+ # reset_column_information
end
# Do not reset @columns
diff --git a/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb b/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb
index 374fcd65b..e03c5e4aa 100644
--- a/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb
+++ b/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb
@@ -92,7 +92,7 @@ module ActsAsXapian
raise "Set RAILS_ENV, so acts_as_xapian can find the right Xapian database" if not environment
# check for a config file
- config_file = RAILS_ROOT + "/config/xapian.yml"
+ config_file = Rails.root.to_s + "/config/xapian.yml"
@@config = File.exists?(config_file) ? YAML.load_file(config_file)[environment] : {}
# figure out where the DBs should go
@@ -758,7 +758,7 @@ module ActsAsXapian
else
values = []
for locale in self.translations.map{|x| x.locale}
- self.class.with_locale(locale) do
+ I18n.with_locale(locale) do
values << single_xapian_value(field, type=type)
end
end
diff --git a/vendor/plugins/globalize2/LICENSE b/vendor/plugins/globalize2/LICENSE
deleted file mode 100644
index 94a6b8160..000000000
--- a/vendor/plugins/globalize2/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License
-
-Copyright (c) 2008, 2009 Joshua Harvey
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/plugins/globalize2/README.textile b/vendor/plugins/globalize2/README.textile
deleted file mode 100644
index e47e9bf37..000000000
--- a/vendor/plugins/globalize2/README.textile
+++ /dev/null
@@ -1,86 +0,0 @@
-h1. Globalize2
-
-Globalize2 is the successor of Globalize for Rails.
-
-It is compatible with and builds on the new "I18n api in Ruby on Rails":http://guides.rubyonrails.org/i18n.html. and adds model translations to ActiveRecord.
-
-Globalize2 is much more lightweight and compatible than its predecessor was. Model translations in Globalize2 use default ActiveRecord features and do not limit any ActiveRecord functionality any more.
-
-h2. Requirements
-
-ActiveRecord
-I18n
-
-(or Rails > 2.2)
-
-h2. Installation
-
-To install Globalize2 with its default setup just use:
-
-<pre><code>
-script/plugin install git://github.com/joshmh/globalize2.git
-</code></pre>
-
-h2. Model translations
-
-Model translations allow you to translate your models' attribute values. E.g.
-
-<pre><code>
-class Post < ActiveRecord::Base
- translates :title, :text
-end
-</code></pre>
-
-Allows you to values for the attributes :title and :text per locale:
-
-<pre><code>
-I18n.locale = :en
-post.title # => Globalize2 rocks!
-
-I18n.locale = :he
-post.title # => גלובאלייז2 שולט!
-</code></pre>
-
-In order to make this work, you'll need to add the appropriate translation tables. Globalize2 comes with a handy helper method to help you do this. It's called @create_translation_table!@. Here's an example:
-
-<pre><code>
-class CreatePosts < ActiveRecord::Migration
- def self.up
- create_table :posts do |t|
- t.timestamps
- end
- Post.create_translation_table! :title => :string, :text => :text
- end
- def self.down
- drop_table :posts
- Post.drop_translation_table!
- end
-end
-</code></pre>
-
-Note that the ActiveRecord model @Post@ must already exist and have a @translates@ directive listing the translated fields.
-
-h2. Migration from Globalize
-
-See this script by Tomasz Stachewicz: http://gist.github.com/120867
-
-h2. Changes since Globalize2 v0.1.0
-
-* The association globalize_translations has been renamed to translations.
-
-h2. Alternative Solutions
-
-* "Veger's fork":http://github.com/veger/globalize2 - uses default AR schema for the default locale, delegates to the translations table for other locales only
-* "TranslatableColumns":http://github.com/iain/translatable_columns - have multiple languages of the same attribute in a model (Iain Hecker)
-* "localized_record":http://github.com/glennpow/localized_record - allows records to have localized attributes without any modifications to the database (Glenn Powell)
-* "model_translations":http://github.com/janne/model_translations - Minimal implementation of Globalize2 style model translations (Jan Andersson)
-
-h2. Related solutions
-
-* "globalize2_versioning":http://github.com/joshmh/globalize2_versioning - acts_as_versioned style versioning for Globalize2 (Joshua Harvey)
-* "i18n_multi_locales_validations":http://github.com/ZenCocoon/i18n_multi_locales_validations - multi-locales attributes validations to validates attributes from Globalize2 translations models (Sébastien Grosjean)
-* "Globalize2 Demo App":http://github.com/svenfuchs/globalize2-demo - demo application for Globalize2 (Sven Fuchs)</li>
-* "migrate_from_globalize1":http://gist.github.com/120867 - migrate model translations from Globalize1 to Globalize2 (Tomasz Stachewicz)</li>
-* "easy_globalize2_accessors":http://github.com/astropanic/easy_globalize2_accessors - easily access (read and write) globalize2-translated fields (astropanic, Tomasz Stachewicz)</li>
-* "globalize2-easy-translate":http://github.com/bsamman/globalize2-easy-translate - adds methods to easily access or set translated attributes to your model (bsamman)</li>
-* "batch_translations":http://github.com/alvarezrilla/batch_translations - allow saving multiple Globalize2 translations in the same request (Jose Alvarez Rilla)</li>
diff --git a/vendor/plugins/globalize2/Rakefile b/vendor/plugins/globalize2/Rakefile
deleted file mode 100644
index bc35dada4..000000000
--- a/vendor/plugins/globalize2/Rakefile
+++ /dev/null
@@ -1,39 +0,0 @@
-require 'rake'
-require 'rake/testtask'
-require 'rake/rdoctask'
-
-desc 'Default: run unit tests.'
-task :default => :test
-
-desc 'Test the globalize2 plugin.'
-Rake::TestTask.new(:test) do |t|
- t.libs << 'lib'
- t.pattern = 'test/**/*_test.rb'
- t.verbose = true
-end
-
-desc 'Generate documentation for the globalize2 plugin.'
-Rake::RDocTask.new(:rdoc) do |rdoc|
- rdoc.rdoc_dir = 'rdoc'
- rdoc.title = 'Globalize2'
- rdoc.options << '--line-numbers' << '--inline-source'
- rdoc.rdoc_files.include('README')
- rdoc.rdoc_files.include('lib/**/*.rb')
-end
-
-begin
- require 'jeweler'
- Jeweler::Tasks.new do |s|
- s.name = "globalize2"
- s.summary = "Rails I18n: de-facto standard library for ActiveRecord data translation"
- s.description = "Rails I18n: de-facto standard library for ActiveRecord data translation"
- s.email = "joshmh@gmail.com"
- s.homepage = "http://github.com/joshmh/globalize2"
- # s.rubyforge_project = ''
- s.authors = ["Sven Fuchs, Joshua Harvey, Clemens Kofler, John-Paul Bader"]
- # s.add_development_dependency ''
- end
- Jeweler::GemcutterTasks.new
-rescue LoadError
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
-end
diff --git a/vendor/plugins/globalize2/VERSION b/vendor/plugins/globalize2/VERSION
deleted file mode 100644
index 0c62199f1..000000000
--- a/vendor/plugins/globalize2/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-0.2.1
diff --git a/vendor/plugins/globalize2/generators/templates/db_backend_migration.rb b/vendor/plugins/globalize2/generators/templates/db_backend_migration.rb
deleted file mode 100644
index 0f0261113..000000000
--- a/vendor/plugins/globalize2/generators/templates/db_backend_migration.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-class ActsAsTaggableMigration < ActiveRecord::Migration
- def self.up
- create_table :globalize_translations do |t|
- t.string :locale, :null => false
- t.string :key, :null => false
- t.string :translation
- t.timestamps
- end
-
-# TODO: FINISH DOING MIGRATION -- stopped in the middle
-
- create_table :globalize_translations_map do |t|
- t.string :key, :null => false
- t.integer :translation_id, :null => false
- end
-
- add_index :taggings, :tag_id
- add_index :taggings, [:taggable_id, :taggable_type]
- end
-
- def self.down
- drop_table :globalize_translations
- drop_table :tags
- end
-end
diff --git a/vendor/plugins/globalize2/globalize2.gemspec b/vendor/plugins/globalize2/globalize2.gemspec
deleted file mode 100644
index 89021115e..000000000
--- a/vendor/plugins/globalize2/globalize2.gemspec
+++ /dev/null
@@ -1,81 +0,0 @@
-# Generated by jeweler
-# DO NOT EDIT THIS FILE DIRECTLY
-# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
-# -*- encoding: utf-8 -*-
-
-Gem::Specification.new do |s|
- s.name = %q{globalize2}
- s.version = "0.2.0"
-
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.authors = ["Sven Fuchs, Joshua Harvey, Clemens Kofler, John-Paul Bader"]
- s.date = %q{2010-04-22}
- s.description = %q{Rails I18n: de-facto standard library for ActiveRecord data translation}
- s.email = %q{joshmh@gmail.com}
- s.extra_rdoc_files = [
- "LICENSE",
- "README.textile"
- ]
- s.files = [
- ".gitignore",
- "LICENSE",
- "README.textile",
- "Rakefile",
- "VERSION",
- "generators/db_backend.rb",
- "generators/templates/db_backend_migration.rb",
- "globalize2.gemspec",
- "init.rb",
- "lib/globalize.rb",
- "lib/globalize/active_record.rb",
- "lib/globalize/active_record/adapter.rb",
- "lib/globalize/active_record/attributes.rb",
- "lib/globalize/active_record/migration.rb",
- "lib/i18n/missing_translations_log_handler.rb",
- "lib/i18n/missing_translations_raise_handler.rb",
- "test/active_record/fallbacks_test.rb",
- "test/active_record/migration_test.rb",
- "test/active_record/sti_translated_test.rb",
- "test/active_record/translates_test.rb",
- "test/active_record/translation_class_test.rb",
- "test/active_record/validation_tests.rb",
- "test/active_record_test.rb",
- "test/all.rb",
- "test/data/models.rb",
- "test/data/no_globalize_schema.rb",
- "test/data/schema.rb",
- "test/i18n/missing_translations_test.rb",
- "test/test_helper.rb"
- ]
- s.homepage = %q{http://github.com/joshmh/globalize2}
- s.rdoc_options = ["--charset=UTF-8"]
- s.require_paths = ["lib"]
- s.rubygems_version = %q{1.3.6}
- s.summary = %q{Rails I18n: de-facto standard library for ActiveRecord data translation}
- s.test_files = [
- "test/active_record/fallbacks_test.rb",
- "test/active_record/migration_test.rb",
- "test/active_record/sti_translated_test.rb",
- "test/active_record/translates_test.rb",
- "test/active_record/translation_class_test.rb",
- "test/active_record/validation_tests.rb",
- "test/active_record_test.rb",
- "test/all.rb",
- "test/data/models.rb",
- "test/data/no_globalize_schema.rb",
- "test/data/schema.rb",
- "test/i18n/missing_translations_test.rb",
- "test/test_helper.rb"
- ]
-
- if s.respond_to? :specification_version then
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
- s.specification_version = 3
-
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
- else
- end
- else
- end
-end
-
diff --git a/vendor/plugins/globalize2/init.rb b/vendor/plugins/globalize2/init.rb
deleted file mode 100644
index a4089251b..000000000
--- a/vendor/plugins/globalize2/init.rb
+++ /dev/null
@@ -1 +0,0 @@
-require 'globalize'
diff --git a/vendor/plugins/globalize2/lib/globalize.rb b/vendor/plugins/globalize2/lib/globalize.rb
deleted file mode 100644
index 67c1878ec..000000000
--- a/vendor/plugins/globalize2/lib/globalize.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Globalize
- autoload :ActiveRecord, 'globalize/active_record'
-
- class << self
- def fallbacks?
- I18n.respond_to?(:fallbacks)
- end
-
- def fallbacks(locale)
- fallbacks? ? I18n.fallbacks[locale] : [locale.to_sym]
- end
- end
-end
-
-ActiveRecord::Base.send(:include, Globalize::ActiveRecord)
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record.rb b/vendor/plugins/globalize2/lib/globalize/active_record.rb
deleted file mode 100644
index 3095bfe28..000000000
--- a/vendor/plugins/globalize2/lib/globalize/active_record.rb
+++ /dev/null
@@ -1,238 +0,0 @@
-module Globalize
- class MigrationError < StandardError; end
- class MigrationMissingTranslatedField < MigrationError; end
- class BadMigrationFieldType < MigrationError; end
-
- module ActiveRecord
- autoload :Adapter, 'globalize/active_record/adapter'
- autoload :Attributes, 'globalize/active_record/attributes'
- autoload :Migration, 'globalize/active_record/migration'
-
- def self.included(base)
- base.extend ActMacro
- end
-
- class << self
- def build_translation_class(target, options)
- options[:table_name] ||= "#{target.table_name.singularize}_translations"
-
- klass = target.const_defined?(:Translation) ?
- target.const_get(:Translation) :
- target.const_set(:Translation, Class.new(::ActiveRecord::Base))
-
- klass.class_eval do
- set_table_name(options[:table_name])
- belongs_to target.name.underscore.gsub('/', '_')
- def locale; read_attribute(:locale).to_sym; end
- def locale=(locale); write_attribute(:locale, locale.to_s); end
- end
-
- klass
- end
- end
-
- module ActMacro
- def locale
- (defined?(@@locale) && @@locale)
- end
-
- def locale=(locale)
- @@locale = locale
- end
-
- def translates(*attr_names)
- return if translates?
- options = attr_names.extract_options!
-
- class_inheritable_accessor :translation_class, :translated_attribute_names
- class_inheritable_writer :required_attributes
- self.translation_class = ActiveRecord.build_translation_class(self, options)
- self.translated_attribute_names = attr_names.map(&:to_sym)
-
- include InstanceMethods
- extend ClassMethods, Migration
-
- after_save :save_translations!
- ActiveSupport::Deprecation.silence do
- # Silence the warning that :class_name is deprecated and will be
- # removed in Rails 3, since it clutters up e.g. test output, and
- # there is nothing obvious that we can replace it with in Rails 2.
-
- has_many :translations, :class_name => translation_class.name,
- :foreign_key => class_name.foreign_key,
- :dependent => :delete_all,
- :extend => HasManyExtensions
- end
-
- named_scope :with_translations, lambda { |locale|
- conditions = required_attributes.map do |attribute|
- "#{quoted_translation_table_name}.#{attribute} IS NOT NULL"
- end
- conditions << "#{quoted_translation_table_name}.locale = ?"
- { :include => :translations, :conditions => [conditions.join(' AND '), locale] }
- }
-
- attr_names.each { |attr_name| translated_attr_accessor(attr_name) }
- end
-
- def translates?
- included_modules.include?(InstanceMethods)
- end
- end
-
- module HasManyExtensions
- def by_locale(locale)
- first(:conditions => { :locale => locale.to_s })
- end
-
- def by_locales(locales)
- all(:conditions => { :locale => locales.map(&:to_s) })
- end
- end
-
- module ClassMethods
- delegate :set_translation_table_name, :to => :translation_class
-
- def with_locale(locale)
- begin
- previous_locale, self.locale = self.locale, locale
- result = yield
- ensure
- self.locale = previous_locale
- end
- result
- end
-
- def translation_table_name
- translation_class.table_name
- end
-
- def quoted_translation_table_name
- translation_class.quoted_table_name
- end
-
- def required_attributes
- @required_attributes ||= reflect_on_all_validations.select do |validation|
- validation.macro == :validates_presence_of && translated_attribute_names.include?(validation.name)
- end.map(&:name)
- end
-
- def respond_to?(method, *args, &block)
- method.to_s =~ /^find_by_(\w+)$/ && translated_attribute_names.include?($1.to_sym) || super
- end
-
- def method_missing(method, *args)
- if method.to_s =~ /^find_by_(\w+)$/ && translated_attribute_names.include?($1.to_sym)
- find_first_by_translated_attr_and_locales($1, args.first)
- elsif method.to_s =~ /^find_all_by_(\w+)$/ && translated_attribute_names.include?($1.to_sym)
- find_all_by_translated_attr_and_locales($1, args.first)
- else
- super
- end
- end
-
- protected
-
- def find_first_by_translated_attr_and_locales(name, value)
- query = "#{translated_attr_name(name)} = ? AND #{translated_attr_name('locale')} IN (?)"
- locales = Globalize.fallbacks(locale || I18n.locale).map(&:to_s)
- find(
- :first,
- :joins => :translations,
- :conditions => [query, value, locales],
- :readonly => false
- )
- end
-
- def find_all_by_translated_attr_and_locales(name, value)
- query = "#{translated_attr_name(name)} = ? AND #{translated_attr_name('locale')} IN (?)"
- locales = Globalize.fallbacks(locale || I18n.locale).map(&:to_s)
- find(
- :all,
- :joins => :translations,
- :conditions => [query, value, locales],
- :readonly => false
- )
- end
-
- def translated_attr_accessor(name)
- define_method "#{name}=", lambda { |value|
- globalize.write(self.class.locale || I18n.locale, name, value)
- self[name] = value
- }
- define_method name, lambda { |*args|
- globalize.fetch(args.first || self.class.locale || I18n.locale, name)
- }
- alias_method "#{name}_before_type_cast", name
- end
-
- def translated_attr_name(name)
- "#{translation_class.table_name}.#{name}"
- end
- end
-
- module InstanceMethods
- def globalize
- @globalize ||= Adapter.new self
- end
-
- def attributes
- self.attribute_names.inject({}) do |attrs, name|
- attrs[name] = read_attribute(name) ||
- (globalize.fetch(I18n.locale, name) rescue nil)
- attrs
- end
- end
-
- def attributes=(attributes, *args)
- if attributes.respond_to?(:delete) && locale = attributes.delete(:locale)
- self.class.with_locale(locale) { super }
- else
- super
- end
- end
-
- def attribute_names
- translated_attribute_names.map(&:to_s) + super
- end
-
- def available_locales
- translations.scoped(:select => 'DISTINCT locale').map(&:locale)
- end
-
- def translated_locales
- translations.map(&:locale)
- end
-
- def translated_attributes
- translated_attribute_names.inject({}) do |attributes, name|
- attributes.merge(name => send(name))
- end
- end
-
- def set_translations(options)
- options.keys.each do |locale|
- translation = translations.find_by_locale(locale.to_s) ||
- translations.build(:locale => locale.to_s)
- translation.update_attributes!(options[locale])
- end
- end
-
- def reload(options = nil)
- translated_attribute_names.each { |name| @attributes.delete(name.to_s) }
- globalize.reset
- super(options)
- end
-
- protected
-
- def save_translations!
- globalize.save_translations!
- end
- end
- end
-end
-
-def globalize_write(name, value)
- globalize.write(self.class.locale || I18n.locale, name, value)
-end
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb b/vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb
deleted file mode 100644
index 12f89ec01..000000000
--- a/vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-module Globalize
- module ActiveRecord
- class Adapter
- # The cache caches attributes that already were looked up for read access.
- # The stash keeps track of new or changed values that need to be saved.
- attr_reader :record, :cache, :stash
-
- def initialize(record)
- @record = record
- @cache = Attributes.new
- @stash = Attributes.new
- end
-
- def fetch(locale, attr_name)
- cache.contains?(locale, attr_name) ?
- cache.read(locale, attr_name) :
- cache.write(locale, attr_name, fetch_attribute(locale, attr_name))
- end
-
- def write(locale, attr_name, value)
- stash.write(locale, attr_name, value)
- cache.write(locale, attr_name, value)
- end
-
- def save_translations!
- stash.each do |locale, attrs|
- translation = record.translations.find_or_initialize_by_locale(locale.to_s)
- attrs.each { |attr_name, value| translation[attr_name] = value }
- translation.save!
- end
- stash.clear
- end
-
- def reset
- cache.clear
- # stash.clear
- end
-
- protected
-
- def fetch_translation(locale)
- locale = locale.to_sym
- record.translations.loaded? ? record.translations.detect { |t| t.locale == locale } :
- record.translations.by_locale(locale)
- end
-
- def fetch_translations(locale)
- # only query if not already included with :include => translations
- record.translations.loaded? ? record.translations :
- record.translations.by_locales(Globalize.fallbacks(locale))
- end
-
- def fetch_attribute(locale, attr_name)
- translations = fetch_translations(locale)
- value, requested_locale = nil, locale
-
- Globalize.fallbacks(locale).each do |fallback|
- translation = translations.detect { |t| t.locale == fallback }
- value = translation && translation.send(attr_name)
- locale = fallback && break if value
- end
-
- set_metadata(value, :locale => locale, :requested_locale => requested_locale)
- value
- end
-
- def set_metadata(object, metadata)
- if object.respond_to?(:translation_metadata)
- object.translation_metadata.merge!(meta_data)
- end
- end
-
- def translation_metadata_accessor(object)
- return if obj.respond_to?(:translation_metadata)
- class << object; attr_accessor :translation_metadata end
- object.translation_metadata ||= {}
- end
- end
- end
-end
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb b/vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb
deleted file mode 100644
index 7bd923ce2..000000000
--- a/vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# Helper class for storing values per locale. Used by Globalize::Adapter
-# to stash and cache attribute values.
-module Globalize
- module ActiveRecord
- class Attributes < Hash
- def [](locale)
- locale = locale.to_sym
- self[locale] = {} unless has_key?(locale)
- self.fetch(locale)
- end
-
- def contains?(locale, attr_name)
- self[locale].has_key?(attr_name)
- end
-
- def read(locale, attr_name)
- self[locale][attr_name]
- end
-
- def write(locale, attr_name, value)
- self[locale][attr_name] = value
- end
- end
- end
-end
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record/migration.rb b/vendor/plugins/globalize2/lib/globalize/active_record/migration.rb
deleted file mode 100644
index fa63aed8c..000000000
--- a/vendor/plugins/globalize2/lib/globalize/active_record/migration.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-module Globalize
- module ActiveRecord
- module Migration
- def create_translation_table!(fields)
- translated_attribute_names.each do |f|
- raise MigrationMissingTranslatedField, "Missing translated field #{f}" unless fields[f]
- end
-
- fields.each do |name, type|
- if translated_attribute_names.include?(name) && ![:string, :text].include?(type)
- raise BadMigrationFieldType, "Bad field type for #{name}, should be :string or :text"
- end
- end
-
- self.connection.create_table(translation_table_name) do |t|
- t.references table_name.sub(/^#{table_name_prefix}/, "").singularize
- t.string :locale
- fields.each do |name, type|
- t.column name, type
- end
- t.timestamps
- end
-
- self.connection.add_index(
- translation_table_name,
- "#{table_name.sub(/^#{table_name_prefix}/, "").singularize}_id",
- :name => translation_index_name
- )
- end
-
- def translation_index_name
- require 'digest/sha1'
- # FIXME what's the max size of an index name?
- index_name = "index_#{translation_table_name}_on_#{self.table_name.singularize}_id"
- index_name.size < 50 ? index_name : "index_#{Digest::SHA1.hexdigest(index_name)}"
- end
-
- def drop_translation_table!
- self.connection.remove_index(translation_table_name, :name => translation_index_name) rescue nil
- self.connection.drop_table(translation_table_name)
- end
- end
- end
-end
diff --git a/vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb b/vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb
deleted file mode 100644
index 24c10890a..000000000
--- a/vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# A simple exception handler that behaves like the default exception handler
-# but additionally logs missing translations to a given log.
-#
-# Useful for identifying missing translations during testing.
-#
-# E.g.
-#
-# require 'globalize/i18n/missing_translations_log_handler'
-# I18n.missing_translations_logger = RAILS_DEFAULT_LOGGER
-# I18n.exception_handler = :missing_translations_log_handler
-#
-# To set up a different log file:
-#
-# logger = Logger.new("#{RAILS_ROOT}/log/missing_translations.log")
-# I18n.missing_translations_logger = logger
-
-module I18n
- @@missing_translations_logger = nil
-
- class << self
- def missing_translations_logger
- @@missing_translations_logger ||= begin
- require 'logger' unless defined?(Logger)
- Logger.new(STDOUT)
- end
- end
-
- def missing_translations_logger=(logger)
- @@missing_translations_logger = logger
- end
-
- def missing_translations_log_handler(exception, locale, key, options)
- if MissingTranslationData === exception
- missing_translations_logger.warn(exception.message)
- return exception.message
- else
- raise exception
- end
- end
- end
-end
diff --git a/vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb b/vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb
deleted file mode 100644
index 18237b151..000000000
--- a/vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# A simple exception handler that behaves like the default exception handler
-# but also raises on missing translations.
-#
-# Useful for identifying missing translations during testing.
-#
-# E.g.
-#
-# require 'globalize/i18n/missing_translations_raise_handler'
-# I18n.exception_handler = :missing_translations_raise_handler
-module I18n
- class << self
- def missing_translations_raise_handler(exception, locale, key, options)
- raise exception
- end
- end
-end
-
-I18n.exception_handler = :missing_translations_raise_handler
-
-ActionView::Helpers::TranslationHelper.module_eval do
- def translate(key, options = {})
- I18n.translate(key, options)
- end
- alias :t :translate
-end
diff --git a/vendor/plugins/globalize2/test/active_record/fallbacks_test.rb b/vendor/plugins/globalize2/test/active_record/fallbacks_test.rb
deleted file mode 100644
index 449ec8b2b..000000000
--- a/vendor/plugins/globalize2/test/active_record/fallbacks_test.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
-require File.expand_path(File.dirname(__FILE__) + '/../data/models')
-
-if I18n.respond_to?(:fallbacks)
- class TranslatedTest < ActiveSupport::TestCase
- def setup
- I18n.locale = :'en-US'
- I18n.fallbacks.clear
- reset_db!
- ActiveRecord::Base.locale = nil
- end
-
- def teardown
- I18n.fallbacks.clear
- end
-
- test "keeping one field in new locale when other field is changed" do
- I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
- post = Post.create :subject => 'foo'
- I18n.locale = 'de-DE'
- post.content = 'bar'
- assert_equal 'foo', post.subject
- end
-
- test "modifying non-required field in a new locale" do
- I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
- post = Post.create :subject => 'foo'
- I18n.locale = 'de-DE'
- post.content = 'bar'
- assert post.save
- end
-
- test "resolves a simple fallback" do
- I18n.locale = 'de-DE'
- post = Post.create :subject => 'foo'
- I18n.locale = 'de'
- post.subject = 'baz'
- post.content = 'bar'
- post.save
- I18n.locale = 'de-DE'
- assert_equal 'foo', post.subject
- assert_equal 'bar', post.content
- end
-
- test "resolves a simple fallback without reloading" do
- I18n.locale = 'de-DE'
- post = Post.new :subject => 'foo'
- I18n.locale = 'de'
- post.subject = 'baz'
- post.content = 'bar'
- I18n.locale = 'de-DE'
- assert_equal 'foo', post.subject
- assert_equal 'bar', post.content
- end
-
- test "resolves a complex fallback without reloading" do
- I18n.fallbacks.map 'de' => %w(en he)
- I18n.locale = 'de'
- post = Post.new
- I18n.locale = 'en'
- post.subject = 'foo'
- I18n.locale = 'he'
- post.subject = 'baz'
- post.content = 'bar'
- I18n.locale = 'de'
- assert_equal 'foo', post.subject
- assert_equal 'bar', post.content
- end
-
- test 'fallbacks with lots of locale switching' do
- I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
- post = Post.create :subject => 'foo'
-
- I18n.locale = :'de-DE'
- assert_equal 'foo', post.subject
-
- I18n.locale = :'en-US'
- post.update_attribute :subject, 'bar'
-
- I18n.locale = :'de-DE'
- assert_equal 'bar', post.subject
- end
-
- test 'fallbacks with lots of locale switching' do
- I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
- child = Child.create :content => 'foo'
-
- I18n.locale = :'de-DE'
- assert_equal 'foo', child.content
-
- I18n.locale = :'en-US'
- child.update_attribute :content, 'bar'
-
- I18n.locale = :'de-DE'
- assert_equal 'bar', child.content
- end
- end
-end
-
-# TODO should validate_presence_of take fallbacks into account? maybe we need
-# an extra validation call, or more options for validate_presence_of.
-
diff --git a/vendor/plugins/globalize2/test/active_record/migration_test.rb b/vendor/plugins/globalize2/test/active_record/migration_test.rb
deleted file mode 100644
index 359d811f0..000000000
--- a/vendor/plugins/globalize2/test/active_record/migration_test.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
-require File.expand_path(File.dirname(__FILE__) + '/../data/models')
-
-class MigrationTest < ActiveSupport::TestCase
- def setup
- reset_db!
- Post.drop_translation_table!
- end
-
- test 'globalize table added' do
- assert !Post.connection.table_exists?(:post_translations)
- assert !Post.connection.index_exists?(:post_translations, :post_id)
-
- Post.create_translation_table!(:subject => :string, :content => :text)
- assert Post.connection.table_exists?(:post_translations)
- assert Post.connection.index_exists?(:post_translations, :post_id)
-
- columns = Post.connection.columns(:post_translations)
- assert locale = columns.detect { |c| c.name == 'locale' }
- assert_equal :string, locale.type
- assert subject = columns.detect { |c| c.name == 'subject' }
- assert_equal :string, subject.type
- assert content = columns.detect { |c| c.name == 'content' }
- assert_equal :text, content.type
- assert post_id = columns.detect { |c| c.name == 'post_id' }
- assert_equal :integer, post_id.type
- assert created_at = columns.detect { |c| c.name == 'created_at' }
- assert_equal :datetime, created_at.type
- assert updated_at = columns.detect { |c| c.name == 'updated_at' }
- assert_equal :datetime, updated_at.type
- end
-
- test 'globalize table dropped' do
- assert !Post.connection.table_exists?( :post_translations )
- assert !Post.connection.index_exists?( :post_translations, :post_id )
- Post.create_translation_table! :subject => :string, :content => :text
- assert Post.connection.table_exists?( :post_translations )
- assert Post.connection.index_exists?( :post_translations, :post_id )
- Post.drop_translation_table!
- assert !Post.connection.table_exists?( :post_translations )
- assert !Post.connection.index_exists?( :post_translations, :post_id )
- end
-
- test 'exception on missing field inputs' do
- assert_raise Globalize::MigrationMissingTranslatedField do
- Post.create_translation_table! :content => :text
- end
- end
-
- test 'exception on bad input type' do
- assert_raise Globalize::BadMigrationFieldType do
- Post.create_translation_table! :subject => :string, :content => :integer
- end
- end
-
- test "exception on bad input type isn't raised for untranslated fields" do
- assert_nothing_raised do
- Post.create_translation_table! :subject => :string, :content => :string, :views_count => :integer
- end
- end
-
- test 'create_translation_table! should not be called on non-translated models' do
- assert_raise NoMethodError do
- Blog.create_translation_table! :name => :string
- end
- end
-
- test 'drop_translation_table! should not be called on non-translated models' do
- assert_raise NoMethodError do
- Blog.drop_translation_table!
- end
- end
-
- test "translation_index_name returns a readable index name when it's not longer than 50 characters" do
- assert_equal 'index_post_translations_on_post_id', Post.send(:translation_index_name)
- end
-
- test "translation_index_name returns a hashed index name when it's longer than 50 characters" do
- class UltraLongModelNameWithoutProper < ActiveRecord::Base
- translates :foo
- end
- name = UltraLongModelNameWithoutProper.send(:translation_index_name)
- assert_match /^index_[a-z0-9]{40}$/, name
- end
-
- test 'globalize table added when table has long name' do
- UltraLongModelNameWithoutProper.create_translation_table!(
- :subject => :string, :content => :text
- )
-
- assert UltraLongModelNameWithoutProper.connection.table_exists?(
- :ultra_long_model_name_without_proper_translations
- )
- assert UltraLongModelNameWithoutProper.connection.index_exists?(
- :ultra_long_model_name_without_proper_translations,
- :name => UltraLongModelNameWithoutProper.send(
- :translation_index_name
- )
- )
- end
-
- test 'globalize table dropped when table has long name' do
- UltraLongModelNameWithoutProper.drop_translation_table!
- UltraLongModelNameWithoutProper.create_translation_table!(
- :subject => :string, :content => :text
- )
- UltraLongModelNameWithoutProper.drop_translation_table!
-
- assert !UltraLongModelNameWithoutProper.connection.table_exists?(
- :ultra_long_model_name_without_proper_translations
- )
- assert !UltraLongModelNameWithoutProper.connection.index_exists?(
- :ultra_long_model_name_without_proper_translations,
- :ultra_long_model_name_without_proper_id
- )
- end
-
-end
diff --git a/vendor/plugins/globalize2/test/active_record/sti_translated_test.rb b/vendor/plugins/globalize2/test/active_record/sti_translated_test.rb
deleted file mode 100644
index f529b8d6e..000000000
--- a/vendor/plugins/globalize2/test/active_record/sti_translated_test.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
-require File.expand_path(File.dirname(__FILE__) + '/../data/models')
-
-class StiTranslatedTest < ActiveSupport::TestCase
- def setup
- I18n.locale = :'en-US'
- reset_db!
- end
-
- test "works with simple dynamic finders" do
- foo = Child.create :content => 'foo'
- Child.create :content => 'bar'
- child = Child.find_by_content('foo')
- assert_equal foo, child
- end
-
- test 'change attribute on globalized model' do
- child = Child.create :content => 'foo'
- assert_equal [], child.changed
- child.content = 'bar'
- assert_equal [ 'content' ], child.changed
- child.content = 'baz'
- assert_member 'content', child.changed
- end
-
- test 'change attribute on globalized model after locale switching' do
- child = Child.create :content => 'foo'
- assert_equal [], child.changed
- child.content = 'bar'
- I18n.locale = :de
- assert_equal [ 'content' ], child.changed
- end
-
- test "saves all locales, even after locale switching" do
- child = Child.new :content => 'foo'
- I18n.locale = 'de-DE'
- child.content = 'bar'
- I18n.locale = 'he-IL'
- child.content = 'baz'
- child.save
- I18n.locale = 'en-US'
- child = Child.first
- assert_equal 'foo', child.content
- I18n.locale = 'de-DE'
- assert_equal 'bar', child.content
- I18n.locale = 'he-IL'
- assert_equal 'baz', child.content
- end
-end
diff --git a/vendor/plugins/globalize2/test/active_record/translates_test.rb b/vendor/plugins/globalize2/test/active_record/translates_test.rb
deleted file mode 100644
index 1831063fb..000000000
--- a/vendor/plugins/globalize2/test/active_record/translates_test.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
-require File.expand_path(File.dirname(__FILE__) + '/../data/models')
-
-class TranslatesTest < ActiveSupport::TestCase
- def setup
- I18n.locale = nil
- ActiveRecord::Base.locale = nil
- reset_db!
- end
-
- test 'defines a :locale accessors on ActiveRecord::Base' do
- ActiveRecord::Base.locale = :de
- assert_equal :de, ActiveRecord::Base.locale
- end
-
- test 'the :locale reader on ActiveRecord::Base does not default to I18n.locale (anymore)' do
- I18n.locale = :en
- assert_nil ActiveRecord::Base.locale
- end
-
- test 'ActiveRecord::Base.with_locale temporarily sets the given locale and yields the block' do
- I18n.locale = :en
- post = Post.with_locale(:de) do
- Post.create!(:subject => 'Titel', :content => 'Inhalt')
- end
- assert_nil Post.locale
- assert_equal :en, I18n.locale
-
- I18n.locale = :de
- assert_equal 'Titel', post.subject
- end
-
- test 'translation_class returns the Translation class' do
- assert_equal Post::Translation, Post.translation_class
- end
-
- test 'defines a has_many association on the model class' do
- assert_has_many Post, :translations
- end
-
- test 'defines a scope for retrieving locales that have complete translations' do
- post = Post.create!(:subject => 'subject', :content => 'content')
- assert_equal [:en], post.translated_locales
- end
-
- test 'sets the given attributes to translated_attribute_names' do
- assert_equal [:subject, :content], Post.translated_attribute_names
- end
-
- test 'defines accessors for the translated attributes' do
- post = Post.new
- assert post.respond_to?(:subject)
- assert post.respond_to?(:subject=)
- end
-
- test 'attribute reader without arguments will use the current locale on ActiveRecord::Base or I18n' do
- post = Post.with_locale(:de) do
- Post.create!(:subject => 'Titel', :content => 'Inhalt')
- end
- I18n.locale = :de
- assert_equal 'Titel', post.subject
-
- I18n.locale = :en
- ActiveRecord::Base.locale = :de
- assert_equal 'Titel', post.subject
- end
-
- test 'attribute reader when passed a locale will use the given locale' do
- post = Post.with_locale(:de) do
- Post.create!(:subject => 'Titel', :content => 'Inhalt')
- end
- assert_equal 'Titel', post.subject(:de)
- end
-
- test 'attribute reader will use the current locale on ActiveRecord::Base or I18n' do
- post = Post.with_locale(:en) do
- Post.create!(:subject => 'title', :content => 'content')
- end
- I18n.locale = :de
- post.subject = 'Titel'
- assert_equal 'Titel', post.subject
-
- ActiveRecord::Base.locale = :en
- post.subject = 'title'
- assert_equal 'title', post.subject
- end
-
- test "find_by_xx records have writable attributes" do
- Post.create :subject => "change me"
- p = Post.find_by_subject("change me")
- p.subject = "changed"
- assert_nothing_raised(ActiveRecord::ReadOnlyRecord) do
- p.save
- end
- end
-end
diff --git a/vendor/plugins/globalize2/test/active_record/translation_class_test.rb b/vendor/plugins/globalize2/test/active_record/translation_class_test.rb
deleted file mode 100644
index 1628416d7..000000000
--- a/vendor/plugins/globalize2/test/active_record/translation_class_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
-require File.expand_path(File.dirname(__FILE__) + '/../data/models')
-
-class TranlationClassTest < ActiveSupport::TestCase
- def setup
- reset_db!
- end
-
- test 'defines a Translation class nested in the model class' do
- assert Post.const_defined?(:Translation)
- end
-
- test 'defines a belongs_to association' do
- assert_belongs_to Post::Translation, :post
- end
-
- test 'defines a reader for :locale that always returns a symbol' do
- post = Post::Translation.new
- post.write_attribute('locale', 'de')
- assert_equal :de, post.locale
- end
-
- test 'defines a write for :locale that always writes a string' do
- post = Post::Translation.new
- post.locale = :de
- assert_equal 'de', post.read_attribute('locale')
- end
-end
-
-
diff --git a/vendor/plugins/globalize2/test/active_record/validation_tests.rb b/vendor/plugins/globalize2/test/active_record/validation_tests.rb
deleted file mode 100644
index 0148fa384..000000000
--- a/vendor/plugins/globalize2/test/active_record/validation_tests.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
-require File.expand_path(File.dirname(__FILE__) + '/../data/models')
-
-class ValidationTest < ActiveSupport::TestCase
- def setup
- reset_db!
- end
-
- def teardown
- Validatee.instance_variable_set(:@validate_callbacks, CallbackChain.new)
- end
-
- test "validates_presence_of" do
- Validatee.class_eval { validates_presence_of :string }
- assert !Validatee.new.valid?
- assert Validatee.new(:string => 'foo').valid?
- end
-
- test "validates_confirmation_of" do
- Validatee.class_eval { validates_confirmation_of :string }
- assert !Validatee.new(:string => 'foo', :string_confirmation => 'bar').valid?
- assert Validatee.new(:string => 'foo', :string_confirmation => 'foo').valid?
- end
-
- test "validates_acceptance_of" do
- Validatee.class_eval { validates_acceptance_of :string, :accept => '1' }
- assert !Validatee.new(:string => '0').valid?
- assert Validatee.new(:string => '1').valid?
- end
-
- test "validates_length_of (:is)" do
- Validatee.class_eval { validates_length_of :string, :is => 1 }
- assert !Validatee.new(:string => 'aa').valid?
- assert Validatee.new(:string => 'a').valid?
- end
-
- test "validates_format_of" do
- Validatee.class_eval { validates_format_of :string, :with => /^\d+$/ }
- assert !Validatee.new(:string => 'a').valid?
- assert Validatee.new(:string => '1').valid?
- end
-
- test "validates_inclusion_of" do
- Validatee.class_eval { validates_inclusion_of :string, :in => %(a) }
- assert !Validatee.new(:string => 'b').valid?
- assert Validatee.new(:string => 'a').valid?
- end
-
- test "validates_exclusion_of" do
- Validatee.class_eval { validates_exclusion_of :string, :in => %(b) }
- assert !Validatee.new(:string => 'b').valid?
- assert Validatee.new(:string => 'a').valid?
- end
-
- test "validates_numericality_of" do
- Validatee.class_eval { validates_numericality_of :string }
- assert !Validatee.new(:string => 'a').valid?
- assert Validatee.new(:string => '1').valid?
- end
-
- # This doesn't pass and Rails' validates_uniqueness_of implementation doesn't
- # seem to be extensible easily. One can work around that by either defining
- # a custom validation on the Validatee model itself, or by using validates_uniqueness_of
- # on Validatee::Translation.
- #
- # test "validates_uniqueness_of" do
- # Validatee.class_eval { validates_uniqueness_of :string }
- # Validatee.create!(:string => 'a')
- # assert !Validatee.new(:string => 'a').valid?
- # assert Validatee.new(:string => 'b').valid?
- # end
-
- # test "validates_associated" do
- # end
-end \ No newline at end of file
diff --git a/vendor/plugins/globalize2/test/active_record_test.rb b/vendor/plugins/globalize2/test/active_record_test.rb
deleted file mode 100644
index 38e247e17..000000000
--- a/vendor/plugins/globalize2/test/active_record_test.rb
+++ /dev/null
@@ -1,467 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/test_helper')
-require File.expand_path(File.dirname(__FILE__) + '/data/models')
-
-# Higher level tests.
-
-class ActiveRecordTest < ActiveSupport::TestCase
- def setup
- I18n.locale = :en
- reset_db!
- ActiveRecord::Base.locale = nil
- end
-
- def assert_translated(locale, record, names, expected)
- I18n.locale = locale
- assert_equal Array(expected), Array(names).map { |name| record.send(name) }
- end
-
- test "a translated record has translations" do
- assert_equal [], Post.new.translations
- end
-
- test "saves a translated version of the record for each locale" do
- post = Post.create(:subject => 'title')
- I18n.locale = :de
- post.update_attributes(:subject => 'Titel')
-
- assert_equal 2, post.translations.size
- assert_equal %w(de en), post.translations.map(&:locale).map(&:to_s).sort
- assert_equal %w(Titel title), post.translations.map(&:subject).sort
- end
-
- test "a translated record has German translations" do
- I18n.locale = :de
- post = Post.create(:subject => 'foo')
- assert_equal 1, post.translations.size
- assert_equal [:de], post.translations.map { |t| t.locale }
- end
-
- test "modifiying translated fields while switching locales" do
- post = Post.create(:subject => 'title', :content => 'content')
- assert_equal %w(title content), [post.subject, post.content]
-
- I18n.locale = :de
- post.subject, post.content = 'Titel', 'Inhalt'
-
- assert_translated(:de, post, [:subject, :content], %w(Titel Inhalt))
- assert_translated(:en, post, [:subject, :content], %w(title content))
- assert_translated(:de, post, [:subject, :content], %w(Titel Inhalt))
-
- post.save
- post.reload
-
- assert_translated(:en, post, [:subject, :content], %w(title content))
- assert_translated(:de, post, [:subject, :content], %w(Titel Inhalt))
- end
-
- test "attribute writers do return their argument" do
- value = Post.new.subject = 'foo'
- assert_equal 'foo', value
- end
-
- test "update_attribute succeeds with valid values" do
- post = Post.create(:subject => 'foo', :content => 'bar')
- post.update_attribute(:subject, 'baz')
- assert_equal 'baz', Post.first.subject
- end
-
- test "update_attributes fails with invalid values" do
- post = Post.create(:subject => 'foo', :content => 'bar')
- assert !post.update_attributes(:subject => '')
- assert_not_nil post.reload.attributes['subject']
- assert_equal 'foo', post.subject
- end
-
- test "passing the locale to create uses the given locale" do
- post = Post.create(:subject => 'Titel', :content => 'Inhalt', :locale => :de)
- assert_equal :en, I18n.locale
- assert_nil ActiveRecord::Base.locale
-
- I18n.locale = :de
- assert_equal 'Titel', post.subject
- end
-
- test "passing the locale to attributes= uses the given locale" do
- post = Post.create(:subject => 'title', :content => 'content')
- post.update_attributes(:subject => 'Titel', :content => 'Inhalt', :locale => :de)
- post.reload
-
- assert_equal :en, I18n.locale
- assert_nil ActiveRecord::Base.locale
-
- assert_equal 'title', post.subject
- I18n.locale = :de
- assert_equal 'Titel', post.subject
- end
-
- test 'reload works' do
- post = Post.create(:subject => 'foo', :content => 'bar')
- post.subject = 'baz'
- post.reload
- assert_equal 'foo', post.subject
- end
-
- test "returns nil if no translations are found (unsaved record)" do
- post = Post.new(:subject => 'foo')
- assert_equal 'foo', post.subject
- assert_nil post.content
- end
-
- test "returns nil if no translations are found (saved record)" do
- post = Post.create(:subject => 'foo')
- post.reload
- assert_equal 'foo', post.subject
- assert_nil post.content
- end
-
- test "finds a German post" do
- post = Post.create(:subject => 'foo (en)', :content => 'bar')
- I18n.locale = :de
- post = Post.first
- post.subject = 'baz (de)'
- post.save
- assert_equal 'baz (de)', Post.first.subject
- I18n.locale = :en
- assert_equal 'foo (en)', Post.first.subject
- end
-
- test "saves an English post and loads correctly" do
- post = Post.create(:subject => 'foo', :content => 'bar')
- assert post.save
- post = Post.first
- assert_equal 'foo', post.subject
- assert_equal 'bar', post.content
- end
-
- test "returns the value for the correct locale, after locale switching" do
- post = Post.create(:subject => 'foo')
- I18n.locale = :de
- post.subject = 'bar'
- post.save
- I18n.locale = :en
- post = Post.first
- assert_equal 'foo', post.subject
- I18n.locale = :de
- assert_equal 'bar', post.subject
- end
-
- test "returns the value for the correct locale, after locale switching, without saving" do
- post = Post.create :subject => 'foo'
- I18n.locale = :de
- post.subject = 'bar'
- I18n.locale = :en
- assert_equal 'foo', post.subject
- I18n.locale = :de
- assert_equal 'bar', post.subject
- end
-
- test "saves all locales, even after locale switching" do
- post = Post.new :subject => 'foo'
- I18n.locale = :de
- post.subject = 'bar'
- I18n.locale = :he
- post.subject = 'baz'
- post.save
- I18n.locale = :en
- post = Post.first
- assert_equal 'foo', post.subject
- I18n.locale = :de
- assert_equal 'bar', post.subject
- I18n.locale = :he
- assert_equal 'baz', post.subject
- end
-
- test "works with associations" do
- blog = Blog.create
- post1 = blog.posts.create(:subject => 'foo')
-
- I18n.locale = :de
- post2 = blog.posts.create(:subject => 'bar')
- assert_equal 2, blog.posts.size
-
- I18n.locale = :en
- assert_equal 'foo', blog.posts.first.subject
- assert_nil blog.posts.last.subject
-
- I18n.locale = :de
- assert_equal 'bar', blog.posts.last.subject
- end
-
- test "works with simple dynamic finders" do
- foo = Post.create(:subject => 'foo')
- Post.create(:subject => 'bar')
- post = Post.find_by_subject('foo')
- assert_equal foo, post
- end
-
- test 'change attribute on globalized model' do
- post = Post.create(:subject => 'foo', :content => 'bar')
- assert_equal [], post.changed
- post.subject = 'baz'
- assert_equal ['subject'], post.changed
- post.content = 'quux'
- assert_member 'subject', post.changed
- assert_member 'content', post.changed
- end
-
- test 'change attribute on globalized model after locale switching' do
- post = Post.create(:subject => 'foo', :content => 'bar')
- assert_equal [], post.changed
- post.subject = 'baz'
- I18n.locale = :de
- assert_equal ['subject'], post.changed
- end
-
- test 'complex writing and stashing' do
- post = Post.create(:subject => 'foo', :content => 'bar')
- post.subject = nil
- assert_nil post.subject
- assert !post.valid?
- post.subject = 'stashed_foo'
- assert_equal 'stashed_foo', post.subject
- end
-
- test 'translated class locale setting' do
- assert ActiveRecord::Base.respond_to?(:locale)
- assert_equal :en, I18n.locale
- assert_nil ActiveRecord::Base.locale
-
- I18n.locale = :de
- assert_equal :de, I18n.locale
- assert_nil ActiveRecord::Base.locale
-
- ActiveRecord::Base.locale = :es
- assert_equal :de, I18n.locale
- assert_equal :es, ActiveRecord::Base.locale
-
- I18n.locale = :fr
- assert_equal :fr, I18n.locale
- assert_equal :es, ActiveRecord::Base.locale
- end
-
- test "untranslated class responds to locale" do
- assert Blog.respond_to?(:locale)
- end
-
- test "to ensure locales in different classes are the same" do
- ActiveRecord::Base.locale = :de
- assert_equal :de, ActiveRecord::Base.locale
- assert_equal :de, Parent.locale
-
- Parent.locale = :es
- assert_equal :es, ActiveRecord::Base.locale
- assert_equal :es, Parent.locale
- end
-
- test "attribute saving goes by content locale and not global locale" do
- ActiveRecord::Base.locale = :de
- assert_equal :en, I18n.locale
- Post.create :subject => 'foo'
- assert_equal :de, Post.first.translations.first.locale
- end
-
- test "attribute loading goes by content locale and not global locale" do
- post = Post.create(:subject => 'foo')
- assert_nil ActiveRecord::Base.locale
-
- ActiveRecord::Base.locale = :de
- assert_equal :en, I18n.locale
- post.update_attribute(:subject, 'foo [de]')
- assert_equal 'foo [de]', Post.first.subject
-
- ActiveRecord::Base.locale = :en
- assert_equal 'foo', Post.first.subject
- end
-
- test "access content locale before setting" do
- Globalize::ActiveRecord::ActMacro.class_eval "remove_class_variable(:@@locale)"
- assert_nothing_raised { ActiveRecord::Base.locale }
- end
-
- test "available_locales" do
- Post.locale = :de
- post = Post.create(:subject => 'foo')
- Post.locale = :es
- post.update_attribute(:subject, 'bar')
- Post.locale = :fr
- post.update_attribute(:subject, 'baz')
- assert_equal [:de, :es, :fr], post.available_locales
- assert_equal [:de, :es, :fr], Post.first.available_locales
- end
-
- test "saving record correctly after post-save reload" do
- reloader = Reloader.create(:content => 'foo')
- assert_equal 'foo', reloader.content
- end
-
- test "including translations" do
- I18n.locale = :de
- Post.create(:subject => "Foo1", :content => "Bar1")
- Post.create(:subject => "Foo2", :content => "Bar2")
-
- class << Post
- def translations_included
- self.all(:include => :translations)
- end
- end
-
- default = Post.all.map { |x| [x.subject, x.content] }
- with_include = Post.translations_included.map { |x| [x.subject, x.content] }
- assert_equal default, with_include
- end
-
- test "setting multiple translations at once with options hash" do
- Post.locale = :de
- post = Post.create(:subject => "foo1", :content => "foo1")
- Post.locale = :en
- post.update_attributes(:subject => "bar1", :content => "bar1")
-
- options = { :de => {:subject => "foo2", :content => "foo2"},
- :en => {:subject => "bar2", :content => "bar2"} }
- post.set_translations options
- post.reload
-
- assert ["bar2", "bar2"], [post.subject, post.content]
- Post.locale = :de
- assert ["foo2", "foo2"], [post.subject, post.content]
- end
-
- test "setting only one translation with set_translations" do
- Post.locale = :de
- post = Post.create(:subject => "foo1", :content => "foo1")
- Post.locale = :en
- post.update_attributes(:subject => "bar1", :content => "bar1")
-
- options = { :en => { :subject => "bar2", :content => "bar2" } }
- post.set_translations options
- post.reload
-
- assert ["bar2", "bar2"], [post.subject, post.content]
- Post.locale = :de
- assert ["foo1", "foo1"], [post.subject, post.content]
- end
-
- test "setting only selected attributes with set_translations" do
- Post.locale = :de
- post = Post.create(:subject => "foo1", :content => "foo1")
- Post.locale = :en
- post.update_attributes(:subject => "bar1", :content => "bar1")
-
- options = { :de => { :content => "foo2" }, :en => { :subject => "bar2" } }
- post.set_translations options
- post.reload
-
- assert ["bar2", "bar1"], [post.subject, post.content]
- Post.locale = :de
- assert ["foo1", "foo2"], [post.subject, post.content]
- end
-
- test "setting invalid attributes raises ArgumentError" do
- Post.locale = :de
- post = Post.create(:subject => "foo1", :content => "foo1")
- Post.locale = :en
- post.update_attributes(:subject => "bar1", :content => "bar1")
-
- options = { :de => {:fake => "foo2"} }
- exception = assert_raise(ActiveRecord::UnknownAttributeError) do
- post.set_translations options
- end
- assert_equal "unknown attribute: fake", exception.message
- end
-
- test "reload accepting find options" do
- p = Post.create(:subject => "Foo", :content => "Bar")
- assert p.reload(:readonly => true, :lock => true)
- assert_raise(ArgumentError) { p.reload(:foo => :bar) }
- end
-
- test "dependent destroy of translation" do
- p = Post.create(:subject => "Foo", :content => "Bar")
- assert_equal 1, PostTranslation.count
- p.destroy
- assert_equal 0, PostTranslation.count
- end
-
- test "translating subclass of untranslated comment model" do
- translated_comment = TranslatedComment.create(:post => @post)
- assert_nothing_raised { translated_comment.translations }
- end
-
- test "modifiying translated comments works as expected" do
- I18n.locale = :en
- translated_comment = TranslatedComment.create(:post => @post, :content => 'foo')
- assert_equal 'foo', translated_comment.content
-
- I18n.locale = :de
- translated_comment.content = 'bar'
- assert translated_comment.save
- assert_equal 'bar', translated_comment.content
-
- I18n.locale = :en
- assert_equal 'foo', translated_comment.content
-
- assert_equal 2, translated_comment.translations.size
- end
-
- test "can create a proxy class for a namespaced model" do
- assert_nothing_raised do
- module Foo
- module Bar
- class Baz < ActiveRecord::Base
- translates :bumm
- end
- end
- end
- end
- end
-
- test "attribute translated before type cast" do
- Post.locale = :en
- post = Post.create(:subject => 'foo', :content => 'bar')
- Post.locale = :de
- post.update_attribute(:subject, "German foo")
- assert_equal 'German foo', post.subject_before_type_cast
- Post.locale = :en
- assert_equal 'foo', post.subject_before_type_cast
- end
-
- test "don't override existing translation class" do
- assert PostTranslation.new.respond_to?(:existing_method)
- end
-
- test "has_many and named scopes work with globalize" do
- blog = Blog.create
- assert_nothing_raised { blog.posts.foobar }
- end
-
- test "required_attribuets don't include non-translated attributes" do
- validations = [
- stub(:name => :name, :macro => :validates_presence_of),
- stub(:name => :email, :macro => :validates_presence_of)
- ]
- User.expects(:reflect_on_all_validations => validations)
- assert_equal [:name], User.required_attributes
- end
-
- test "attribute_names returns translated and regular attribute names" do
- Post.create :subject => "foo", :content => "bar"
- assert_equal Post.last.attribute_names.sort, %w[blog_id content id subject]
- end
-
- test "attributes returns translated and regular attributes" do
- Post.create :subject => "foo", :content => "bar"
- assert_equal Post.last.attributes.keys.sort, %w[blog_id content id subject]
- end
-
- test "to_xml includes translated fields" do
- Post.create :subject => "foo", :content => "bar"
- assert Post.last.to_xml =~ /subject/
- assert Post.last.to_xml =~ /content/
- end
-end
-
-# TODO error checking for fields that exist in main table, don't exist in
-# proxy table, aren't strings or text
-#
-# TODO allow finding by translated attributes in conditions?
-# TODO generate advanced dynamic finders?
diff --git a/vendor/plugins/globalize2/test/all.rb b/vendor/plugins/globalize2/test/all.rb
deleted file mode 100644
index ff467a176..000000000
--- a/vendor/plugins/globalize2/test/all.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-files = Dir[File.dirname(__FILE__) + '/**/*_test.rb']
-files.each { |file| require file } \ No newline at end of file
diff --git a/vendor/plugins/globalize2/test/data/models.rb b/vendor/plugins/globalize2/test/data/models.rb
deleted file mode 100644
index 5408d6e23..000000000
--- a/vendor/plugins/globalize2/test/data/models.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-#require 'ruby2ruby'
-#require 'parse_tree'
-#require 'parse_tree_extensions'
-#require 'pp'
-
-class PostTranslation < ActiveRecord::Base
- def existing_method ; end
-end
-
-class Post < ActiveRecord::Base
- translates :subject, :content
- validates_presence_of :subject
- named_scope :foobar, :conditions => { :title => "foobar" }
-end
-
-class Blog < ActiveRecord::Base
- has_many :posts, :order => 'id ASC'
-end
-
-class Parent < ActiveRecord::Base
- translates :content
-end
-
-class Child < Parent
-end
-
-class Comment < ActiveRecord::Base
- validates_presence_of :content
- belongs_to :post
-end
-
-class TranslatedComment < Comment
- translates :content
-end
-
-class UltraLongModelNameWithoutProper < ActiveRecord::Base
- translates :subject, :content
- validates_presence_of :subject
-end
-
-class Reloader < Parent
- after_create :do_reload
-
- def do_reload
- reload
- end
-end
-
-class Validatee < ActiveRecord::Base
- translates :string
-end
-
-class User < ActiveRecord::Base
- translates :name
- validates_presence_of :name, :email
-end
diff --git a/vendor/plugins/globalize2/test/data/no_globalize_schema.rb b/vendor/plugins/globalize2/test/data/no_globalize_schema.rb
deleted file mode 100644
index 379455ddb..000000000
--- a/vendor/plugins/globalize2/test/data/no_globalize_schema.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# This schema creates tables without columns for the translated fields
-ActiveRecord::Schema.define do
- create_table :blogs, :force => true do |t|
- t.string :name
- end
-
- create_table :posts, :force => true do |t|
- t.references :blog
- end
-end
-
diff --git a/vendor/plugins/globalize2/test/data/schema.rb b/vendor/plugins/globalize2/test/data/schema.rb
deleted file mode 100644
index 910dd0855..000000000
--- a/vendor/plugins/globalize2/test/data/schema.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-ActiveRecord::Schema.define do
- create_table :blogs, :force => true do |t|
- t.string :description
- end
-
- create_table :posts, :force => true do |t|
- t.references :blog
- end
-
- create_table :post_translations, :force => true do |t|
- t.string :locale
- t.references :post
- t.string :subject
- t.text :content
- end
-
- create_table :parents, :force => true do |t|
- end
-
- create_table :parent_translations, :force => true do |t|
- t.string :locale
- t.references :parent
- t.text :content
- t.string :type
- end
-
- create_table :comments, :force => true do |t|
- t.references :post
- end
-
- create_table :comment_translations, :force => true do |t|
- t.string :locale
- t.references :comment
- t.string :subject
- t.text :content
- end
-
- create_table :validatees, :force => true do |t|
- end
-
- create_table :validatee_translations, :force => true do |t|
- t.string :locale
- t.references :validatee
- t.string :string
- end
-
- create_table :users, :force => true do |t|
- t.string :email
- end
-
- create_table :users_translations, :force => true do |t|
- t.references :user
- t.string :name
- end
-end
diff --git a/vendor/plugins/globalize2/test/i18n/missing_translations_test.rb b/vendor/plugins/globalize2/test/i18n/missing_translations_test.rb
deleted file mode 100644
index 5d0ecd6fc..000000000
--- a/vendor/plugins/globalize2/test/i18n/missing_translations_test.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require File.dirname(__FILE__) + '/../test_helper'
-require 'i18n/missing_translations_log_handler'
-
-class MissingTranslationsTest < ActiveSupport::TestCase
- test "defines I18n.missing_translations_logger accessor" do
- assert I18n.respond_to?(:missing_translations_logger)
- end
-
- test "defines I18n.missing_translations_logger= writer" do
- assert I18n.respond_to?(:missing_translations_logger=)
- end
-end
-
-class TestLogger < String
- def warn(msg) self.concat msg; end
-end
-
-class LogMissingTranslationsTest < ActiveSupport::TestCase
- def setup
- @locale, @key, @options = :en, :foo, {}
- @exception = I18n::MissingTranslationData.new(@locale, @key, @options)
-
- @logger = TestLogger.new
- I18n.missing_translations_logger = @logger
- end
-
- test "still returns the exception message for MissingTranslationData exceptions" do
- result = I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
- assert_equal 'translation missing: en, foo', result
- end
-
- test "logs the missing translation to I18n.missing_translations_logger" do
- I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
- assert_equal 'translation missing: en, foo', @logger
- end
-end
diff --git a/vendor/plugins/globalize2/test/test_helper.rb b/vendor/plugins/globalize2/test/test_helper.rb
deleted file mode 100644
index 99a5d3950..000000000
--- a/vendor/plugins/globalize2/test/test_helper.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-$LOAD_PATH << File.expand_path( File.dirname(__FILE__) + '/../lib' )
-
-require 'rubygems'
-require 'test/unit'
-require 'active_record'
-require 'active_support'
-require 'active_support/test_case'
-require 'mocha'
-require 'globalize'
-# require 'validation_reflection'
-
-config = { :adapter => 'sqlite3', :database => ':memory:' }
-ActiveRecord::Base.establish_connection(config)
-
-class ActiveSupport::TestCase
- def reset_db!(schema_path = nil)
- schema_path ||= File.expand_path(File.dirname(__FILE__) + '/data/schema.rb')
- ActiveRecord::Migration.verbose = false
- ActiveRecord::Base.silence { load(schema_path) }
- end
-
- def assert_member(item, array)
- assert_block "Item #{item} is not in array #{array}" do
- array.member?(item)
- end
- end
-
- def assert_belongs_to(model, associated)
- assert model.reflect_on_all_associations(:belongs_to).detect { |association|
- association.name.to_s == associated.to_s
- }
- end
-
- def assert_has_many(model, associated)
- assert model.reflect_on_all_associations(:has_many).detect { |association|
- association.name.to_s == associated.to_s
- }
- end
-end
-
-module ActiveRecord
- module ConnectionAdapters
- class AbstractAdapter
- def index_exists?(table_name, column_name)
- indexes(table_name).any? { |index| index.name == index_name(table_name, column_name) }
- end
- end
- end
-end
-
-# module ActiveRecord
-# class BaseWithoutTable < Base
-# self.abstract_class = true
-#
-# def create_or_update
-# errors.empty?
-# end
-#
-# class << self
-# def columns()
-# @columns ||= []
-# end
-#
-# def column(name, sql_type = nil, default = nil, null = true)
-# columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
-# reset_column_information
-# end
-#
-# # Do not reset @columns
-# def reset_column_information
-# read_methods.each { |name| undef_method(name) }
-# @column_names = @columns_hash = @content_columns = @dynamic_methods_hash = @read_methods = nil
-# end
-# end
-# end
-# end \ No newline at end of file
diff --git a/vendor/plugins/interlock/.gitignore b/vendor/plugins/interlock/.gitignore
deleted file mode 100644
index 2cb5972a2..000000000
--- a/vendor/plugins/interlock/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-# MacOSX temp files
-.DS_Store
-
-# Vim swap files
-*.swp
-
-# test throwaway files
-test/integration/app/db/schema.rb
-test/integration/app/log/
-
diff --git a/vendor/plugins/interlock/CHANGELOG b/vendor/plugins/interlock/CHANGELOG
deleted file mode 100644
index 939594153..000000000
--- a/vendor/plugins/interlock/CHANGELOG
+++ /dev/null
@@ -1,10 +0,0 @@
-
-v1.4. Rails 2.1 compatibility. Store dependency trees per object, when possible. Use shorter namespaces for Memcached.
-
-v1.3. Add basic finder memoization.
-
-v1.2. Add compatibility with Memcached client.
-
-v1.1. Add :perform => false option. Cache :content_for. Test nested view_cache blocks. Don't assign conventional dependency when called from the view, because we don't know what the controller might have already specified.
-
-v1.0. First release.
diff --git a/vendor/plugins/interlock/LICENSE b/vendor/plugins/interlock/LICENSE
deleted file mode 100644
index 90eec26be..000000000
--- a/vendor/plugins/interlock/LICENSE
+++ /dev/null
@@ -1,184 +0,0 @@
-Academic Free License (AFL) v. 3.0
-
-This Academic Free License (the "License") applies to any original work
-of authorship (the "Original Work") whose owner (the "Licensor") has
-placed the following licensing notice adjacent to the copyright notice
-for the Original Work:
-
-Licensed under the Academic Free License version 3.0
-
-1) Grant of Copyright License. Licensor grants You a worldwide,
-royalty-free, non-exclusive, sublicensable license, for the duration of
-the copyright, to do the following:
-
-a) to reproduce the Original Work in copies, either alone or as part of
-a collective work;
-
-b) to translate, adapt, alter, transform, modify, or arrange the
-Original Work, thereby creating derivative works ("Derivative Works")
-based upon the Original Work;
-
-c) to distribute or communicate copies of the Original Work and
-Derivative Works to the public, under any license of your choice that
-does not contradict the terms and conditions, including Licensor's
-reserved rights and remedies, in this Academic Free License;
-
-d) to perform the Original Work publicly; and
-
-e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor grants You a worldwide,
-royalty-free, non-exclusive, sublicensable license, under patent claims
-owned or controlled by the Licensor that are embodied in the Original
-Work as furnished by the Licensor, for the duration of the patents, to
-make, use, sell, offer for sale, have made, and import the Original Work
-and Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the
-preferred form of the Original Work for making modifications to it and
-all available documentation describing how to modify the Original Work.
-Licensor agrees to provide a machine-readable copy of the Source Code of
-the Original Work along with each copy of the Original Work that
-Licensor distributes. Licensor reserves the right to satisfy this
-obligation by placing a machine-readable copy of the Source Code in an
-information repository reasonably calculated to permit inexpensive and
-convenient access by You for as long as Licensor continues to distribute
-the Original Work.
-
-4) Exclusions From License Grant. Neither the names of Licensor, nor the
-names of any contributors to the Original Work, nor any of their
-trademarks or service marks, may be used to endorse or promote products
-derived from this Original Work without express prior permission of the
-Licensor. Except as expressly stated herein, nothing in this License
-grants any license to Licensor's trademarks, copyrights, patents, trade
-secrets or any other intellectual property. No patent license is granted
-to make, use, sell, offer for sale, have made, or import embodiments of
-any patent claims other than the licensed claims defined in Section 2.
-No license is granted to the trademarks of Licensor even if such marks
-are included in the Original Work. Nothing in this License shall be
-interpreted to prohibit Licensor from licensing under terms different
-from this License any Original Work that Licensor otherwise would have a
-right to license.
-
-5) External Deployment. The term "External Deployment" means the use,
-distribution, or communication of the Original Work or Derivative Works
-in any way such that the Original Work or Derivative Works may be used
-by anyone other than You, whether those works are distributed or
-communicated to those persons or made available as an application
-intended for use over a network. As an express condition for the grants
-of license hereunder, You must treat any External Deployment by You of
-the Original Work or a Derivative Work as a distribution under section
-1(c).
-
-6) Attribution Rights. You must retain, in the Source Code of any
-Derivative Works that You create, all copyright, patent, or trademark
-notices from the Source Code of the Original Work, as well as any
-notices of licensing and any descriptive text identified therein as an
-"Attribution Notice." You must cause the Source Code for any Derivative
-Works that You create to carry a prominent Attribution Notice reasonably
-calculated to inform recipients that You have modified the Original
-Work.
-
-7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants
-that the copyright in and to the Original Work and the patent rights
-granted herein by Licensor are owned by the Licensor or are sublicensed
-to You under the terms of this License with the permission of the
-contributor(s) of those copyrights and patent rights. Except as
-expressly stated in the immediately preceding sentence, the Original
-Work is provided under this License on an "AS IS" BASIS and WITHOUT
-WARRANTY, either express or implied, including, without limitation, the
-warranties of non-infringement, merchantability or fitness for a
-particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
-WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential
-part of this License. No license to the Original Work is granted by this
-License except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal
-theory, whether in tort (including negligence), contract, or otherwise,
-shall the Licensor be liable to anyone for any indirect, special,
-incidental, or consequential damages of any character arising as a
-result of this License or the use of the Original Work including,
-without limitation, damages for loss of goodwill, work stoppage,
-computer failure or malfunction, or any and all other commercial damages
-or losses. This limitation of liability shall not apply to the extent
-applicable law prohibits such limitation.
-
-9) Acceptance and Termination. If, at any time, You expressly assented
-to this License, that assent indicates your clear and irrevocable
-acceptance of this License and all of its terms and conditions. If You
-distribute or communicate copies of the Original Work or a Derivative
-Work, You must make a reasonable effort under the circumstances to
-obtain the express assent of recipients to the terms of this License.
-This License conditions your rights to undertake the activities listed
-in Section 1, including your right to create Derivative Works based upon
-the Original Work, and doing so without honoring these terms and
-conditions is prohibited by copyright law and international treaty.
-Nothing in this License is intended to affect copyright exceptions and
-limitations (including "fair use" or "fair dealing"). This License shall
-terminate immediately and You may no longer exercise any of the rights
-granted to You by this License upon your failure to honor the conditions
-in Section 1(c).
-
-10) Termination for Patent Action. This License shall terminate
-automatically and You may no longer exercise any of the rights granted
-to You by this License as of the date You commence an action, including
-a cross-claim or counterclaim, against Licensor or any licensee alleging
-that the Original Work infringes a patent. This termination provision
-shall not apply for an action alleging patent infringement by
-combinations of the Original Work with other software or hardware.
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating
-to this License may be brought only in the courts of a jurisdiction
-wherein the Licensor resides or in which Licensor conducts its primary
-business, and under the laws of that jurisdiction excluding its
-conflict-of-law provisions. The application of the United Nations
-Convention on Contracts for the International Sale of Goods is expressly
-excluded. Any use of the Original Work outside the scope of this License
-or after its termination shall be subject to the requirements and
-penalties of copyright or patent law in the appropriate jurisdiction.
-This section shall survive the termination of this License.
-
-12) Attorneys' Fees. In any action to enforce the terms of this License
-or seeking damages relating thereto, the prevailing party shall be
-entitled to recover its costs and expenses, including, without
-limitation, reasonable attorneys' fees and costs incurred in connection
-with such action, including any appeal of such action. This section
-shall survive the termination of this License.
-
-13) Miscellaneous. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this License,
-whether in upper or lower case, means an individual or a legal entity
-exercising rights under, and complying with all of the terms of, this
-License. For legal entities, "You" includes any entity that controls, is
-controlled by, or is under common control with you. For purposes of this
-definition, "control" means (i) the power, direct or indirect, to cause
-the direction or management of such entity, whether by contract or
-otherwise, or (ii) ownership of fifty percent (50%) or more of the
-outstanding shares, or (iii) beneficial ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not
-otherwise restricted or conditioned by this License or by law, and
-Licensor promises not to interfere with or be responsible for such uses
-by You.
-
-16) Modification of This License. This License is Copyright (c) 2005
-Lawrence Rosen. Permission is granted to copy, distribute, or
-communicate this License without modification. Nothing in this License
-permits You to modify this License as applied to the Original Work or to
-Derivative Works. However, You may modify the text of this License and
-copy, distribute or communicate your modified version (the "Modified
-License") and apply it to other original works of authorship subject to
-the following conditions: (i) You may not indicate in any way that your
-Modified License is the "Academic Free License" or "AFL" and you may not
-use those names in the name of your Modified License; (ii) You must
-replace the notice specified in the first paragraph above with the
-notice "Licensed under <insert your license name here>" or with a notice
-of your own that is not confusingly similar to the notice in this
-License; and (iii) You may not claim that your original works are open
-source software unless your Modified License has been approved by Open
-Source Initiative (OSI) and You comply with its license review and
-certification process.
-
diff --git a/vendor/plugins/interlock/Manifest b/vendor/plugins/interlock/Manifest
deleted file mode 100644
index 79ce97764..000000000
--- a/vendor/plugins/interlock/Manifest
+++ /dev/null
@@ -1,85 +0,0 @@
-CHANGELOG
-examples/memcached.yml
-init.rb
-lib/interlock/action_controller.rb
-lib/interlock/action_view.rb
-lib/interlock/active_record.rb
-lib/interlock/config.rb
-lib/interlock/core_extensions.rb
-lib/interlock/finders.rb
-lib/interlock/interlock.rb
-lib/interlock/lock.rb
-lib/interlock/pass_through_store.rb
-lib/interlock.rb
-LICENSE
-Manifest
-README
-tasks/interlock.rake
-test/integration/app/app/controllers/application.rb
-test/integration/app/app/controllers/eval_controller.rb
-test/integration/app/app/controllers/items_controller.rb
-test/integration/app/app/helpers/application_helper.rb
-test/integration/app/app/helpers/eval_helper.rb
-test/integration/app/app/helpers/items_helper.rb
-test/integration/app/app/models/item.rb
-test/integration/app/app/views/items/detail.rhtml
-test/integration/app/app/views/items/list.rhtml
-test/integration/app/app/views/items/recent.rhtml
-test/integration/app/app/views/items/show.rhtml
-test/integration/app/app/views/layouts/application.html.erb
-test/integration/app/app/views/shared/_related.rhtml
-test/integration/app/config/boot.rb
-test/integration/app/config/database.yml
-test/integration/app/config/environment.rb
-test/integration/app/config/environments/development.rb
-test/integration/app/config/environments/production.rb
-test/integration/app/config/environments/test.rb
-test/integration/app/config/initializers/inflections.rb
-test/integration/app/config/initializers/mime_types.rb
-test/integration/app/config/memcached.yml
-test/integration/app/config/routes.rb
-test/integration/app/db/migrate/001_create_items.rb
-test/integration/app/doc/README_FOR_APP
-test/integration/app/public/404.html
-test/integration/app/public/422.html
-test/integration/app/public/500.html
-test/integration/app/public/dispatch.cgi
-test/integration/app/public/dispatch.fcgi
-test/integration/app/public/dispatch.rb
-test/integration/app/public/favicon.ico
-test/integration/app/public/images/rails.png
-test/integration/app/public/index.html
-test/integration/app/public/javascripts/application.js
-test/integration/app/public/javascripts/controls.js
-test/integration/app/public/javascripts/dragdrop.js
-test/integration/app/public/javascripts/effects.js
-test/integration/app/public/javascripts/prototype.js
-test/integration/app/public/robots.txt
-test/integration/app/Rakefile
-test/integration/app/README
-test/integration/app/script/about
-test/integration/app/script/console
-test/integration/app/script/destroy
-test/integration/app/script/generate
-test/integration/app/script/performance/benchmarker
-test/integration/app/script/performance/profiler
-test/integration/app/script/performance/request
-test/integration/app/script/plugin
-test/integration/app/script/process/inspector
-test/integration/app/script/process/reaper
-test/integration/app/script/process/spawner
-test/integration/app/script/runner
-test/integration/app/script/server
-test/integration/app/test/fixtures/items.yml
-test/integration/app/test/functional/eval_controller_test.rb
-test/integration/app/test/functional/items_controller_test.rb
-test/integration/app/test/test_helper.rb
-test/integration/app/test/unit/item_test.rb
-test/integration/server_test.rb
-test/setup.rb
-test/teardown.rb
-test/test_helper.rb
-test/unit/finder_test.rb
-test/unit/interlock_test.rb
-test/unit/lock_test.rb
-TODO
diff --git a/vendor/plugins/interlock/README b/vendor/plugins/interlock/README
deleted file mode 100644
index ca15b35d2..000000000
--- a/vendor/plugins/interlock/README
+++ /dev/null
@@ -1,130 +0,0 @@
-
-Interlock
-
-A Rails plugin for maintainable and high-efficiency caching.
-
-== License
-
-Copyright 2007, 2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file. Portions copyright 2006 Chris Wanstrath and used with permission.
-
-The public certificate for the gem is here[http://blog.evanweaver.com/files/evan_weaver-original-public_cert.pem].
-
-If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.
-
-== Requirements
-
-* memcached (http://www.danga.com/memcached)
-* memcache-client gem
-* Rails 2.1
-
-== Features
-
-Interlock is an intelligent fragment cache for Rails.
-
-It works by making your view fragments and associated controller blocks march along together. If a fragment is fresh, the controller behavior won't run. This eliminates duplicate effort from your request cycle. Your controller blocks run so infrequently that you can use regular ActiveRecord finders and not worry about object caching at all.
-
-Invalidations are automatically tracked based on the model lifecyle, and you can scope any block to an arbitrary level. Interlock also caches <tt>content_for</tt> calls, unlike regular Rails, and can optionally cache simple finders.
-
-Interlock uses a tiered caching layer so that multiple lookups of a key only hit memcached once per request.
-
-== Installation
-
-First, compile and install memcached itself. Get a memcached server running.
-
-You also need either <tt>memcache-client</tt> or {memcached}[http://blog.evanweaver.com/files/doc/fauna/memcached]:
- sudo gem install memcache-client
-
-Then, install the plugin:
- script/plugin install git://github.com/fauna/interlock.git
-
-Lastly, configure your Rails app for memcached by creating a <tt>config/memcached.yml</tt> file. The format is compatible with Cache_fu:
-
- defaults:
- namespace: myapp
- sessions: false
- client: memcache-client
- development:
- servers:
- - 127.0.0.1:11211 # Default host and port
- production:
- servers:
- - 10.12.128.1:11211
- - 10.12.128.2:11211
-
-Now you're ready to go.
-
-Note that if you have the {memcached}[http://blog.evanweaver.com/files/doc/fauna/memcached] client, you can use <tt>client: memcached</tt> for better performance.
-
-== Usage
-
-Interlock provides two similar caching methods: <tt>behavior_cache</tt> for controllers and <tt>view_cache</tt> for views. They both accept an optional list or hash of model dependencies, and an optional <tt>:tag</tt> keypair. <tt>view_cache</tt> also accepts a <tt>:ttl</tt> keypair.
-
-The simplest usage doesn't require any parameters. In the controller:
-
- class ItemsController < ActionController::Base
-
- def slow_action
- behavior_cache do
- @items = Item.find(:all, :conditions => "be slow")
- end
- end
-
- end
-
-Now, in the view, wrap the largest section of ERB you can find that uses data from <tt>@items</tt> in a <tt>view_cache</tt> block. No other part of the view can refer to <tt>@items</tt>, because <tt>@items</tt> won't get set unless the cache is stale.
-
- <% @title = "My Sweet Items" %>
-
- <% view_cache do %>
- <% @items.each do |item| %>
- <h1><%= item.name %></h1>
- <% end %>
- <% end %>
-
-You have to do them both.
-
-This automatically registers a caching dependency on Item for <tt>slow_action</tt>. The controller block won't run if the <tt>slow_action</tt> view fragment is fresh, and the view fragment will only get invalidated when an Item is changed.
-
-You can use multiple instance variables in one block, of course. Just make sure the <tt>behavior_cache</tt> provides whatever the <tt>view_cache</tt> uses.
-
-See ActionController::Base and ActionView::Helpers::CacheHelper for more details.
-
-== Caching finders
-
-Interlock 1.3 adds the ability to cache simple finder lookups. Add this line in <tt>config/memcached.yml</tt>:
-
- with_finders: true
-
-Now, whenever you call <b>find</b>, <b>find_by_id</b>, or <b>find_all_by_id</b> with a single id or an array of ids, the cache will be used. The cache key for each record invalidates when the record is saved or destroyed. Memcached's multiget mode is used for maximum performance.
-
-If you pass any parameters other than ids, or use dynamic finders, the cache will not be used. This means that <tt>:include</tt> works as expected and does not require complicated invalidation.
-
-See Interlock::Finders for more.
-
-== Notes
-
-You will not see any actual cache reuse in development mode unless you set <tt>config.action_controller.perform_caching = true</tt> in <tt>config/environments/development.rb</tt>.
-
-<b>If you have custom <tt>render</tt> calls in the controller, they must be outside the <tt>behavior_cache</tt> blocks.</b> No exceptions. For example:
-
- def profile
- behavior_cache do
- @items = Item.find(:all, :conditions => "be slow")
- end
- render :action => 'home'
- end
-
-You can write custom invalidation rules if you really want to, but try hard to avoid it; it has a significant cost in long-term maintainability.
-
-Also, Interlock obeys the <tt>ENV['RAILS_ASSET_ID']</tt> setting, so if you need to blanket-invalidate all your caches, just change <tt>RAILS_ASSET_ID</tt> (for example, you could have it increment on every deploy).
-
-== Further resources
-
-* http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/
-* http://www.socialtext.net/memcached/index.cgi?faq
-
-== Reporting problems
-
-The support forum is here[http://github.com/fauna/interlock/issues].
-
-Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
diff --git a/vendor/plugins/interlock/Rakefile b/vendor/plugins/interlock/Rakefile
deleted file mode 100644
index d01b405c1..000000000
--- a/vendor/plugins/interlock/Rakefile
+++ /dev/null
@@ -1,34 +0,0 @@
-
-require 'echoe'
-
-Echoe.new("interlock") do |p|
- p.project = "fauna"
- p.summary = "A Rails plugin for maintainable and high-efficiency caching."
- p.url = "http://blog.evanweaver.com/files/doc/fauna/interlock/"
- p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
- p.test_pattern = ["test/integration/*.rb", "test/unit/*.rb"]
- p.rdoc_pattern = ["README", "CHANGELOG", "TODO", "LICENSE", "lib/interlock/lock.rb", "lib/interlock/interlock.rb", "lib/interlock/action_controller.rb", "lib/interlock/active_record.rb", "lib/interlock/finders.rb", "lib/interlock/action_view.rb", "lib/interlock/config.rb"]
- p.clean_pattern += ['test/integration/app/coverage', 'test/integration/app/db/schema.rb',
- 'test/integration/app/vendor/plugins/interlock']
-end
-
-desc "Run all the tests in production and development mode both"
-task "test_all" do
- ['memcache-client', 'memcached'].each do |client|
- ENV['CLIENT'] = client
- ['false', 'true'].each do |finder|
- ENV['FINDERS'] = finder
- ['false', 'true'].each do |env|
- ENV['PRODUCTION'] = env
- mode = env == 'false' ? "Development" : "Production"
- STDERR.puts "#{'='*80}\n#{mode} mode, #{client}, finders #{finder}\n#{'='*80}"
- system("rake test:multi_rails:all")
- end
- end
- end
-end
-
-task "tail" do
- log = "test/integration/app/log/development.log"
- system("touch #{log} && tail -f #{log} | grep interlock")
-end \ No newline at end of file
diff --git a/vendor/plugins/interlock/TEST_HOWTO b/vendor/plugins/interlock/TEST_HOWTO
deleted file mode 100644
index f52491fdd..000000000
--- a/vendor/plugins/interlock/TEST_HOWTO
+++ /dev/null
@@ -1,20 +0,0 @@
-Steps to run the tests.
-
- $ mkdir test/integration/app/log
- $ RAILS_ENV=development; export RAILS_ENV
-
-If you don't run mysql as root, edit the user in test/integration/app/config/database.yml,
-and grant him permissions to create new tables (/opt/local/lib/mysql5/bin/mysql -u root -p).
-
- $ sudo gem install multi_rails echoe
- $ rake
-
-To run an individual suite, first run:
-
- $ ruby test/setup.rb
-
-Then:
-
- $ ruby test/unit/finder_test.rb
-
-etc.
diff --git a/vendor/plugins/interlock/TODO b/vendor/plugins/interlock/TODO
deleted file mode 100644
index 23250e15f..000000000
--- a/vendor/plugins/interlock/TODO
+++ /dev/null
@@ -1,11 +0,0 @@
-
-Soon:
-
-* Throw a nice error message when the requested memcache client cannot be found
-* Add anti-dogpiling logic
-
-Someday:
-
-* Add test coverage of STI child classes
-* Figure out how to cache Builder blocks
-* Figure out how to cache RJS
diff --git a/vendor/plugins/interlock/examples/memcached.yml b/vendor/plugins/interlock/examples/memcached.yml
deleted file mode 100644
index ce72bb1d6..000000000
--- a/vendor/plugins/interlock/examples/memcached.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-defaults:
- namespace: myapp
- sessions: false
- client: memcache-client
-development:
- servers:
- - 127.0.0.1:11211
-production:
- servers
- - 10.12.128.1
- - 10.12.128.2
diff --git a/vendor/plugins/interlock/init.rb b/vendor/plugins/interlock/init.rb
deleted file mode 100644
index dfd46ab31..000000000
--- a/vendor/plugins/interlock/init.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-
-require 'interlock'
diff --git a/vendor/plugins/interlock/lib/interlock.rb b/vendor/plugins/interlock/lib/interlock.rb
deleted file mode 100644
index e4cfb91c3..000000000
--- a/vendor/plugins/interlock/lib/interlock.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-
-module Interlock
-end
-
-require 'interlock/core_extensions'
-require 'interlock/config'
-require 'interlock/interlock'
-require 'interlock/lock'
-require 'interlock/pass_through_store'
-require 'interlock/action_controller'
-require 'interlock/action_view'
-require 'interlock/finders'
-require 'interlock/active_record'
-
-begin
- if defined?(JRUBY_VERSION)
- require 'memcache-client'
- else
- require 'memcached'
- end
-rescue LoadError
-end
-
-unless ActionController::Base.perform_caching
- RAILS_DEFAULT_LOGGER.warn "** interlock warning; config.perform_caching == false"
-end
-
-Interlock::Config.run!
diff --git a/vendor/plugins/interlock/lib/interlock/action_controller.rb b/vendor/plugins/interlock/lib/interlock/action_controller.rb
deleted file mode 100644
index 73ed81843..000000000
--- a/vendor/plugins/interlock/lib/interlock/action_controller.rb
+++ /dev/null
@@ -1,253 +0,0 @@
-
-module ActionController #:nodoc:
- class Base
-
-
-=begin rdoc
-Build the fragment key from a particular context. This must be deterministic and stateful except for the tag. We can't scope the key to arbitrary params because the view doesn't have access to which are relevant and which are not.
-
-Note that the tag can be pretty much any object. Define <tt>to_interlock_tag</tt> if you need custom tagging for some class. ActiveRecord::Base already has it defined appropriately.
-
-If you pass an Array of symbols as the tag, it will get value-mapped onto params and sorted. This makes granular scoping easier, although it doesn't sidestep the normal blanket invalidations.
-=end
-
- def caching_key(ignore = nil, tag = nil)
- ignore = Array(ignore)
- ignore = Interlock::SCOPE_KEYS if ignore.include? :all
-
- if (Interlock::SCOPE_KEYS - ignore).empty? and !tag
- raise Interlock::UsageError, "You must specify a :tag if you are ignoring the entire default scope."
- end
-
- if tag.is_a? Array and tag.all? {|x| x.is_a? Symbol}
- tag = tag.sort_by do |key|
- key.to_s
- end.map do |key|
- params[key].to_interlock_tag
- end.join(";")
- end
-
- Interlock.caching_key(
- ignore.include?(:controller) ? 'any' : controller_name,
- ignore.include?(:action) ? 'any' : action_name,
- ignore.include?(:id) ? 'all' : params[:id],
- tag
- )
- end
-
-
-=begin rdoc
-
-<tt>behavior_cache</tt> marks a controller block for caching. It accepts a list of class dependencies for invalidation, as well as as <tt>:tag</tt> and <tt>:ignore</tt> keys for explicit fragment scoping. It does not accept a <tt>:ttl</tt> key.
-
-Please note that the behavior of nested <tt>behavior_cache</tt> blocks is undefined.
-
-== Declaring dependencies
-
-You can declare non-default invalidation dependencies by passing models to <tt>behavior_cache</tt> (you can also pass them to <tt>view_cache</tt>, but you should only do that if you are caching a fragment without an associated behavior block in the controller).
-
-<b>No dependencies (cache never invalidates):</b>
- behavior_cache nil do
- end
-
-<b>Invalidate on any Media change:</b>
- behavior_cache Media do
- end
-
-<b>Invalidate on any Media or Item change:</b>
- behavior_cache Media, Item do
- end
-
-<b>Invalidate on Item changes if the Item <tt>id</tt> matches the current <tt>params[:id]</tt> value:</b>
- behavior_cache Item => :id do
- end
-
-You do not have to pass the same dependencies to <tt>behavior_cache</tt> and <tt>view_cache</tt> even for the same action. The set union of both dependency lists will be used.
-
-== Narrowing scope and caching multiple blocks
-
-Sometimes you need to cache multiple blocks in a controller, or otherwise get a more fine-grained scope. Interlock provides the <tt>:tag</tt> key for this purpose. <tt>:tag</tt> accepts either an array of symbols, which are mapped to <tt>params</tt> values, or an arbitrary object, which is converted to a string identifier. <b>Your corresponding behavior caches and view caches must have identical <tt>:tag</tt> values for the interlocking to take effect.</b>
-
-Note that <tt>:tag</tt> can be used to scope caches. You can simultaneously cache different versions of the same block, differentiating based on params or other logic. This is great for caching per-user, for example:
-
- def profile
- @user = current_user
- behavior_cache :tag => @user do
- @items = @user.items
- end
- end
-
-In the view, use the same <tt>:tag</tt> value (<tt>@user</tt>). Note that <tt>@user</tt> must be set outside of the behavior block in the controller, because its contents are used to decide whether to run the block in the first place.
-
-This way each user will see only their own cache. Pretty neat.
-
-== Broadening scope
-
-Sometimes the default scope (<tt>controller</tt>, <tt>action</tt>, <tt>params[:id]</tt>) is too narrow. For example, you might share a partial across actions, and set up its data via a filter. By default, Interlock will cache a separate version of it for each action. To avoid this, you can use the <tt>:ignore</tt> key, which lets you list parts of the default scope to ignore:
-
- before_filter :recent
-
- private
-
- def recent
- behavior_cache :ignore => :action do
- @recent = Item.find(:all, :limit => 5, :order => 'updated_at DESC')
- end
- end
-
-Valid values for <tt>:ignore</tt> are <tt>:controller</tt>, <tt>:action</tt>, <tt>:id</tt>, and <tt>:all</tt>. You can pass an array of multiple values. <b>Just like with <tt>:tag</tt>, your corresponding behavior caches and view caches must have identical <tt>:ignore</tt> values.</b> Note that cache blocks with <tt>:ignore</tt> values still obey the regular invalidation rules.
-
-A good way to get started is to just use the default scope. Then <tt>grep</tt> in the production log for <tt>interlock</tt> and see what keys are being set and read. If you see lots of different keys go by for data that you know is the same, then set some <tt>:ignore</tt> values.
-
-== Skipping caching
-
-You can pass <tt>:perform => false</tt> to disable caching, for example, in a preview action. Note that <tt>:perform</tt> only responds to <tt>false</tt>, not <tt>nil</tt>. This allows for handier view reuse because you can set <tt>:perform</tt> to an instance variable and it will still cache if the instance variable is not set:
-
- def preview
- @perform = false
- behavior_cache :perform => @perform do
- end
- render :action => 'show'
- end
-
-And in the <tt>show.html.erb</tt> view:
-
- <% view_cache :perform => @perform do %>
- <% end %>
-
-=end
-
- def behavior_cache(*args)
- conventional_class = begin; controller_name.classify.constantize; rescue NameError; end
- options, dependencies = Interlock.extract_options_and_dependencies(args, conventional_class)
-
- raise Interlock::UsageError, ":ttl has no effect in a behavior_cache block" if options[:ttl]
-
- Interlock.say "key", "yo: #{options.inspect} -- #{dependencies.inspect}"
-
- key = caching_key(options.value_for_indifferent_key(:ignore), options.value_for_indifferent_key(:tag))
-
- if options[:perform] == false || Interlock.config[:disabled]
- Interlock.say key, "is not cached"
- yield
- else
- Interlock.register_dependencies(dependencies, key)
-
- # See if the fragment exists, and run the block if it doesn't.
- unless read_fragment(key, :assign_content_for => false)
- Interlock.say key, "is running the controller block"
- yield
- end
- end
- end
-
- #:stopdoc:
- alias :caching :behavior_cache # Deprecated
- #:startdoc:
-
- private
-
- #
- # Callback to reset the local cache.
- #
- def clear_interlock_local_cache
- Interlock.local_cache = ::ActiveSupport::Cache::MemoryStore.new
- Interlock.log "** cleared interlock local cache"
- end
-
- # Should be registered first in the chain
- prepend_before_filter :clear_interlock_local_cache
-
- end
-
- module Caching #:nodoc:
- module Fragments
-
- #
- # Replaces Rail's write_fragment method. Avoids extra checks for regex keys
- # which are unsupported, adds more detailed logging information, stores writes
- # in the local process cache too to avoid duplicate memcached requests, and
- # includes the content_for cache in the fragment.
- #
- def write_fragment(key, block_content, options = nil)
- return unless perform_caching
-
- content = [block_content, @template.cached_content_for]
-
- cache_store.write(key, content, options)
- Interlock.local_cache.write(key, content, options)
-
- Interlock.say key, "wrote"
-
- block_content
- end
-
- #
- # Replaces Rail's read_fragment method. Avoids checks for regex keys,
- # which are unsupported, adds more detailed logging information, checks
- # the local process cache before hitting memcached, and restores the
- # content_for cache. Hits on memcached are also stored back locally to
- # avoid duplicate requests.
- #
- def read_fragment(key, options = nil)
- return unless perform_caching
-
- begin
- if content = Interlock.local_cache.read(key, options)
- # Interlock.say key, "read from local cache"
- elsif content = cache_store.read(key, options)
- raise Interlock::FragmentConsistencyError, "#{key} expected Array but got #{content.class}" unless content.is_a? Array
- Interlock.say key, "read from memcached"
- Interlock.local_cache.write(key, content, options)
- else
- # Not found
- return nil
- end
-
- raise Interlock::FragmentConsistencyError, "#{key}::content expected String but got #{content.first.class}" unless content.first.is_a? String
-
- options ||= {}
- # Note that 'nil' is considered true for :assign_content_for
- if options[:assign_content_for] != false and content.last
- # Extract content_for variables
- content.last.each do |name, value|
- raise Interlock::FragmentConsistencyError, "#{key}::content_for(:#{name}) expected String but got #{value.class}" unless value.is_a? String
- # We'll just call the helper because that will handle nested view_caches properly.
- @template.send(:content_for, name, value)
- end
- end
-
- content.first
- rescue Interlock::FragmentConsistencyError => e
- # Delete the bogus key
- Interlock.invalidate(key)
- # Reraise the error
- raise e
- end
- end
-
- end
-
- # With Rails 2.1 action caching, we need to slip in our :expire param into the ActionCacheFilter options, so that when we
- # write_fragment we can pass that in and allow shane's #{key}_expiry to take effect
- # (see def write in interlock/config.rb)
- module Actions
-
- module ClassMethods
- def caches_action(*actions)
- return unless cache_configured?
- options = actions.extract_options!
- around_filter(ActionCacheFilter.new(:cache_path => options.delete(:cache_path), :expire => options.delete(:expire)), {:only => actions}.merge(options))
- end
- end
-
- class ActionCacheFilter #:nodoc:
- def after(controller)
- return if controller.rendered_action_cache || !caching_allowed(controller)
- controller.write_fragment(controller.action_cache_path.path, controller.response.body, :expire => @options[:expire]) # pass in our :expire
- end
- end
- end
-
- end
-end
diff --git a/vendor/plugins/interlock/lib/interlock/action_view.rb b/vendor/plugins/interlock/lib/interlock/action_view.rb
deleted file mode 100644
index 12497b2ef..000000000
--- a/vendor/plugins/interlock/lib/interlock/action_view.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-
-module ActionView #:nodoc:
- class Base #:nodoc:
- attr_accessor :cached_content_for
- end
-
- module Helpers #:nodoc:
- module CacheHelper
-
-=begin rdoc
-
-<tt>view_cache</tt> marks a corresponding view block for caching. It accepts <tt>:tag</tt> and <tt>:ignore</tt> keys for explicit scoping, as well as a <tt>:ttl</tt> key and a <tt>:perform</tt> key.
-
-You can specify dependencies in <tt>view_cache</tt> if you really want to. Note that unlike <tt>behavior_cache</tt>, <tt>view_cache</tt> doesn't set up any default dependencies.
-
-Nested <tt>view_cache</tt> blocks work fine. You would only need to nest if you had a slowly invalidating block contained in a more quickly invalidating block; otherwise there's no benefit.
-
-Finally, caching <tt>content_for</tt> within a <tt>view_cache</tt> works, unlike regular Rails. It even works in nested caches.
-
-== Setting a TTL
-
-Use the <tt>:ttl</tt> key to specify a maximum time-to-live, in seconds:
-
- <% view_cache :ttl => 5.minutes do %>
- <% end %>
-
-Note that the cached item is not guaranteed to live this long. An invalidation rule could trigger first, or memcached could eject the item early due to the LRU.
-
-== View caching without action caching
-
-It's fine to use a <tt>view_cache</tt> block without a <tt>behavior_cache</tt> block. For example, to mimic regular fragment cache behavior, but take advantage of memcached's <tt>:ttl</tt> support, call:
-
- <% view_cache :ignore => :all, :tag => 'sidebar', :ttl => 5.minutes do %>
- <% end %>
-
-== Dependencies, scoping, and other options
-
-See ActionController::Base for explanations of the rest of the options. The <tt>view_cache</tt> and <tt>behavior_cache</tt> APIs are identical except for setting the <tt>:ttl</tt>, which can only be done in the view, and the default dependency, which is only set by <tt>behavior_cache</tt>.
-
-=end
- def view_cache(*args, &block)
- # conventional_class = begin; controller.controller_name.classify.constantize; rescue NameError; end
- options, dependencies = Interlock.extract_options_and_dependencies(args, nil)
-
- key = controller.caching_key(options.value_for_indifferent_key(:ignore), options.value_for_indifferent_key(:tag))
-
- if options[:perform] == false || Interlock.config[:disabled]
- # Interlock.say key, "is not cached"
- block.call
- else
- Interlock.register_dependencies(dependencies, key)
-
- # Interlock.say key, "is rendering"
-
- @cached_content_for, previous_cached_content_for = {}, @cached_content_for
-
- cache key, :ttl => (options.value_for_indifferent_key(:ttl) or Interlock.config[:ttl]), &block
-
- # This is tricky. If we were already caching content_fors in a parent block, we need to
- # append the content_fors set in the inner block to those already set in the outer block.
- if previous_cached_content_for
- @cached_content_for.each do |key, value|
- previous_cached_content_for[key] = "#{previous_cached_content_for[key]}#{value}"
- end
- end
-
- # Restore the cache state
- @cached_content_for = previous_cached_content_for
- end
- end
-
- #:stopdoc:
- alias :caching :view_cache # Deprecated
- #:startdoc:
-
- end
-
-
- module CaptureHelper
- #
- # Override content_for so we can cache the instance variables it sets along with the fragment.
- #
- def content_for(name, content = nil, &block)
- ivar = "@content_for_#{name}"
- existing_content = instance_variable_get(ivar).to_s
- this_content = (block_given? ? capture(&block) : content)
-
- # If we are in a view_cache block, cache what we added to this instance variable
- if @cached_content_for
- @cached_content_for[name] = "#{@cached_content_for[name]}#{this_content}"
- end
-
- instance_variable_set(ivar, existing_content + this_content)
- end
- end
-
- end
-end
diff --git a/vendor/plugins/interlock/lib/interlock/active_record.rb b/vendor/plugins/interlock/lib/interlock/active_record.rb
deleted file mode 100644
index 16a448aaa..000000000
--- a/vendor/plugins/interlock/lib/interlock/active_record.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-
-module ActiveRecord #:nodoc:
- class Base
-
- @@nil_sentinel = :_nil
-
- class << self # Class methods
- def update_counters_with_expiry(id, counters)
- update_counters_without_expiry(id, counters)
- find(id).expire_interlock_keys
- end
- alias_method_chain :update_counters, :expiry
- end
-
- #
- # Convert this record to a tag string.
- #
- def to_interlock_tag
- "#{self.class.name}-#{self.id}".escape_tag_fragment
- end
-
- #
- # The expiry callback.
- #
- def expire_interlock_keys
- return if Interlock.config[:disabled] or (defined? CGI::Session::ActiveRecordStore and is_a? CGI::Session::ActiveRecordStore::Session)
-
- # Fragments
- self.expire_interlock_keys_for_dependency(Interlock.dependency_key(self.class.base_class, :all, nil))
- self.expire_interlock_keys_for_dependency(Interlock.dependency_key(self.class.base_class, :id, "::::#{to_param}:"))
-
- # Models
- if Interlock.config[:with_finders]
- key = self.class.base_class.caching_key(self.id)
- Interlock.say key, 'invalidated with finders', 'model'
- Interlock.invalidate key
- end
- end
-
- before_save :expire_interlock_keys
- after_save :expire_interlock_keys
- after_destroy :expire_interlock_keys
-
- #
- # Reload. Expires the cache and force reload from db.
- #
- def reload_with_expiry(*args)
- expire_interlock_keys
- reload_without_expiry(*args)
- end
- alias_method_chain :reload, :expiry
-
- def expire_interlock_keys_for_dependency(dependency_key)
- (CACHE.get(dependency_key) || {}).each do |key, scope|
- Interlock.say key, "invalidated by rule #{self.class} -> #{scope.inspect}."
- Interlock.invalidate key
- end
- end
-
- end
-end
diff --git a/vendor/plugins/interlock/lib/interlock/config.rb b/vendor/plugins/interlock/lib/interlock/config.rb
deleted file mode 100644
index 956002dd5..000000000
--- a/vendor/plugins/interlock/lib/interlock/config.rb
+++ /dev/null
@@ -1,168 +0,0 @@
-#require 'ehcache'
-module Interlock
-
- DEFAULTS = {
- :ttl => 1.day,
- :namespace => 'app',
- :servers => ['127.0.0.1:11211'],
- :client => 'memcache-client',
- :with_finders => false
- }
-
- CLIENT_KEYS =[
- :prefix_key,
- :distribution,
- :verify_key,
- :tcp_nodelay,
- :hash,
- :hash_with_prefix_key,
- :show_backtraces,
- :default_ttl,
- :ketama_weighted,
- :retry_timeout,
- :default_weight,
- :buffer_requests,
- :timeout,
- :sort_hosts,
- :cache_lookups,
- :connect_timeout,
- :no_block,
- :failover,
- :support_cas,
- :namespace
- ]
-
- mattr_accessor :config
- @@config = DEFAULTS
-
- module Config
-
- CONFIG_FILE = "#{RAILS_ROOT}/config/memcached.yml"
-
- class << self
-
- #
- # Load the configuration file, if available, and then set up the Memcached instance,
- # Rails settings, and CACHE constants. Should be more or less compatible with
- # Cache_fu.
- #
- def run!
- if File.exist?(CONFIG_FILE)
- template = ERB.new(File.open(CONFIG_FILE) {|f| f.read})
- config = YAML.load(template.result(binding))
- config.deep_symbolize_keys!
-
- Interlock.config.merge!(config[:defaults] || {})
- Interlock.config.merge!(config[RAILS_ENV.to_sym] || {})
- end
-
- install_memcached
- install_fragments
-
- # Always install the finders.
- install_finders if Interlock.config[:with_finders]
- end
-
- #
- # Configure memcached for this app.
- #
- def install_memcached
- Interlock.config[:namespace] << "-#{RAILS_ENV}"
-
- unless defined? Object::CACHE
-
- # Give people a choice of client, even though I don't like conditional dependencies.
- klass = case Interlock.config[:client]
- when 'memcached'
- begin
- Memcached::Rails
- rescue ArgumentError
- raise ConfigurationError, "'memcached' client requested but not installed. Try 'sudo gem install memcached'."
- end
-
- when 'memcache-client'
- begin
- if MemCache.constants.include?('SVNURL')
- raise ConfigurationError, "You have the Ruby-MemCache gem installed. Please uninstall Ruby-MemCache, or otherwise guarantee that memcache-client will load instead."
- end
- MemCache
- rescue ArgumentError
- raise ConfigurationError, "'memcache-client' client requested but not installed. Try 'sudo gem install memcache-client'."
- end
- end
-
- Object.const_set('CACHE',
- klass.new(
- Interlock.config[:servers],
- Interlock.config.slice(*CLIENT_KEYS)
- )
- )
-
- # Mark that we're the ones who did it.
- class << CACHE
- def installed_by_interlock; true; end
- end
-
- else
- begin
- CACHE.installed_by_interlock
- rescue NoMethodError
- RAILS_DEFAULT_LOGGER.warn "** interlock: Object::CACHE already defined; will not install a new one"
- # Mark that somebody else installed this CACHE.
- class << CACHE
- def installed_by_interlock; false; end
- end
- end
- end
-
- # Add the fragment cache and lock APIs to the cache singleton. This happens no matter
- # who installed the singleton.
- class << CACHE
- include Interlock::Lock
-
- def read(*args)
- get args.first.to_s
- end
-
- def write(name, content, options = {})
- set(name.to_s,
- content,
- options.is_a?(Hash) ? options[:ttl] : Interlock.config[:ttl] )
- end
- end
- end
-
- #
- # Configure Rails to use the memcached store for fragments, and optionally, sessions.
- #
- def install_fragments
- # Memcached fragment caching is mandatory
- ActionView::Helpers::CacheHelper.class_eval do
- def cache(name = {}, options = nil, &block)
- # Things explode if options does not default to nil
- RAILS_DEFAULT_LOGGER.debug "** fragment #{name} stored via obsolete cache() call"
- @controller.fragment_for(output_buffer, name, options, &block)
- end
- end
- ActionController::Base.cache_store = CACHE
-
- # Sessions are optional
- if Interlock.config[:sessions]
- # XXX Right now this requires memcache-client to be installed, due to a Rails problem.
- # http://dev.rubyonrails.org/ticket/11290
- ActionController::Base.session_store = :mem_cache_store
- ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.update 'cache' => CACHE
- end
- end
-
- #
- # Configure ActiveRecord#find caching.
- #
- def install_finders
- RAILS_DEFAULT_LOGGER.warn "** using interlock finder caches"
- ActiveRecord::Base.send(:include, Interlock::Finders)
- end
-
- end
- end
-end
diff --git a/vendor/plugins/interlock/lib/interlock/core_extensions.rb b/vendor/plugins/interlock/lib/interlock/core_extensions.rb
deleted file mode 100644
index 863085846..000000000
--- a/vendor/plugins/interlock/lib/interlock/core_extensions.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-
-class Object
- def to_interlock_tag
- string = to_s
- string = "empty_string" if string.empty?
- string.escape_tag_fragment
- end
-end
-
-class NilClass
- def to_interlock_tag
- "untagged".escape_tag_fragment
- end
-end
-
-class Hash
- alias :fetch_safely :[]
-
- def value_for_indifferent_key(key)
- fetch_safely(key) or fetch_safely(key.to_s) or fetch_safely(key.to_sym)
- end
-
- alias :v :value_for_indifferent_key
-
- def indifferentiate!
- class << self
- def [](key); value_for_indifferent_key(key); end
- end
- self
- end
-
- def indifferentiate
- self.dup.indifferentiate!
- end
-
- def deep_symbolize_keys!
- symbolize_keys!
- values.each do |value|
- value.deep_symbolize_keys! if value.is_a? Hash
- end
- end
-
- # Compatibility method for Rails 1.2.6. It's also faster.
- unless Hash.instance_methods.include? "slice"
- def slice(*keys)
- hash = {}
- keys.each do |key|
- hash[key] = self[key] if self[key]
- end
- hash
- end
- end
-
-end
-
-class Array
- # Compatibility method for Rails 1.2.6.
- unless Array.instance_methods.include? "extract_options!"
- def extract_options!
- # Method added in Rails rev 7217
- last.is_a?(Hash) ? pop : {}
- end
- end
-end
-
-class String
- def field(i)
- split(":")[i]
- end
-
- def escape_tag_fragment
- gsub(':', '-').gsub(/[^\w\d\-;]/, '_')
- end
-end
diff --git a/vendor/plugins/interlock/lib/interlock/finders.rb b/vendor/plugins/interlock/lib/interlock/finders.rb
deleted file mode 100644
index 4e75238ea..000000000
--- a/vendor/plugins/interlock/lib/interlock/finders.rb
+++ /dev/null
@@ -1,155 +0,0 @@
-
-module Interlock
- module Finders
- def self.included(klass)
- class << klass
- alias_method :find_via_db, :find
- remove_method :find
- end
-
- klass.extend ClassMethods
- end
-
- module ClassMethods
-
- #
- # Cached find.
- #
- # Any other options besides ids short-circuit the cache.
- #
- def find(*args)
- return find_via_db(*args) if args.last.is_a? Hash or args.first.is_a? Symbol
- ids = args.flatten.compact.uniq
- return find_via_db(ids) if ids.blank?
-
- records = find_via_cache(ids, true)
-
- if ids.length > 1 or args.first.is_a?(Array)
- records
- else
- records.first
- end
-
- end
-
- #
- # Cached find_by_id. Short-circuiting works the same as find.
- #
- def find_by_id(*args)
- return method_missing(:find_by_id, *args) if args.last.is_a? Hash
- find_via_cache(args, false).first
- end
-
- #
- # Cached find_all_by_id. Ultrasphinx uses this. Short-circuiting works the same as find.
- #
- def find_all_by_id(*args)
- return method_missing(:find_all_by_id, *args) if args.last.is_a? Hash
- find_via_cache(args, false)
- end
-
- #
- # Build the model cache key for a particular id.
- #
- def caching_key(id)
- Interlock.caching_key(
- self.base_class.name,
- "find",
- id,
- "default"
- )
- end
-
- def finder_ttl
- 0
- end
-
- private
-
- def find_via_cache(ids, should_raise) #:doc:
- results = []
-
- ordered_keys_to_ids = ids.flatten.map { |id| [caching_key(id), id.to_i] }
- keys_to_ids = Hash[*ordered_keys_to_ids.flatten]
-
- records = {}
-
- if ActionController::Base.perform_caching
- load_from_local_cache(records, keys_to_ids)
- load_from_memcached(records, keys_to_ids)
- end
-
- load_from_db(records, keys_to_ids)
-
- # Put them in order
-
- ordered_keys_to_ids.each do |key, |
- record = records[key]
- raise ActiveRecord::RecordNotFound, "Couldn't find #{self.name} with ID=#{keys_to_ids[key]}" if should_raise and !record
- results << record
- end
-
- # Don't return Nil objects, only the found records
- results.compact
- end
-
- def load_from_local_cache(current, keys_to_ids) #:doc:
- # Load from the local cache
- records = {}
- keys_to_ids.each do |key, |
- record = Interlock.local_cache.read(key, nil)
- records[key] = record if record
- end
- current.merge!(records)
- end
-
- def load_from_memcached(current, keys_to_ids) #:doc:
- # Drop to memcached if necessary
- if current.size < keys_to_ids.size
- records = {}
- missed = keys_to_ids.reject { |key, | current[key] }
-
- records = CACHE.get_multi(*missed.keys)
-
- # Set missed to the caches
- records.each do |key, value|
- Interlock.say key, "is loading from memcached", "model"
- Interlock.local_cache.write(key, value, nil)
- end
-
- current.merge!(records)
- end
- end
-
- def load_from_db(current, keys_to_ids) #:doc:
- # Drop to db if necessary
- if current.size < keys_to_ids.size
- missed = keys_to_ids.reject { |key, | current[key] }
- ids_to_keys = keys_to_ids.invert
-
- # Load from the db
- ids_to_find = missed.values
- if ids_to_find.length > 1
- records = send("find_all_by_#{primary_key}".to_sym, ids_to_find, {})
- else
- records = [send("find_by_#{primary_key}".to_sym, ids_to_find.first, {})].compact # explicitly just look for one if that's all that's needed
- end
-
- records = Hash[*(records.map do |record|
- [ids_to_keys[record.id], record]
- end.flatten)]
-
- # Set missed to the caches
- records.each do |key, value|
- Interlock.say key, "is loading from the db", "model"
- Interlock.local_cache.write(key, value, nil)
- CACHE.set key, value, value.class.finder_ttl unless Interlock.config[:disabled]
- end
-
- current.merge!(records)
- end
- end
- end
- end # Finders
-end # Interlock
-
diff --git a/vendor/plugins/interlock/lib/interlock/interlock.rb b/vendor/plugins/interlock/lib/interlock/interlock.rb
deleted file mode 100644
index 29eaa8804..000000000
--- a/vendor/plugins/interlock/lib/interlock/interlock.rb
+++ /dev/null
@@ -1,155 +0,0 @@
-
-module Interlock
-
- class InterlockError < StandardError #:nodoc:
- end
- class DependencyError < InterlockError #:nodoc:
- end
- class ConfigurationError < InterlockError #:nodoc:
- end
- class UsageError < InterlockError #:nodoc:
- end
- class LockAcquisitionError < InterlockError #:nodoc:
- end
- class FragmentConsistencyError < InterlockError #:nodoc:
- end
-
- SCOPE_KEYS = [:controller, :action, :id]
-
- # Buried value extracted from memcache.rb in the memcache-client gem.
- # If one tries to request a key that is too long, the client throws an error.
- # In practice, it seems better to simply avoid ever setting such long keys,
- # so we use this value to achieve such for keys generated by Interlock.
- KEY_LENGTH_LIMIT = 250
-
- # Similarly buried and useful: no whitespace allowed in keys.
- ILLEGAL_KEY_CHARACTERS_PATTERN = /\s/
-
- mattr_accessor :local_cache
-
- # Install the pass-through store. This is used in the console and test
- # environment. In a server environment, the controller callbacks install
- # the memory store before each request.
- @@local_cache = Interlock::PassThroughStore.new
-
- class << self
- #
- # Extract the dependencies from the rest of the arguments and registers
- # them with the appropriate models.
- #
- def extract_options_and_dependencies(dependencies, default = nil)
- options = dependencies.extract_options!
-
- # Hook up the dependencies nested array.
- dependencies.map! { |klass| [klass, :all] }
- options.each do |klass, scope|
- if klass.is_a? Class
- #
- # Beware! Scoping to :id means that a request's params[:id] must equal
- # klass#id or the rule will not trigger. This is because params[:id] is the
- # only record-specific scope include in the key.
- #
- # If you want fancier invalidation, think hard about whether it really
- # matters. Over-invalidation is rarely a problem, but under-invalidation
- # frequently is.
- #
- # "But I need it!" you say. All right, then start using key tags.
- #
- raise Interlock::DependencyError, "#{scope.inspect} is not a valid scope" unless [:all, :id].include?(scope)
- dependencies << [klass, scope.to_sym]
- end
- end
-
- unless dependencies.any?
- # Use the conventional controller/model association if none are provided
- # Can be skipped by calling caching(nil)
- dependencies = [[default, :all]]
- end
-
- # Remove nils
- dependencies.reject! {|klass, scope| klass.nil? }
-
- [options.indifferentiate, dependencies]
- end
-
- #
- # Add each key with scope to the appropriate dependencies array.
- #
- def register_dependencies(dependencies, key)
- return if Interlock.config[:disabled]
-
- Array(dependencies).each do |klass, scope|
- dep_key = dependency_key(klass, scope, key)
-
- # Get the value for this class/key out of the global store.
- this = (CACHE.get(dep_key) || {})[key]
-
- # Make sure to not overwrite broader scopes.
- unless this == :all or this == scope
- # We need to write, so acquire the lock.
- CACHE.lock(dep_key) do |hash|
- Interlock.say key, "registered a dependency on #{klass} -> #{scope.inspect}."
- (hash || {}).merge({key => scope})
- end
- end
- end
- end
-
- def say(key, msg, type = "fragment") #:nodoc:
- log "** #{type} #{key.inspect[1..-2]} #{msg}"
- end
-
- def log(msg)
- case Interlock.config[:log_level]
- when 'debug', 'info', 'warn', 'error'
- log_method = Interlock.config[:log_level]
- else
- log_method = :debug
- end
-
- RAILS_DEFAULT_LOGGER.send( log_method, msg )
- end
-
- #
- # Get the Memcached key for a class's dependency list. We store per-class
- # to reduce lock contention.
- #
- def dependency_key(klass, scope, key)
- id = (scope == :id ? ":#{key.field(4)}" : nil)
- "interlock:#{ENV['RAILS_ASSET_ID']}:dependency:#{klass.name}#{id}"
- end
-
- #
- # Build a fragment key for an explicitly passed context. Shouldn't be called
- # unless you need to write your own fine-grained invalidation rules. Make sure
- # the default ones are really unacceptable before you go to the trouble of
- # rolling your own.
- #
- def caching_key(controller, action, id, tag)
- raise ArgumentError, 'Both controller and action must be specified' unless controller and action
-
- id = (id or 'all').to_interlock_tag
- tag = tag.to_interlock_tag
-
- key = "interlock:#{ENV['RAILS_ASSET_ID']}:#{controller}:#{action}:#{id}:#{tag}"
-
- if key.length > KEY_LENGTH_LIMIT
- old_key = key
- key = key[0..KEY_LENGTH_LIMIT-1]
- say old_key, "truncated to #{key}"
- end
-
- key.gsub ILLEGAL_KEY_CHARACTERS_PATTERN, ''
- end
-
- #
- # Invalidate a particular key.
- #
- def invalidate(key)
- # Console and tests do not install the local cache
- Interlock.local_cache.delete(key) if Interlock.local_cache
- ActionController::Base.cache_store.delete key
- end
-
- end
-end
diff --git a/vendor/plugins/interlock/lib/interlock/lock.rb b/vendor/plugins/interlock/lib/interlock/lock.rb
deleted file mode 100644
index 89479334c..000000000
--- a/vendor/plugins/interlock/lib/interlock/lock.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-
-module Interlock
- module Lock
-
- #
- # Try to acquire a global lock from memcached for a particular key.
- # If successful, yield and set the key to the return value, then release
- # the lock.
- #
- # Based on http://rubyurl.com/Sw7 , which I partially wrote.
- #
-
- def lock(key, lock_expiry = 30, retries = 5)
- retries.times do |count|
-
- # We have to be compatible with both client APIs. Eventually we can use Memcached#cas
- # for this.
- begin
- response = CACHE.add("lock:#{key}", "Locked by #{Process.pid}", lock_expiry)
- # Nil is a successful response for Memcached 0.11, and "STORED\r\n" for MemCache.
- response = [true, nil, "STORED\r\n"].include?(response)
- rescue Memcached::NotStored
- # Do nothing
- end
-
- if response
- begin
- value = yield(CACHE.get(key))
- CACHE.set(key, value)
- return value
- ensure
- CACHE.delete("lock:#{key}")
- end
- else
- # Wait
- sleep((2**count) / 2.0)
- end
- end
-
- raise ::Interlock::LockAcquisitionError, "Couldn't acquire lock for #{key}"
- end
-
- end
-end
diff --git a/vendor/plugins/interlock/lib/interlock/pass_through_store.rb b/vendor/plugins/interlock/lib/interlock/pass_through_store.rb
deleted file mode 100644
index b3926ba01..000000000
--- a/vendor/plugins/interlock/lib/interlock/pass_through_store.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-
-module Interlock
- #
- # A stub class so that does not cache, for use in the test environment and the console
- # when the MemoryStore is not available.
- #
- class PassThroughStore
-
- # Do nothing.
- def nothing(*args)
- nil
- end
-
- alias :read :nothing
- alias :write :nothing
- alias :delete :nothing
-
- end
-end \ No newline at end of file
diff --git a/vendor/plugins/interlock/lib/tasks/interlock.rake b/vendor/plugins/interlock/lib/tasks/interlock.rake
deleted file mode 100644
index a7089f112..000000000
--- a/vendor/plugins/interlock/lib/tasks/interlock.rake
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace :interlock do
- desc "Watch the Rails log for Interlock-specific messages"
- task :tail do
- Dir.chdir RAILS_ROOT do
- exec("tail -f log/#{RAILS_ENV}.log | grep interlock")
- end
- end
-end
- \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/README b/vendor/plugins/interlock/test/integration/app/README
deleted file mode 100644
index a1db73c4b..000000000
--- a/vendor/plugins/interlock/test/integration/app/README
+++ /dev/null
@@ -1,203 +0,0 @@
-== Welcome to Rails
-
-Rails is a web-application and persistence framework that includes everything
-needed to create database-backed web-applications according to the
-Model-View-Control pattern of separation. This pattern splits the view (also
-called the presentation) into "dumb" templates that are primarily responsible
-for inserting pre-built data in between HTML tags. The model contains the
-"smart" domain objects (such as Account, Product, Person, Post) that holds all
-the business logic and knows how to persist themselves to a database. The
-controller handles the incoming requests (such as Save New Account, Update
-Product, Show Post) by manipulating the model and directing data to the view.
-
-In Rails, the model is handled by what's called an object-relational mapping
-layer entitled Active Record. This layer allows you to present the data from
-database rows as objects and embellish these data objects with business logic
-methods. You can read more about Active Record in
-link:files/vendor/rails/activerecord/README.html.
-
-The controller and view are handled by the Action Pack, which handles both
-layers by its two parts: Action View and Action Controller. These two layers
-are bundled in a single package due to their heavy interdependence. This is
-unlike the relationship between the Active Record and Action Pack that is much
-more separate. Each of these packages can be used independently outside of
-Rails. You can read more about Action Pack in
-link:files/vendor/rails/actionpack/README.html.
-
-
-== Getting Started
-
-1. At the command prompt, start a new Rails application using the <tt>rails</tt> command
- and your application name. Ex: rails myapp
- (If you've downloaded Rails in a complete tgz or zip, this step is already done)
-2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
-3. Go to http://localhost:3000/ and get "Welcome aboard: You’re riding the Rails!"
-4. Follow the guidelines to start developing your application
-
-
-== Web Servers
-
-By default, Rails will try to use Mongrel and lighttpd if they are installed, otherwise
-Rails will use WEBrick, the webserver that ships with Ruby. When you run script/server,
-Rails will check if Mongrel exists, then lighttpd and finally fall back to WEBrick. This ensures
-that you can always get up and running quickly.
-
-Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is
-suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
-getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
-More info at: http://mongrel.rubyforge.org
-
-If Mongrel is not installed, Rails will look for lighttpd. It's considerably faster than
-Mongrel and WEBrick and also suited for production use, but requires additional
-installation and currently only works well on OS X/Unix (Windows users are encouraged
-to start with Mongrel). We recommend version 1.4.11 and higher. You can download it from
-http://www.lighttpd.net.
-
-And finally, if neither Mongrel or lighttpd are installed, Rails will use the built-in Ruby
-web server, WEBrick. WEBrick is a small Ruby web server suitable for development, but not
-for production.
-
-But of course its also possible to run Rails on any platform that supports FCGI.
-Apache, LiteSpeed, IIS are just a few. For more information on FCGI,
-please visit: http://wiki.rubyonrails.com/rails/pages/FastCGI
-
-
-== Debugging Rails
-
-Sometimes your application goes wrong. Fortunately there are a lot of tools that
-will help you debug it and get it back on the rails.
-
-First area to check is the application log files. Have "tail -f" commands running
-on the server.log and development.log. Rails will automatically display debugging
-and runtime information to these files. Debugging info will also be shown in the
-browser on requests from 127.0.0.1.
-
-You can also log your own messages directly into the log file from your code using
-the Ruby logger class from inside your controllers. Example:
-
- class WeblogController < ActionController::Base
- def destroy
- @weblog = Weblog.find(params[:id])
- @weblog.destroy
- logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
- end
- end
-
-The result will be a message in your log file along the lines of:
-
- Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1
-
-More information on how to use the logger is at http://www.ruby-doc.org/core/
-
-Also, Ruby documentation can be found at http://www.ruby-lang.org/ including:
-
-* The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/
-* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
-
-These two online (and free) books will bring you up to speed on the Ruby language
-and also on programming in general.
-
-
-== Debugger
-
-Debugger support is available through the debugger command when you start your Mongrel or
-Webrick server with --debugger. This means that you can break out of execution at any point
-in the code, investigate and change the model, AND then resume execution! Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.find(:all)
- debugger
- end
- end
-
-So the controller will accept the action, run the first line, then present you
-with a IRB prompt in the server window. Here you can do things like:
-
- >> @posts.inspect
- => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
- #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
- >> @posts.first.title = "hello from a debugger"
- => "hello from a debugger"
-
-...and even better is that you can examine how your runtime objects actually work:
-
- >> f = @posts.first
- => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
- >> f.
- Display all 152 possibilities? (y or n)
-
-Finally, when you're ready to resume execution, you enter "cont"
-
-
-== Console
-
-You can interact with the domain model by starting the console through <tt>script/console</tt>.
-Here you'll have all parts of the application configured, just like it is when the
-application is running. You can inspect domain models, change values, and save to the
-database. Starting the script without arguments will launch it in the development environment.
-Passing an argument will specify a different environment, like <tt>script/console production</tt>.
-
-To reload your controllers and models after launching the console run <tt>reload!</tt>
-
-
-== Description of Contents
-
-app
- Holds all the code that's specific to this particular application.
-
-app/controllers
- Holds controllers that should be named like weblogs_controller.rb for
- automated URL mapping. All controllers should descend from ApplicationController
- which itself descends from ActionController::Base.
-
-app/models
- Holds models that should be named like post.rb.
- Most models will descend from ActiveRecord::Base.
-
-app/views
- Holds the template files for the view that should be named like
- weblogs/index.erb for the WeblogsController#index action. All views use eRuby
- syntax.
-
-app/views/layouts
- Holds the template files for layouts to be used with views. This models the common
- header/footer method of wrapping views. In your views, define a layout using the
- <tt>layout :default</tt> and create a file named default.erb. Inside default.erb,
- call <% yield %> to render the view using this layout.
-
-app/helpers
- Holds view helpers that should be named like weblogs_helper.rb. These are generated
- for you automatically when using script/generate for controllers. Helpers can be used to
- wrap functionality for your views into methods.
-
-config
- Configuration files for the Rails environment, the routing map, the database, and other dependencies.
-
-db
- Contains the database schema in schema.rb. db/migrate contains all
- the sequence of Migrations for your schema.
-
-doc
- This directory is where your application documentation will be stored when generated
- using <tt>rake doc:app</tt>
-
-lib
- Application specific libraries. Basically, any kind of custom code that doesn't
- belong under controllers, models, or helpers. This directory is in the load path.
-
-public
- The directory available for the web server. Contains subdirectories for images, stylesheets,
- and javascripts. Also contains the dispatchers and the default HTML files. This should be
- set as the DOCUMENT_ROOT of your web server.
-
-script
- Helper scripts for automation and generation.
-
-test
- Unit and functional tests along with fixtures. When using the script/generate scripts, template
- test files will be generated for you and placed in this directory.
-
-vendor
- External libraries that the application depends on. Also includes the plugins subdirectory.
- This directory is in the load path.
diff --git a/vendor/plugins/interlock/test/integration/app/Rakefile b/vendor/plugins/interlock/test/integration/app/Rakefile
deleted file mode 100644
index 3bb0e8592..000000000
--- a/vendor/plugins/interlock/test/integration/app/Rakefile
+++ /dev/null
@@ -1,10 +0,0 @@
-# Add your own tasks in files placed in lib/tasks ending in .rake,
-# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
-
-require(File.join(File.dirname(__FILE__), 'config', 'boot'))
-
-require 'rake'
-require 'rake/testtask'
-require 'rake/rdoctask'
-
-require 'tasks/rails'
diff --git a/vendor/plugins/interlock/test/integration/app/app/controllers/application.rb b/vendor/plugins/interlock/test/integration/app/app/controllers/application.rb
deleted file mode 100644
index ab63c229d..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/controllers/application.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# Filters added to this controller apply to all controllers in the application.
-# Likewise, all the methods added will be available for all controllers.
-
-class ApplicationController < ActionController::Base
- # helper :all # include all helpers, all the time
-
- # See ActionController::RequestForgeryProtection for details
- # Uncomment the :secret if you're not using the cookie session store
- # protect_from_forgery # :secret => '491bb7f9ad07a91046fcc3756839524b'
-end
diff --git a/vendor/plugins/interlock/test/integration/app/app/controllers/eval_controller.rb b/vendor/plugins/interlock/test/integration/app/app/controllers/eval_controller.rb
deleted file mode 100644
index 66a99bd08..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/controllers/eval_controller.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class EvalController < ApplicationController
-
- def index
- render :text => eval(params['string']).inspect
- end
-
-end
diff --git a/vendor/plugins/interlock/test/integration/app/app/controllers/items_controller.rb b/vendor/plugins/interlock/test/integration/app/app/controllers/items_controller.rb
deleted file mode 100644
index 203f9fc5a..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/controllers/items_controller.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-class ItemsController < ApplicationController
-
- before_filter :related
-
- def index
- behavior_cache do
- @items = Item.find(:all)
- end
- render :action => 'list'
- end
-
- def detail
- # Nesting is theoretically useful when the outer view block invalidates faster than the inner one
- behavior_cache :tag => :outer do
- @items = Item.find(:all)
- end
- behavior_cache Item => :id, :tag => :inner do
- @item = Item.find(params[:id])
- end
- end
-
- def show
- behavior_cache Item => :id do
- @item = Item.find(params['id'])
- end
- end
-
- def recent
- behavior_cache nil, :tag => [:seconds] do
- @items = Item.find(:all, :conditions => ['updated_at >= ?', params['seconds'].to_i.ago])
- end
- end
-
- def preview
- @perform = false
- behavior_cache Item => :id, :perform => @perform do
- @item = Item.find(params['id'])
- end
- render :action => 'show'
- end
-
- private
-
- def related
- behavior_cache :ignore => :all, :tag => 'related' do
- @related = "Delicious cake"
- end
- end
-
-end
diff --git a/vendor/plugins/interlock/test/integration/app/app/helpers/application_helper.rb b/vendor/plugins/interlock/test/integration/app/app/helpers/application_helper.rb
deleted file mode 100644
index 22a7940eb..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/helpers/application_helper.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# Methods added to this helper will be available to all templates in the application.
-module ApplicationHelper
-end
diff --git a/vendor/plugins/interlock/test/integration/app/app/helpers/eval_helper.rb b/vendor/plugins/interlock/test/integration/app/app/helpers/eval_helper.rb
deleted file mode 100644
index 1b2675d21..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/helpers/eval_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module EvalHelper
-end
diff --git a/vendor/plugins/interlock/test/integration/app/app/helpers/items_helper.rb b/vendor/plugins/interlock/test/integration/app/app/helpers/items_helper.rb
deleted file mode 100644
index cff0c9fe2..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/helpers/items_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module ItemsHelper
-end
diff --git a/vendor/plugins/interlock/test/integration/app/app/models/book.rb b/vendor/plugins/interlock/test/integration/app/app/models/book.rb
deleted file mode 100644
index e09543723..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/models/book.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-class Book < ActiveRecord::Base
- set_primary_key :guid
-end \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/app/models/item.rb b/vendor/plugins/interlock/test/integration/app/app/models/item.rb
deleted file mode 100644
index 89606ad1b..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/models/item.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class Item < ActiveRecord::Base
-end
diff --git a/vendor/plugins/interlock/test/integration/app/app/views/items/detail.rhtml b/vendor/plugins/interlock/test/integration/app/app/views/items/detail.rhtml
deleted file mode 100644
index 44d108216..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/views/items/detail.rhtml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-<% view_cache :tag => :outer do %>
- <div style="border: 1px solid black; padding: 5px;">
- <p>I invalidate quickly.</p>
- <h1><%= @items.size %> total items</h1>
-
- <% content_for :title do %>Outer: <% end %>
-
- <% view_cache :tag => :inner do %>
- <div style="margin: 20px; padding: 5px; border: 1px solid black;">
- <p>I invalidate slowly.</p>
- <h2>Item <%= @item.id %> is <%= @item.name %>.</h2>
-
- <% content_for :title do %>Inner<% end %>
-
- </div>
- <% end %>
-
- </div>
-<% end %>
diff --git a/vendor/plugins/interlock/test/integration/app/app/views/items/list.rhtml b/vendor/plugins/interlock/test/integration/app/app/views/items/list.rhtml
deleted file mode 100644
index 9f1416ab6..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/views/items/list.rhtml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-<h1>List</h1>
-<% view_cache do %>
-
- <% content_for :title do %>
- <%= @items.size %> Items
- <% end %>
-
- <% @items.each do |item| %>
- <h3><%= item.name %></h3>
- <p><%= item.description %></p>
- <% end %>
-
-<% end %>
-
-<%= render :partial => 'shared/related' %>
diff --git a/vendor/plugins/interlock/test/integration/app/app/views/items/recent.rhtml b/vendor/plugins/interlock/test/integration/app/app/views/items/recent.rhtml
deleted file mode 100644
index 9b334d749..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/views/items/recent.rhtml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-<h1>Recent</h1>
-<% view_cache :tag => [:seconds] do %>
- <% if @items.any? %>
- <% @items.each do |item| %>
- <h3><%= item.name %></h3>
- <p><%= item.description %></p>
- <% end %>
- <% else %>
- <h3>Nothing found</h3>
- <% end %>
-<% end %>
-
-<%= render :partial => 'shared/related' %>
diff --git a/vendor/plugins/interlock/test/integration/app/app/views/items/show.rhtml b/vendor/plugins/interlock/test/integration/app/app/views/items/show.rhtml
deleted file mode 100644
index 1b004933e..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/views/items/show.rhtml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-<h1>Show</h1>
-<% view_cache :perform => @perform do %>
- <h3><%= @item.name %></h3>
- <p><%= @item.description %></p>
-<% end %>
-
-<%= render :partial => 'shared/related' %>
diff --git a/vendor/plugins/interlock/test/integration/app/app/views/layouts/application.html.erb b/vendor/plugins/interlock/test/integration/app/app/views/layouts/application.html.erb
deleted file mode 100644
index b053bd4e1..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/views/layouts/application.html.erb
+++ /dev/null
@@ -1,9 +0,0 @@
-<html>
- <head>
- <title>Interlock Test: <%= yield :title %></title>
- </head>
- <body>
- <%= yield %>
- </body>
-</html>
- \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/app/views/shared/_related.rhtml b/vendor/plugins/interlock/test/integration/app/app/views/shared/_related.rhtml
deleted file mode 100644
index 1eea19356..000000000
--- a/vendor/plugins/interlock/test/integration/app/app/views/shared/_related.rhtml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-<% view_cache :ignore => :all, :tag => 'related' do %>
- <hr>
- <h3>Related</h3>
- <p><i><%= @related %></i></p>
-<% end %>
diff --git a/vendor/plugins/interlock/test/integration/app/config/boot.rb b/vendor/plugins/interlock/test/integration/app/config/boot.rb
deleted file mode 100644
index cb9a72dad..000000000
--- a/vendor/plugins/interlock/test/integration/app/config/boot.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-# Don't change this file!
-# Configure your app in config/environment.rb and config/environments/*.rb
-
-RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
-
-module Rails
- class << self
- def boot!
- unless booted?
- preinitialize
- pick_boot.run
- end
- end
-
- def booted?
- defined? Rails::Initializer
- end
-
- def pick_boot
- (vendor_rails? ? VendorBoot : GemBoot).new
- end
-
- def vendor_rails?
- File.exist?("#{RAILS_ROOT}/vendor/rails")
- end
-
- def preinitialize
- load(preinitializer_path) if File.exists?(preinitializer_path)
- end
-
- def preinitializer_path
- "#{RAILS_ROOT}/config/preinitializer.rb"
- end
- end
-
- class Boot
- def run
- load_initializer
- Rails::Initializer.run(:set_load_path)
- end
- end
-
- class VendorBoot < Boot
- def load_initializer
- require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
- end
- end
-
- class GemBoot < Boot
- def load_initializer
- self.class.load_rubygems
- load_rails_gem
- require 'initializer'
- end
-
- def load_rails_gem
- if version = self.class.gem_version
- STDERR.puts "Boot.rb loading version #{version}"
- gem 'rails', version
- else
- STDERR.puts "Boot.rb loading latest available version"
- gem 'rails'
- end
- rescue Gem::LoadError => load_error
- $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
- exit 1
- end
-
- class << self
- def rubygems_version
- Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
- end
-
- def gem_version
- if defined? RAILS_GEM_VERSION
- RAILS_GEM_VERSION
- elsif ENV.include?('RAILS_GEM_VERSION')
- ENV['RAILS_GEM_VERSION']
- else
- parse_gem_version(read_environment_rb)
- end
- end
-
- def load_rubygems
- require 'rubygems'
-
- unless rubygems_version >= '0.9.4'
- $stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
- exit 1
- end
-
- rescue LoadError
- $stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
- exit 1
- end
-
- def parse_gem_version(text)
- $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*'([!~<>=]*\s*[\d.]+)'/
- end
-
- private
- def read_environment_rb
- File.read("#{RAILS_ROOT}/config/environment.rb")
- end
- end
- end
-end
-
-# All that for this:
-Rails.boot!
diff --git a/vendor/plugins/interlock/test/integration/app/config/database.yml b/vendor/plugins/interlock/test/integration/app/config/database.yml
deleted file mode 100644
index c9d56622c..000000000
--- a/vendor/plugins/interlock/test/integration/app/config/database.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-development:
- adapter: mysql
- encoding: utf8
- database: interlock_development
- username: root
- password:
- socket: /tmp/mysql.sock
-
-test:
- adapter: mysql
- encoding: utf8
- database: interlock_test
- username: root
- password:
- socket: /tmp/mysql.sock
-
-production:
- adapter: mysql
- encoding: utf8
- database: interlock_production
- username: root
- password:
- socket: /tmp/mysql.sock
diff --git a/vendor/plugins/interlock/test/integration/app/config/environment.rb b/vendor/plugins/interlock/test/integration/app/config/environment.rb
deleted file mode 100644
index db5db62fe..000000000
--- a/vendor/plugins/interlock/test/integration/app/config/environment.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-
-require File.join(File.dirname(__FILE__), 'boot')
-require 'action_controller'
-
-Rails::Initializer.run do |config|
-
- if ActionController::Base.respond_to? 'session='
- config.action_controller.session = {:session_key => rand.to_s, :secret => '22cde4d5c1a61ba69a81795322cde4d5c1a61ba69a817953'}
- end
-
- # config.to_prepare do
- # RAILS_DEFAULT_LOGGER.info "** interlock dependencies:"
- # Interlock.dependencies.each do |klass, list|
- # RAILS_DEFAULT_LOGGER.info " #{klass}:"
- # list.each do |key, scope|
- # RAILS_DEFAULT_LOGGER.info " #{key} => #{scope.inspect}"
- # end
- # end
- # end
-
-end
-
-ENV['RAILS_ASSET_ID'] = rand.to_s
diff --git a/vendor/plugins/interlock/test/integration/app/config/environments/development.rb b/vendor/plugins/interlock/test/integration/app/config/environments/development.rb
deleted file mode 100644
index 10840f89b..000000000
--- a/vendor/plugins/interlock/test/integration/app/config/environments/development.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-
-production = (ENV['PRODUCTION'] == "true")
-
-config.cache_classes = production
-config.whiny_nils = true
-config.action_controller.consider_all_requests_local = !production
-config.action_controller.perform_caching = true
-config.action_view.debug_rjs = !production
-config.action_mailer.raise_delivery_errors = false
diff --git a/vendor/plugins/interlock/test/integration/app/config/environments/production.rb b/vendor/plugins/interlock/test/integration/app/config/environments/production.rb
deleted file mode 100644
index cb295b83f..000000000
--- a/vendor/plugins/interlock/test/integration/app/config/environments/production.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# Settings specified here will take precedence over those in config/environment.rb
-
-# The production environment is meant for finished, "live" apps.
-# Code is not reloaded between requests
-config.cache_classes = true
-
-# Use a different logger for distributed setups
-# config.logger = SyslogLogger.new
-
-# Full error reports are disabled and caching is turned on
-config.action_controller.consider_all_requests_local = false
-config.action_controller.perform_caching = true
-
-# Enable serving of images, stylesheets, and javascripts from an asset server
-# config.action_controller.asset_host = "http://assets.example.com"
-
-# Disable delivery errors, bad email addresses will be ignored
-# config.action_mailer.raise_delivery_errors = false
diff --git a/vendor/plugins/interlock/test/integration/app/config/environments/test.rb b/vendor/plugins/interlock/test/integration/app/config/environments/test.rb
deleted file mode 100644
index 58850a797..000000000
--- a/vendor/plugins/interlock/test/integration/app/config/environments/test.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# Settings specified here will take precedence over those in config/environment.rb
-
-# The test environment is used exclusively to run your application's
-# test suite. You never need to work with it otherwise. Remember that
-# your test database is "scratch space" for the test suite and is wiped
-# and recreated between test runs. Don't rely on the data there!
-config.cache_classes = true
-
-# Log error messages when you accidentally call methods on nil.
-config.whiny_nils = true
-
-# Show full error reports and disable caching
-config.action_controller.consider_all_requests_local = true
-config.action_controller.perform_caching = false
-
-# Disable request forgery protection in test environment
-config.action_controller.allow_forgery_protection = false
-
-# Tell ActionMailer not to deliver emails to the real world.
-# The :test delivery method accumulates sent emails in the
-# ActionMailer::Base.deliveries array.
-config.action_mailer.delivery_method = :test
diff --git a/vendor/plugins/interlock/test/integration/app/config/memcached.yml b/vendor/plugins/interlock/test/integration/app/config/memcached.yml
deleted file mode 100644
index bdd49fc84..000000000
--- a/vendor/plugins/interlock/test/integration/app/config/memcached.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-defaults:
- ttl: 30
- namespace: interlock
- sessions: true
- client: <%= ENV['CLIENT'] || 'memcached' %>
- with_finders: <%= ENV['FINDERS'] || true %>
-development:
- servers:
- - 127.0.0.1:43042
- - 127.0.0.1:43043
diff --git a/vendor/plugins/interlock/test/integration/app/config/routes.rb b/vendor/plugins/interlock/test/integration/app/config/routes.rb
deleted file mode 100644
index d94afa1b9..000000000
--- a/vendor/plugins/interlock/test/integration/app/config/routes.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-ActionController::Routing::Routes.draw do |map|
- # The priority is based upon order of creation: first created -> highest priority.
-
- # Sample of regular route:
- # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
- # Keep in mind you can assign values other than :controller and :action
-
- # Sample of named route:
- # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
- # This route can be invoked with purchase_url(:id => product.id)
-
- # Sample resource route (maps HTTP verbs to controller actions automatically):
- # map.resources :products
-
- # Sample resource route with options:
- # map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }
-
- # Sample resource route with sub-resources:
- # map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller
-
- # Sample resource route within a namespace:
- # map.namespace :admin do |admin|
- # # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)
- # admin.resources :products
- # end
-
- # You can have the root of your site routed with map.root -- just remember to delete public/index.html.
- # map.root :controller => "welcome"
-
- # See how all your routes lay out with "rake routes"
-
- # Install the default routes as the lowest priority.
- map.connect ':controller/:action/:id'
- map.connect ':controller/:action/:id.:format'
-end
diff --git a/vendor/plugins/interlock/test/integration/app/db/migrate/001_create_items.rb b/vendor/plugins/interlock/test/integration/app/db/migrate/001_create_items.rb
deleted file mode 100644
index 58da1077e..000000000
--- a/vendor/plugins/interlock/test/integration/app/db/migrate/001_create_items.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-class CreateItems < ActiveRecord::Migration
- def self.up
- create_table :items do |t|
- t.string :name
- t.string :description
- t.timestamps
- end
- end
-
- def self.down
- drop_table :items
- end
-end
diff --git a/vendor/plugins/interlock/test/integration/app/db/migrate/002_create_books.rb b/vendor/plugins/interlock/test/integration/app/db/migrate/002_create_books.rb
deleted file mode 100644
index 4e8155f8b..000000000
--- a/vendor/plugins/interlock/test/integration/app/db/migrate/002_create_books.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class CreateBooks < ActiveRecord::Migration
- def self.up
- create_table :books, :id => false do |t|
- t.string :name
- t.string :description
- t.timestamps
- end
- add_column :books, :guid, :integer, :primary => true
- end
-
- def self.down
- drop_table :books
- end
-end
diff --git a/vendor/plugins/interlock/test/integration/app/db/migrate/003_add_counter_to_items.rb b/vendor/plugins/interlock/test/integration/app/db/migrate/003_add_counter_to_items.rb
deleted file mode 100644
index df9f8a9e3..000000000
--- a/vendor/plugins/interlock/test/integration/app/db/migrate/003_add_counter_to_items.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class AddCounterToItems < ActiveRecord::Migration
- def self.up
- add_column :items, :counting_something, :integer, :default => 0
- end
-
- def self.down
- remove_column :items, :countering_something
- end
-end
diff --git a/vendor/plugins/interlock/test/integration/app/doc/README_FOR_APP b/vendor/plugins/interlock/test/integration/app/doc/README_FOR_APP
deleted file mode 100644
index fe41f5cc2..000000000
--- a/vendor/plugins/interlock/test/integration/app/doc/README_FOR_APP
+++ /dev/null
@@ -1,2 +0,0 @@
-Use this README file to introduce your application and point to useful places in the API for learning more.
-Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
diff --git a/vendor/plugins/interlock/test/integration/app/public/.htaccess b/vendor/plugins/interlock/test/integration/app/public/.htaccess
deleted file mode 100644
index d9d211c05..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/.htaccess
+++ /dev/null
@@ -1,40 +0,0 @@
-# General Apache options
-AddHandler fastcgi-script .fcgi
-AddHandler cgi-script .cgi
-Options +FollowSymLinks +ExecCGI
-
-# If you don't want Rails to look in certain directories,
-# use the following rewrite rules so that Apache won't rewrite certain requests
-#
-# Example:
-# RewriteCond %{REQUEST_URI} ^/notrails.*
-# RewriteRule .* - [L]
-
-# Redirect all requests not available on the filesystem to Rails
-# By default the cgi dispatcher is used which is very slow
-#
-# For better performance replace the dispatcher with the fastcgi one
-#
-# Example:
-# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
-RewriteEngine On
-
-# If your Rails application is accessed via an Alias directive,
-# then you MUST also set the RewriteBase in this htaccess file.
-#
-# Example:
-# Alias /myrailsapp /path/to/myrailsapp/public
-# RewriteBase /myrailsapp
-
-RewriteRule ^$ index.html [QSA]
-RewriteRule ^([^.]+)$ $1.html [QSA]
-RewriteCond %{REQUEST_FILENAME} !-f
-RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
-
-# In case Rails experiences terminal errors
-# Instead of displaying this message you can supply a file here which will be rendered instead
-#
-# Example:
-# ErrorDocument 500 /500.html
-
-ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
diff --git a/vendor/plugins/interlock/test/integration/app/public/404.html b/vendor/plugins/interlock/test/integration/app/public/404.html
deleted file mode 100644
index eff660b90..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/404.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
-<head>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
- <title>The page you were looking for doesn't exist (404)</title>
- <style type="text/css">
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
- div.dialog {
- width: 25em;
- padding: 0 4em;
- margin: 4em auto 0 auto;
- border: 1px solid #ccc;
- border-right-color: #999;
- border-bottom-color: #999;
- }
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
- </style>
-</head>
-
-<body>
- <!-- This file lives in public/404.html -->
- <div class="dialog">
- <h1>The page you were looking for doesn't exist.</h1>
- <p>You may have mistyped the address or the page may have moved.</p>
- </div>
-</body>
-</html> \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/public/422.html b/vendor/plugins/interlock/test/integration/app/public/422.html
deleted file mode 100644
index b54e4a3ca..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/422.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
-<head>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
- <title>The change you wanted was rejected (422)</title>
- <style type="text/css">
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
- div.dialog {
- width: 25em;
- padding: 0 4em;
- margin: 4em auto 0 auto;
- border: 1px solid #ccc;
- border-right-color: #999;
- border-bottom-color: #999;
- }
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
- </style>
-</head>
-
-<body>
- <!-- This file lives in public/422.html -->
- <div class="dialog">
- <h1>The change you wanted was rejected.</h1>
- <p>Maybe you tried to change something you didn't have access to.</p>
- </div>
-</body>
-</html> \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/public/500.html b/vendor/plugins/interlock/test/integration/app/public/500.html
deleted file mode 100644
index 0e9c14f4c..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/500.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
-<head>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
- <title>We're sorry, but something went wrong (500)</title>
- <style type="text/css">
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
- div.dialog {
- width: 25em;
- padding: 0 4em;
- margin: 4em auto 0 auto;
- border: 1px solid #ccc;
- border-right-color: #999;
- border-bottom-color: #999;
- }
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
- </style>
-</head>
-
-<body>
- <!-- This file lives in public/500.html -->
- <div class="dialog">
- <h1>We're sorry, but something went wrong.</h1>
- <p>We've been notified about this issue and we'll take a look at it shortly.</p>
- </div>
-</body>
-</html> \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/public/dispatch.cgi b/vendor/plugins/interlock/test/integration/app/public/dispatch.cgi
deleted file mode 100755
index a76782ae0..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/dispatch.cgi
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/opt/local/bin/ruby
-
-require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
-
-# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
-# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
-require "dispatcher"
-
-ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
-Dispatcher.dispatch \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/public/dispatch.fcgi b/vendor/plugins/interlock/test/integration/app/public/dispatch.fcgi
deleted file mode 100755
index a5267664e..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/dispatch.fcgi
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/opt/local/bin/ruby
-#
-# You may specify the path to the FastCGI crash log (a log of unhandled
-# exceptions which forced the FastCGI instance to exit, great for debugging)
-# and the number of requests to process before running garbage collection.
-#
-# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log
-# and the GC period is nil (turned off). A reasonable number of requests
-# could range from 10-100 depending on the memory footprint of your app.
-#
-# Example:
-# # Default log path, normal GC behavior.
-# RailsFCGIHandler.process!
-#
-# # Default log path, 50 requests between GC.
-# RailsFCGIHandler.process! nil, 50
-#
-# # Custom log path, normal GC behavior.
-# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
-#
-require File.dirname(__FILE__) + "/../config/environment"
-require 'fcgi_handler'
-
-RailsFCGIHandler.process!
diff --git a/vendor/plugins/interlock/test/integration/app/public/dispatch.rb b/vendor/plugins/interlock/test/integration/app/public/dispatch.rb
deleted file mode 100755
index a76782ae0..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/dispatch.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/opt/local/bin/ruby
-
-require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
-
-# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
-# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
-require "dispatcher"
-
-ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
-Dispatcher.dispatch \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/public/favicon.ico b/vendor/plugins/interlock/test/integration/app/public/favicon.ico
deleted file mode 100644
index e69de29bb..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/favicon.ico
+++ /dev/null
diff --git a/vendor/plugins/interlock/test/integration/app/public/images/rails.png b/vendor/plugins/interlock/test/integration/app/public/images/rails.png
deleted file mode 100644
index b8441f182..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/images/rails.png
+++ /dev/null
Binary files differ
diff --git a/vendor/plugins/interlock/test/integration/app/public/index.html b/vendor/plugins/interlock/test/integration/app/public/index.html
deleted file mode 100644
index 84b7b57c9..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/index.html
+++ /dev/null
@@ -1,277 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html>
- <head>
- <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
- <title>Ruby on Rails: Welcome aboard</title>
- <style type="text/css" media="screen">
- body {
- margin: 0;
- margin-bottom: 25px;
- padding: 0;
- background-color: #f0f0f0;
- font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
- font-size: 13px;
- color: #333;
- }
-
- h1 {
- font-size: 28px;
- color: #000;
- }
-
- a {color: #03c}
- a:hover {
- background-color: #03c;
- color: white;
- text-decoration: none;
- }
-
-
- #page {
- background-color: #f0f0f0;
- width: 750px;
- margin: 0;
- margin-left: auto;
- margin-right: auto;
- }
-
- #content {
- float: left;
- background-color: white;
- border: 3px solid #aaa;
- border-top: none;
- padding: 25px;
- width: 500px;
- }
-
- #sidebar {
- float: right;
- width: 175px;
- }
-
- #footer {
- clear: both;
- }
-
-
- #header, #about, #getting-started {
- padding-left: 75px;
- padding-right: 30px;
- }
-
-
- #header {
- background-image: url("images/rails.png");
- background-repeat: no-repeat;
- background-position: top left;
- height: 64px;
- }
- #header h1, #header h2 {margin: 0}
- #header h2 {
- color: #888;
- font-weight: normal;
- font-size: 16px;
- }
-
-
- #about h3 {
- margin: 0;
- margin-bottom: 10px;
- font-size: 14px;
- }
-
- #about-content {
- background-color: #ffd;
- border: 1px solid #fc0;
- margin-left: -11px;
- }
- #about-content table {
- margin-top: 10px;
- margin-bottom: 10px;
- font-size: 11px;
- border-collapse: collapse;
- }
- #about-content td {
- padding: 10px;
- padding-top: 3px;
- padding-bottom: 3px;
- }
- #about-content td.name {color: #555}
- #about-content td.value {color: #000}
-
- #about-content.failure {
- background-color: #fcc;
- border: 1px solid #f00;
- }
- #about-content.failure p {
- margin: 0;
- padding: 10px;
- }
-
-
- #getting-started {
- border-top: 1px solid #ccc;
- margin-top: 25px;
- padding-top: 15px;
- }
- #getting-started h1 {
- margin: 0;
- font-size: 20px;
- }
- #getting-started h2 {
- margin: 0;
- font-size: 14px;
- font-weight: normal;
- color: #333;
- margin-bottom: 25px;
- }
- #getting-started ol {
- margin-left: 0;
- padding-left: 0;
- }
- #getting-started li {
- font-size: 18px;
- color: #888;
- margin-bottom: 25px;
- }
- #getting-started li h2 {
- margin: 0;
- font-weight: normal;
- font-size: 18px;
- color: #333;
- }
- #getting-started li p {
- color: #555;
- font-size: 13px;
- }
-
-
- #search {
- margin: 0;
- padding-top: 10px;
- padding-bottom: 10px;
- font-size: 11px;
- }
- #search input {
- font-size: 11px;
- margin: 2px;
- }
- #search-text {width: 170px}
-
-
- #sidebar ul {
- margin-left: 0;
- padding-left: 0;
- }
- #sidebar ul h3 {
- margin-top: 25px;
- font-size: 16px;
- padding-bottom: 10px;
- border-bottom: 1px solid #ccc;
- }
- #sidebar li {
- list-style-type: none;
- }
- #sidebar ul.links li {
- margin-bottom: 5px;
- }
-
- </style>
- <script type="text/javascript" src="javascripts/prototype.js"></script>
- <script type="text/javascript" src="javascripts/effects.js"></script>
- <script type="text/javascript">
- function about() {
- if (Element.empty('about-content')) {
- new Ajax.Updater('about-content', 'rails/info/properties', {
- method: 'get',
- onFailure: function() {Element.classNames('about-content').add('failure')},
- onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
- });
- } else {
- new Effect[Element.visible('about-content') ?
- 'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
- }
- }
-
- window.onload = function() {
- $('search-text').value = '';
- $('search').onsubmit = function() {
- $('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
- }
- }
- </script>
- </head>
- <body>
- <div id="page">
- <div id="sidebar">
- <ul id="sidebar-items">
- <li>
- <form id="search" action="http://www.google.com/search" method="get">
- <input type="hidden" name="hl" value="en" />
- <input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
- <input type="submit" value="Search" /> the Rails site
- </form>
- </li>
-
- <li>
- <h3>Join the community</h3>
- <ul class="links">
- <li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
- <li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
- <li><a href="http://lists.rubyonrails.org/">Mailing lists</a></li>
- <li><a href="http://wiki.rubyonrails.org/rails/pages/IRC">IRC channel</a></li>
- <li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
- <li><a href="http://dev.rubyonrails.org/">Bug tracker</a></li>
- </ul>
- </li>
-
- <li>
- <h3>Browse the documentation</h3>
- <ul class="links">
- <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
- <li><a href="http://stdlib.rubyonrails.org/">Ruby standard library</a></li>
- <li><a href="http://corelib.rubyonrails.org/">Ruby core</a></li>
- </ul>
- </li>
- </ul>
- </div>
-
- <div id="content">
- <div id="header">
- <h1>Welcome aboard</h1>
- <h2>You&rsquo;re riding Ruby on Rails!</h2>
- </div>
-
- <div id="about">
- <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
- <div id="about-content" style="display: none"></div>
- </div>
-
- <div id="getting-started">
- <h1>Getting started</h1>
- <h2>Here&rsquo;s how to get rolling:</h2>
-
- <ol>
- <li>
- <h2>Create your databases and edit <tt>config/database.yml</tt></h2>
- <p>Rails needs to know your login and password.</p>
- </li>
-
- <li>
- <h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
- <p>To see all available options, run it without parameters.</p>
- </li>
-
- <li>
- <h2>Set up a default route and remove or rename this file</h2>
- <p>Routes are set up in config/routes.rb.</p>
- </li>
- </ol>
- </div>
- </div>
-
- <div id="footer">&nbsp;</div>
- </div>
- </body>
-</html> \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/public/javascripts/application.js b/vendor/plugins/interlock/test/integration/app/public/javascripts/application.js
deleted file mode 100644
index fe4577696..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/javascripts/application.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Place your application-specific JavaScript functions and classes here
-// This file is automatically included by javascript_include_tag :defaults
diff --git a/vendor/plugins/interlock/test/integration/app/public/javascripts/controls.js b/vendor/plugins/interlock/test/integration/app/public/javascripts/controls.js
deleted file mode 100644
index da7bfa2a7..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/javascripts/controls.js
+++ /dev/null
@@ -1,963 +0,0 @@
-// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
-// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
-// Contributors:
-// Richard Livsey
-// Rahul Bhargava
-// Rob Wills
-//
-// script.aculo.us is freely distributable under the terms of an MIT-style license.
-// For details, see the script.aculo.us web site: http://script.aculo.us/
-
-// Autocompleter.Base handles all the autocompletion functionality
-// that's independent of the data source for autocompletion. This
-// includes drawing the autocompletion menu, observing keyboard
-// and mouse events, and similar.
-//
-// Specific autocompleters need to provide, at the very least,
-// a getUpdatedChoices function that will be invoked every time
-// the text inside the monitored textbox changes. This method
-// should get the text for which to provide autocompletion by
-// invoking this.getToken(), NOT by directly accessing
-// this.element.value. This is to allow incremental tokenized
-// autocompletion. Specific auto-completion logic (AJAX, etc)
-// belongs in getUpdatedChoices.
-//
-// Tokenized incremental autocompletion is enabled automatically
-// when an autocompleter is instantiated with the 'tokens' option
-// in the options parameter, e.g.:
-// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
-// will incrementally autocomplete with a comma as the token.
-// Additionally, ',' in the above example can be replaced with
-// a token array, e.g. { tokens: [',', '\n'] } which
-// enables autocompletion on multiple tokens. This is most
-// useful when one of the tokens is \n (a newline), as it
-// allows smart autocompletion after linebreaks.
-
-if(typeof Effect == 'undefined')
- throw("controls.js requires including script.aculo.us' effects.js library");
-
-var Autocompleter = { }
-Autocompleter.Base = Class.create({
- baseInitialize: function(element, update, options) {
- element = $(element)
- this.element = element;
- this.update = $(update);
- this.hasFocus = false;
- this.changed = false;
- this.active = false;
- this.index = 0;
- this.entryCount = 0;
- this.oldElementValue = this.element.value;
-
- if(this.setOptions)
- this.setOptions(options);
- else
- this.options = options || { };
-
- this.options.paramName = this.options.paramName || this.element.name;
- this.options.tokens = this.options.tokens || [];
- this.options.frequency = this.options.frequency || 0.4;
- this.options.minChars = this.options.minChars || 1;
- this.options.onShow = this.options.onShow ||
- function(element, update){
- if(!update.style.position || update.style.position=='absolute') {
- update.style.position = 'absolute';
- Position.clone(element, update, {
- setHeight: false,
- offsetTop: element.offsetHeight
- });
- }
- Effect.Appear(update,{duration:0.15});
- };
- this.options.onHide = this.options.onHide ||
- function(element, update){ new Effect.Fade(update,{duration:0.15}) };
-
- if(typeof(this.options.tokens) == 'string')
- this.options.tokens = new Array(this.options.tokens);
- // Force carriage returns as token delimiters anyway
- if (!this.options.tokens.include('\n'))
- this.options.tokens.push('\n');
-
- this.observer = null;
-
- this.element.setAttribute('autocomplete','off');
-
- Element.hide(this.update);
-
- Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
- Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this));
- },
-
- show: function() {
- if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
- if(!this.iefix &&
- (Prototype.Browser.IE) &&
- (Element.getStyle(this.update, 'position')=='absolute')) {
- new Insertion.After(this.update,
- '<iframe id="' + this.update.id + '_iefix" '+
- 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
- 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
- this.iefix = $(this.update.id+'_iefix');
- }
- if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
- },
-
- fixIEOverlapping: function() {
- Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
- this.iefix.style.zIndex = 1;
- this.update.style.zIndex = 2;
- Element.show(this.iefix);
- },
-
- hide: function() {
- this.stopIndicator();
- if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
- if(this.iefix) Element.hide(this.iefix);
- },
-
- startIndicator: function() {
- if(this.options.indicator) Element.show(this.options.indicator);
- },
-
- stopIndicator: function() {
- if(this.options.indicator) Element.hide(this.options.indicator);
- },
-
- onKeyPress: function(event) {
- if(this.active)
- switch(event.keyCode) {
- case Event.KEY_TAB:
- case Event.KEY_RETURN:
- this.selectEntry();
- Event.stop(event);
- case Event.KEY_ESC:
- this.hide();
- this.active = false;
- Event.stop(event);
- return;
- case Event.KEY_LEFT:
- case Event.KEY_RIGHT:
- return;
- case Event.KEY_UP:
- this.markPrevious();
- this.render();
- if(Prototype.Browser.WebKit) Event.stop(event);
- return;
- case Event.KEY_DOWN:
- this.markNext();
- this.render();
- if(Prototype.Browser.WebKit) Event.stop(event);
- return;
- }
- else
- if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
- (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
-
- this.changed = true;
- this.hasFocus = true;
-
- if(this.observer) clearTimeout(this.observer);
- this.observer =
- setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
- },
-
- activate: function() {
- this.changed = false;
- this.hasFocus = true;
- this.getUpdatedChoices();
- },
-
- onHover: function(event) {
- var element = Event.findElement(event, 'LI');
- if(this.index != element.autocompleteIndex)
- {
- this.index = element.autocompleteIndex;
- this.render();
- }
- Event.stop(event);
- },
-
- onClick: function(event) {
- var element = Event.findElement(event, 'LI');
- this.index = element.autocompleteIndex;
- this.selectEntry();
- this.hide();
- },
-
- onBlur: function(event) {
- // needed to make click events working
- setTimeout(this.hide.bind(this), 250);
- this.hasFocus = false;
- this.active = false;
- },
-
- render: function() {
- if(this.entryCount > 0) {
- for (var i = 0; i < this.entryCount; i++)
- this.index==i ?
- Element.addClassName(this.getEntry(i),"selected") :
- Element.removeClassName(this.getEntry(i),"selected");
- if(this.hasFocus) {
- this.show();
- this.active = true;
- }
- } else {
- this.active = false;
- this.hide();
- }
- },
-
- markPrevious: function() {
- if(this.index > 0) this.index--
- else this.index = this.entryCount-1;
- this.getEntry(this.index).scrollIntoView(true);
- },
-
- markNext: function() {
- if(this.index < this.entryCount-1) this.index++
- else this.index = 0;
- this.getEntry(this.index).scrollIntoView(false);
- },
-
- getEntry: function(index) {
- return this.update.firstChild.childNodes[index];
- },
-
- getCurrentEntry: function() {
- return this.getEntry(this.index);
- },
-
- selectEntry: function() {
- this.active = false;
- this.updateElement(this.getCurrentEntry());
- },
-
- updateElement: function(selectedElement) {
- if (this.options.updateElement) {
- this.options.updateElement(selectedElement);
- return;
- }
- var value = '';
- if (this.options.select) {
- var nodes = $(selectedElement).select('.' + this.options.select) || [];
- if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
- } else
- value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
-
- var bounds = this.getTokenBounds();
- if (bounds[0] != -1) {
- var newValue = this.element.value.substr(0, bounds[0]);
- var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
- if (whitespace)
- newValue += whitespace[0];
- this.element.value = newValue + value + this.element.value.substr(bounds[1]);
- } else {
- this.element.value = value;
- }
- this.oldElementValue = this.element.value;
- this.element.focus();
-
- if (this.options.afterUpdateElement)
- this.options.afterUpdateElement(this.element, selectedElement);
- },
-
- updateChoices: function(choices) {
- if(!this.changed && this.hasFocus) {
- this.update.innerHTML = choices;
- Element.cleanWhitespace(this.update);
- Element.cleanWhitespace(this.update.down());
-
- if(this.update.firstChild && this.update.down().childNodes) {
- this.entryCount =
- this.update.down().childNodes.length;
- for (var i = 0; i < this.entryCount; i++) {
- var entry = this.getEntry(i);
- entry.autocompleteIndex = i;
- this.addObservers(entry);
- }
- } else {
- this.entryCount = 0;
- }
-
- this.stopIndicator();
- this.index = 0;
-
- if(this.entryCount==1 && this.options.autoSelect) {
- this.selectEntry();
- this.hide();
- } else {
- this.render();
- }
- }
- },
-
- addObservers: function(element) {
- Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
- Event.observe(element, "click", this.onClick.bindAsEventListener(this));
- },
-
- onObserverEvent: function() {
- this.changed = false;
- this.tokenBounds = null;
- if(this.getToken().length>=this.options.minChars) {
- this.getUpdatedChoices();
- } else {
- this.active = false;
- this.hide();
- }
- this.oldElementValue = this.element.value;
- },
-
- getToken: function() {
- var bounds = this.getTokenBounds();
- return this.element.value.substring(bounds[0], bounds[1]).strip();
- },
-
- getTokenBounds: function() {
- if (null != this.tokenBounds) return this.tokenBounds;
- var value = this.element.value;
- if (value.strip().empty()) return [-1, 0];
- var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
- var offset = (diff == this.oldElementValue.length ? 1 : 0);
- var prevTokenPos = -1, nextTokenPos = value.length;
- var tp;
- for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
- tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
- if (tp > prevTokenPos) prevTokenPos = tp;
- tp = value.indexOf(this.options.tokens[index], diff + offset);
- if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
- }
- return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
- }
-});
-
-Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
- var boundary = Math.min(newS.length, oldS.length);
- for (var index = 0; index < boundary; ++index)
- if (newS[index] != oldS[index])
- return index;
- return boundary;
-};
-
-Ajax.Autocompleter = Class.create(Autocompleter.Base, {
- initialize: function(element, update, url, options) {
- this.baseInitialize(element, update, options);
- this.options.asynchronous = true;
- this.options.onComplete = this.onComplete.bind(this);
- this.options.defaultParams = this.options.parameters || null;
- this.url = url;
- },
-
- getUpdatedChoices: function() {
- this.startIndicator();
-
- var entry = encodeURIComponent(this.options.paramName) + '=' +
- encodeURIComponent(this.getToken());
-
- this.options.parameters = this.options.callback ?
- this.options.callback(this.element, entry) : entry;
-
- if(this.options.defaultParams)
- this.options.parameters += '&' + this.options.defaultParams;
-
- new Ajax.Request(this.url, this.options);
- },
-
- onComplete: function(request) {
- this.updateChoices(request.responseText);
- }
-});
-
-// The local array autocompleter. Used when you'd prefer to
-// inject an array of autocompletion options into the page, rather
-// than sending out Ajax queries, which can be quite slow sometimes.
-//
-// The constructor takes four parameters. The first two are, as usual,
-// the id of the monitored textbox, and id of the autocompletion menu.
-// The third is the array you want to autocomplete from, and the fourth
-// is the options block.
-//
-// Extra local autocompletion options:
-// - choices - How many autocompletion choices to offer
-//
-// - partialSearch - If false, the autocompleter will match entered
-// text only at the beginning of strings in the
-// autocomplete array. Defaults to true, which will
-// match text at the beginning of any *word* in the
-// strings in the autocomplete array. If you want to
-// search anywhere in the string, additionally set
-// the option fullSearch to true (default: off).
-//
-// - fullSsearch - Search anywhere in autocomplete array strings.
-//
-// - partialChars - How many characters to enter before triggering
-// a partial match (unlike minChars, which defines
-// how many characters are required to do any match
-// at all). Defaults to 2.
-//
-// - ignoreCase - Whether to ignore case when autocompleting.
-// Defaults to true.
-//
-// It's possible to pass in a custom function as the 'selector'
-// option, if you prefer to write your own autocompletion logic.
-// In that case, the other options above will not apply unless
-// you support them.
-
-Autocompleter.Local = Class.create(Autocompleter.Base, {
- initialize: function(element, update, array, options) {
- this.baseInitialize(element, update, options);
- this.options.array = array;
- },
-
- getUpdatedChoices: function() {
- this.updateChoices(this.options.selector(this));
- },
-
- setOptions: function(options) {
- this.options = Object.extend({
- choices: 10,
- partialSearch: true,
- partialChars: 2,
- ignoreCase: true,
- fullSearch: false,
- selector: function(instance) {
- var ret = []; // Beginning matches
- var partial = []; // Inside matches
- var entry = instance.getToken();
- var count = 0;
-
- for (var i = 0; i < instance.options.array.length &&
- ret.length < instance.options.choices ; i++) {
-
- var elem = instance.options.array[i];
- var foundPos = instance.options.ignoreCase ?
- elem.toLowerCase().indexOf(entry.toLowerCase()) :
- elem.indexOf(entry);
-
- while (foundPos != -1) {
- if (foundPos == 0 && elem.length != entry.length) {
- ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
- elem.substr(entry.length) + "</li>");
- break;
- } else if (entry.length >= instance.options.partialChars &&
- instance.options.partialSearch && foundPos != -1) {
- if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
- partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
- elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
- foundPos + entry.length) + "</li>");
- break;
- }
- }
-
- foundPos = instance.options.ignoreCase ?
- elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
- elem.indexOf(entry, foundPos + 1);
-
- }
- }
- if (partial.length)
- ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
- return "<ul>" + ret.join('') + "</ul>";
- }
- }, options || { });
- }
-});
-
-// AJAX in-place editor and collection editor
-// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
-
-// Use this if you notice weird scrolling problems on some browsers,
-// the DOM might be a bit confused when this gets called so do this
-// waits 1 ms (with setTimeout) until it does the activation
-Field.scrollFreeActivate = function(field) {
- setTimeout(function() {
- Field.activate(field);
- }, 1);
-}
-
-Ajax.InPlaceEditor = Class.create({
- initialize: function(element, url, options) {
- this.url = url;
- this.element = element = $(element);
- this.prepareOptions();
- this._controls = { };
- arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
- Object.extend(this.options, options || { });
- if (!this.options.formId && this.element.id) {
- this.options.formId = this.element.id + '-inplaceeditor';
- if ($(this.options.formId))
- this.options.formId = '';
- }
- if (this.options.externalControl)
- this.options.externalControl = $(this.options.externalControl);
- if (!this.options.externalControl)
- this.options.externalControlOnly = false;
- this._originalBackground = this.element.getStyle('background-color') || 'transparent';
- this.element.title = this.options.clickToEditText;
- this._boundCancelHandler = this.handleFormCancellation.bind(this);
- this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
- this._boundFailureHandler = this.handleAJAXFailure.bind(this);
- this._boundSubmitHandler = this.handleFormSubmission.bind(this);
- this._boundWrapperHandler = this.wrapUp.bind(this);
- this.registerListeners();
- },
- checkForEscapeOrReturn: function(e) {
- if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
- if (Event.KEY_ESC == e.keyCode)
- this.handleFormCancellation(e);
- else if (Event.KEY_RETURN == e.keyCode)
- this.handleFormSubmission(e);
- },
- createControl: function(mode, handler, extraClasses) {
- var control = this.options[mode + 'Control'];
- var text = this.options[mode + 'Text'];
- if ('button' == control) {
- var btn = document.createElement('input');
- btn.type = 'submit';
- btn.value = text;
- btn.className = 'editor_' + mode + '_button';
- if ('cancel' == mode)
- btn.onclick = this._boundCancelHandler;
- this._form.appendChild(btn);
- this._controls[mode] = btn;
- } else if ('link' == control) {
- var link = document.createElement('a');
- link.href = '#';
- link.appendChild(document.createTextNode(text));
- link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
- link.className = 'editor_' + mode + '_link';
- if (extraClasses)
- link.className += ' ' + extraClasses;
- this._form.appendChild(link);
- this._controls[mode] = link;
- }
- },
- createEditField: function() {
- var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
- var fld;
- if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
- fld = document.createElement('input');
- fld.type = 'text';
- var size = this.options.size || this.options.cols || 0;
- if (0 < size) fld.size = size;
- } else {
- fld = document.createElement('textarea');
- fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
- fld.cols = this.options.cols || 40;
- }
- fld.name = this.options.paramName;
- fld.value = text; // No HTML breaks conversion anymore
- fld.className = 'editor_field';
- if (this.options.submitOnBlur)
- fld.onblur = this._boundSubmitHandler;
- this._controls.editor = fld;
- if (this.options.loadTextURL)
- this.loadExternalText();
- this._form.appendChild(this._controls.editor);
- },
- createForm: function() {
- var ipe = this;
- function addText(mode, condition) {
- var text = ipe.options['text' + mode + 'Controls'];
- if (!text || condition === false) return;
- ipe._form.appendChild(document.createTextNode(text));
- };
- this._form = $(document.createElement('form'));
- this._form.id = this.options.formId;
- this._form.addClassName(this.options.formClassName);
- this._form.onsubmit = this._boundSubmitHandler;
- this.createEditField();
- if ('textarea' == this._controls.editor.tagName.toLowerCase())
- this._form.appendChild(document.createElement('br'));
- if (this.options.onFormCustomization)
- this.options.onFormCustomization(this, this._form);
- addText('Before', this.options.okControl || this.options.cancelControl);
- this.createControl('ok', this._boundSubmitHandler);
- addText('Between', this.options.okControl && this.options.cancelControl);
- this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
- addText('After', this.options.okControl || this.options.cancelControl);
- },
- destroy: function() {
- if (this._oldInnerHTML)
- this.element.innerHTML = this._oldInnerHTML;
- this.leaveEditMode();
- this.unregisterListeners();
- },
- enterEditMode: function(e) {
- if (this._saving || this._editing) return;
- this._editing = true;
- this.triggerCallback('onEnterEditMode');
- if (this.options.externalControl)
- this.options.externalControl.hide();
- this.element.hide();
- this.createForm();
- this.element.parentNode.insertBefore(this._form, this.element);
- if (!this.options.loadTextURL)
- this.postProcessEditField();
- if (e) Event.stop(e);
- },
- enterHover: function(e) {
- if (this.options.hoverClassName)
- this.element.addClassName(this.options.hoverClassName);
- if (this._saving) return;
- this.triggerCallback('onEnterHover');
- },
- getText: function() {
- return this.element.innerHTML;
- },
- handleAJAXFailure: function(transport) {
- this.triggerCallback('onFailure', transport);
- if (this._oldInnerHTML) {
- this.element.innerHTML = this._oldInnerHTML;
- this._oldInnerHTML = null;
- }
- },
- handleFormCancellation: function(e) {
- this.wrapUp();
- if (e) Event.stop(e);
- },
- handleFormSubmission: function(e) {
- var form = this._form;
- var value = $F(this._controls.editor);
- this.prepareSubmission();
- var params = this.options.callback(form, value) || '';
- if (Object.isString(params))
- params = params.toQueryParams();
- params.editorId = this.element.id;
- if (this.options.htmlResponse) {
- var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
- Object.extend(options, {
- parameters: params,
- onComplete: this._boundWrapperHandler,
- onFailure: this._boundFailureHandler
- });
- new Ajax.Updater({ success: this.element }, this.url, options);
- } else {
- var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
- Object.extend(options, {
- parameters: params,
- onComplete: this._boundWrapperHandler,
- onFailure: this._boundFailureHandler
- });
- new Ajax.Request(this.url, options);
- }
- if (e) Event.stop(e);
- },
- leaveEditMode: function() {
- this.element.removeClassName(this.options.savingClassName);
- this.removeForm();
- this.leaveHover();
- this.element.style.backgroundColor = this._originalBackground;
- this.element.show();
- if (this.options.externalControl)
- this.options.externalControl.show();
- this._saving = false;
- this._editing = false;
- this._oldInnerHTML = null;
- this.triggerCallback('onLeaveEditMode');
- },
- leaveHover: function(e) {
- if (this.options.hoverClassName)
- this.element.removeClassName(this.options.hoverClassName);
- if (this._saving) return;
- this.triggerCallback('onLeaveHover');
- },
- loadExternalText: function() {
- this._form.addClassName(this.options.loadingClassName);
- this._controls.editor.disabled = true;
- var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
- Object.extend(options, {
- parameters: 'editorId=' + encodeURIComponent(this.element.id),
- onComplete: Prototype.emptyFunction,
- onSuccess: function(transport) {
- this._form.removeClassName(this.options.loadingClassName);
- var text = transport.responseText;
- if (this.options.stripLoadedTextTags)
- text = text.stripTags();
- this._controls.editor.value = text;
- this._controls.editor.disabled = false;
- this.postProcessEditField();
- }.bind(this),
- onFailure: this._boundFailureHandler
- });
- new Ajax.Request(this.options.loadTextURL, options);
- },
- postProcessEditField: function() {
- var fpc = this.options.fieldPostCreation;
- if (fpc)
- $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
- },
- prepareOptions: function() {
- this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
- Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
- [this._extraDefaultOptions].flatten().compact().each(function(defs) {
- Object.extend(this.options, defs);
- }.bind(this));
- },
- prepareSubmission: function() {
- this._saving = true;
- this.removeForm();
- this.leaveHover();
- this.showSaving();
- },
- registerListeners: function() {
- this._listeners = { };
- var listener;
- $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
- listener = this[pair.value].bind(this);
- this._listeners[pair.key] = listener;
- if (!this.options.externalControlOnly)
- this.element.observe(pair.key, listener);
- if (this.options.externalControl)
- this.options.externalControl.observe(pair.key, listener);
- }.bind(this));
- },
- removeForm: function() {
- if (!this._form) return;
- this._form.remove();
- this._form = null;
- this._controls = { };
- },
- showSaving: function() {
- this._oldInnerHTML = this.element.innerHTML;
- this.element.innerHTML = this.options.savingText;
- this.element.addClassName(this.options.savingClassName);
- this.element.style.backgroundColor = this._originalBackground;
- this.element.show();
- },
- triggerCallback: function(cbName, arg) {
- if ('function' == typeof this.options[cbName]) {
- this.options[cbName](this, arg);
- }
- },
- unregisterListeners: function() {
- $H(this._listeners).each(function(pair) {
- if (!this.options.externalControlOnly)
- this.element.stopObserving(pair.key, pair.value);
- if (this.options.externalControl)
- this.options.externalControl.stopObserving(pair.key, pair.value);
- }.bind(this));
- },
- wrapUp: function(transport) {
- this.leaveEditMode();
- // Can't use triggerCallback due to backward compatibility: requires
- // binding + direct element
- this._boundComplete(transport, this.element);
- }
-});
-
-Object.extend(Ajax.InPlaceEditor.prototype, {
- dispose: Ajax.InPlaceEditor.prototype.destroy
-});
-
-Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
- initialize: function($super, element, url, options) {
- this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
- $super(element, url, options);
- },
-
- createEditField: function() {
- var list = document.createElement('select');
- list.name = this.options.paramName;
- list.size = 1;
- this._controls.editor = list;
- this._collection = this.options.collection || [];
- if (this.options.loadCollectionURL)
- this.loadCollection();
- else
- this.checkForExternalText();
- this._form.appendChild(this._controls.editor);
- },
-
- loadCollection: function() {
- this._form.addClassName(this.options.loadingClassName);
- this.showLoadingText(this.options.loadingCollectionText);
- var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
- Object.extend(options, {
- parameters: 'editorId=' + encodeURIComponent(this.element.id),
- onComplete: Prototype.emptyFunction,
- onSuccess: function(transport) {
- var js = transport.responseText.strip();
- if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
- throw 'Server returned an invalid collection representation.';
- this._collection = eval(js);
- this.checkForExternalText();
- }.bind(this),
- onFailure: this.onFailure
- });
- new Ajax.Request(this.options.loadCollectionURL, options);
- },
-
- showLoadingText: function(text) {
- this._controls.editor.disabled = true;
- var tempOption = this._controls.editor.firstChild;
- if (!tempOption) {
- tempOption = document.createElement('option');
- tempOption.value = '';
- this._controls.editor.appendChild(tempOption);
- tempOption.selected = true;
- }
- tempOption.update((text || '').stripScripts().stripTags());
- },
-
- checkForExternalText: function() {
- this._text = this.getText();
- if (this.options.loadTextURL)
- this.loadExternalText();
- else
- this.buildOptionList();
- },
-
- loadExternalText: function() {
- this.showLoadingText(this.options.loadingText);
- var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
- Object.extend(options, {
- parameters: 'editorId=' + encodeURIComponent(this.element.id),
- onComplete: Prototype.emptyFunction,
- onSuccess: function(transport) {
- this._text = transport.responseText.strip();
- this.buildOptionList();
- }.bind(this),
- onFailure: this.onFailure
- });
- new Ajax.Request(this.options.loadTextURL, options);
- },
-
- buildOptionList: function() {
- this._form.removeClassName(this.options.loadingClassName);
- this._collection = this._collection.map(function(entry) {
- return 2 === entry.length ? entry : [entry, entry].flatten();
- });
- var marker = ('value' in this.options) ? this.options.value : this._text;
- var textFound = this._collection.any(function(entry) {
- return entry[0] == marker;
- }.bind(this));
- this._controls.editor.update('');
- var option;
- this._collection.each(function(entry, index) {
- option = document.createElement('option');
- option.value = entry[0];
- option.selected = textFound ? entry[0] == marker : 0 == index;
- option.appendChild(document.createTextNode(entry[1]));
- this._controls.editor.appendChild(option);
- }.bind(this));
- this._controls.editor.disabled = false;
- Field.scrollFreeActivate(this._controls.editor);
- }
-});
-
-//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
-//**** This only exists for a while, in order to let ****
-//**** users adapt to the new API. Read up on the new ****
-//**** API and convert your code to it ASAP! ****
-
-Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
- if (!options) return;
- function fallback(name, expr) {
- if (name in options || expr === undefined) return;
- options[name] = expr;
- };
- fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
- options.cancelLink == options.cancelButton == false ? false : undefined)));
- fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
- options.okLink == options.okButton == false ? false : undefined)));
- fallback('highlightColor', options.highlightcolor);
- fallback('highlightEndColor', options.highlightendcolor);
-};
-
-Object.extend(Ajax.InPlaceEditor, {
- DefaultOptions: {
- ajaxOptions: { },
- autoRows: 3, // Use when multi-line w/ rows == 1
- cancelControl: 'link', // 'link'|'button'|false
- cancelText: 'cancel',
- clickToEditText: 'Click to edit',
- externalControl: null, // id|elt
- externalControlOnly: false,
- fieldPostCreation: 'activate', // 'activate'|'focus'|false
- formClassName: 'inplaceeditor-form',
- formId: null, // id|elt
- highlightColor: '#ffff99',
- highlightEndColor: '#ffffff',
- hoverClassName: '',
- htmlResponse: true,
- loadingClassName: 'inplaceeditor-loading',
- loadingText: 'Loading...',
- okControl: 'button', // 'link'|'button'|false
- okText: 'ok',
- paramName: 'value',
- rows: 1, // If 1 and multi-line, uses autoRows
- savingClassName: 'inplaceeditor-saving',
- savingText: 'Saving...',
- size: 0,
- stripLoadedTextTags: false,
- submitOnBlur: false,
- textAfterControls: '',
- textBeforeControls: '',
- textBetweenControls: ''
- },
- DefaultCallbacks: {
- callback: function(form) {
- return Form.serialize(form);
- },
- onComplete: function(transport, element) {
- // For backward compatibility, this one is bound to the IPE, and passes
- // the element directly. It was too often customized, so we don't break it.
- new Effect.Highlight(element, {
- startcolor: this.options.highlightColor, keepBackgroundImage: true });
- },
- onEnterEditMode: null,
- onEnterHover: function(ipe) {
- ipe.element.style.backgroundColor = ipe.options.highlightColor;
- if (ipe._effect)
- ipe._effect.cancel();
- },
- onFailure: function(transport, ipe) {
- alert('Error communication with the server: ' + transport.responseText.stripTags());
- },
- onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
- onLeaveEditMode: null,
- onLeaveHover: function(ipe) {
- ipe._effect = new Effect.Highlight(ipe.element, {
- startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
- restorecolor: ipe._originalBackground, keepBackgroundImage: true
- });
- }
- },
- Listeners: {
- click: 'enterEditMode',
- keydown: 'checkForEscapeOrReturn',
- mouseover: 'enterHover',
- mouseout: 'leaveHover'
- }
-});
-
-Ajax.InPlaceCollectionEditor.DefaultOptions = {
- loadingCollectionText: 'Loading options...'
-};
-
-// Delayed observer, like Form.Element.Observer,
-// but waits for delay after last key input
-// Ideal for live-search fields
-
-Form.Element.DelayedObserver = Class.create({
- initialize: function(element, delay, callback) {
- this.delay = delay || 0.5;
- this.element = $(element);
- this.callback = callback;
- this.timer = null;
- this.lastValue = $F(this.element);
- Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
- },
- delayedListener: function(event) {
- if(this.lastValue == $F(this.element)) return;
- if(this.timer) clearTimeout(this.timer);
- this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
- this.lastValue = $F(this.element);
- },
- onTimerEvent: function() {
- this.timer = null;
- this.callback(this.element, $F(this.element));
- }
-});
diff --git a/vendor/plugins/interlock/test/integration/app/public/javascripts/dragdrop.js b/vendor/plugins/interlock/test/integration/app/public/javascripts/dragdrop.js
deleted file mode 100644
index ccf4a1e45..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/javascripts/dragdrop.js
+++ /dev/null
@@ -1,972 +0,0 @@
-// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
-//
-// script.aculo.us is freely distributable under the terms of an MIT-style license.
-// For details, see the script.aculo.us web site: http://script.aculo.us/
-
-if(Object.isUndefined(Effect))
- throw("dragdrop.js requires including script.aculo.us' effects.js library");
-
-var Droppables = {
- drops: [],
-
- remove: function(element) {
- this.drops = this.drops.reject(function(d) { return d.element==$(element) });
- },
-
- add: function(element) {
- element = $(element);
- var options = Object.extend({
- greedy: true,
- hoverclass: null,
- tree: false
- }, arguments[1] || { });
-
- // cache containers
- if(options.containment) {
- options._containers = [];
- var containment = options.containment;
- if(Object.isArray(containment)) {
- containment.each( function(c) { options._containers.push($(c)) });
- } else {
- options._containers.push($(containment));
- }
- }
-
- if(options.accept) options.accept = [options.accept].flatten();
-
- Element.makePositioned(element); // fix IE
- options.element = element;
-
- this.drops.push(options);
- },
-
- findDeepestChild: function(drops) {
- deepest = drops[0];
-
- for (i = 1; i < drops.length; ++i)
- if (Element.isParent(drops[i].element, deepest.element))
- deepest = drops[i];
-
- return deepest;
- },
-
- isContained: function(element, drop) {
- var containmentNode;
- if(drop.tree) {
- containmentNode = element.treeNode;
- } else {
- containmentNode = element.parentNode;
- }
- return drop._containers.detect(function(c) { return containmentNode == c });
- },
-
- isAffected: function(point, element, drop) {
- return (
- (drop.element!=element) &&
- ((!drop._containers) ||
- this.isContained(element, drop)) &&
- ((!drop.accept) ||
- (Element.classNames(element).detect(
- function(v) { return drop.accept.include(v) } ) )) &&
- Position.within(drop.element, point[0], point[1]) );
- },
-
- deactivate: function(drop) {
- if(drop.hoverclass)
- Element.removeClassName(drop.element, drop.hoverclass);
- this.last_active = null;
- },
-
- activate: function(drop) {
- if(drop.hoverclass)
- Element.addClassName(drop.element, drop.hoverclass);
- this.last_active = drop;
- },
-
- show: function(point, element) {
- if(!this.drops.length) return;
- var drop, affected = [];
-
- this.drops.each( function(drop) {
- if(Droppables.isAffected(point, element, drop))
- affected.push(drop);
- });
-
- if(affected.length>0)
- drop = Droppables.findDeepestChild(affected);
-
- if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
- if (drop) {
- Position.within(drop.element, point[0], point[1]);
- if(drop.onHover)
- drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
-
- if (drop != this.last_active) Droppables.activate(drop);
- }
- },
-
- fire: function(event, element) {
- if(!this.last_active) return;
- Position.prepare();
-
- if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
- if (this.last_active.onDrop) {
- this.last_active.onDrop(element, this.last_active.element, event);
- return true;
- }
- },
-
- reset: function() {
- if(this.last_active)
- this.deactivate(this.last_active);
- }
-}
-
-var Draggables = {
- drags: [],
- observers: [],
-
- register: function(draggable) {
- if(this.drags.length == 0) {
- this.eventMouseUp = this.endDrag.bindAsEventListener(this);
- this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
- this.eventKeypress = this.keyPress.bindAsEventListener(this);
-
- Event.observe(document, "mouseup", this.eventMouseUp);
- Event.observe(document, "mousemove", this.eventMouseMove);
- Event.observe(document, "keypress", this.eventKeypress);
- }
- this.drags.push(draggable);
- },
-
- unregister: function(draggable) {
- this.drags = this.drags.reject(function(d) { return d==draggable });
- if(this.drags.length == 0) {
- Event.stopObserving(document, "mouseup", this.eventMouseUp);
- Event.stopObserving(document, "mousemove", this.eventMouseMove);
- Event.stopObserving(document, "keypress", this.eventKeypress);
- }
- },
-
- activate: function(draggable) {
- if(draggable.options.delay) {
- this._timeout = setTimeout(function() {
- Draggables._timeout = null;
- window.focus();
- Draggables.activeDraggable = draggable;
- }.bind(this), draggable.options.delay);
- } else {
- window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
- this.activeDraggable = draggable;
- }
- },
-
- deactivate: function() {
- this.activeDraggable = null;
- },
-
- updateDrag: function(event) {
- if(!this.activeDraggable) return;
- var pointer = [Event.pointerX(event), Event.pointerY(event)];
- // Mozilla-based browsers fire successive mousemove events with
- // the same coordinates, prevent needless redrawing (moz bug?)
- if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
- this._lastPointer = pointer;
-
- this.activeDraggable.updateDrag(event, pointer);
- },
-
- endDrag: function(event) {
- if(this._timeout) {
- clearTimeout(this._timeout);
- this._timeout = null;
- }
- if(!this.activeDraggable) return;
- this._lastPointer = null;
- this.activeDraggable.endDrag(event);
- this.activeDraggable = null;
- },
-
- keyPress: function(event) {
- if(this.activeDraggable)
- this.activeDraggable.keyPress(event);
- },
-
- addObserver: function(observer) {
- this.observers.push(observer);
- this._cacheObserverCallbacks();
- },
-
- removeObserver: function(element) { // element instead of observer fixes mem leaks
- this.observers = this.observers.reject( function(o) { return o.element==element });
- this._cacheObserverCallbacks();
- },
-
- notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
- if(this[eventName+'Count'] > 0)
- this.observers.each( function(o) {
- if(o[eventName]) o[eventName](eventName, draggable, event);
- });
- if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
- },
-
- _cacheObserverCallbacks: function() {
- ['onStart','onEnd','onDrag'].each( function(eventName) {
- Draggables[eventName+'Count'] = Draggables.observers.select(
- function(o) { return o[eventName]; }
- ).length;
- });
- }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var Draggable = Class.create({
- initialize: function(element) {
- var defaults = {
- handle: false,
- reverteffect: function(element, top_offset, left_offset) {
- var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
- new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
- queue: {scope:'_draggable', position:'end'}
- });
- },
- endeffect: function(element) {
- var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
- new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
- queue: {scope:'_draggable', position:'end'},
- afterFinish: function(){
- Draggable._dragging[element] = false
- }
- });
- },
- zindex: 1000,
- revert: false,
- quiet: false,
- scroll: false,
- scrollSensitivity: 20,
- scrollSpeed: 15,
- snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
- delay: 0
- };
-
- if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
- Object.extend(defaults, {
- starteffect: function(element) {
- element._opacity = Element.getOpacity(element);
- Draggable._dragging[element] = true;
- new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
- }
- });
-
- var options = Object.extend(defaults, arguments[1] || { });
-
- this.element = $(element);
-
- if(options.handle && Object.isString(options.handle))
- this.handle = this.element.down('.'+options.handle, 0);
-
- if(!this.handle) this.handle = $(options.handle);
- if(!this.handle) this.handle = this.element;
-
- if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
- options.scroll = $(options.scroll);
- this._isScrollChild = Element.childOf(this.element, options.scroll);
- }
-
- Element.makePositioned(this.element); // fix IE
-
- this.options = options;
- this.dragging = false;
-
- this.eventMouseDown = this.initDrag.bindAsEventListener(this);
- Event.observe(this.handle, "mousedown", this.eventMouseDown);
-
- Draggables.register(this);
- },
-
- destroy: function() {
- Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
- Draggables.unregister(this);
- },
-
- currentDelta: function() {
- return([
- parseInt(Element.getStyle(this.element,'left') || '0'),
- parseInt(Element.getStyle(this.element,'top') || '0')]);
- },
-
- initDrag: function(event) {
- if(!Object.isUndefined(Draggable._dragging[this.element]) &&
- Draggable._dragging[this.element]) return;
- if(Event.isLeftClick(event)) {
- // abort on form elements, fixes a Firefox issue
- var src = Event.element(event);
- if((tag_name = src.tagName.toUpperCase()) && (
- tag_name=='INPUT' ||
- tag_name=='SELECT' ||
- tag_name=='OPTION' ||
- tag_name=='BUTTON' ||
- tag_name=='TEXTAREA')) return;
-
- var pointer = [Event.pointerX(event), Event.pointerY(event)];
- var pos = Position.cumulativeOffset(this.element);
- this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
-
- Draggables.activate(this);
- Event.stop(event);
- }
- },
-
- startDrag: function(event) {
- this.dragging = true;
- if(!this.delta)
- this.delta = this.currentDelta();
-
- if(this.options.zindex) {
- this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
- this.element.style.zIndex = this.options.zindex;
- }
-
- if(this.options.ghosting) {
- this._clone = this.element.cloneNode(true);
- this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
- if (!this.element._originallyAbsolute)
- Position.absolutize(this.element);
- this.element.parentNode.insertBefore(this._clone, this.element);
- }
-
- if(this.options.scroll) {
- if (this.options.scroll == window) {
- var where = this._getWindowScroll(this.options.scroll);
- this.originalScrollLeft = where.left;
- this.originalScrollTop = where.top;
- } else {
- this.originalScrollLeft = this.options.scroll.scrollLeft;
- this.originalScrollTop = this.options.scroll.scrollTop;
- }
- }
-
- Draggables.notify('onStart', this, event);
-
- if(this.options.starteffect) this.options.starteffect(this.element);
- },
-
- updateDrag: function(event, pointer) {
- if(!this.dragging) this.startDrag(event);
-
- if(!this.options.quiet){
- Position.prepare();
- Droppables.show(pointer, this.element);
- }
-
- Draggables.notify('onDrag', this, event);
-
- this.draw(pointer);
- if(this.options.change) this.options.change(this);
-
- if(this.options.scroll) {
- this.stopScrolling();
-
- var p;
- if (this.options.scroll == window) {
- with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
- } else {
- p = Position.page(this.options.scroll);
- p[0] += this.options.scroll.scrollLeft + Position.deltaX;
- p[1] += this.options.scroll.scrollTop + Position.deltaY;
- p.push(p[0]+this.options.scroll.offsetWidth);
- p.push(p[1]+this.options.scroll.offsetHeight);
- }
- var speed = [0,0];
- if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
- if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
- if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
- if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
- this.startScrolling(speed);
- }
-
- // fix AppleWebKit rendering
- if(Prototype.Browser.WebKit) window.scrollBy(0,0);
-
- Event.stop(event);
- },
-
- finishDrag: function(event, success) {
- this.dragging = false;
-
- if(this.options.quiet){
- Position.prepare();
- var pointer = [Event.pointerX(event), Event.pointerY(event)];
- Droppables.show(pointer, this.element);
- }
-
- if(this.options.ghosting) {
- if (!this.element._originallyAbsolute)
- Position.relativize(this.element);
- delete this.element._originallyAbsolute;
- Element.remove(this._clone);
- this._clone = null;
- }
-
- var dropped = false;
- if(success) {
- dropped = Droppables.fire(event, this.element);
- if (!dropped) dropped = false;
- }
- if(dropped && this.options.onDropped) this.options.onDropped(this.element);
- Draggables.notify('onEnd', this, event);
-
- var revert = this.options.revert;
- if(revert && Object.isFunction(revert)) revert = revert(this.element);
-
- var d = this.currentDelta();
- if(revert && this.options.reverteffect) {
- if (dropped == 0 || revert != 'failure')
- this.options.reverteffect(this.element,
- d[1]-this.delta[1], d[0]-this.delta[0]);
- } else {
- this.delta = d;
- }
-
- if(this.options.zindex)
- this.element.style.zIndex = this.originalZ;
-
- if(this.options.endeffect)
- this.options.endeffect(this.element);
-
- Draggables.deactivate(this);
- Droppables.reset();
- },
-
- keyPress: function(event) {
- if(event.keyCode!=Event.KEY_ESC) return;
- this.finishDrag(event, false);
- Event.stop(event);
- },
-
- endDrag: function(event) {
- if(!this.dragging) return;
- this.stopScrolling();
- this.finishDrag(event, true);
- Event.stop(event);
- },
-
- draw: function(point) {
- var pos = Position.cumulativeOffset(this.element);
- if(this.options.ghosting) {
- var r = Position.realOffset(this.element);
- pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
- }
-
- var d = this.currentDelta();
- pos[0] -= d[0]; pos[1] -= d[1];
-
- if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
- pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
- pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
- }
-
- var p = [0,1].map(function(i){
- return (point[i]-pos[i]-this.offset[i])
- }.bind(this));
-
- if(this.options.snap) {
- if(Object.isFunction(this.options.snap)) {
- p = this.options.snap(p[0],p[1],this);
- } else {
- if(Object.isArray(this.options.snap)) {
- p = p.map( function(v, i) {
- return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
- } else {
- p = p.map( function(v) {
- return (v/this.options.snap).round()*this.options.snap }.bind(this))
- }
- }}
-
- var style = this.element.style;
- if((!this.options.constraint) || (this.options.constraint=='horizontal'))
- style.left = p[0] + "px";
- if((!this.options.constraint) || (this.options.constraint=='vertical'))
- style.top = p[1] + "px";
-
- if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
- },
-
- stopScrolling: function() {
- if(this.scrollInterval) {
- clearInterval(this.scrollInterval);
- this.scrollInterval = null;
- Draggables._lastScrollPointer = null;
- }
- },
-
- startScrolling: function(speed) {
- if(!(speed[0] || speed[1])) return;
- this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
- this.lastScrolled = new Date();
- this.scrollInterval = setInterval(this.scroll.bind(this), 10);
- },
-
- scroll: function() {
- var current = new Date();
- var delta = current - this.lastScrolled;
- this.lastScrolled = current;
- if(this.options.scroll == window) {
- with (this._getWindowScroll(this.options.scroll)) {
- if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
- var d = delta / 1000;
- this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
- }
- }
- } else {
- this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
- this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
- }
-
- Position.prepare();
- Droppables.show(Draggables._lastPointer, this.element);
- Draggables.notify('onDrag', this);
- if (this._isScrollChild) {
- Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
- Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
- Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
- if (Draggables._lastScrollPointer[0] < 0)
- Draggables._lastScrollPointer[0] = 0;
- if (Draggables._lastScrollPointer[1] < 0)
- Draggables._lastScrollPointer[1] = 0;
- this.draw(Draggables._lastScrollPointer);
- }
-
- if(this.options.change) this.options.change(this);
- },
-
- _getWindowScroll: function(w) {
- var T, L, W, H;
- with (w.document) {
- if (w.document.documentElement && documentElement.scrollTop) {
- T = documentElement.scrollTop;
- L = documentElement.scrollLeft;
- } else if (w.document.body) {
- T = body.scrollTop;
- L = body.scrollLeft;
- }
- if (w.innerWidth) {
- W = w.innerWidth;
- H = w.innerHeight;
- } else if (w.document.documentElement && documentElement.clientWidth) {
- W = documentElement.clientWidth;
- H = documentElement.clientHeight;
- } else {
- W = body.offsetWidth;
- H = body.offsetHeight
- }
- }
- return { top: T, left: L, width: W, height: H };
- }
-});
-
-Draggable._dragging = { };
-
-/*--------------------------------------------------------------------------*/
-
-var SortableObserver = Class.create({
- initialize: function(element, observer) {
- this.element = $(element);
- this.observer = observer;
- this.lastValue = Sortable.serialize(this.element);
- },
-
- onStart: function() {
- this.lastValue = Sortable.serialize(this.element);
- },
-
- onEnd: function() {
- Sortable.unmark();
- if(this.lastValue != Sortable.serialize(this.element))
- this.observer(this.element)
- }
-});
-
-var Sortable = {
- SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
-
- sortables: { },
-
- _findRootElement: function(element) {
- while (element.tagName.toUpperCase() != "BODY") {
- if(element.id && Sortable.sortables[element.id]) return element;
- element = element.parentNode;
- }
- },
-
- options: function(element) {
- element = Sortable._findRootElement($(element));
- if(!element) return;
- return Sortable.sortables[element.id];
- },
-
- destroy: function(element){
- var s = Sortable.options(element);
-
- if(s) {
- Draggables.removeObserver(s.element);
- s.droppables.each(function(d){ Droppables.remove(d) });
- s.draggables.invoke('destroy');
-
- delete Sortable.sortables[s.element.id];
- }
- },
-
- create: function(element) {
- element = $(element);
- var options = Object.extend({
- element: element,
- tag: 'li', // assumes li children, override with tag: 'tagname'
- dropOnEmpty: false,
- tree: false,
- treeTag: 'ul',
- overlap: 'vertical', // one of 'vertical', 'horizontal'
- constraint: 'vertical', // one of 'vertical', 'horizontal', false
- containment: element, // also takes array of elements (or id's); or false
- handle: false, // or a CSS class
- only: false,
- delay: 0,
- hoverclass: null,
- ghosting: false,
- quiet: false,
- scroll: false,
- scrollSensitivity: 20,
- scrollSpeed: 15,
- format: this.SERIALIZE_RULE,
-
- // these take arrays of elements or ids and can be
- // used for better initialization performance
- elements: false,
- handles: false,
-
- onChange: Prototype.emptyFunction,
- onUpdate: Prototype.emptyFunction
- }, arguments[1] || { });
-
- // clear any old sortable with same element
- this.destroy(element);
-
- // build options for the draggables
- var options_for_draggable = {
- revert: true,
- quiet: options.quiet,
- scroll: options.scroll,
- scrollSpeed: options.scrollSpeed,
- scrollSensitivity: options.scrollSensitivity,
- delay: options.delay,
- ghosting: options.ghosting,
- constraint: options.constraint,
- handle: options.handle };
-
- if(options.starteffect)
- options_for_draggable.starteffect = options.starteffect;
-
- if(options.reverteffect)
- options_for_draggable.reverteffect = options.reverteffect;
- else
- if(options.ghosting) options_for_draggable.reverteffect = function(element) {
- element.style.top = 0;
- element.style.left = 0;
- };
-
- if(options.endeffect)
- options_for_draggable.endeffect = options.endeffect;
-
- if(options.zindex)
- options_for_draggable.zindex = options.zindex;
-
- // build options for the droppables
- var options_for_droppable = {
- overlap: options.overlap,
- containment: options.containment,
- tree: options.tree,
- hoverclass: options.hoverclass,
- onHover: Sortable.onHover
- }
-
- var options_for_tree = {
- onHover: Sortable.onEmptyHover,
- overlap: options.overlap,
- containment: options.containment,
- hoverclass: options.hoverclass
- }
-
- // fix for gecko engine
- Element.cleanWhitespace(element);
-
- options.draggables = [];
- options.droppables = [];
-
- // drop on empty handling
- if(options.dropOnEmpty || options.tree) {
- Droppables.add(element, options_for_tree);
- options.droppables.push(element);
- }
-
- (options.elements || this.findElements(element, options) || []).each( function(e,i) {
- var handle = options.handles ? $(options.handles[i]) :
- (options.handle ? $(e).select('.' + options.handle)[0] : e);
- options.draggables.push(
- new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
- Droppables.add(e, options_for_droppable);
- if(options.tree) e.treeNode = element;
- options.droppables.push(e);
- });
-
- if(options.tree) {
- (Sortable.findTreeElements(element, options) || []).each( function(e) {
- Droppables.add(e, options_for_tree);
- e.treeNode = element;
- options.droppables.push(e);
- });
- }
-
- // keep reference
- this.sortables[element.id] = options;
-
- // for onupdate
- Draggables.addObserver(new SortableObserver(element, options.onUpdate));
-
- },
-
- // return all suitable-for-sortable elements in a guaranteed order
- findElements: function(element, options) {
- return Element.findChildren(
- element, options.only, options.tree ? true : false, options.tag);
- },
-
- findTreeElements: function(element, options) {
- return Element.findChildren(
- element, options.only, options.tree ? true : false, options.treeTag);
- },
-
- onHover: function(element, dropon, overlap) {
- if(Element.isParent(dropon, element)) return;
-
- if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
- return;
- } else if(overlap>0.5) {
- Sortable.mark(dropon, 'before');
- if(dropon.previousSibling != element) {
- var oldParentNode = element.parentNode;
- element.style.visibility = "hidden"; // fix gecko rendering
- dropon.parentNode.insertBefore(element, dropon);
- if(dropon.parentNode!=oldParentNode)
- Sortable.options(oldParentNode).onChange(element);
- Sortable.options(dropon.parentNode).onChange(element);
- }
- } else {
- Sortable.mark(dropon, 'after');
- var nextElement = dropon.nextSibling || null;
- if(nextElement != element) {
- var oldParentNode = element.parentNode;
- element.style.visibility = "hidden"; // fix gecko rendering
- dropon.parentNode.insertBefore(element, nextElement);
- if(dropon.parentNode!=oldParentNode)
- Sortable.options(oldParentNode).onChange(element);
- Sortable.options(dropon.parentNode).onChange(element);
- }
- }
- },
-
- onEmptyHover: function(element, dropon, overlap) {
- var oldParentNode = element.parentNode;
- var droponOptions = Sortable.options(dropon);
-
- if(!Element.isParent(dropon, element)) {
- var index;
-
- var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
- var child = null;
-
- if(children) {
- var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
-
- for (index = 0; index < children.length; index += 1) {
- if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
- offset -= Element.offsetSize (children[index], droponOptions.overlap);
- } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
- child = index + 1 < children.length ? children[index + 1] : null;
- break;
- } else {
- child = children[index];
- break;
- }
- }
- }
-
- dropon.insertBefore(element, child);
-
- Sortable.options(oldParentNode).onChange(element);
- droponOptions.onChange(element);
- }
- },
-
- unmark: function() {
- if(Sortable._marker) Sortable._marker.hide();
- },
-
- mark: function(dropon, position) {
- // mark on ghosting only
- var sortable = Sortable.options(dropon.parentNode);
- if(sortable && !sortable.ghosting) return;
-
- if(!Sortable._marker) {
- Sortable._marker =
- ($('dropmarker') || Element.extend(document.createElement('DIV'))).
- hide().addClassName('dropmarker').setStyle({position:'absolute'});
- document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
- }
- var offsets = Position.cumulativeOffset(dropon);
- Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
-
- if(position=='after')
- if(sortable.overlap == 'horizontal')
- Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
- else
- Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
-
- Sortable._marker.show();
- },
-
- _tree: function(element, options, parent) {
- var children = Sortable.findElements(element, options) || [];
-
- for (var i = 0; i < children.length; ++i) {
- var match = children[i].id.match(options.format);
-
- if (!match) continue;
-
- var child = {
- id: encodeURIComponent(match ? match[1] : null),
- element: element,
- parent: parent,
- children: [],
- position: parent.children.length,
- container: $(children[i]).down(options.treeTag)
- }
-
- /* Get the element containing the children and recurse over it */
- if (child.container)
- this._tree(child.container, options, child)
-
- parent.children.push (child);
- }
-
- return parent;
- },
-
- tree: function(element) {
- element = $(element);
- var sortableOptions = this.options(element);
- var options = Object.extend({
- tag: sortableOptions.tag,
- treeTag: sortableOptions.treeTag,
- only: sortableOptions.only,
- name: element.id,
- format: sortableOptions.format
- }, arguments[1] || { });
-
- var root = {
- id: null,
- parent: null,
- children: [],
- container: element,
- position: 0
- }
-
- return Sortable._tree(element, options, root);
- },
-
- /* Construct a [i] index for a particular node */
- _constructIndex: function(node) {
- var index = '';
- do {
- if (node.id) index = '[' + node.position + ']' + index;
- } while ((node = node.parent) != null);
- return index;
- },
-
- sequence: function(element) {
- element = $(element);
- var options = Object.extend(this.options(element), arguments[1] || { });
-
- return $(this.findElements(element, options) || []).map( function(item) {
- return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
- });
- },
-
- setSequence: function(element, new_sequence) {
- element = $(element);
- var options = Object.extend(this.options(element), arguments[2] || { });
-
- var nodeMap = { };
- this.findElements(element, options).each( function(n) {
- if (n.id.match(options.format))
- nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
- n.parentNode.removeChild(n);
- });
-
- new_sequence.each(function(ident) {
- var n = nodeMap[ident];
- if (n) {
- n[1].appendChild(n[0]);
- delete nodeMap[ident];
- }
- });
- },
-
- serialize: function(element) {
- element = $(element);
- var options = Object.extend(Sortable.options(element), arguments[1] || { });
- var name = encodeURIComponent(
- (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
-
- if (options.tree) {
- return Sortable.tree(element, arguments[1]).children.map( function (item) {
- return [name + Sortable._constructIndex(item) + "[id]=" +
- encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
- }).flatten().join('&');
- } else {
- return Sortable.sequence(element, arguments[1]).map( function(item) {
- return name + "[]=" + encodeURIComponent(item);
- }).join('&');
- }
- }
-}
-
-// Returns true if child is contained within element
-Element.isParent = function(child, element) {
- if (!child.parentNode || child == element) return false;
- if (child.parentNode == element) return true;
- return Element.isParent(child.parentNode, element);
-}
-
-Element.findChildren = function(element, only, recursive, tagName) {
- if(!element.hasChildNodes()) return null;
- tagName = tagName.toUpperCase();
- if(only) only = [only].flatten();
- var elements = [];
- $A(element.childNodes).each( function(e) {
- if(e.tagName && e.tagName.toUpperCase()==tagName &&
- (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
- elements.push(e);
- if(recursive) {
- var grandchildren = Element.findChildren(e, only, recursive, tagName);
- if(grandchildren) elements.push(grandchildren);
- }
- });
-
- return (elements.length>0 ? elements.flatten() : []);
-}
-
-Element.offsetSize = function (element, type) {
- return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
-}
diff --git a/vendor/plugins/interlock/test/integration/app/public/javascripts/effects.js b/vendor/plugins/interlock/test/integration/app/public/javascripts/effects.js
deleted file mode 100644
index 65aed2395..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/javascripts/effects.js
+++ /dev/null
@@ -1,1120 +0,0 @@
-// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// Contributors:
-// Justin Palmer (http://encytemedia.com/)
-// Mark Pilgrim (http://diveintomark.org/)
-// Martin Bialasinki
-//
-// script.aculo.us is freely distributable under the terms of an MIT-style license.
-// For details, see the script.aculo.us web site: http://script.aculo.us/
-
-// converts rgb() and #xxx to #xxxxxx format,
-// returns self (or first argument) if not convertable
-String.prototype.parseColor = function() {
- var color = '#';
- if (this.slice(0,4) == 'rgb(') {
- var cols = this.slice(4,this.length-1).split(',');
- var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
- } else {
- if (this.slice(0,1) == '#') {
- if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
- if (this.length==7) color = this.toLowerCase();
- }
- }
- return (color.length==7 ? color : (arguments[0] || this));
-};
-
-/*--------------------------------------------------------------------------*/
-
-Element.collectTextNodes = function(element) {
- return $A($(element).childNodes).collect( function(node) {
- return (node.nodeType==3 ? node.nodeValue :
- (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
- }).flatten().join('');
-};
-
-Element.collectTextNodesIgnoreClass = function(element, className) {
- return $A($(element).childNodes).collect( function(node) {
- return (node.nodeType==3 ? node.nodeValue :
- ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
- Element.collectTextNodesIgnoreClass(node, className) : ''));
- }).flatten().join('');
-};
-
-Element.setContentZoom = function(element, percent) {
- element = $(element);
- element.setStyle({fontSize: (percent/100) + 'em'});
- if (Prototype.Browser.WebKit) window.scrollBy(0,0);
- return element;
-};
-
-Element.getInlineOpacity = function(element){
- return $(element).style.opacity || '';
-};
-
-Element.forceRerendering = function(element) {
- try {
- element = $(element);
- var n = document.createTextNode(' ');
- element.appendChild(n);
- element.removeChild(n);
- } catch(e) { }
-};
-
-/*--------------------------------------------------------------------------*/
-
-var Effect = {
- _elementDoesNotExistError: {
- name: 'ElementDoesNotExistError',
- message: 'The specified DOM element does not exist, but is required for this effect to operate'
- },
- Transitions: {
- linear: Prototype.K,
- sinoidal: function(pos) {
- return (-Math.cos(pos*Math.PI)/2) + 0.5;
- },
- reverse: function(pos) {
- return 1-pos;
- },
- flicker: function(pos) {
- var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
- return pos > 1 ? 1 : pos;
- },
- wobble: function(pos) {
- return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
- },
- pulse: function(pos, pulses) {
- pulses = pulses || 5;
- return (
- ((pos % (1/pulses)) * pulses).round() == 0 ?
- ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
- 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
- );
- },
- spring: function(pos) {
- return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
- },
- none: function(pos) {
- return 0;
- },
- full: function(pos) {
- return 1;
- }
- },
- DefaultOptions: {
- duration: 1.0, // seconds
- fps: 100, // 100= assume 66fps max.
- sync: false, // true for combining
- from: 0.0,
- to: 1.0,
- delay: 0.0,
- queue: 'parallel'
- },
- tagifyText: function(element) {
- var tagifyStyle = 'position:relative';
- if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
-
- element = $(element);
- $A(element.childNodes).each( function(child) {
- if (child.nodeType==3) {
- child.nodeValue.toArray().each( function(character) {
- element.insertBefore(
- new Element('span', {style: tagifyStyle}).update(
- character == ' ' ? String.fromCharCode(160) : character),
- child);
- });
- Element.remove(child);
- }
- });
- },
- multiple: function(element, effect) {
- var elements;
- if (((typeof element == 'object') ||
- Object.isFunction(element)) &&
- (element.length))
- elements = element;
- else
- elements = $(element).childNodes;
-
- var options = Object.extend({
- speed: 0.1,
- delay: 0.0
- }, arguments[2] || { });
- var masterDelay = options.delay;
-
- $A(elements).each( function(element, index) {
- new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
- });
- },
- PAIRS: {
- 'slide': ['SlideDown','SlideUp'],
- 'blind': ['BlindDown','BlindUp'],
- 'appear': ['Appear','Fade']
- },
- toggle: function(element, effect) {
- element = $(element);
- effect = (effect || 'appear').toLowerCase();
- var options = Object.extend({
- queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
- }, arguments[2] || { });
- Effect[element.visible() ?
- Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
- }
-};
-
-Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
-
-/* ------------- core effects ------------- */
-
-Effect.ScopedQueue = Class.create(Enumerable, {
- initialize: function() {
- this.effects = [];
- this.interval = null;
- },
- _each: function(iterator) {
- this.effects._each(iterator);
- },
- add: function(effect) {
- var timestamp = new Date().getTime();
-
- var position = Object.isString(effect.options.queue) ?
- effect.options.queue : effect.options.queue.position;
-
- switch(position) {
- case 'front':
- // move unstarted effects after this effect
- this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
- e.startOn += effect.finishOn;
- e.finishOn += effect.finishOn;
- });
- break;
- case 'with-last':
- timestamp = this.effects.pluck('startOn').max() || timestamp;
- break;
- case 'end':
- // start effect after last queued effect has finished
- timestamp = this.effects.pluck('finishOn').max() || timestamp;
- break;
- }
-
- effect.startOn += timestamp;
- effect.finishOn += timestamp;
-
- if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
- this.effects.push(effect);
-
- if (!this.interval)
- this.interval = setInterval(this.loop.bind(this), 15);
- },
- remove: function(effect) {
- this.effects = this.effects.reject(function(e) { return e==effect });
- if (this.effects.length == 0) {
- clearInterval(this.interval);
- this.interval = null;
- }
- },
- loop: function() {
- var timePos = new Date().getTime();
- for(var i=0, len=this.effects.length;i<len;i++)
- this.effects[i] && this.effects[i].loop(timePos);
- }
-});
-
-Effect.Queues = {
- instances: $H(),
- get: function(queueName) {
- if (!Object.isString(queueName)) return queueName;
-
- return this.instances.get(queueName) ||
- this.instances.set(queueName, new Effect.ScopedQueue());
- }
-};
-Effect.Queue = Effect.Queues.get('global');
-
-Effect.Base = Class.create({
- position: null,
- start: function(options) {
- function codeForEvent(options,eventName){
- return (
- (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
- (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
- );
- }
- if (options && options.transition === false) options.transition = Effect.Transitions.linear;
- this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
- this.currentFrame = 0;
- this.state = 'idle';
- this.startOn = this.options.delay*1000;
- this.finishOn = this.startOn+(this.options.duration*1000);
- this.fromToDelta = this.options.to-this.options.from;
- this.totalTime = this.finishOn-this.startOn;
- this.totalFrames = this.options.fps*this.options.duration;
-
- eval('this.render = function(pos){ '+
- 'if (this.state=="idle"){this.state="running";'+
- codeForEvent(this.options,'beforeSetup')+
- (this.setup ? 'this.setup();':'')+
- codeForEvent(this.options,'afterSetup')+
- '};if (this.state=="running"){'+
- 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
- 'this.position=pos;'+
- codeForEvent(this.options,'beforeUpdate')+
- (this.update ? 'this.update(pos);':'')+
- codeForEvent(this.options,'afterUpdate')+
- '}}');
-
- this.event('beforeStart');
- if (!this.options.sync)
- Effect.Queues.get(Object.isString(this.options.queue) ?
- 'global' : this.options.queue.scope).add(this);
- },
- loop: function(timePos) {
- if (timePos >= this.startOn) {
- if (timePos >= this.finishOn) {
- this.render(1.0);
- this.cancel();
- this.event('beforeFinish');
- if (this.finish) this.finish();
- this.event('afterFinish');
- return;
- }
- var pos = (timePos - this.startOn) / this.totalTime,
- frame = (pos * this.totalFrames).round();
- if (frame > this.currentFrame) {
- this.render(pos);
- this.currentFrame = frame;
- }
- }
- },
- cancel: function() {
- if (!this.options.sync)
- Effect.Queues.get(Object.isString(this.options.queue) ?
- 'global' : this.options.queue.scope).remove(this);
- this.state = 'finished';
- },
- event: function(eventName) {
- if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
- if (this.options[eventName]) this.options[eventName](this);
- },
- inspect: function() {
- var data = $H();
- for(property in this)
- if (!Object.isFunction(this[property])) data.set(property, this[property]);
- return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
- }
-});
-
-Effect.Parallel = Class.create(Effect.Base, {
- initialize: function(effects) {
- this.effects = effects || [];
- this.start(arguments[1]);
- },
- update: function(position) {
- this.effects.invoke('render', position);
- },
- finish: function(position) {
- this.effects.each( function(effect) {
- effect.render(1.0);
- effect.cancel();
- effect.event('beforeFinish');
- if (effect.finish) effect.finish(position);
- effect.event('afterFinish');
- });
- }
-});
-
-Effect.Tween = Class.create(Effect.Base, {
- initialize: function(object, from, to) {
- object = Object.isString(object) ? $(object) : object;
- var args = $A(arguments), method = args.last(),
- options = args.length == 5 ? args[3] : null;
- this.method = Object.isFunction(method) ? method.bind(object) :
- Object.isFunction(object[method]) ? object[method].bind(object) :
- function(value) { object[method] = value };
- this.start(Object.extend({ from: from, to: to }, options || { }));
- },
- update: function(position) {
- this.method(position);
- }
-});
-
-Effect.Event = Class.create(Effect.Base, {
- initialize: function() {
- this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
- },
- update: Prototype.emptyFunction
-});
-
-Effect.Opacity = Class.create(Effect.Base, {
- initialize: function(element) {
- this.element = $(element);
- if (!this.element) throw(Effect._elementDoesNotExistError);
- // make this work on IE on elements without 'layout'
- if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
- this.element.setStyle({zoom: 1});
- var options = Object.extend({
- from: this.element.getOpacity() || 0.0,
- to: 1.0
- }, arguments[1] || { });
- this.start(options);
- },
- update: function(position) {
- this.element.setOpacity(position);
- }
-});
-
-Effect.Move = Class.create(Effect.Base, {
- initialize: function(element) {
- this.element = $(element);
- if (!this.element) throw(Effect._elementDoesNotExistError);
- var options = Object.extend({
- x: 0,
- y: 0,
- mode: 'relative'
- }, arguments[1] || { });
- this.start(options);
- },
- setup: function() {
- this.element.makePositioned();
- this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
- this.originalTop = parseFloat(this.element.getStyle('top') || '0');
- if (this.options.mode == 'absolute') {
- this.options.x = this.options.x - this.originalLeft;
- this.options.y = this.options.y - this.originalTop;
- }
- },
- update: function(position) {
- this.element.setStyle({
- left: (this.options.x * position + this.originalLeft).round() + 'px',
- top: (this.options.y * position + this.originalTop).round() + 'px'
- });
- }
-});
-
-// for backwards compatibility
-Effect.MoveBy = function(element, toTop, toLeft) {
- return new Effect.Move(element,
- Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
-};
-
-Effect.Scale = Class.create(Effect.Base, {
- initialize: function(element, percent) {
- this.element = $(element);
- if (!this.element) throw(Effect._elementDoesNotExistError);
- var options = Object.extend({
- scaleX: true,
- scaleY: true,
- scaleContent: true,
- scaleFromCenter: false,
- scaleMode: 'box', // 'box' or 'contents' or { } with provided values
- scaleFrom: 100.0,
- scaleTo: percent
- }, arguments[2] || { });
- this.start(options);
- },
- setup: function() {
- this.restoreAfterFinish = this.options.restoreAfterFinish || false;
- this.elementPositioning = this.element.getStyle('position');
-
- this.originalStyle = { };
- ['top','left','width','height','fontSize'].each( function(k) {
- this.originalStyle[k] = this.element.style[k];
- }.bind(this));
-
- this.originalTop = this.element.offsetTop;
- this.originalLeft = this.element.offsetLeft;
-
- var fontSize = this.element.getStyle('font-size') || '100%';
- ['em','px','%','pt'].each( function(fontSizeType) {
- if (fontSize.indexOf(fontSizeType)>0) {
- this.fontSize = parseFloat(fontSize);
- this.fontSizeType = fontSizeType;
- }
- }.bind(this));
-
- this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
-
- this.dims = null;
- if (this.options.scaleMode=='box')
- this.dims = [this.element.offsetHeight, this.element.offsetWidth];
- if (/^content/.test(this.options.scaleMode))
- this.dims = [this.element.scrollHeight, this.element.scrollWidth];
- if (!this.dims)
- this.dims = [this.options.scaleMode.originalHeight,
- this.options.scaleMode.originalWidth];
- },
- update: function(position) {
- var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
- if (this.options.scaleContent && this.fontSize)
- this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
- this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
- },
- finish: function(position) {
- if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
- },
- setDimensions: function(height, width) {
- var d = { };
- if (this.options.scaleX) d.width = width.round() + 'px';
- if (this.options.scaleY) d.height = height.round() + 'px';
- if (this.options.scaleFromCenter) {
- var topd = (height - this.dims[0])/2;
- var leftd = (width - this.dims[1])/2;
- if (this.elementPositioning == 'absolute') {
- if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
- if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
- } else {
- if (this.options.scaleY) d.top = -topd + 'px';
- if (this.options.scaleX) d.left = -leftd + 'px';
- }
- }
- this.element.setStyle(d);
- }
-});
-
-Effect.Highlight = Class.create(Effect.Base, {
- initialize: function(element) {
- this.element = $(element);
- if (!this.element) throw(Effect._elementDoesNotExistError);
- var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
- this.start(options);
- },
- setup: function() {
- // Prevent executing on elements not in the layout flow
- if (this.element.getStyle('display')=='none') { this.cancel(); return; }
- // Disable background image during the effect
- this.oldStyle = { };
- if (!this.options.keepBackgroundImage) {
- this.oldStyle.backgroundImage = this.element.getStyle('background-image');
- this.element.setStyle({backgroundImage: 'none'});
- }
- if (!this.options.endcolor)
- this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
- if (!this.options.restorecolor)
- this.options.restorecolor = this.element.getStyle('background-color');
- // init color calculations
- this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
- this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
- },
- update: function(position) {
- this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
- return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
- },
- finish: function() {
- this.element.setStyle(Object.extend(this.oldStyle, {
- backgroundColor: this.options.restorecolor
- }));
- }
-});
-
-Effect.ScrollTo = function(element) {
- var options = arguments[1] || { },
- scrollOffsets = document.viewport.getScrollOffsets(),
- elementOffsets = $(element).cumulativeOffset(),
- max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
-
- if (options.offset) elementOffsets[1] += options.offset;
-
- return new Effect.Tween(null,
- scrollOffsets.top,
- elementOffsets[1] > max ? max : elementOffsets[1],
- options,
- function(p){ scrollTo(scrollOffsets.left, p.round()) }
- );
-};
-
-/* ------------- combination effects ------------- */
-
-Effect.Fade = function(element) {
- element = $(element);
- var oldOpacity = element.getInlineOpacity();
- var options = Object.extend({
- from: element.getOpacity() || 1.0,
- to: 0.0,
- afterFinishInternal: function(effect) {
- if (effect.options.to!=0) return;
- effect.element.hide().setStyle({opacity: oldOpacity});
- }
- }, arguments[1] || { });
- return new Effect.Opacity(element,options);
-};
-
-Effect.Appear = function(element) {
- element = $(element);
- var options = Object.extend({
- from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
- to: 1.0,
- // force Safari to render floated elements properly
- afterFinishInternal: function(effect) {
- effect.element.forceRerendering();
- },
- beforeSetup: function(effect) {
- effect.element.setOpacity(effect.options.from).show();
- }}, arguments[1] || { });
- return new Effect.Opacity(element,options);
-};
-
-Effect.Puff = function(element) {
- element = $(element);
- var oldStyle = {
- opacity: element.getInlineOpacity(),
- position: element.getStyle('position'),
- top: element.style.top,
- left: element.style.left,
- width: element.style.width,
- height: element.style.height
- };
- return new Effect.Parallel(
- [ new Effect.Scale(element, 200,
- { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
- new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
- Object.extend({ duration: 1.0,
- beforeSetupInternal: function(effect) {
- Position.absolutize(effect.effects[0].element)
- },
- afterFinishInternal: function(effect) {
- effect.effects[0].element.hide().setStyle(oldStyle); }
- }, arguments[1] || { })
- );
-};
-
-Effect.BlindUp = function(element) {
- element = $(element);
- element.makeClipping();
- return new Effect.Scale(element, 0,
- Object.extend({ scaleContent: false,
- scaleX: false,
- restoreAfterFinish: true,
- afterFinishInternal: function(effect) {
- effect.element.hide().undoClipping();
- }
- }, arguments[1] || { })
- );
-};
-
-Effect.BlindDown = function(element) {
- element = $(element);
- var elementDimensions = element.getDimensions();
- return new Effect.Scale(element, 100, Object.extend({
- scaleContent: false,
- scaleX: false,
- scaleFrom: 0,
- scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
- restoreAfterFinish: true,
- afterSetup: function(effect) {
- effect.element.makeClipping().setStyle({height: '0px'}).show();
- },
- afterFinishInternal: function(effect) {
- effect.element.undoClipping();
- }
- }, arguments[1] || { }));
-};
-
-Effect.SwitchOff = function(element) {
- element = $(element);
- var oldOpacity = element.getInlineOpacity();
- return new Effect.Appear(element, Object.extend({
- duration: 0.4,
- from: 0,
- transition: Effect.Transitions.flicker,
- afterFinishInternal: function(effect) {
- new Effect.Scale(effect.element, 1, {
- duration: 0.3, scaleFromCenter: true,
- scaleX: false, scaleContent: false, restoreAfterFinish: true,
- beforeSetup: function(effect) {
- effect.element.makePositioned().makeClipping();
- },
- afterFinishInternal: function(effect) {
- effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
- }
- })
- }
- }, arguments[1] || { }));
-};
-
-Effect.DropOut = function(element) {
- element = $(element);
- var oldStyle = {
- top: element.getStyle('top'),
- left: element.getStyle('left'),
- opacity: element.getInlineOpacity() };
- return new Effect.Parallel(
- [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
- new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
- Object.extend(
- { duration: 0.5,
- beforeSetup: function(effect) {
- effect.effects[0].element.makePositioned();
- },
- afterFinishInternal: function(effect) {
- effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
- }
- }, arguments[1] || { }));
-};
-
-Effect.Shake = function(element) {
- element = $(element);
- var options = Object.extend({
- distance: 20,
- duration: 0.5
- }, arguments[1] || {});
- var distance = parseFloat(options.distance);
- var split = parseFloat(options.duration) / 10.0;
- var oldStyle = {
- top: element.getStyle('top'),
- left: element.getStyle('left') };
- return new Effect.Move(element,
- { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
- new Effect.Move(effect.element,
- { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
- new Effect.Move(effect.element,
- { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
- new Effect.Move(effect.element,
- { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
- new Effect.Move(effect.element,
- { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
- new Effect.Move(effect.element,
- { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
- effect.element.undoPositioned().setStyle(oldStyle);
- }}) }}) }}) }}) }}) }});
-};
-
-Effect.SlideDown = function(element) {
- element = $(element).cleanWhitespace();
- // SlideDown need to have the content of the element wrapped in a container element with fixed height!
- var oldInnerBottom = element.down().getStyle('bottom');
- var elementDimensions = element.getDimensions();
- return new Effect.Scale(element, 100, Object.extend({
- scaleContent: false,
- scaleX: false,
- scaleFrom: window.opera ? 0 : 1,
- scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
- restoreAfterFinish: true,
- afterSetup: function(effect) {
- effect.element.makePositioned();
- effect.element.down().makePositioned();
- if (window.opera) effect.element.setStyle({top: ''});
- effect.element.makeClipping().setStyle({height: '0px'}).show();
- },
- afterUpdateInternal: function(effect) {
- effect.element.down().setStyle({bottom:
- (effect.dims[0] - effect.element.clientHeight) + 'px' });
- },
- afterFinishInternal: function(effect) {
- effect.element.undoClipping().undoPositioned();
- effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
- }, arguments[1] || { })
- );
-};
-
-Effect.SlideUp = function(element) {
- element = $(element).cleanWhitespace();
- var oldInnerBottom = element.down().getStyle('bottom');
- var elementDimensions = element.getDimensions();
- return new Effect.Scale(element, window.opera ? 0 : 1,
- Object.extend({ scaleContent: false,
- scaleX: false,
- scaleMode: 'box',
- scaleFrom: 100,
- scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
- restoreAfterFinish: true,
- afterSetup: function(effect) {
- effect.element.makePositioned();
- effect.element.down().makePositioned();
- if (window.opera) effect.element.setStyle({top: ''});
- effect.element.makeClipping().show();
- },
- afterUpdateInternal: function(effect) {
- effect.element.down().setStyle({bottom:
- (effect.dims[0] - effect.element.clientHeight) + 'px' });
- },
- afterFinishInternal: function(effect) {
- effect.element.hide().undoClipping().undoPositioned();
- effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
- }
- }, arguments[1] || { })
- );
-};
-
-// Bug in opera makes the TD containing this element expand for a instance after finish
-Effect.Squish = function(element) {
- return new Effect.Scale(element, window.opera ? 1 : 0, {
- restoreAfterFinish: true,
- beforeSetup: function(effect) {
- effect.element.makeClipping();
- },
- afterFinishInternal: function(effect) {
- effect.element.hide().undoClipping();
- }
- });
-};
-
-Effect.Grow = function(element) {
- element = $(element);
- var options = Object.extend({
- direction: 'center',
- moveTransition: Effect.Transitions.sinoidal,
- scaleTransition: Effect.Transitions.sinoidal,
- opacityTransition: Effect.Transitions.full
- }, arguments[1] || { });
- var oldStyle = {
- top: element.style.top,
- left: element.style.left,
- height: element.style.height,
- width: element.style.width,
- opacity: element.getInlineOpacity() };
-
- var dims = element.getDimensions();
- var initialMoveX, initialMoveY;
- var moveX, moveY;
-
- switch (options.direction) {
- case 'top-left':
- initialMoveX = initialMoveY = moveX = moveY = 0;
- break;
- case 'top-right':
- initialMoveX = dims.width;
- initialMoveY = moveY = 0;
- moveX = -dims.width;
- break;
- case 'bottom-left':
- initialMoveX = moveX = 0;
- initialMoveY = dims.height;
- moveY = -dims.height;
- break;
- case 'bottom-right':
- initialMoveX = dims.width;
- initialMoveY = dims.height;
- moveX = -dims.width;
- moveY = -dims.height;
- break;
- case 'center':
- initialMoveX = dims.width / 2;
- initialMoveY = dims.height / 2;
- moveX = -dims.width / 2;
- moveY = -dims.height / 2;
- break;
- }
-
- return new Effect.Move(element, {
- x: initialMoveX,
- y: initialMoveY,
- duration: 0.01,
- beforeSetup: function(effect) {
- effect.element.hide().makeClipping().makePositioned();
- },
- afterFinishInternal: function(effect) {
- new Effect.Parallel(
- [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
- new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
- new Effect.Scale(effect.element, 100, {
- scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
- sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
- ], Object.extend({
- beforeSetup: function(effect) {
- effect.effects[0].element.setStyle({height: '0px'}).show();
- },
- afterFinishInternal: function(effect) {
- effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
- }
- }, options)
- )
- }
- });
-};
-
-Effect.Shrink = function(element) {
- element = $(element);
- var options = Object.extend({
- direction: 'center',
- moveTransition: Effect.Transitions.sinoidal,
- scaleTransition: Effect.Transitions.sinoidal,
- opacityTransition: Effect.Transitions.none
- }, arguments[1] || { });
- var oldStyle = {
- top: element.style.top,
- left: element.style.left,
- height: element.style.height,
- width: element.style.width,
- opacity: element.getInlineOpacity() };
-
- var dims = element.getDimensions();
- var moveX, moveY;
-
- switch (options.direction) {
- case 'top-left':
- moveX = moveY = 0;
- break;
- case 'top-right':
- moveX = dims.width;
- moveY = 0;
- break;
- case 'bottom-left':
- moveX = 0;
- moveY = dims.height;
- break;
- case 'bottom-right':
- moveX = dims.width;
- moveY = dims.height;
- break;
- case 'center':
- moveX = dims.width / 2;
- moveY = dims.height / 2;
- break;
- }
-
- return new Effect.Parallel(
- [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
- new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
- new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
- ], Object.extend({
- beforeStartInternal: function(effect) {
- effect.effects[0].element.makePositioned().makeClipping();
- },
- afterFinishInternal: function(effect) {
- effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
- }, options)
- );
-};
-
-Effect.Pulsate = function(element) {
- element = $(element);
- var options = arguments[1] || { };
- var oldOpacity = element.getInlineOpacity();
- var transition = options.transition || Effect.Transitions.sinoidal;
- var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
- reverser.bind(transition);
- return new Effect.Opacity(element,
- Object.extend(Object.extend({ duration: 2.0, from: 0,
- afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
- }, options), {transition: reverser}));
-};
-
-Effect.Fold = function(element) {
- element = $(element);
- var oldStyle = {
- top: element.style.top,
- left: element.style.left,
- width: element.style.width,
- height: element.style.height };
- element.makeClipping();
- return new Effect.Scale(element, 5, Object.extend({
- scaleContent: false,
- scaleX: false,
- afterFinishInternal: function(effect) {
- new Effect.Scale(element, 1, {
- scaleContent: false,
- scaleY: false,
- afterFinishInternal: function(effect) {
- effect.element.hide().undoClipping().setStyle(oldStyle);
- } });
- }}, arguments[1] || { }));
-};
-
-Effect.Morph = Class.create(Effect.Base, {
- initialize: function(element) {
- this.element = $(element);
- if (!this.element) throw(Effect._elementDoesNotExistError);
- var options = Object.extend({
- style: { }
- }, arguments[1] || { });
-
- if (!Object.isString(options.style)) this.style = $H(options.style);
- else {
- if (options.style.include(':'))
- this.style = options.style.parseStyle();
- else {
- this.element.addClassName(options.style);
- this.style = $H(this.element.getStyles());
- this.element.removeClassName(options.style);
- var css = this.element.getStyles();
- this.style = this.style.reject(function(style) {
- return style.value == css[style.key];
- });
- options.afterFinishInternal = function(effect) {
- effect.element.addClassName(effect.options.style);
- effect.transforms.each(function(transform) {
- effect.element.style[transform.style] = '';
- });
- }
- }
- }
- this.start(options);
- },
-
- setup: function(){
- function parseColor(color){
- if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
- color = color.parseColor();
- return $R(0,2).map(function(i){
- return parseInt( color.slice(i*2+1,i*2+3), 16 )
- });
- }
- this.transforms = this.style.map(function(pair){
- var property = pair[0], value = pair[1], unit = null;
-
- if (value.parseColor('#zzzzzz') != '#zzzzzz') {
- value = value.parseColor();
- unit = 'color';
- } else if (property == 'opacity') {
- value = parseFloat(value);
- if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
- this.element.setStyle({zoom: 1});
- } else if (Element.CSS_LENGTH.test(value)) {
- var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
- value = parseFloat(components[1]);
- unit = (components.length == 3) ? components[2] : null;
- }
-
- var originalValue = this.element.getStyle(property);
- return {
- style: property.camelize(),
- originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
- targetValue: unit=='color' ? parseColor(value) : value,
- unit: unit
- };
- }.bind(this)).reject(function(transform){
- return (
- (transform.originalValue == transform.targetValue) ||
- (
- transform.unit != 'color' &&
- (isNaN(transform.originalValue) || isNaN(transform.targetValue))
- )
- )
- });
- },
- update: function(position) {
- var style = { }, transform, i = this.transforms.length;
- while(i--)
- style[(transform = this.transforms[i]).style] =
- transform.unit=='color' ? '#'+
- (Math.round(transform.originalValue[0]+
- (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
- (Math.round(transform.originalValue[1]+
- (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
- (Math.round(transform.originalValue[2]+
- (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
- (transform.originalValue +
- (transform.targetValue - transform.originalValue) * position).toFixed(3) +
- (transform.unit === null ? '' : transform.unit);
- this.element.setStyle(style, true);
- }
-});
-
-Effect.Transform = Class.create({
- initialize: function(tracks){
- this.tracks = [];
- this.options = arguments[1] || { };
- this.addTracks(tracks);
- },
- addTracks: function(tracks){
- tracks.each(function(track){
- track = $H(track);
- var data = track.values().first();
- this.tracks.push($H({
- ids: track.keys().first(),
- effect: Effect.Morph,
- options: { style: data }
- }));
- }.bind(this));
- return this;
- },
- play: function(){
- return new Effect.Parallel(
- this.tracks.map(function(track){
- var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
- var elements = [$(ids) || $$(ids)].flatten();
- return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
- }).flatten(),
- this.options
- );
- }
-});
-
-Element.CSS_PROPERTIES = $w(
- 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
- 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
- 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
- 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
- 'fontSize fontWeight height left letterSpacing lineHeight ' +
- 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
- 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
- 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
- 'right textIndent top width wordSpacing zIndex');
-
-Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
-
-String.__parseStyleElement = document.createElement('div');
-String.prototype.parseStyle = function(){
- var style, styleRules = $H();
- if (Prototype.Browser.WebKit)
- style = new Element('div',{style:this}).style;
- else {
- String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
- style = String.__parseStyleElement.childNodes[0].style;
- }
-
- Element.CSS_PROPERTIES.each(function(property){
- if (style[property]) styleRules.set(property, style[property]);
- });
-
- if (Prototype.Browser.IE && this.include('opacity'))
- styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
-
- return styleRules;
-};
-
-if (document.defaultView && document.defaultView.getComputedStyle) {
- Element.getStyles = function(element) {
- var css = document.defaultView.getComputedStyle($(element), null);
- return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
- styles[property] = css[property];
- return styles;
- });
- };
-} else {
- Element.getStyles = function(element) {
- element = $(element);
- var css = element.currentStyle, styles;
- styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
- hash.set(property, css[property]);
- return hash;
- });
- if (!styles.opacity) styles.set('opacity', element.getOpacity());
- return styles;
- };
-};
-
-Effect.Methods = {
- morph: function(element, style) {
- element = $(element);
- new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
- return element;
- },
- visualEffect: function(element, effect, options) {
- element = $(element)
- var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
- new Effect[klass](element, options);
- return element;
- },
- highlight: function(element, options) {
- element = $(element);
- new Effect.Highlight(element, options);
- return element;
- }
-};
-
-$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
- 'pulsate shake puff squish switchOff dropOut').each(
- function(effect) {
- Effect.Methods[effect] = function(element, options){
- element = $(element);
- Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
- return element;
- }
- }
-);
-
-$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
- function(f) { Effect.Methods[f] = Element[f]; }
-);
-
-Element.addMethods(Effect.Methods);
diff --git a/vendor/plugins/interlock/test/integration/app/public/javascripts/prototype.js b/vendor/plugins/interlock/test/integration/app/public/javascripts/prototype.js
deleted file mode 100644
index 086ba2ea6..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/javascripts/prototype.js
+++ /dev/null
@@ -1,4200 +0,0 @@
-/* Prototype JavaScript framework, version 1.6.0
- * (c) 2005-2007 Sam Stephenson
- *
- * Prototype is freely distributable under the terms of an MIT-style license.
- * For details, see the Prototype web site: http://www.prototypejs.org/
- *
- *--------------------------------------------------------------------------*/
-
-var Prototype = {
- Version: '1.6.0',
-
- Browser: {
- IE: !!(window.attachEvent && !window.opera),
- Opera: !!window.opera,
- WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
- Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
- MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
- },
-
- BrowserFeatures: {
- XPath: !!document.evaluate,
- ElementExtensions: !!window.HTMLElement,
- SpecificElementExtensions:
- document.createElement('div').__proto__ &&
- document.createElement('div').__proto__ !==
- document.createElement('form').__proto__
- },
-
- ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
- JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
-
- emptyFunction: function() { },
- K: function(x) { return x }
-};
-
-if (Prototype.Browser.MobileSafari)
- Prototype.BrowserFeatures.SpecificElementExtensions = false;
-
-
-/* Based on Alex Arnell's inheritance implementation. */
-var Class = {
- create: function() {
- var parent = null, properties = $A(arguments);
- if (Object.isFunction(properties[0]))
- parent = properties.shift();
-
- function klass() {
- this.initialize.apply(this, arguments);
- }
-
- Object.extend(klass, Class.Methods);
- klass.superclass = parent;
- klass.subclasses = [];
-
- if (parent) {
- var subclass = function() { };
- subclass.prototype = parent.prototype;
- klass.prototype = new subclass;
- parent.subclasses.push(klass);
- }
-
- for (var i = 0; i < properties.length; i++)
- klass.addMethods(properties[i]);
-
- if (!klass.prototype.initialize)
- klass.prototype.initialize = Prototype.emptyFunction;
-
- klass.prototype.constructor = klass;
-
- return klass;
- }
-};
-
-Class.Methods = {
- addMethods: function(source) {
- var ancestor = this.superclass && this.superclass.prototype;
- var properties = Object.keys(source);
-
- if (!Object.keys({ toString: true }).length)
- properties.push("toString", "valueOf");
-
- for (var i = 0, length = properties.length; i < length; i++) {
- var property = properties[i], value = source[property];
- if (ancestor && Object.isFunction(value) &&
- value.argumentNames().first() == "$super") {
- var method = value, value = Object.extend((function(m) {
- return function() { return ancestor[m].apply(this, arguments) };
- })(property).wrap(method), {
- valueOf: function() { return method },
- toString: function() { return method.toString() }
- });
- }
- this.prototype[property] = value;
- }
-
- return this;
- }
-};
-
-var Abstract = { };
-
-Object.extend = function(destination, source) {
- for (var property in source)
- destination[property] = source[property];
- return destination;
-};
-
-Object.extend(Object, {
- inspect: function(object) {
- try {
- if (Object.isUndefined(object)) return 'undefined';
- if (object === null) return 'null';
- return object.inspect ? object.inspect() : object.toString();
- } catch (e) {
- if (e instanceof RangeError) return '...';
- throw e;
- }
- },
-
- toJSON: function(object) {
- var type = typeof object;
- switch (type) {
- case 'undefined':
- case 'function':
- case 'unknown': return;
- case 'boolean': return object.toString();
- }
-
- if (object === null) return 'null';
- if (object.toJSON) return object.toJSON();
- if (Object.isElement(object)) return;
-
- var results = [];
- for (var property in object) {
- var value = Object.toJSON(object[property]);
- if (!Object.isUndefined(value))
- results.push(property.toJSON() + ': ' + value);
- }
-
- return '{' + results.join(', ') + '}';
- },
-
- toQueryString: function(object) {
- return $H(object).toQueryString();
- },
-
- toHTML: function(object) {
- return object && object.toHTML ? object.toHTML() : String.interpret(object);
- },
-
- keys: function(object) {
- var keys = [];
- for (var property in object)
- keys.push(property);
- return keys;
- },
-
- values: function(object) {
- var values = [];
- for (var property in object)
- values.push(object[property]);
- return values;
- },
-
- clone: function(object) {
- return Object.extend({ }, object);
- },
-
- isElement: function(object) {
- return object && object.nodeType == 1;
- },
-
- isArray: function(object) {
- return object && object.constructor === Array;
- },
-
- isHash: function(object) {
- return object instanceof Hash;
- },
-
- isFunction: function(object) {
- return typeof object == "function";
- },
-
- isString: function(object) {
- return typeof object == "string";
- },
-
- isNumber: function(object) {
- return typeof object == "number";
- },
-
- isUndefined: function(object) {
- return typeof object == "undefined";
- }
-});
-
-Object.extend(Function.prototype, {
- argumentNames: function() {
- var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
- return names.length == 1 && !names[0] ? [] : names;
- },
-
- bind: function() {
- if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
- var __method = this, args = $A(arguments), object = args.shift();
- return function() {
- return __method.apply(object, args.concat($A(arguments)));
- }
- },
-
- bindAsEventListener: function() {
- var __method = this, args = $A(arguments), object = args.shift();
- return function(event) {
- return __method.apply(object, [event || window.event].concat(args));
- }
- },
-
- curry: function() {
- if (!arguments.length) return this;
- var __method = this, args = $A(arguments);
- return function() {
- return __method.apply(this, args.concat($A(arguments)));
- }
- },
-
- delay: function() {
- var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
- return window.setTimeout(function() {
- return __method.apply(__method, args);
- }, timeout);
- },
-
- wrap: function(wrapper) {
- var __method = this;
- return function() {
- return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
- }
- },
-
- methodize: function() {
- if (this._methodized) return this._methodized;
- var __method = this;
- return this._methodized = function() {
- return __method.apply(null, [this].concat($A(arguments)));
- };
- }
-});
-
-Function.prototype.defer = Function.prototype.delay.curry(0.01);
-
-Date.prototype.toJSON = function() {
- return '"' + this.getUTCFullYear() + '-' +
- (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
- this.getUTCDate().toPaddedString(2) + 'T' +
- this.getUTCHours().toPaddedString(2) + ':' +
- this.getUTCMinutes().toPaddedString(2) + ':' +
- this.getUTCSeconds().toPaddedString(2) + 'Z"';
-};
-
-var Try = {
- these: function() {
- var returnValue;
-
- for (var i = 0, length = arguments.length; i < length; i++) {
- var lambda = arguments[i];
- try {
- returnValue = lambda();
- break;
- } catch (e) { }
- }
-
- return returnValue;
- }
-};
-
-RegExp.prototype.match = RegExp.prototype.test;
-
-RegExp.escape = function(str) {
- return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
-};
-
-/*--------------------------------------------------------------------------*/
-
-var PeriodicalExecuter = Class.create({
- initialize: function(callback, frequency) {
- this.callback = callback;
- this.frequency = frequency;
- this.currentlyExecuting = false;
-
- this.registerCallback();
- },
-
- registerCallback: function() {
- this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
- },
-
- execute: function() {
- this.callback(this);
- },
-
- stop: function() {
- if (!this.timer) return;
- clearInterval(this.timer);
- this.timer = null;
- },
-
- onTimerEvent: function() {
- if (!this.currentlyExecuting) {
- try {
- this.currentlyExecuting = true;
- this.execute();
- } finally {
- this.currentlyExecuting = false;
- }
- }
- }
-});
-Object.extend(String, {
- interpret: function(value) {
- return value == null ? '' : String(value);
- },
- specialChar: {
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '\\': '\\\\'
- }
-});
-
-Object.extend(String.prototype, {
- gsub: function(pattern, replacement) {
- var result = '', source = this, match;
- replacement = arguments.callee.prepareReplacement(replacement);
-
- while (source.length > 0) {
- if (match = source.match(pattern)) {
- result += source.slice(0, match.index);
- result += String.interpret(replacement(match));
- source = source.slice(match.index + match[0].length);
- } else {
- result += source, source = '';
- }
- }
- return result;
- },
-
- sub: function(pattern, replacement, count) {
- replacement = this.gsub.prepareReplacement(replacement);
- count = Object.isUndefined(count) ? 1 : count;
-
- return this.gsub(pattern, function(match) {
- if (--count < 0) return match[0];
- return replacement(match);
- });
- },
-
- scan: function(pattern, iterator) {
- this.gsub(pattern, iterator);
- return String(this);
- },
-
- truncate: function(length, truncation) {
- length = length || 30;
- truncation = Object.isUndefined(truncation) ? '...' : truncation;
- return this.length > length ?
- this.slice(0, length - truncation.length) + truncation : String(this);
- },
-
- strip: function() {
- return this.replace(/^\s+/, '').replace(/\s+$/, '');
- },
-
- stripTags: function() {
- return this.replace(/<\/?[^>]+>/gi, '');
- },
-
- stripScripts: function() {
- return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
- },
-
- extractScripts: function() {
- var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
- var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
- return (this.match(matchAll) || []).map(function(scriptTag) {
- return (scriptTag.match(matchOne) || ['', ''])[1];
- });
- },
-
- evalScripts: function() {
- return this.extractScripts().map(function(script) { return eval(script) });
- },
-
- escapeHTML: function() {
- var self = arguments.callee;
- self.text.data = this;
- return self.div.innerHTML;
- },
-
- unescapeHTML: function() {
- var div = new Element('div');
- div.innerHTML = this.stripTags();
- return div.childNodes[0] ? (div.childNodes.length > 1 ?
- $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
- div.childNodes[0].nodeValue) : '';
- },
-
- toQueryParams: function(separator) {
- var match = this.strip().match(/([^?#]*)(#.*)?$/);
- if (!match) return { };
-
- return match[1].split(separator || '&').inject({ }, function(hash, pair) {
- if ((pair = pair.split('='))[0]) {
- var key = decodeURIComponent(pair.shift());
- var value = pair.length > 1 ? pair.join('=') : pair[0];
- if (value != undefined) value = decodeURIComponent(value);
-
- if (key in hash) {
- if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
- hash[key].push(value);
- }
- else hash[key] = value;
- }
- return hash;
- });
- },
-
- toArray: function() {
- return this.split('');
- },
-
- succ: function() {
- return this.slice(0, this.length - 1) +
- String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
- },
-
- times: function(count) {
- return count < 1 ? '' : new Array(count + 1).join(this);
- },
-
- camelize: function() {
- var parts = this.split('-'), len = parts.length;
- if (len == 1) return parts[0];
-
- var camelized = this.charAt(0) == '-'
- ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
- : parts[0];
-
- for (var i = 1; i < len; i++)
- camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
-
- return camelized;
- },
-
- capitalize: function() {
- return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
- },
-
- underscore: function() {
- return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
- },
-
- dasherize: function() {
- return this.gsub(/_/,'-');
- },
-
- inspect: function(useDoubleQuotes) {
- var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
- var character = String.specialChar[match[0]];
- return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
- });
- if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
- return "'" + escapedString.replace(/'/g, '\\\'') + "'";
- },
-
- toJSON: function() {
- return this.inspect(true);
- },
-
- unfilterJSON: function(filter) {
- return this.sub(filter || Prototype.JSONFilter, '#{1}');
- },
-
- isJSON: function() {
- var str = this;
- if (str.blank()) return false;
- str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
- return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
- },
-
- evalJSON: function(sanitize) {
- var json = this.unfilterJSON();
- try {
- if (!sanitize || json.isJSON()) return eval('(' + json + ')');
- } catch (e) { }
- throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
- },
-
- include: function(pattern) {
- return this.indexOf(pattern) > -1;
- },
-
- startsWith: function(pattern) {
- return this.indexOf(pattern) === 0;
- },
-
- endsWith: function(pattern) {
- var d = this.length - pattern.length;
- return d >= 0 && this.lastIndexOf(pattern) === d;
- },
-
- empty: function() {
- return this == '';
- },
-
- blank: function() {
- return /^\s*$/.test(this);
- },
-
- interpolate: function(object, pattern) {
- return new Template(this, pattern).evaluate(object);
- }
-});
-
-if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
- escapeHTML: function() {
- return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
- },
- unescapeHTML: function() {
- return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
- }
-});
-
-String.prototype.gsub.prepareReplacement = function(replacement) {
- if (Object.isFunction(replacement)) return replacement;
- var template = new Template(replacement);
- return function(match) { return template.evaluate(match) };
-};
-
-String.prototype.parseQuery = String.prototype.toQueryParams;
-
-Object.extend(String.prototype.escapeHTML, {
- div: document.createElement('div'),
- text: document.createTextNode('')
-});
-
-with (String.prototype.escapeHTML) div.appendChild(text);
-
-var Template = Class.create({
- initialize: function(template, pattern) {
- this.template = template.toString();
- this.pattern = pattern || Template.Pattern;
- },
-
- evaluate: function(object) {
- if (Object.isFunction(object.toTemplateReplacements))
- object = object.toTemplateReplacements();
-
- return this.template.gsub(this.pattern, function(match) {
- if (object == null) return '';
-
- var before = match[1] || '';
- if (before == '\\') return match[2];
-
- var ctx = object, expr = match[3];
- var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
- match = pattern.exec(expr);
- if (match == null) return before;
-
- while (match != null) {
- var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
- ctx = ctx[comp];
- if (null == ctx || '' == match[3]) break;
- expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
- match = pattern.exec(expr);
- }
-
- return before + String.interpret(ctx);
- }.bind(this));
- }
-});
-Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
-
-var $break = { };
-
-var Enumerable = {
- each: function(iterator, context) {
- var index = 0;
- iterator = iterator.bind(context);
- try {
- this._each(function(value) {
- iterator(value, index++);
- });
- } catch (e) {
- if (e != $break) throw e;
- }
- return this;
- },
-
- eachSlice: function(number, iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
- var index = -number, slices = [], array = this.toArray();
- while ((index += number) < array.length)
- slices.push(array.slice(index, index+number));
- return slices.collect(iterator, context);
- },
-
- all: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
- var result = true;
- this.each(function(value, index) {
- result = result && !!iterator(value, index);
- if (!result) throw $break;
- });
- return result;
- },
-
- any: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
- var result = false;
- this.each(function(value, index) {
- if (result = !!iterator(value, index))
- throw $break;
- });
- return result;
- },
-
- collect: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
- var results = [];
- this.each(function(value, index) {
- results.push(iterator(value, index));
- });
- return results;
- },
-
- detect: function(iterator, context) {
- iterator = iterator.bind(context);
- var result;
- this.each(function(value, index) {
- if (iterator(value, index)) {
- result = value;
- throw $break;
- }
- });
- return result;
- },
-
- findAll: function(iterator, context) {
- iterator = iterator.bind(context);
- var results = [];
- this.each(function(value, index) {
- if (iterator(value, index))
- results.push(value);
- });
- return results;
- },
-
- grep: function(filter, iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
- var results = [];
-
- if (Object.isString(filter))
- filter = new RegExp(filter);
-
- this.each(function(value, index) {
- if (filter.match(value))
- results.push(iterator(value, index));
- });
- return results;
- },
-
- include: function(object) {
- if (Object.isFunction(this.indexOf))
- if (this.indexOf(object) != -1) return true;
-
- var found = false;
- this.each(function(value) {
- if (value == object) {
- found = true;
- throw $break;
- }
- });
- return found;
- },
-
- inGroupsOf: function(number, fillWith) {
- fillWith = Object.isUndefined(fillWith) ? null : fillWith;
- return this.eachSlice(number, function(slice) {
- while(slice.length < number) slice.push(fillWith);
- return slice;
- });
- },
-
- inject: function(memo, iterator, context) {
- iterator = iterator.bind(context);
- this.each(function(value, index) {
- memo = iterator(memo, value, index);
- });
- return memo;
- },
-
- invoke: function(method) {
- var args = $A(arguments).slice(1);
- return this.map(function(value) {
- return value[method].apply(value, args);
- });
- },
-
- max: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
- var result;
- this.each(function(value, index) {
- value = iterator(value, index);
- if (result == null || value >= result)
- result = value;
- });
- return result;
- },
-
- min: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
- var result;
- this.each(function(value, index) {
- value = iterator(value, index);
- if (result == null || value < result)
- result = value;
- });
- return result;
- },
-
- partition: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
- var trues = [], falses = [];
- this.each(function(value, index) {
- (iterator(value, index) ?
- trues : falses).push(value);
- });
- return [trues, falses];
- },
-
- pluck: function(property) {
- var results = [];
- this.each(function(value) {
- results.push(value[property]);
- });
- return results;
- },
-
- reject: function(iterator, context) {
- iterator = iterator.bind(context);
- var results = [];
- this.each(function(value, index) {
- if (!iterator(value, index))
- results.push(value);
- });
- return results;
- },
-
- sortBy: function(iterator, context) {
- iterator = iterator.bind(context);
- return this.map(function(value, index) {
- return {value: value, criteria: iterator(value, index)};
- }).sort(function(left, right) {
- var a = left.criteria, b = right.criteria;
- return a < b ? -1 : a > b ? 1 : 0;
- }).pluck('value');
- },
-
- toArray: function() {
- return this.map();
- },
-
- zip: function() {
- var iterator = Prototype.K, args = $A(arguments);
- if (Object.isFunction(args.last()))
- iterator = args.pop();
-
- var collections = [this].concat(args).map($A);
- return this.map(function(value, index) {
- return iterator(collections.pluck(index));
- });
- },
-
- size: function() {
- return this.toArray().length;
- },
-
- inspect: function() {
- return '#<Enumerable:' + this.toArray().inspect() + '>';
- }
-};
-
-Object.extend(Enumerable, {
- map: Enumerable.collect,
- find: Enumerable.detect,
- select: Enumerable.findAll,
- filter: Enumerable.findAll,
- member: Enumerable.include,
- entries: Enumerable.toArray,
- every: Enumerable.all,
- some: Enumerable.any
-});
-function $A(iterable) {
- if (!iterable) return [];
- if (iterable.toArray) return iterable.toArray();
- var length = iterable.length, results = new Array(length);
- while (length--) results[length] = iterable[length];
- return results;
-}
-
-if (Prototype.Browser.WebKit) {
- function $A(iterable) {
- if (!iterable) return [];
- if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
- iterable.toArray) return iterable.toArray();
- var length = iterable.length, results = new Array(length);
- while (length--) results[length] = iterable[length];
- return results;
- }
-}
-
-Array.from = $A;
-
-Object.extend(Array.prototype, Enumerable);
-
-if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
-
-Object.extend(Array.prototype, {
- _each: function(iterator) {
- for (var i = 0, length = this.length; i < length; i++)
- iterator(this[i]);
- },
-
- clear: function() {
- this.length = 0;
- return this;
- },
-
- first: function() {
- return this[0];
- },
-
- last: function() {
- return this[this.length - 1];
- },
-
- compact: function() {
- return this.select(function(value) {
- return value != null;
- });
- },
-
- flatten: function() {
- return this.inject([], function(array, value) {
- return array.concat(Object.isArray(value) ?
- value.flatten() : [value]);
- });
- },
-
- without: function() {
- var values = $A(arguments);
- return this.select(function(value) {
- return !values.include(value);
- });
- },
-
- reverse: function(inline) {
- return (inline !== false ? this : this.toArray())._reverse();
- },
-
- reduce: function() {
- return this.length > 1 ? this : this[0];
- },
-
- uniq: function(sorted) {
- return this.inject([], function(array, value, index) {
- if (0 == index || (sorted ? array.last() != value : !array.include(value)))
- array.push(value);
- return array;
- });
- },
-
- intersect: function(array) {
- return this.uniq().findAll(function(item) {
- return array.detect(function(value) { return item === value });
- });
- },
-
- clone: function() {
- return [].concat(this);
- },
-
- size: function() {
- return this.length;
- },
-
- inspect: function() {
- return '[' + this.map(Object.inspect).join(', ') + ']';
- },
-
- toJSON: function() {
- var results = [];
- this.each(function(object) {
- var value = Object.toJSON(object);
- if (!Object.isUndefined(value)) results.push(value);
- });
- return '[' + results.join(', ') + ']';
- }
-});
-
-// use native browser JS 1.6 implementation if available
-if (Object.isFunction(Array.prototype.forEach))
- Array.prototype._each = Array.prototype.forEach;
-
-if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
- i || (i = 0);
- var length = this.length;
- if (i < 0) i = length + i;
- for (; i < length; i++)
- if (this[i] === item) return i;
- return -1;
-};
-
-if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
- i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
- var n = this.slice(0, i).reverse().indexOf(item);
- return (n < 0) ? n : i - n - 1;
-};
-
-Array.prototype.toArray = Array.prototype.clone;
-
-function $w(string) {
- if (!Object.isString(string)) return [];
- string = string.strip();
- return string ? string.split(/\s+/) : [];
-}
-
-if (Prototype.Browser.Opera){
- Array.prototype.concat = function() {
- var array = [];
- for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
- for (var i = 0, length = arguments.length; i < length; i++) {
- if (Object.isArray(arguments[i])) {
- for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
- array.push(arguments[i][j]);
- } else {
- array.push(arguments[i]);
- }
- }
- return array;
- };
-}
-Object.extend(Number.prototype, {
- toColorPart: function() {
- return this.toPaddedString(2, 16);
- },
-
- succ: function() {
- return this + 1;
- },
-
- times: function(iterator) {
- $R(0, this, true).each(iterator);
- return this;
- },
-
- toPaddedString: function(length, radix) {
- var string = this.toString(radix || 10);
- return '0'.times(length - string.length) + string;
- },
-
- toJSON: function() {
- return isFinite(this) ? this.toString() : 'null';
- }
-});
-
-$w('abs round ceil floor').each(function(method){
- Number.prototype[method] = Math[method].methodize();
-});
-function $H(object) {
- return new Hash(object);
-};
-
-var Hash = Class.create(Enumerable, (function() {
-
- function toQueryPair(key, value) {
- if (Object.isUndefined(value)) return key;
- return key + '=' + encodeURIComponent(String.interpret(value));
- }
-
- return {
- initialize: function(object) {
- this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
- },
-
- _each: function(iterator) {
- for (var key in this._object) {
- var value = this._object[key], pair = [key, value];
- pair.key = key;
- pair.value = value;
- iterator(pair);
- }
- },
-
- set: function(key, value) {
- return this._object[key] = value;
- },
-
- get: function(key) {
- return this._object[key];
- },
-
- unset: function(key) {
- var value = this._object[key];
- delete this._object[key];
- return value;
- },
-
- toObject: function() {
- return Object.clone(this._object);
- },
-
- keys: function() {
- return this.pluck('key');
- },
-
- values: function() {
- return this.pluck('value');
- },
-
- index: function(value) {
- var match = this.detect(function(pair) {
- return pair.value === value;
- });
- return match && match.key;
- },
-
- merge: function(object) {
- return this.clone().update(object);
- },
-
- update: function(object) {
- return new Hash(object).inject(this, function(result, pair) {
- result.set(pair.key, pair.value);
- return result;
- });
- },
-
- toQueryString: function() {
- return this.map(function(pair) {
- var key = encodeURIComponent(pair.key), values = pair.value;
-
- if (values && typeof values == 'object') {
- if (Object.isArray(values))
- return values.map(toQueryPair.curry(key)).join('&');
- }
- return toQueryPair(key, values);
- }).join('&');
- },
-
- inspect: function() {
- return '#<Hash:{' + this.map(function(pair) {
- return pair.map(Object.inspect).join(': ');
- }).join(', ') + '}>';
- },
-
- toJSON: function() {
- return Object.toJSON(this.toObject());
- },
-
- clone: function() {
- return new Hash(this);
- }
- }
-})());
-
-Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
-Hash.from = $H;
-var ObjectRange = Class.create(Enumerable, {
- initialize: function(start, end, exclusive) {
- this.start = start;
- this.end = end;
- this.exclusive = exclusive;
- },
-
- _each: function(iterator) {
- var value = this.start;
- while (this.include(value)) {
- iterator(value);
- value = value.succ();
- }
- },
-
- include: function(value) {
- if (value < this.start)
- return false;
- if (this.exclusive)
- return value < this.end;
- return value <= this.end;
- }
-});
-
-var $R = function(start, end, exclusive) {
- return new ObjectRange(start, end, exclusive);
-};
-
-var Ajax = {
- getTransport: function() {
- return Try.these(
- function() {return new XMLHttpRequest()},
- function() {return new ActiveXObject('Msxml2.XMLHTTP')},
- function() {return new ActiveXObject('Microsoft.XMLHTTP')}
- ) || false;
- },
-
- activeRequestCount: 0
-};
-
-Ajax.Responders = {
- responders: [],
-
- _each: function(iterator) {
- this.responders._each(iterator);
- },
-
- register: function(responder) {
- if (!this.include(responder))
- this.responders.push(responder);
- },
-
- unregister: function(responder) {
- this.responders = this.responders.without(responder);
- },
-
- dispatch: function(callback, request, transport, json) {
- this.each(function(responder) {
- if (Object.isFunction(responder[callback])) {
- try {
- responder[callback].apply(responder, [request, transport, json]);
- } catch (e) { }
- }
- });
- }
-};
-
-Object.extend(Ajax.Responders, Enumerable);
-
-Ajax.Responders.register({
- onCreate: function() { Ajax.activeRequestCount++ },
- onComplete: function() { Ajax.activeRequestCount-- }
-});
-
-Ajax.Base = Class.create({
- initialize: function(options) {
- this.options = {
- method: 'post',
- asynchronous: true,
- contentType: 'application/x-www-form-urlencoded',
- encoding: 'UTF-8',
- parameters: '',
- evalJSON: true,
- evalJS: true
- };
- Object.extend(this.options, options || { });
-
- this.options.method = this.options.method.toLowerCase();
-
- if (Object.isString(this.options.parameters))
- this.options.parameters = this.options.parameters.toQueryParams();
- else if (Object.isHash(this.options.parameters))
- this.options.parameters = this.options.parameters.toObject();
- }
-});
-
-Ajax.Request = Class.create(Ajax.Base, {
- _complete: false,
-
- initialize: function($super, url, options) {
- $super(options);
- this.transport = Ajax.getTransport();
- this.request(url);
- },
-
- request: function(url) {
- this.url = url;
- this.method = this.options.method;
- var params = Object.clone(this.options.parameters);
-
- if (!['get', 'post'].include(this.method)) {
- // simulate other verbs over post
- params['_method'] = this.method;
- this.method = 'post';
- }
-
- this.parameters = params;
-
- if (params = Object.toQueryString(params)) {
- // when GET, append parameters to URL
- if (this.method == 'get')
- this.url += (this.url.include('?') ? '&' : '?') + params;
- else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
- params += '&_=';
- }
-
- try {
- var response = new Ajax.Response(this);
- if (this.options.onCreate) this.options.onCreate(response);
- Ajax.Responders.dispatch('onCreate', this, response);
-
- this.transport.open(this.method.toUpperCase(), this.url,
- this.options.asynchronous);
-
- if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
-
- this.transport.onreadystatechange = this.onStateChange.bind(this);
- this.setRequestHeaders();
-
- this.body = this.method == 'post' ? (this.options.postBody || params) : null;
- this.transport.send(this.body);
-
- /* Force Firefox to handle ready state 4 for synchronous requests */
- if (!this.options.asynchronous && this.transport.overrideMimeType)
- this.onStateChange();
-
- }
- catch (e) {
- this.dispatchException(e);
- }
- },
-
- onStateChange: function() {
- var readyState = this.transport.readyState;
- if (readyState > 1 && !((readyState == 4) && this._complete))
- this.respondToReadyState(this.transport.readyState);
- },
-
- setRequestHeaders: function() {
- var headers = {
- 'X-Requested-With': 'XMLHttpRequest',
- 'X-Prototype-Version': Prototype.Version,
- 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
- };
-
- if (this.method == 'post') {
- headers['Content-type'] = this.options.contentType +
- (this.options.encoding ? '; charset=' + this.options.encoding : '');
-
- /* Force "Connection: close" for older Mozilla browsers to work
- * around a bug where XMLHttpRequest sends an incorrect
- * Content-length header. See Mozilla Bugzilla #246651.
- */
- if (this.transport.overrideMimeType &&
- (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
- headers['Connection'] = 'close';
- }
-
- // user-defined headers
- if (typeof this.options.requestHeaders == 'object') {
- var extras = this.options.requestHeaders;
-
- if (Object.isFunction(extras.push))
- for (var i = 0, length = extras.length; i < length; i += 2)
- headers[extras[i]] = extras[i+1];
- else
- $H(extras).each(function(pair) { headers[pair.key] = pair.value });
- }
-
- for (var name in headers)
- this.transport.setRequestHeader(name, headers[name]);
- },
-
- success: function() {
- var status = this.getStatus();
- return !status || (status >= 200 && status < 300);
- },
-
- getStatus: function() {
- try {
- return this.transport.status || 0;
- } catch (e) { return 0 }
- },
-
- respondToReadyState: function(readyState) {
- var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
-
- if (state == 'Complete') {
- try {
- this._complete = true;
- (this.options['on' + response.status]
- || this.options['on' + (this.success() ? 'Success' : 'Failure')]
- || Prototype.emptyFunction)(response, response.headerJSON);
- } catch (e) {
- this.dispatchException(e);
- }
-
- var contentType = response.getHeader('Content-type');
- if (this.options.evalJS == 'force'
- || (this.options.evalJS && contentType
- && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
- this.evalResponse();
- }
-
- try {
- (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
- Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
- } catch (e) {
- this.dispatchException(e);
- }
-
- if (state == 'Complete') {
- // avoid memory leak in MSIE: clean up
- this.transport.onreadystatechange = Prototype.emptyFunction;
- }
- },
-
- getHeader: function(name) {
- try {
- return this.transport.getResponseHeader(name);
- } catch (e) { return null }
- },
-
- evalResponse: function() {
- try {
- return eval((this.transport.responseText || '').unfilterJSON());
- } catch (e) {
- this.dispatchException(e);
- }
- },
-
- dispatchException: function(exception) {
- (this.options.onException || Prototype.emptyFunction)(this, exception);
- Ajax.Responders.dispatch('onException', this, exception);
- }
-});
-
-Ajax.Request.Events =
- ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
-
-Ajax.Response = Class.create({
- initialize: function(request){
- this.request = request;
- var transport = this.transport = request.transport,
- readyState = this.readyState = transport.readyState;
-
- if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
- this.status = this.getStatus();
- this.statusText = this.getStatusText();
- this.responseText = String.interpret(transport.responseText);
- this.headerJSON = this._getHeaderJSON();
- }
-
- if(readyState == 4) {
- var xml = transport.responseXML;
- this.responseXML = Object.isUndefined(xml) ? null : xml;
- this.responseJSON = this._getResponseJSON();
- }
- },
-
- status: 0,
- statusText: '',
-
- getStatus: Ajax.Request.prototype.getStatus,
-
- getStatusText: function() {
- try {
- return this.transport.statusText || '';
- } catch (e) { return '' }
- },
-
- getHeader: Ajax.Request.prototype.getHeader,
-
- getAllHeaders: function() {
- try {
- return this.getAllResponseHeaders();
- } catch (e) { return null }
- },
-
- getResponseHeader: function(name) {
- return this.transport.getResponseHeader(name);
- },
-
- getAllResponseHeaders: function() {
- return this.transport.getAllResponseHeaders();
- },
-
- _getHeaderJSON: function() {
- var json = this.getHeader('X-JSON');
- if (!json) return null;
- json = decodeURIComponent(escape(json));
- try {
- return json.evalJSON(this.request.options.sanitizeJSON);
- } catch (e) {
- this.request.dispatchException(e);
- }
- },
-
- _getResponseJSON: function() {
- var options = this.request.options;
- if (!options.evalJSON || (options.evalJSON != 'force' &&
- !(this.getHeader('Content-type') || '').include('application/json')) ||
- this.responseText.blank())
- return null;
- try {
- return this.responseText.evalJSON(options.sanitizeJSON);
- } catch (e) {
- this.request.dispatchException(e);
- }
- }
-});
-
-Ajax.Updater = Class.create(Ajax.Request, {
- initialize: function($super, container, url, options) {
- this.container = {
- success: (container.success || container),
- failure: (container.failure || (container.success ? null : container))
- };
-
- options = Object.clone(options);
- var onComplete = options.onComplete;
- options.onComplete = (function(response, json) {
- this.updateContent(response.responseText);
- if (Object.isFunction(onComplete)) onComplete(response, json);
- }).bind(this);
-
- $super(url, options);
- },
-
- updateContent: function(responseText) {
- var receiver = this.container[this.success() ? 'success' : 'failure'],
- options = this.options;
-
- if (!options.evalScripts) responseText = responseText.stripScripts();
-
- if (receiver = $(receiver)) {
- if (options.insertion) {
- if (Object.isString(options.insertion)) {
- var insertion = { }; insertion[options.insertion] = responseText;
- receiver.insert(insertion);
- }
- else options.insertion(receiver, responseText);
- }
- else receiver.update(responseText);
- }
- }
-});
-
-Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
- initialize: function($super, container, url, options) {
- $super(options);
- this.onComplete = this.options.onComplete;
-
- this.frequency = (this.options.frequency || 2);
- this.decay = (this.options.decay || 1);
-
- this.updater = { };
- this.container = container;
- this.url = url;
-
- this.start();
- },
-
- start: function() {
- this.options.onComplete = this.updateComplete.bind(this);
- this.onTimerEvent();
- },
-
- stop: function() {
- this.updater.options.onComplete = undefined;
- clearTimeout(this.timer);
- (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
- },
-
- updateComplete: function(response) {
- if (this.options.decay) {
- this.decay = (response.responseText == this.lastText ?
- this.decay * this.options.decay : 1);
-
- this.lastText = response.responseText;
- }
- this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
- },
-
- onTimerEvent: function() {
- this.updater = new Ajax.Updater(this.container, this.url, this.options);
- }
-});
-function $(element) {
- if (arguments.length > 1) {
- for (var i = 0, elements = [], length = arguments.length; i < length; i++)
- elements.push($(arguments[i]));
- return elements;
- }
- if (Object.isString(element))
- element = document.getElementById(element);
- return Element.extend(element);
-}
-
-if (Prototype.BrowserFeatures.XPath) {
- document._getElementsByXPath = function(expression, parentElement) {
- var results = [];
- var query = document.evaluate(expression, $(parentElement) || document,
- null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
- for (var i = 0, length = query.snapshotLength; i < length; i++)
- results.push(Element.extend(query.snapshotItem(i)));
- return results;
- };
-}
-
-/*--------------------------------------------------------------------------*/
-
-if (!window.Node) var Node = { };
-
-if (!Node.ELEMENT_NODE) {
- // DOM level 2 ECMAScript Language Binding
- Object.extend(Node, {
- ELEMENT_NODE: 1,
- ATTRIBUTE_NODE: 2,
- TEXT_NODE: 3,
- CDATA_SECTION_NODE: 4,
- ENTITY_REFERENCE_NODE: 5,
- ENTITY_NODE: 6,
- PROCESSING_INSTRUCTION_NODE: 7,
- COMMENT_NODE: 8,
- DOCUMENT_NODE: 9,
- DOCUMENT_TYPE_NODE: 10,
- DOCUMENT_FRAGMENT_NODE: 11,
- NOTATION_NODE: 12
- });
-}
-
-(function() {
- var element = this.Element;
- this.Element = function(tagName, attributes) {
- attributes = attributes || { };
- tagName = tagName.toLowerCase();
- var cache = Element.cache;
- if (Prototype.Browser.IE && attributes.name) {
- tagName = '<' + tagName + ' name="' + attributes.name + '">';
- delete attributes.name;
- return Element.writeAttribute(document.createElement(tagName), attributes);
- }
- if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
- return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
- };
- Object.extend(this.Element, element || { });
-}).call(window);
-
-Element.cache = { };
-
-Element.Methods = {
- visible: function(element) {
- return $(element).style.display != 'none';
- },
-
- toggle: function(element) {
- element = $(element);
- Element[Element.visible(element) ? 'hide' : 'show'](element);
- return element;
- },
-
- hide: function(element) {
- $(element).style.display = 'none';
- return element;
- },
-
- show: function(element) {
- $(element).style.display = '';
- return element;
- },
-
- remove: function(element) {
- element = $(element);
- element.parentNode.removeChild(element);
- return element;
- },
-
- update: function(element, content) {
- element = $(element);
- if (content && content.toElement) content = content.toElement();
- if (Object.isElement(content)) return element.update().insert(content);
- content = Object.toHTML(content);
- element.innerHTML = content.stripScripts();
- content.evalScripts.bind(content).defer();
- return element;
- },
-
- replace: function(element, content) {
- element = $(element);
- if (content && content.toElement) content = content.toElement();
- else if (!Object.isElement(content)) {
- content = Object.toHTML(content);
- var range = element.ownerDocument.createRange();
- range.selectNode(element);
- content.evalScripts.bind(content).defer();
- content = range.createContextualFragment(content.stripScripts());
- }
- element.parentNode.replaceChild(content, element);
- return element;
- },
-
- insert: function(element, insertions) {
- element = $(element);
-
- if (Object.isString(insertions) || Object.isNumber(insertions) ||
- Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
- insertions = {bottom:insertions};
-
- var content, t, range;
-
- for (position in insertions) {
- content = insertions[position];
- position = position.toLowerCase();
- t = Element._insertionTranslations[position];
-
- if (content && content.toElement) content = content.toElement();
- if (Object.isElement(content)) {
- t.insert(element, content);
- continue;
- }
-
- content = Object.toHTML(content);
-
- range = element.ownerDocument.createRange();
- t.initializeRange(element, range);
- t.insert(element, range.createContextualFragment(content.stripScripts()));
-
- content.evalScripts.bind(content).defer();
- }
-
- return element;
- },
-
- wrap: function(element, wrapper, attributes) {
- element = $(element);
- if (Object.isElement(wrapper))
- $(wrapper).writeAttribute(attributes || { });
- else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
- else wrapper = new Element('div', wrapper);
- if (element.parentNode)
- element.parentNode.replaceChild(wrapper, element);
- wrapper.appendChild(element);
- return wrapper;
- },
-
- inspect: function(element) {
- element = $(element);
- var result = '<' + element.tagName.toLowerCase();
- $H({'id': 'id', 'className': 'class'}).each(function(pair) {
- var property = pair.first(), attribute = pair.last();
- var value = (element[property] || '').toString();
- if (value) result += ' ' + attribute + '=' + value.inspect(true);
- });
- return result + '>';
- },
-
- recursivelyCollect: function(element, property) {
- element = $(element);
- var elements = [];
- while (element = element[property])
- if (element.nodeType == 1)
- elements.push(Element.extend(element));
- return elements;
- },
-
- ancestors: function(element) {
- return $(element).recursivelyCollect('parentNode');
- },
-
- descendants: function(element) {
- return $(element).getElementsBySelector("*");
- },
-
- firstDescendant: function(element) {
- element = $(element).firstChild;
- while (element && element.nodeType != 1) element = element.nextSibling;
- return $(element);
- },
-
- immediateDescendants: function(element) {
- if (!(element = $(element).firstChild)) return [];
- while (element && element.nodeType != 1) element = element.nextSibling;
- if (element) return [element].concat($(element).nextSiblings());
- return [];
- },
-
- previousSiblings: function(element) {
- return $(element).recursivelyCollect('previousSibling');
- },
-
- nextSiblings: function(element) {
- return $(element).recursivelyCollect('nextSibling');
- },
-
- siblings: function(element) {
- element = $(element);
- return element.previousSiblings().reverse().concat(element.nextSiblings());
- },
-
- match: function(element, selector) {
- if (Object.isString(selector))
- selector = new Selector(selector);
- return selector.match($(element));
- },
-
- up: function(element, expression, index) {
- element = $(element);
- if (arguments.length == 1) return $(element.parentNode);
- var ancestors = element.ancestors();
- return expression ? Selector.findElement(ancestors, expression, index) :
- ancestors[index || 0];
- },
-
- down: function(element, expression, index) {
- element = $(element);
- if (arguments.length == 1) return element.firstDescendant();
- var descendants = element.descendants();
- return expression ? Selector.findElement(descendants, expression, index) :
- descendants[index || 0];
- },
-
- previous: function(element, expression, index) {
- element = $(element);
- if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
- var previousSiblings = element.previousSiblings();
- return expression ? Selector.findElement(previousSiblings, expression, index) :
- previousSiblings[index || 0];
- },
-
- next: function(element, expression, index) {
- element = $(element);
- if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
- var nextSiblings = element.nextSiblings();
- return expression ? Selector.findElement(nextSiblings, expression, index) :
- nextSiblings[index || 0];
- },
-
- select: function() {
- var args = $A(arguments), element = $(args.shift());
- return Selector.findChildElements(element, args);
- },
-
- adjacent: function() {
- var args = $A(arguments), element = $(args.shift());
- return Selector.findChildElements(element.parentNode, args).without(element);
- },
-
- identify: function(element) {
- element = $(element);
- var id = element.readAttribute('id'), self = arguments.callee;
- if (id) return id;
- do { id = 'anonymous_element_' + self.counter++ } while ($(id));
- element.writeAttribute('id', id);
- return id;
- },
-
- readAttribute: function(element, name) {
- element = $(element);
- if (Prototype.Browser.IE) {
- var t = Element._attributeTranslations.read;
- if (t.values[name]) return t.values[name](element, name);
- if (t.names[name]) name = t.names[name];
- if (name.include(':')) {
- return (!element.attributes || !element.attributes[name]) ? null :
- element.attributes[name].value;
- }
- }
- return element.getAttribute(name);
- },
-
- writeAttribute: function(element, name, value) {
- element = $(element);
- var attributes = { }, t = Element._attributeTranslations.write;
-
- if (typeof name == 'object') attributes = name;
- else attributes[name] = Object.isUndefined(value) ? true : value;
-
- for (var attr in attributes) {
- name = t.names[attr] || attr;
- value = attributes[attr];
- if (t.values[attr]) name = t.values[attr](element, value);
- if (value === false || value === null)
- element.removeAttribute(name);
- else if (value === true)
- element.setAttribute(name, name);
- else element.setAttribute(name, value);
- }
- return element;
- },
-
- getHeight: function(element) {
- return $(element).getDimensions().height;
- },
-
- getWidth: function(element) {
- return $(element).getDimensions().width;
- },
-
- classNames: function(element) {
- return new Element.ClassNames(element);
- },
-
- hasClassName: function(element, className) {
- if (!(element = $(element))) return;
- var elementClassName = element.className;
- return (elementClassName.length > 0 && (elementClassName == className ||
- new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
- },
-
- addClassName: function(element, className) {
- if (!(element = $(element))) return;
- if (!element.hasClassName(className))
- element.className += (element.className ? ' ' : '') + className;
- return element;
- },
-
- removeClassName: function(element, className) {
- if (!(element = $(element))) return;
- element.className = element.className.replace(
- new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
- return element;
- },
-
- toggleClassName: function(element, className) {
- if (!(element = $(element))) return;
- return element[element.hasClassName(className) ?
- 'removeClassName' : 'addClassName'](className);
- },
-
- // removes whitespace-only text node children
- cleanWhitespace: function(element) {
- element = $(element);
- var node = element.firstChild;
- while (node) {
- var nextNode = node.nextSibling;
- if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
- element.removeChild(node);
- node = nextNode;
- }
- return element;
- },
-
- empty: function(element) {
- return $(element).innerHTML.blank();
- },
-
- descendantOf: function(element, ancestor) {
- element = $(element), ancestor = $(ancestor);
- var originalAncestor = ancestor;
-
- if (element.compareDocumentPosition)
- return (element.compareDocumentPosition(ancestor) & 8) === 8;
-
- if (element.sourceIndex && !Prototype.Browser.Opera) {
- var e = element.sourceIndex, a = ancestor.sourceIndex,
- nextAncestor = ancestor.nextSibling;
- if (!nextAncestor) {
- do { ancestor = ancestor.parentNode; }
- while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
- }
- if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
- }
-
- while (element = element.parentNode)
- if (element == originalAncestor) return true;
- return false;
- },
-
- scrollTo: function(element) {
- element = $(element);
- var pos = element.cumulativeOffset();
- window.scrollTo(pos[0], pos[1]);
- return element;
- },
-
- getStyle: function(element, style) {
- element = $(element);
- style = style == 'float' ? 'cssFloat' : style.camelize();
- var value = element.style[style];
- if (!value) {
- var css = document.defaultView.getComputedStyle(element, null);
- value = css ? css[style] : null;
- }
- if (style == 'opacity') return value ? parseFloat(value) : 1.0;
- return value == 'auto' ? null : value;
- },
-
- getOpacity: function(element) {
- return $(element).getStyle('opacity');
- },
-
- setStyle: function(element, styles) {
- element = $(element);
- var elementStyle = element.style, match;
- if (Object.isString(styles)) {
- element.style.cssText += ';' + styles;
- return styles.include('opacity') ?
- element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
- }
- for (var property in styles)
- if (property == 'opacity') element.setOpacity(styles[property]);
- else
- elementStyle[(property == 'float' || property == 'cssFloat') ?
- (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
- property] = styles[property];
-
- return element;
- },
-
- setOpacity: function(element, value) {
- element = $(element);
- element.style.opacity = (value == 1 || value === '') ? '' :
- (value < 0.00001) ? 0 : value;
- return element;
- },
-
- getDimensions: function(element) {
- element = $(element);
- var display = $(element).getStyle('display');
- if (display != 'none' && display != null) // Safari bug
- return {width: element.offsetWidth, height: element.offsetHeight};
-
- // All *Width and *Height properties give 0 on elements with display none,
- // so enable the element temporarily
- var els = element.style;
- var originalVisibility = els.visibility;
- var originalPosition = els.position;
- var originalDisplay = els.display;
- els.visibility = 'hidden';
- els.position = 'absolute';
- els.display = 'block';
- var originalWidth = element.clientWidth;
- var originalHeight = element.clientHeight;
- els.display = originalDisplay;
- els.position = originalPosition;
- els.visibility = originalVisibility;
- return {width: originalWidth, height: originalHeight};
- },
-
- makePositioned: function(element) {
- element = $(element);
- var pos = Element.getStyle(element, 'position');
- if (pos == 'static' || !pos) {
- element._madePositioned = true;
- element.style.position = 'relative';
- // Opera returns the offset relative to the positioning context, when an
- // element is position relative but top and left have not been defined
- if (window.opera) {
- element.style.top = 0;
- element.style.left = 0;
- }
- }
- return element;
- },
-
- undoPositioned: function(element) {
- element = $(element);
- if (element._madePositioned) {
- element._madePositioned = undefined;
- element.style.position =
- element.style.top =
- element.style.left =
- element.style.bottom =
- element.style.right = '';
- }
- return element;
- },
-
- makeClipping: function(element) {
- element = $(element);
- if (element._overflow) return element;
- element._overflow = Element.getStyle(element, 'overflow') || 'auto';
- if (element._overflow !== 'hidden')
- element.style.overflow = 'hidden';
- return element;
- },
-
- undoClipping: function(element) {
- element = $(element);
- if (!element._overflow) return element;
- element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
- element._overflow = null;
- return element;
- },
-
- cumulativeOffset: function(element) {
- var valueT = 0, valueL = 0;
- do {
- valueT += element.offsetTop || 0;
- valueL += element.offsetLeft || 0;
- element = element.offsetParent;
- } while (element);
- return Element._returnOffset(valueL, valueT);
- },
-
- positionedOffset: function(element) {
- var valueT = 0, valueL = 0;
- do {
- valueT += element.offsetTop || 0;
- valueL += element.offsetLeft || 0;
- element = element.offsetParent;
- if (element) {
- if (element.tagName == 'BODY') break;
- var p = Element.getStyle(element, 'position');
- if (p == 'relative' || p == 'absolute') break;
- }
- } while (element);
- return Element._returnOffset(valueL, valueT);
- },
-
- absolutize: function(element) {
- element = $(element);
- if (element.getStyle('position') == 'absolute') return;
- // Position.prepare(); // To be done manually by Scripty when it needs it.
-
- var offsets = element.positionedOffset();
- var top = offsets[1];
- var left = offsets[0];
- var width = element.clientWidth;
- var height = element.clientHeight;
-
- element._originalLeft = left - parseFloat(element.style.left || 0);
- element._originalTop = top - parseFloat(element.style.top || 0);
- element._originalWidth = element.style.width;
- element._originalHeight = element.style.height;
-
- element.style.position = 'absolute';
- element.style.top = top + 'px';
- element.style.left = left + 'px';
- element.style.width = width + 'px';
- element.style.height = height + 'px';
- return element;
- },
-
- relativize: function(element) {
- element = $(element);
- if (element.getStyle('position') == 'relative') return;
- // Position.prepare(); // To be done manually by Scripty when it needs it.
-
- element.style.position = 'relative';
- var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
- var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
-
- element.style.top = top + 'px';
- element.style.left = left + 'px';
- element.style.height = element._originalHeight;
- element.style.width = element._originalWidth;
- return element;
- },
-
- cumulativeScrollOffset: function(element) {
- var valueT = 0, valueL = 0;
- do {
- valueT += element.scrollTop || 0;
- valueL += element.scrollLeft || 0;
- element = element.parentNode;
- } while (element);
- return Element._returnOffset(valueL, valueT);
- },
-
- getOffsetParent: function(element) {
- if (element.offsetParent) return $(element.offsetParent);
- if (element == document.body) return $(element);
-
- while ((element = element.parentNode) && element != document.body)
- if (Element.getStyle(element, 'position') != 'static')
- return $(element);
-
- return $(document.body);
- },
-
- viewportOffset: function(forElement) {
- var valueT = 0, valueL = 0;
-
- var element = forElement;
- do {
- valueT += element.offsetTop || 0;
- valueL += element.offsetLeft || 0;
-
- // Safari fix
- if (element.offsetParent == document.body &&
- Element.getStyle(element, 'position') == 'absolute') break;
-
- } while (element = element.offsetParent);
-
- element = forElement;
- do {
- if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
- valueT -= element.scrollTop || 0;
- valueL -= element.scrollLeft || 0;
- }
- } while (element = element.parentNode);
-
- return Element._returnOffset(valueL, valueT);
- },
-
- clonePosition: function(element, source) {
- var options = Object.extend({
- setLeft: true,
- setTop: true,
- setWidth: true,
- setHeight: true,
- offsetTop: 0,
- offsetLeft: 0
- }, arguments[2] || { });
-
- // find page position of source
- source = $(source);
- var p = source.viewportOffset();
-
- // find coordinate system to use
- element = $(element);
- var delta = [0, 0];
- var parent = null;
- // delta [0,0] will do fine with position: fixed elements,
- // position:absolute needs offsetParent deltas
- if (Element.getStyle(element, 'position') == 'absolute') {
- parent = element.getOffsetParent();
- delta = parent.viewportOffset();
- }
-
- // correct by body offsets (fixes Safari)
- if (parent == document.body) {
- delta[0] -= document.body.offsetLeft;
- delta[1] -= document.body.offsetTop;
- }
-
- // set position
- if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
- if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
- if (options.setWidth) element.style.width = source.offsetWidth + 'px';
- if (options.setHeight) element.style.height = source.offsetHeight + 'px';
- return element;
- }
-};
-
-Element.Methods.identify.counter = 1;
-
-Object.extend(Element.Methods, {
- getElementsBySelector: Element.Methods.select,
- childElements: Element.Methods.immediateDescendants
-});
-
-Element._attributeTranslations = {
- write: {
- names: {
- className: 'class',
- htmlFor: 'for'
- },
- values: { }
- }
-};
-
-
-if (!document.createRange || Prototype.Browser.Opera) {
- Element.Methods.insert = function(element, insertions) {
- element = $(element);
-
- if (Object.isString(insertions) || Object.isNumber(insertions) ||
- Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
- insertions = { bottom: insertions };
-
- var t = Element._insertionTranslations, content, position, pos, tagName;
-
- for (position in insertions) {
- content = insertions[position];
- position = position.toLowerCase();
- pos = t[position];
-
- if (content && content.toElement) content = content.toElement();
- if (Object.isElement(content)) {
- pos.insert(element, content);
- continue;
- }
-
- content = Object.toHTML(content);
- tagName = ((position == 'before' || position == 'after')
- ? element.parentNode : element).tagName.toUpperCase();
-
- if (t.tags[tagName]) {
- var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
- if (position == 'top' || position == 'after') fragments.reverse();
- fragments.each(pos.insert.curry(element));
- }
- else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
-
- content.evalScripts.bind(content).defer();
- }
-
- return element;
- };
-}
-
-if (Prototype.Browser.Opera) {
- Element.Methods._getStyle = Element.Methods.getStyle;
- Element.Methods.getStyle = function(element, style) {
- switch(style) {
- case 'left':
- case 'top':
- case 'right':
- case 'bottom':
- if (Element._getStyle(element, 'position') == 'static') return null;
- default: return Element._getStyle(element, style);
- }
- };
- Element.Methods._readAttribute = Element.Methods.readAttribute;
- Element.Methods.readAttribute = function(element, attribute) {
- if (attribute == 'title') return element.title;
- return Element._readAttribute(element, attribute);
- };
-}
-
-else if (Prototype.Browser.IE) {
- $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
- Element.Methods[method] = Element.Methods[method].wrap(
- function(proceed, element) {
- element = $(element);
- var position = element.getStyle('position');
- if (position != 'static') return proceed(element);
- element.setStyle({ position: 'relative' });
- var value = proceed(element);
- element.setStyle({ position: position });
- return value;
- }
- );
- });
-
- Element.Methods.getStyle = function(element, style) {
- element = $(element);
- style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
- var value = element.style[style];
- if (!value && element.currentStyle) value = element.currentStyle[style];
-
- if (style == 'opacity') {
- if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
- if (value[1]) return parseFloat(value[1]) / 100;
- return 1.0;
- }
-
- if (value == 'auto') {
- if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
- return element['offset' + style.capitalize()] + 'px';
- return null;
- }
- return value;
- };
-
- Element.Methods.setOpacity = function(element, value) {
- function stripAlpha(filter){
- return filter.replace(/alpha\([^\)]*\)/gi,'');
- }
- element = $(element);
- var currentStyle = element.currentStyle;
- if ((currentStyle && !currentStyle.hasLayout) ||
- (!currentStyle && element.style.zoom == 'normal'))
- element.style.zoom = 1;
-
- var filter = element.getStyle('filter'), style = element.style;
- if (value == 1 || value === '') {
- (filter = stripAlpha(filter)) ?
- style.filter = filter : style.removeAttribute('filter');
- return element;
- } else if (value < 0.00001) value = 0;
- style.filter = stripAlpha(filter) +
- 'alpha(opacity=' + (value * 100) + ')';
- return element;
- };
-
- Element._attributeTranslations = {
- read: {
- names: {
- 'class': 'className',
- 'for': 'htmlFor'
- },
- values: {
- _getAttr: function(element, attribute) {
- return element.getAttribute(attribute, 2);
- },
- _getAttrNode: function(element, attribute) {
- var node = element.getAttributeNode(attribute);
- return node ? node.value : "";
- },
- _getEv: function(element, attribute) {
- attribute = element.getAttribute(attribute);
- return attribute ? attribute.toString().slice(23, -2) : null;
- },
- _flag: function(element, attribute) {
- return $(element).hasAttribute(attribute) ? attribute : null;
- },
- style: function(element) {
- return element.style.cssText.toLowerCase();
- },
- title: function(element) {
- return element.title;
- }
- }
- }
- };
-
- Element._attributeTranslations.write = {
- names: Object.clone(Element._attributeTranslations.read.names),
- values: {
- checked: function(element, value) {
- element.checked = !!value;
- },
-
- style: function(element, value) {
- element.style.cssText = value ? value : '';
- }
- }
- };
-
- Element._attributeTranslations.has = {};
-
- $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
- 'encType maxLength readOnly longDesc').each(function(attr) {
- Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
- Element._attributeTranslations.has[attr.toLowerCase()] = attr;
- });
-
- (function(v) {
- Object.extend(v, {
- href: v._getAttr,
- src: v._getAttr,
- type: v._getAttr,
- action: v._getAttrNode,
- disabled: v._flag,
- checked: v._flag,
- readonly: v._flag,
- multiple: v._flag,
- onload: v._getEv,
- onunload: v._getEv,
- onclick: v._getEv,
- ondblclick: v._getEv,
- onmousedown: v._getEv,
- onmouseup: v._getEv,
- onmouseover: v._getEv,
- onmousemove: v._getEv,
- onmouseout: v._getEv,
- onfocus: v._getEv,
- onblur: v._getEv,
- onkeypress: v._getEv,
- onkeydown: v._getEv,
- onkeyup: v._getEv,
- onsubmit: v._getEv,
- onreset: v._getEv,
- onselect: v._getEv,
- onchange: v._getEv
- });
- })(Element._attributeTranslations.read.values);
-}
-
-else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
- Element.Methods.setOpacity = function(element, value) {
- element = $(element);
- element.style.opacity = (value == 1) ? 0.999999 :
- (value === '') ? '' : (value < 0.00001) ? 0 : value;
- return element;
- };
-}
-
-else if (Prototype.Browser.WebKit) {
- Element.Methods.setOpacity = function(element, value) {
- element = $(element);
- element.style.opacity = (value == 1 || value === '') ? '' :
- (value < 0.00001) ? 0 : value;
-
- if (value == 1)
- if(element.tagName == 'IMG' && element.width) {
- element.width++; element.width--;
- } else try {
- var n = document.createTextNode(' ');
- element.appendChild(n);
- element.removeChild(n);
- } catch (e) { }
-
- return element;
- };
-
- // Safari returns margins on body which is incorrect if the child is absolutely
- // positioned. For performance reasons, redefine Position.cumulativeOffset for
- // KHTML/WebKit only.
- Element.Methods.cumulativeOffset = function(element) {
- var valueT = 0, valueL = 0;
- do {
- valueT += element.offsetTop || 0;
- valueL += element.offsetLeft || 0;
- if (element.offsetParent == document.body)
- if (Element.getStyle(element, 'position') == 'absolute') break;
-
- element = element.offsetParent;
- } while (element);
-
- return Element._returnOffset(valueL, valueT);
- };
-}
-
-if (Prototype.Browser.IE || Prototype.Browser.Opera) {
- // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
- Element.Methods.update = function(element, content) {
- element = $(element);
-
- if (content && content.toElement) content = content.toElement();
- if (Object.isElement(content)) return element.update().insert(content);
-
- content = Object.toHTML(content);
- var tagName = element.tagName.toUpperCase();
-
- if (tagName in Element._insertionTranslations.tags) {
- $A(element.childNodes).each(function(node) { element.removeChild(node) });
- Element._getContentFromAnonymousElement(tagName, content.stripScripts())
- .each(function(node) { element.appendChild(node) });
- }
- else element.innerHTML = content.stripScripts();
-
- content.evalScripts.bind(content).defer();
- return element;
- };
-}
-
-if (document.createElement('div').outerHTML) {
- Element.Methods.replace = function(element, content) {
- element = $(element);
-
- if (content && content.toElement) content = content.toElement();
- if (Object.isElement(content)) {
- element.parentNode.replaceChild(content, element);
- return element;
- }
-
- content = Object.toHTML(content);
- var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
-
- if (Element._insertionTranslations.tags[tagName]) {
- var nextSibling = element.next();
- var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
- parent.removeChild(element);
- if (nextSibling)
- fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
- else
- fragments.each(function(node) { parent.appendChild(node) });
- }
- else element.outerHTML = content.stripScripts();
-
- content.evalScripts.bind(content).defer();
- return element;
- };
-}
-
-Element._returnOffset = function(l, t) {
- var result = [l, t];
- result.left = l;
- result.top = t;
- return result;
-};
-
-Element._getContentFromAnonymousElement = function(tagName, html) {
- var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
- div.innerHTML = t[0] + html + t[1];
- t[2].times(function() { div = div.firstChild });
- return $A(div.childNodes);
-};
-
-Element._insertionTranslations = {
- before: {
- adjacency: 'beforeBegin',
- insert: function(element, node) {
- element.parentNode.insertBefore(node, element);
- },
- initializeRange: function(element, range) {
- range.setStartBefore(element);
- }
- },
- top: {
- adjacency: 'afterBegin',
- insert: function(element, node) {
- element.insertBefore(node, element.firstChild);
- },
- initializeRange: function(element, range) {
- range.selectNodeContents(element);
- range.collapse(true);
- }
- },
- bottom: {
- adjacency: 'beforeEnd',
- insert: function(element, node) {
- element.appendChild(node);
- }
- },
- after: {
- adjacency: 'afterEnd',
- insert: function(element, node) {
- element.parentNode.insertBefore(node, element.nextSibling);
- },
- initializeRange: function(element, range) {
- range.setStartAfter(element);
- }
- },
- tags: {
- TABLE: ['<table>', '</table>', 1],
- TBODY: ['<table><tbody>', '</tbody></table>', 2],
- TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
- TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
- SELECT: ['<select>', '</select>', 1]
- }
-};
-
-(function() {
- this.bottom.initializeRange = this.top.initializeRange;
- Object.extend(this.tags, {
- THEAD: this.tags.TBODY,
- TFOOT: this.tags.TBODY,
- TH: this.tags.TD
- });
-}).call(Element._insertionTranslations);
-
-Element.Methods.Simulated = {
- hasAttribute: function(element, attribute) {
- attribute = Element._attributeTranslations.has[attribute] || attribute;
- var node = $(element).getAttributeNode(attribute);
- return node && node.specified;
- }
-};
-
-Element.Methods.ByTag = { };
-
-Object.extend(Element, Element.Methods);
-
-if (!Prototype.BrowserFeatures.ElementExtensions &&
- document.createElement('div').__proto__) {
- window.HTMLElement = { };
- window.HTMLElement.prototype = document.createElement('div').__proto__;
- Prototype.BrowserFeatures.ElementExtensions = true;
-}
-
-Element.extend = (function() {
- if (Prototype.BrowserFeatures.SpecificElementExtensions)
- return Prototype.K;
-
- var Methods = { }, ByTag = Element.Methods.ByTag;
-
- var extend = Object.extend(function(element) {
- if (!element || element._extendedByPrototype ||
- element.nodeType != 1 || element == window) return element;
-
- var methods = Object.clone(Methods),
- tagName = element.tagName, property, value;
-
- // extend methods for specific tags
- if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
-
- for (property in methods) {
- value = methods[property];
- if (Object.isFunction(value) && !(property in element))
- element[property] = value.methodize();
- }
-
- element._extendedByPrototype = Prototype.emptyFunction;
- return element;
-
- }, {
- refresh: function() {
- // extend methods for all tags (Safari doesn't need this)
- if (!Prototype.BrowserFeatures.ElementExtensions) {
- Object.extend(Methods, Element.Methods);
- Object.extend(Methods, Element.Methods.Simulated);
- }
- }
- });
-
- extend.refresh();
- return extend;
-})();
-
-Element.hasAttribute = function(element, attribute) {
- if (element.hasAttribute) return element.hasAttribute(attribute);
- return Element.Methods.Simulated.hasAttribute(element, attribute);
-};
-
-Element.addMethods = function(methods) {
- var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
-
- if (!methods) {
- Object.extend(Form, Form.Methods);
- Object.extend(Form.Element, Form.Element.Methods);
- Object.extend(Element.Methods.ByTag, {
- "FORM": Object.clone(Form.Methods),
- "INPUT": Object.clone(Form.Element.Methods),
- "SELECT": Object.clone(Form.Element.Methods),
- "TEXTAREA": Object.clone(Form.Element.Methods)
- });
- }
-
- if (arguments.length == 2) {
- var tagName = methods;
- methods = arguments[1];
- }
-
- if (!tagName) Object.extend(Element.Methods, methods || { });
- else {
- if (Object.isArray(tagName)) tagName.each(extend);
- else extend(tagName);
- }
-
- function extend(tagName) {
- tagName = tagName.toUpperCase();
- if (!Element.Methods.ByTag[tagName])
- Element.Methods.ByTag[tagName] = { };
- Object.extend(Element.Methods.ByTag[tagName], methods);
- }
-
- function copy(methods, destination, onlyIfAbsent) {
- onlyIfAbsent = onlyIfAbsent || false;
- for (var property in methods) {
- var value = methods[property];
- if (!Object.isFunction(value)) continue;
- if (!onlyIfAbsent || !(property in destination))
- destination[property] = value.methodize();
- }
- }
-
- function findDOMClass(tagName) {
- var klass;
- var trans = {
- "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
- "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
- "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
- "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
- "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
- "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
- "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
- "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
- "FrameSet", "IFRAME": "IFrame"
- };
- if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
- if (window[klass]) return window[klass];
- klass = 'HTML' + tagName + 'Element';
- if (window[klass]) return window[klass];
- klass = 'HTML' + tagName.capitalize() + 'Element';
- if (window[klass]) return window[klass];
-
- window[klass] = { };
- window[klass].prototype = document.createElement(tagName).__proto__;
- return window[klass];
- }
-
- if (F.ElementExtensions) {
- copy(Element.Methods, HTMLElement.prototype);
- copy(Element.Methods.Simulated, HTMLElement.prototype, true);
- }
-
- if (F.SpecificElementExtensions) {
- for (var tag in Element.Methods.ByTag) {
- var klass = findDOMClass(tag);
- if (Object.isUndefined(klass)) continue;
- copy(T[tag], klass.prototype);
- }
- }
-
- Object.extend(Element, Element.Methods);
- delete Element.ByTag;
-
- if (Element.extend.refresh) Element.extend.refresh();
- Element.cache = { };
-};
-
-document.viewport = {
- getDimensions: function() {
- var dimensions = { };
- $w('width height').each(function(d) {
- var D = d.capitalize();
- dimensions[d] = self['inner' + D] ||
- (document.documentElement['client' + D] || document.body['client' + D]);
- });
- return dimensions;
- },
-
- getWidth: function() {
- return this.getDimensions().width;
- },
-
- getHeight: function() {
- return this.getDimensions().height;
- },
-
- getScrollOffsets: function() {
- return Element._returnOffset(
- window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
- window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
- }
-};
-/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
- * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
- * license. Please see http://www.yui-ext.com/ for more information. */
-
-var Selector = Class.create({
- initialize: function(expression) {
- this.expression = expression.strip();
- this.compileMatcher();
- },
-
- shouldUseXPath: function() {
- if (!Prototype.BrowserFeatures.XPath) return false;
-
- var e = this.expression;
-
- // Safari 3 chokes on :*-of-type and :empty
- if (Prototype.Browser.WebKit &&
- (e.include("-of-type") || e.include(":empty")))
- return false;
-
- // XPath can't do namespaced attributes, nor can it read
- // the "checked" property from DOM nodes
- if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
- return false;
-
- return true;
- },
-
- compileMatcher: function() {
- if (this.shouldUseXPath())
- return this.compileXPathMatcher();
-
- var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
- c = Selector.criteria, le, p, m;
-
- if (Selector._cache[e]) {
- this.matcher = Selector._cache[e];
- return;
- }
-
- this.matcher = ["this.matcher = function(root) {",
- "var r = root, h = Selector.handlers, c = false, n;"];
-
- while (e && le != e && (/\S/).test(e)) {
- le = e;
- for (var i in ps) {
- p = ps[i];
- if (m = e.match(p)) {
- this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
- new Template(c[i]).evaluate(m));
- e = e.replace(m[0], '');
- break;
- }
- }
- }
-
- this.matcher.push("return h.unique(n);\n}");
- eval(this.matcher.join('\n'));
- Selector._cache[this.expression] = this.matcher;
- },
-
- compileXPathMatcher: function() {
- var e = this.expression, ps = Selector.patterns,
- x = Selector.xpath, le, m;
-
- if (Selector._cache[e]) {
- this.xpath = Selector._cache[e]; return;
- }
-
- this.matcher = ['.//*'];
- while (e && le != e && (/\S/).test(e)) {
- le = e;
- for (var i in ps) {
- if (m = e.match(ps[i])) {
- this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
- new Template(x[i]).evaluate(m));
- e = e.replace(m[0], '');
- break;
- }
- }
- }
-
- this.xpath = this.matcher.join('');
- Selector._cache[this.expression] = this.xpath;
- },
-
- findElements: function(root) {
- root = root || document;
- if (this.xpath) return document._getElementsByXPath(this.xpath, root);
- return this.matcher(root);
- },
-
- match: function(element) {
- this.tokens = [];
-
- var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
- var le, p, m;
-
- while (e && le !== e && (/\S/).test(e)) {
- le = e;
- for (var i in ps) {
- p = ps[i];
- if (m = e.match(p)) {
- // use the Selector.assertions methods unless the selector
- // is too complex.
- if (as[i]) {
- this.tokens.push([i, Object.clone(m)]);
- e = e.replace(m[0], '');
- } else {
- // reluctantly do a document-wide search
- // and look for a match in the array
- return this.findElements(document).include(element);
- }
- }
- }
- }
-
- var match = true, name, matches;
- for (var i = 0, token; token = this.tokens[i]; i++) {
- name = token[0], matches = token[1];
- if (!Selector.assertions[name](element, matches)) {
- match = false; break;
- }
- }
-
- return match;
- },
-
- toString: function() {
- return this.expression;
- },
-
- inspect: function() {
- return "#<Selector:" + this.expression.inspect() + ">";
- }
-});
-
-Object.extend(Selector, {
- _cache: { },
-
- xpath: {
- descendant: "//*",
- child: "/*",
- adjacent: "/following-sibling::*[1]",
- laterSibling: '/following-sibling::*',
- tagName: function(m) {
- if (m[1] == '*') return '';
- return "[local-name()='" + m[1].toLowerCase() +
- "' or local-name()='" + m[1].toUpperCase() + "']";
- },
- className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
- id: "[@id='#{1}']",
- attrPresence: function(m) {
- m[1] = m[1].toLowerCase();
- return new Template("[@#{1}]").evaluate(m);
- },
- attr: function(m) {
- m[1] = m[1].toLowerCase();
- m[3] = m[5] || m[6];
- return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
- },
- pseudo: function(m) {
- var h = Selector.xpath.pseudos[m[1]];
- if (!h) return '';
- if (Object.isFunction(h)) return h(m);
- return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
- },
- operators: {
- '=': "[@#{1}='#{3}']",
- '!=': "[@#{1}!='#{3}']",
- '^=': "[starts-with(@#{1}, '#{3}')]",
- '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
- '*=': "[contains(@#{1}, '#{3}')]",
- '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
- '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
- },
- pseudos: {
- 'first-child': '[not(preceding-sibling::*)]',
- 'last-child': '[not(following-sibling::*)]',
- 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
- 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
- 'checked': "[@checked]",
- 'disabled': "[@disabled]",
- 'enabled': "[not(@disabled)]",
- 'not': function(m) {
- var e = m[6], p = Selector.patterns,
- x = Selector.xpath, le, v;
-
- var exclusion = [];
- while (e && le != e && (/\S/).test(e)) {
- le = e;
- for (var i in p) {
- if (m = e.match(p[i])) {
- v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
- exclusion.push("(" + v.substring(1, v.length - 1) + ")");
- e = e.replace(m[0], '');
- break;
- }
- }
- }
- return "[not(" + exclusion.join(" and ") + ")]";
- },
- 'nth-child': function(m) {
- return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
- },
- 'nth-last-child': function(m) {
- return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
- },
- 'nth-of-type': function(m) {
- return Selector.xpath.pseudos.nth("position() ", m);
- },
- 'nth-last-of-type': function(m) {
- return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
- },
- 'first-of-type': function(m) {
- m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
- },
- 'last-of-type': function(m) {
- m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
- },
- 'only-of-type': function(m) {
- var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
- },
- nth: function(fragment, m) {
- var mm, formula = m[6], predicate;
- if (formula == 'even') formula = '2n+0';
- if (formula == 'odd') formula = '2n+1';
- if (mm = formula.match(/^(\d+)$/)) // digit only
- return '[' + fragment + "= " + mm[1] + ']';
- if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
- if (mm[1] == "-") mm[1] = -1;
- var a = mm[1] ? Number(mm[1]) : 1;
- var b = mm[2] ? Number(mm[2]) : 0;
- predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
- "((#{fragment} - #{b}) div #{a} >= 0)]";
- return new Template(predicate).evaluate({
- fragment: fragment, a: a, b: b });
- }
- }
- }
- },
-
- criteria: {
- tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
- className: 'n = h.className(n, r, "#{1}", c); c = false;',
- id: 'n = h.id(n, r, "#{1}", c); c = false;',
- attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
- attr: function(m) {
- m[3] = (m[5] || m[6]);
- return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
- },
- pseudo: function(m) {
- if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
- return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
- },
- descendant: 'c = "descendant";',
- child: 'c = "child";',
- adjacent: 'c = "adjacent";',
- laterSibling: 'c = "laterSibling";'
- },
-
- patterns: {
- // combinators must be listed first
- // (and descendant needs to be last combinator)
- laterSibling: /^\s*~\s*/,
- child: /^\s*>\s*/,
- adjacent: /^\s*\+\s*/,
- descendant: /^\s/,
-
- // selectors follow
- tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
- id: /^#([\w\-\*]+)(\b|$)/,
- className: /^\.([\w\-\*]+)(\b|$)/,
- pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
- attrPresence: /^\[([\w]+)\]/,
- attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
- },
-
- // for Selector.match and Element#match
- assertions: {
- tagName: function(element, matches) {
- return matches[1].toUpperCase() == element.tagName.toUpperCase();
- },
-
- className: function(element, matches) {
- return Element.hasClassName(element, matches[1]);
- },
-
- id: function(element, matches) {
- return element.id === matches[1];
- },
-
- attrPresence: function(element, matches) {
- return Element.hasAttribute(element, matches[1]);
- },
-
- attr: function(element, matches) {
- var nodeValue = Element.readAttribute(element, matches[1]);
- return Selector.operators[matches[2]](nodeValue, matches[3]);
- }
- },
-
- handlers: {
- // UTILITY FUNCTIONS
- // joins two collections
- concat: function(a, b) {
- for (var i = 0, node; node = b[i]; i++)
- a.push(node);
- return a;
- },
-
- // marks an array of nodes for counting
- mark: function(nodes) {
- for (var i = 0, node; node = nodes[i]; i++)
- node._counted = true;
- return nodes;
- },
-
- unmark: function(nodes) {
- for (var i = 0, node; node = nodes[i]; i++)
- node._counted = undefined;
- return nodes;
- },
-
- // mark each child node with its position (for nth calls)
- // "ofType" flag indicates whether we're indexing for nth-of-type
- // rather than nth-child
- index: function(parentNode, reverse, ofType) {
- parentNode._counted = true;
- if (reverse) {
- for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
- var node = nodes[i];
- if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
- }
- } else {
- for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
- if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
- }
- },
-
- // filters out duplicates and extends all nodes
- unique: function(nodes) {
- if (nodes.length == 0) return nodes;
- var results = [], n;
- for (var i = 0, l = nodes.length; i < l; i++)
- if (!(n = nodes[i])._counted) {
- n._counted = true;
- results.push(Element.extend(n));
- }
- return Selector.handlers.unmark(results);
- },
-
- // COMBINATOR FUNCTIONS
- descendant: function(nodes) {
- var h = Selector.handlers;
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- h.concat(results, node.getElementsByTagName('*'));
- return results;
- },
-
- child: function(nodes) {
- var h = Selector.handlers;
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- for (var j = 0, child; child = node.childNodes[j]; j++)
- if (child.nodeType == 1 && child.tagName != '!') results.push(child);
- }
- return results;
- },
-
- adjacent: function(nodes) {
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- var next = this.nextElementSibling(node);
- if (next) results.push(next);
- }
- return results;
- },
-
- laterSibling: function(nodes) {
- var h = Selector.handlers;
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- h.concat(results, Element.nextSiblings(node));
- return results;
- },
-
- nextElementSibling: function(node) {
- while (node = node.nextSibling)
- if (node.nodeType == 1) return node;
- return null;
- },
-
- previousElementSibling: function(node) {
- while (node = node.previousSibling)
- if (node.nodeType == 1) return node;
- return null;
- },
-
- // TOKEN FUNCTIONS
- tagName: function(nodes, root, tagName, combinator) {
- tagName = tagName.toUpperCase();
- var results = [], h = Selector.handlers;
- if (nodes) {
- if (combinator) {
- // fastlane for ordinary descendant combinators
- if (combinator == "descendant") {
- for (var i = 0, node; node = nodes[i]; i++)
- h.concat(results, node.getElementsByTagName(tagName));
- return results;
- } else nodes = this[combinator](nodes);
- if (tagName == "*") return nodes;
- }
- for (var i = 0, node; node = nodes[i]; i++)
- if (node.tagName.toUpperCase() == tagName) results.push(node);
- return results;
- } else return root.getElementsByTagName(tagName);
- },
-
- id: function(nodes, root, id, combinator) {
- var targetNode = $(id), h = Selector.handlers;
- if (!targetNode) return [];
- if (!nodes && root == document) return [targetNode];
- if (nodes) {
- if (combinator) {
- if (combinator == 'child') {
- for (var i = 0, node; node = nodes[i]; i++)
- if (targetNode.parentNode == node) return [targetNode];
- } else if (combinator == 'descendant') {
- for (var i = 0, node; node = nodes[i]; i++)
- if (Element.descendantOf(targetNode, node)) return [targetNode];
- } else if (combinator == 'adjacent') {
- for (var i = 0, node; node = nodes[i]; i++)
- if (Selector.handlers.previousElementSibling(targetNode) == node)
- return [targetNode];
- } else nodes = h[combinator](nodes);
- }
- for (var i = 0, node; node = nodes[i]; i++)
- if (node == targetNode) return [targetNode];
- return [];
- }
- return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
- },
-
- className: function(nodes, root, className, combinator) {
- if (nodes && combinator) nodes = this[combinator](nodes);
- return Selector.handlers.byClassName(nodes, root, className);
- },
-
- byClassName: function(nodes, root, className) {
- if (!nodes) nodes = Selector.handlers.descendant([root]);
- var needle = ' ' + className + ' ';
- for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
- nodeClassName = node.className;
- if (nodeClassName.length == 0) continue;
- if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
- results.push(node);
- }
- return results;
- },
-
- attrPresence: function(nodes, root, attr) {
- if (!nodes) nodes = root.getElementsByTagName("*");
- var results = [];
- for (var i = 0, node; node = nodes[i]; i++)
- if (Element.hasAttribute(node, attr)) results.push(node);
- return results;
- },
-
- attr: function(nodes, root, attr, value, operator) {
- if (!nodes) nodes = root.getElementsByTagName("*");
- var handler = Selector.operators[operator], results = [];
- for (var i = 0, node; node = nodes[i]; i++) {
- var nodeValue = Element.readAttribute(node, attr);
- if (nodeValue === null) continue;
- if (handler(nodeValue, value)) results.push(node);
- }
- return results;
- },
-
- pseudo: function(nodes, name, value, root, combinator) {
- if (nodes && combinator) nodes = this[combinator](nodes);
- if (!nodes) nodes = root.getElementsByTagName("*");
- return Selector.pseudos[name](nodes, value, root);
- }
- },
-
- pseudos: {
- 'first-child': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- if (Selector.handlers.previousElementSibling(node)) continue;
- results.push(node);
- }
- return results;
- },
- 'last-child': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- if (Selector.handlers.nextElementSibling(node)) continue;
- results.push(node);
- }
- return results;
- },
- 'only-child': function(nodes, value, root) {
- var h = Selector.handlers;
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
- results.push(node);
- return results;
- },
- 'nth-child': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, formula, root);
- },
- 'nth-last-child': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, formula, root, true);
- },
- 'nth-of-type': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, formula, root, false, true);
- },
- 'nth-last-of-type': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, formula, root, true, true);
- },
- 'first-of-type': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, "1", root, false, true);
- },
- 'last-of-type': function(nodes, formula, root) {
- return Selector.pseudos.nth(nodes, "1", root, true, true);
- },
- 'only-of-type': function(nodes, formula, root) {
- var p = Selector.pseudos;
- return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
- },
-
- // handles the an+b logic
- getIndices: function(a, b, total) {
- if (a == 0) return b > 0 ? [b] : [];
- return $R(1, total).inject([], function(memo, i) {
- if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
- return memo;
- });
- },
-
- // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
- nth: function(nodes, formula, root, reverse, ofType) {
- if (nodes.length == 0) return [];
- if (formula == 'even') formula = '2n+0';
- if (formula == 'odd') formula = '2n+1';
- var h = Selector.handlers, results = [], indexed = [], m;
- h.mark(nodes);
- for (var i = 0, node; node = nodes[i]; i++) {
- if (!node.parentNode._counted) {
- h.index(node.parentNode, reverse, ofType);
- indexed.push(node.parentNode);
- }
- }
- if (formula.match(/^\d+$/)) { // just a number
- formula = Number(formula);
- for (var i = 0, node; node = nodes[i]; i++)
- if (node.nodeIndex == formula) results.push(node);
- } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
- if (m[1] == "-") m[1] = -1;
- var a = m[1] ? Number(m[1]) : 1;
- var b = m[2] ? Number(m[2]) : 0;
- var indices = Selector.pseudos.getIndices(a, b, nodes.length);
- for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
- for (var j = 0; j < l; j++)
- if (node.nodeIndex == indices[j]) results.push(node);
- }
- }
- h.unmark(nodes);
- h.unmark(indexed);
- return results;
- },
-
- 'empty': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
- // IE treats comments as element nodes
- if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
- results.push(node);
- }
- return results;
- },
-
- 'not': function(nodes, selector, root) {
- var h = Selector.handlers, selectorType, m;
- var exclusions = new Selector(selector).findElements(root);
- h.mark(exclusions);
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (!node._counted) results.push(node);
- h.unmark(exclusions);
- return results;
- },
-
- 'enabled': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (!node.disabled) results.push(node);
- return results;
- },
-
- 'disabled': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (node.disabled) results.push(node);
- return results;
- },
-
- 'checked': function(nodes, value, root) {
- for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (node.checked) results.push(node);
- return results;
- }
- },
-
- operators: {
- '=': function(nv, v) { return nv == v; },
- '!=': function(nv, v) { return nv != v; },
- '^=': function(nv, v) { return nv.startsWith(v); },
- '$=': function(nv, v) { return nv.endsWith(v); },
- '*=': function(nv, v) { return nv.include(v); },
- '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
- '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
- },
-
- matchElements: function(elements, expression) {
- var matches = new Selector(expression).findElements(), h = Selector.handlers;
- h.mark(matches);
- for (var i = 0, results = [], element; element = elements[i]; i++)
- if (element._counted) results.push(element);
- h.unmark(matches);
- return results;
- },
-
- findElement: function(elements, expression, index) {
- if (Object.isNumber(expression)) {
- index = expression; expression = false;
- }
- return Selector.matchElements(elements, expression || '*')[index || 0];
- },
-
- findChildElements: function(element, expressions) {
- var exprs = expressions.join(',');
- expressions = [];
- exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
- expressions.push(m[1].strip());
- });
- var results = [], h = Selector.handlers;
- for (var i = 0, l = expressions.length, selector; i < l; i++) {
- selector = new Selector(expressions[i].strip());
- h.concat(results, selector.findElements(element));
- }
- return (l > 1) ? h.unique(results) : results;
- }
-});
-
-if (Prototype.Browser.IE) {
- // IE returns comment nodes on getElementsByTagName("*").
- // Filter them out.
- Selector.handlers.concat = function(a, b) {
- for (var i = 0, node; node = b[i]; i++)
- if (node.tagName !== "!") a.push(node);
- return a;
- };
-}
-
-function $$() {
- return Selector.findChildElements(document, $A(arguments));
-}
-var Form = {
- reset: function(form) {
- $(form).reset();
- return form;
- },
-
- serializeElements: function(elements, options) {
- if (typeof options != 'object') options = { hash: !!options };
- else if (Object.isUndefined(options.hash)) options.hash = true;
- var key, value, submitted = false, submit = options.submit;
-
- var data = elements.inject({ }, function(result, element) {
- if (!element.disabled && element.name) {
- key = element.name; value = $(element).getValue();
- if (value != null && (element.type != 'submit' || (!submitted &&
- submit !== false && (!submit || key == submit) && (submitted = true)))) {
- if (key in result) {
- // a key is already present; construct an array of values
- if (!Object.isArray(result[key])) result[key] = [result[key]];
- result[key].push(value);
- }
- else result[key] = value;
- }
- }
- return result;
- });
-
- return options.hash ? data : Object.toQueryString(data);
- }
-};
-
-Form.Methods = {
- serialize: function(form, options) {
- return Form.serializeElements(Form.getElements(form), options);
- },
-
- getElements: function(form) {
- return $A($(form).getElementsByTagName('*')).inject([],
- function(elements, child) {
- if (Form.Element.Serializers[child.tagName.toLowerCase()])
- elements.push(Element.extend(child));
- return elements;
- }
- );
- },
-
- getInputs: function(form, typeName, name) {
- form = $(form);
- var inputs = form.getElementsByTagName('input');
-
- if (!typeName && !name) return $A(inputs).map(Element.extend);
-
- for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
- var input = inputs[i];
- if ((typeName && input.type != typeName) || (name && input.name != name))
- continue;
- matchingInputs.push(Element.extend(input));
- }
-
- return matchingInputs;
- },
-
- disable: function(form) {
- form = $(form);
- Form.getElements(form).invoke('disable');
- return form;
- },
-
- enable: function(form) {
- form = $(form);
- Form.getElements(form).invoke('enable');
- return form;
- },
-
- findFirstElement: function(form) {
- var elements = $(form).getElements().findAll(function(element) {
- return 'hidden' != element.type && !element.disabled;
- });
- var firstByIndex = elements.findAll(function(element) {
- return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
- }).sortBy(function(element) { return element.tabIndex }).first();
-
- return firstByIndex ? firstByIndex : elements.find(function(element) {
- return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
- });
- },
-
- focusFirstElement: function(form) {
- form = $(form);
- form.findFirstElement().activate();
- return form;
- },
-
- request: function(form, options) {
- form = $(form), options = Object.clone(options || { });
-
- var params = options.parameters, action = form.readAttribute('action') || '';
- if (action.blank()) action = window.location.href;
- options.parameters = form.serialize(true);
-
- if (params) {
- if (Object.isString(params)) params = params.toQueryParams();
- Object.extend(options.parameters, params);
- }
-
- if (form.hasAttribute('method') && !options.method)
- options.method = form.method;
-
- return new Ajax.Request(action, options);
- }
-};
-
-/*--------------------------------------------------------------------------*/
-
-Form.Element = {
- focus: function(element) {
- $(element).focus();
- return element;
- },
-
- select: function(element) {
- $(element).select();
- return element;
- }
-};
-
-Form.Element.Methods = {
- serialize: function(element) {
- element = $(element);
- if (!element.disabled && element.name) {
- var value = element.getValue();
- if (value != undefined) {
- var pair = { };
- pair[element.name] = value;
- return Object.toQueryString(pair);
- }
- }
- return '';
- },
-
- getValue: function(element) {
- element = $(element);
- var method = element.tagName.toLowerCase();
- return Form.Element.Serializers[method](element);
- },
-
- setValue: function(element, value) {
- element = $(element);
- var method = element.tagName.toLowerCase();
- Form.Element.Serializers[method](element, value);
- return element;
- },
-
- clear: function(element) {
- $(element).value = '';
- return element;
- },
-
- present: function(element) {
- return $(element).value != '';
- },
-
- activate: function(element) {
- element = $(element);
- try {
- element.focus();
- if (element.select && (element.tagName.toLowerCase() != 'input' ||
- !['button', 'reset', 'submit'].include(element.type)))
- element.select();
- } catch (e) { }
- return element;
- },
-
- disable: function(element) {
- element = $(element);
- element.blur();
- element.disabled = true;
- return element;
- },
-
- enable: function(element) {
- element = $(element);
- element.disabled = false;
- return element;
- }
-};
-
-/*--------------------------------------------------------------------------*/
-
-var Field = Form.Element;
-var $F = Form.Element.Methods.getValue;
-
-/*--------------------------------------------------------------------------*/
-
-Form.Element.Serializers = {
- input: function(element, value) {
- switch (element.type.toLowerCase()) {
- case 'checkbox':
- case 'radio':
- return Form.Element.Serializers.inputSelector(element, value);
- default:
- return Form.Element.Serializers.textarea(element, value);
- }
- },
-
- inputSelector: function(element, value) {
- if (Object.isUndefined(value)) return element.checked ? element.value : null;
- else element.checked = !!value;
- },
-
- textarea: function(element, value) {
- if (Object.isUndefined(value)) return element.value;
- else element.value = value;
- },
-
- select: function(element, index) {
- if (Object.isUndefined(index))
- return this[element.type == 'select-one' ?
- 'selectOne' : 'selectMany'](element);
- else {
- var opt, value, single = !Object.isArray(index);
- for (var i = 0, length = element.length; i < length; i++) {
- opt = element.options[i];
- value = this.optionValue(opt);
- if (single) {
- if (value == index) {
- opt.selected = true;
- return;
- }
- }
- else opt.selected = index.include(value);
- }
- }
- },
-
- selectOne: function(element) {
- var index = element.selectedIndex;
- return index >= 0 ? this.optionValue(element.options[index]) : null;
- },
-
- selectMany: function(element) {
- var values, length = element.length;
- if (!length) return null;
-
- for (var i = 0, values = []; i < length; i++) {
- var opt = element.options[i];
- if (opt.selected) values.push(this.optionValue(opt));
- }
- return values;
- },
-
- optionValue: function(opt) {
- // extend element because hasAttribute may not be native
- return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
- }
-};
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
- initialize: function($super, element, frequency, callback) {
- $super(callback, frequency);
- this.element = $(element);
- this.lastValue = this.getValue();
- },
-
- execute: function() {
- var value = this.getValue();
- if (Object.isString(this.lastValue) && Object.isString(value) ?
- this.lastValue != value : String(this.lastValue) != String(value)) {
- this.callback(this.element, value);
- this.lastValue = value;
- }
- }
-});
-
-Form.Element.Observer = Class.create(Abstract.TimedObserver, {
- getValue: function() {
- return Form.Element.getValue(this.element);
- }
-});
-
-Form.Observer = Class.create(Abstract.TimedObserver, {
- getValue: function() {
- return Form.serialize(this.element);
- }
-});
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.EventObserver = Class.create({
- initialize: function(element, callback) {
- this.element = $(element);
- this.callback = callback;
-
- this.lastValue = this.getValue();
- if (this.element.tagName.toLowerCase() == 'form')
- this.registerFormCallbacks();
- else
- this.registerCallback(this.element);
- },
-
- onElementEvent: function() {
- var value = this.getValue();
- if (this.lastValue != value) {
- this.callback(this.element, value);
- this.lastValue = value;
- }
- },
-
- registerFormCallbacks: function() {
- Form.getElements(this.element).each(this.registerCallback, this);
- },
-
- registerCallback: function(element) {
- if (element.type) {
- switch (element.type.toLowerCase()) {
- case 'checkbox':
- case 'radio':
- Event.observe(element, 'click', this.onElementEvent.bind(this));
- break;
- default:
- Event.observe(element, 'change', this.onElementEvent.bind(this));
- break;
- }
- }
- }
-});
-
-Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
- getValue: function() {
- return Form.Element.getValue(this.element);
- }
-});
-
-Form.EventObserver = Class.create(Abstract.EventObserver, {
- getValue: function() {
- return Form.serialize(this.element);
- }
-});
-if (!window.Event) var Event = { };
-
-Object.extend(Event, {
- KEY_BACKSPACE: 8,
- KEY_TAB: 9,
- KEY_RETURN: 13,
- KEY_ESC: 27,
- KEY_LEFT: 37,
- KEY_UP: 38,
- KEY_RIGHT: 39,
- KEY_DOWN: 40,
- KEY_DELETE: 46,
- KEY_HOME: 36,
- KEY_END: 35,
- KEY_PAGEUP: 33,
- KEY_PAGEDOWN: 34,
- KEY_INSERT: 45,
-
- cache: { },
-
- relatedTarget: function(event) {
- var element;
- switch(event.type) {
- case 'mouseover': element = event.fromElement; break;
- case 'mouseout': element = event.toElement; break;
- default: return null;
- }
- return Element.extend(element);
- }
-});
-
-Event.Methods = (function() {
- var isButton;
-
- if (Prototype.Browser.IE) {
- var buttonMap = { 0: 1, 1: 4, 2: 2 };
- isButton = function(event, code) {
- return event.button == buttonMap[code];
- };
-
- } else if (Prototype.Browser.WebKit) {
- isButton = function(event, code) {
- switch (code) {
- case 0: return event.which == 1 && !event.metaKey;
- case 1: return event.which == 1 && event.metaKey;
- default: return false;
- }
- };
-
- } else {
- isButton = function(event, code) {
- return event.which ? (event.which === code + 1) : (event.button === code);
- };
- }
-
- return {
- isLeftClick: function(event) { return isButton(event, 0) },
- isMiddleClick: function(event) { return isButton(event, 1) },
- isRightClick: function(event) { return isButton(event, 2) },
-
- element: function(event) {
- var node = Event.extend(event).target;
- return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
- },
-
- findElement: function(event, expression) {
- var element = Event.element(event);
- if (!expression) return element;
- var elements = [element].concat(element.ancestors());
- return Selector.findElement(elements, expression, 0);
- },
-
- pointer: function(event) {
- return {
- x: event.pageX || (event.clientX +
- (document.documentElement.scrollLeft || document.body.scrollLeft)),
- y: event.pageY || (event.clientY +
- (document.documentElement.scrollTop || document.body.scrollTop))
- };
- },
-
- pointerX: function(event) { return Event.pointer(event).x },
- pointerY: function(event) { return Event.pointer(event).y },
-
- stop: function(event) {
- Event.extend(event);
- event.preventDefault();
- event.stopPropagation();
- event.stopped = true;
- }
- };
-})();
-
-Event.extend = (function() {
- var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
- m[name] = Event.Methods[name].methodize();
- return m;
- });
-
- if (Prototype.Browser.IE) {
- Object.extend(methods, {
- stopPropagation: function() { this.cancelBubble = true },
- preventDefault: function() { this.returnValue = false },
- inspect: function() { return "[object Event]" }
- });
-
- return function(event) {
- if (!event) return false;
- if (event._extendedByPrototype) return event;
-
- event._extendedByPrototype = Prototype.emptyFunction;
- var pointer = Event.pointer(event);
- Object.extend(event, {
- target: event.srcElement,
- relatedTarget: Event.relatedTarget(event),
- pageX: pointer.x,
- pageY: pointer.y
- });
- return Object.extend(event, methods);
- };
-
- } else {
- Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
- Object.extend(Event.prototype, methods);
- return Prototype.K;
- }
-})();
-
-Object.extend(Event, (function() {
- var cache = Event.cache;
-
- function getEventID(element) {
- if (element._eventID) return element._eventID;
- arguments.callee.id = arguments.callee.id || 1;
- return element._eventID = ++arguments.callee.id;
- }
-
- function getDOMEventName(eventName) {
- if (eventName && eventName.include(':')) return "dataavailable";
- return eventName;
- }
-
- function getCacheForID(id) {
- return cache[id] = cache[id] || { };
- }
-
- function getWrappersForEventName(id, eventName) {
- var c = getCacheForID(id);
- return c[eventName] = c[eventName] || [];
- }
-
- function createWrapper(element, eventName, handler) {
- var id = getEventID(element);
- var c = getWrappersForEventName(id, eventName);
- if (c.pluck("handler").include(handler)) return false;
-
- var wrapper = function(event) {
- if (!Event || !Event.extend ||
- (event.eventName && event.eventName != eventName))
- return false;
-
- Event.extend(event);
- handler.call(element, event)
- };
-
- wrapper.handler = handler;
- c.push(wrapper);
- return wrapper;
- }
-
- function findWrapper(id, eventName, handler) {
- var c = getWrappersForEventName(id, eventName);
- return c.find(function(wrapper) { return wrapper.handler == handler });
- }
-
- function destroyWrapper(id, eventName, handler) {
- var c = getCacheForID(id);
- if (!c[eventName]) return false;
- c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
- }
-
- function destroyCache() {
- for (var id in cache)
- for (var eventName in cache[id])
- cache[id][eventName] = null;
- }
-
- if (window.attachEvent) {
- window.attachEvent("onunload", destroyCache);
- }
-
- return {
- observe: function(element, eventName, handler) {
- element = $(element);
- var name = getDOMEventName(eventName);
-
- var wrapper = createWrapper(element, eventName, handler);
- if (!wrapper) return element;
-
- if (element.addEventListener) {
- element.addEventListener(name, wrapper, false);
- } else {
- element.attachEvent("on" + name, wrapper);
- }
-
- return element;
- },
-
- stopObserving: function(element, eventName, handler) {
- element = $(element);
- var id = getEventID(element), name = getDOMEventName(eventName);
-
- if (!handler && eventName) {
- getWrappersForEventName(id, eventName).each(function(wrapper) {
- element.stopObserving(eventName, wrapper.handler);
- });
- return element;
-
- } else if (!eventName) {
- Object.keys(getCacheForID(id)).each(function(eventName) {
- element.stopObserving(eventName);
- });
- return element;
- }
-
- var wrapper = findWrapper(id, eventName, handler);
- if (!wrapper) return element;
-
- if (element.removeEventListener) {
- element.removeEventListener(name, wrapper, false);
- } else {
- element.detachEvent("on" + name, wrapper);
- }
-
- destroyWrapper(id, eventName, handler);
-
- return element;
- },
-
- fire: function(element, eventName, memo) {
- element = $(element);
- if (element == document && document.createEvent && !element.dispatchEvent)
- element = document.documentElement;
-
- if (document.createEvent) {
- var event = document.createEvent("HTMLEvents");
- event.initEvent("dataavailable", true, true);
- } else {
- var event = document.createEventObject();
- event.eventType = "ondataavailable";
- }
-
- event.eventName = eventName;
- event.memo = memo || { };
-
- if (document.createEvent) {
- element.dispatchEvent(event);
- } else {
- element.fireEvent(event.eventType, event);
- }
-
- return Event.extend(event);
- }
- };
-})());
-
-Object.extend(Event, Event.Methods);
-
-Element.addMethods({
- fire: Event.fire,
- observe: Event.observe,
- stopObserving: Event.stopObserving
-});
-
-Object.extend(document, {
- fire: Element.Methods.fire.methodize(),
- observe: Element.Methods.observe.methodize(),
- stopObserving: Element.Methods.stopObserving.methodize()
-});
-
-(function() {
- /* Support for the DOMContentLoaded event is based on work by Dan Webb,
- Matthias Miller, Dean Edwards and John Resig. */
-
- var timer, fired = false;
-
- function fireContentLoadedEvent() {
- if (fired) return;
- if (timer) window.clearInterval(timer);
- document.fire("dom:loaded");
- fired = true;
- }
-
- if (document.addEventListener) {
- if (Prototype.Browser.WebKit) {
- timer = window.setInterval(function() {
- if (/loaded|complete/.test(document.readyState))
- fireContentLoadedEvent();
- }, 0);
-
- Event.observe(window, "load", fireContentLoadedEvent);
-
- } else {
- document.addEventListener("DOMContentLoaded",
- fireContentLoadedEvent, false);
- }
-
- } else {
- document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
- $("__onDOMContentLoaded").onreadystatechange = function() {
- if (this.readyState == "complete") {
- this.onreadystatechange = null;
- fireContentLoadedEvent();
- }
- };
- }
-})();
-/*------------------------------- DEPRECATED -------------------------------*/
-
-Hash.toQueryString = Object.toQueryString;
-
-var Toggle = { display: Element.toggle };
-
-Element.Methods.childOf = Element.Methods.descendantOf;
-
-var Insertion = {
- Before: function(element, content) {
- return Element.insert(element, {before:content});
- },
-
- Top: function(element, content) {
- return Element.insert(element, {top:content});
- },
-
- Bottom: function(element, content) {
- return Element.insert(element, {bottom:content});
- },
-
- After: function(element, content) {
- return Element.insert(element, {after:content});
- }
-};
-
-var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
-
-// This should be moved to script.aculo.us; notice the deprecated methods
-// further below, that map to the newer Element methods.
-var Position = {
- // set to true if needed, warning: firefox performance problems
- // NOT neeeded for page scrolling, only if draggable contained in
- // scrollable elements
- includeScrollOffsets: false,
-
- // must be called before calling withinIncludingScrolloffset, every time the
- // page is scrolled
- prepare: function() {
- this.deltaX = window.pageXOffset
- || document.documentElement.scrollLeft
- || document.body.scrollLeft
- || 0;
- this.deltaY = window.pageYOffset
- || document.documentElement.scrollTop
- || document.body.scrollTop
- || 0;
- },
-
- // caches x/y coordinate pair to use with overlap
- within: function(element, x, y) {
- if (this.includeScrollOffsets)
- return this.withinIncludingScrolloffsets(element, x, y);
- this.xcomp = x;
- this.ycomp = y;
- this.offset = Element.cumulativeOffset(element);
-
- return (y >= this.offset[1] &&
- y < this.offset[1] + element.offsetHeight &&
- x >= this.offset[0] &&
- x < this.offset[0] + element.offsetWidth);
- },
-
- withinIncludingScrolloffsets: function(element, x, y) {
- var offsetcache = Element.cumulativeScrollOffset(element);
-
- this.xcomp = x + offsetcache[0] - this.deltaX;
- this.ycomp = y + offsetcache[1] - this.deltaY;
- this.offset = Element.cumulativeOffset(element);
-
- return (this.ycomp >= this.offset[1] &&
- this.ycomp < this.offset[1] + element.offsetHeight &&
- this.xcomp >= this.offset[0] &&
- this.xcomp < this.offset[0] + element.offsetWidth);
- },
-
- // within must be called directly before
- overlap: function(mode, element) {
- if (!mode) return 0;
- if (mode == 'vertical')
- return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
- element.offsetHeight;
- if (mode == 'horizontal')
- return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
- element.offsetWidth;
- },
-
- // Deprecation layer -- use newer Element methods now (1.5.2).
-
- cumulativeOffset: Element.Methods.cumulativeOffset,
-
- positionedOffset: Element.Methods.positionedOffset,
-
- absolutize: function(element) {
- Position.prepare();
- return Element.absolutize(element);
- },
-
- relativize: function(element) {
- Position.prepare();
- return Element.relativize(element);
- },
-
- realOffset: Element.Methods.cumulativeScrollOffset,
-
- offsetParent: Element.Methods.getOffsetParent,
-
- page: Element.Methods.viewportOffset,
-
- clone: function(source, target, options) {
- options = options || { };
- return Element.clonePosition(target, source, options);
- }
-};
-
-/*--------------------------------------------------------------------------*/
-
-if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
- function iter(name) {
- return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
- }
-
- instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
- function(element, className) {
- className = className.toString().strip();
- var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
- return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
- } : function(element, className) {
- className = className.toString().strip();
- var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
- if (!classNames && !className) return elements;
-
- var nodes = $(element).getElementsByTagName('*');
- className = ' ' + className + ' ';
-
- for (var i = 0, child, cn; child = nodes[i]; i++) {
- if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
- (classNames && classNames.all(function(name) {
- return !name.toString().blank() && cn.include(' ' + name + ' ');
- }))))
- elements.push(Element.extend(child));
- }
- return elements;
- };
-
- return function(className, parentElement) {
- return $(parentElement || document.body).getElementsByClassName(className);
- };
-}(Element.Methods);
-
-/*--------------------------------------------------------------------------*/
-
-Element.ClassNames = Class.create();
-Element.ClassNames.prototype = {
- initialize: function(element) {
- this.element = $(element);
- },
-
- _each: function(iterator) {
- this.element.className.split(/\s+/).select(function(name) {
- return name.length > 0;
- })._each(iterator);
- },
-
- set: function(className) {
- this.element.className = className;
- },
-
- add: function(classNameToAdd) {
- if (this.include(classNameToAdd)) return;
- this.set($A(this).concat(classNameToAdd).join(' '));
- },
-
- remove: function(classNameToRemove) {
- if (!this.include(classNameToRemove)) return;
- this.set($A(this).without(classNameToRemove).join(' '));
- },
-
- toString: function() {
- return $A(this).join(' ');
- }
-};
-
-Object.extend(Element.ClassNames.prototype, Enumerable);
-
-/*--------------------------------------------------------------------------*/
-
-Element.addMethods(); \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/app/public/robots.txt b/vendor/plugins/interlock/test/integration/app/public/robots.txt
deleted file mode 100644
index 085187fa5..000000000
--- a/vendor/plugins/interlock/test/integration/app/public/robots.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
-#
-# To ban all spiders from the entire site uncomment the next two lines:
-# User-Agent: *
-# Disallow: /
diff --git a/vendor/plugins/interlock/test/integration/app/script/about b/vendor/plugins/interlock/test/integration/app/script/about
deleted file mode 100755
index cd38a32ab..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/about
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/about'
diff --git a/vendor/plugins/interlock/test/integration/app/script/console b/vendor/plugins/interlock/test/integration/app/script/console
deleted file mode 100755
index 498077ab3..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/console
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/console'
diff --git a/vendor/plugins/interlock/test/integration/app/script/destroy b/vendor/plugins/interlock/test/integration/app/script/destroy
deleted file mode 100755
index a4df765a3..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/destroy
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/destroy'
diff --git a/vendor/plugins/interlock/test/integration/app/script/generate b/vendor/plugins/interlock/test/integration/app/script/generate
deleted file mode 100755
index 173a9f147..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/generate
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/generate'
diff --git a/vendor/plugins/interlock/test/integration/app/script/performance/benchmarker b/vendor/plugins/interlock/test/integration/app/script/performance/benchmarker
deleted file mode 100755
index c842d35d3..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/performance/benchmarker
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../config/boot'
-require 'commands/performance/benchmarker'
diff --git a/vendor/plugins/interlock/test/integration/app/script/performance/profiler b/vendor/plugins/interlock/test/integration/app/script/performance/profiler
deleted file mode 100755
index d855ac8b1..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/performance/profiler
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../config/boot'
-require 'commands/performance/profiler'
diff --git a/vendor/plugins/interlock/test/integration/app/script/performance/request b/vendor/plugins/interlock/test/integration/app/script/performance/request
deleted file mode 100755
index ae3f38c74..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/performance/request
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../config/boot'
-require 'commands/performance/request'
diff --git a/vendor/plugins/interlock/test/integration/app/script/plugin b/vendor/plugins/interlock/test/integration/app/script/plugin
deleted file mode 100755
index 87cd2070f..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/plugin
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/plugin'
diff --git a/vendor/plugins/interlock/test/integration/app/script/process/inspector b/vendor/plugins/interlock/test/integration/app/script/process/inspector
deleted file mode 100755
index bf25ad86d..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/process/inspector
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../config/boot'
-require 'commands/process/inspector'
diff --git a/vendor/plugins/interlock/test/integration/app/script/process/reaper b/vendor/plugins/interlock/test/integration/app/script/process/reaper
deleted file mode 100755
index c77f04535..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/process/reaper
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../config/boot'
-require 'commands/process/reaper'
diff --git a/vendor/plugins/interlock/test/integration/app/script/process/spawner b/vendor/plugins/interlock/test/integration/app/script/process/spawner
deleted file mode 100755
index 7118f3983..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/process/spawner
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../config/boot'
-require 'commands/process/spawner'
diff --git a/vendor/plugins/interlock/test/integration/app/script/runner b/vendor/plugins/interlock/test/integration/app/script/runner
deleted file mode 100755
index a4a7cb25b..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/runner
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/runner'
diff --git a/vendor/plugins/interlock/test/integration/app/script/server b/vendor/plugins/interlock/test/integration/app/script/server
deleted file mode 100755
index 3c67f39b6..000000000
--- a/vendor/plugins/interlock/test/integration/app/script/server
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/server'
diff --git a/vendor/plugins/interlock/test/integration/app/test/fixtures/books.yml b/vendor/plugins/interlock/test/integration/app/test/fixtures/books.yml
deleted file mode 100644
index 7841212db..000000000
--- a/vendor/plugins/interlock/test/integration/app/test/fixtures/books.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-one:
- guid: 1137
- name: Book of Shred
- description: The epic and definitive guide to shredding.
- created_at: 2007-12-04 13:47:27
- updated_at: 2007-12-04 13:47:27
-two:
- guid: 2001
- name: Red Hot and Rollin
- description: Rip City Baby!
- created_at: 2007-12-04 13:47:27
- updated_at: 2007-12-04 13:47:27
diff --git a/vendor/plugins/interlock/test/integration/app/test/fixtures/items.yml b/vendor/plugins/interlock/test/integration/app/test/fixtures/items.yml
deleted file mode 100644
index bf18ebcfe..000000000
--- a/vendor/plugins/interlock/test/integration/app/test/fixtures/items.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-one:
- id: 1
- name: Artichoke
- description: The Globe Artichoke (Cynara scolymus) is a perennial thistle originating in southern Europe around the Mediterranean.
- created_at: 2007-12-04 13:47:27
- updated_at: 2007-12-04 13:47:27
-two:
- id: 2
- name: Fire Apparatus
- description: A fire apparatus, fire engine, fire truck, or fire appliance is a vehicle designed to assist in fighting fires, by transporting firefighters to the scene, and providing them with access, water or other equipment.
- created_at: 2007-12-04 13:47:27
- updated_at: 2007-12-04 13:47:27
diff --git a/vendor/plugins/interlock/test/integration/app/test/functional/eval_controller_test.rb b/vendor/plugins/interlock/test/integration/app/test/functional/eval_controller_test.rb
deleted file mode 100644
index 214842e26..000000000
--- a/vendor/plugins/interlock/test/integration/app/test/functional/eval_controller_test.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require File.dirname(__FILE__) + '/../test_helper'
-
-class EvalControllerTest < ActionController::TestCase
- tests EvalController
-
- # Replace this with your real tests.
- def test_truth
- assert true
- end
-end
diff --git a/vendor/plugins/interlock/test/integration/app/test/functional/items_controller_test.rb b/vendor/plugins/interlock/test/integration/app/test/functional/items_controller_test.rb
deleted file mode 100644
index dd6e8e42a..000000000
--- a/vendor/plugins/interlock/test/integration/app/test/functional/items_controller_test.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require File.dirname(__FILE__) + '/../test_helper'
-
-class ItemsControllerTest < ActionController::TestCase
- tests ItemsController
-
- # Replace this with your real tests.
- def test_truth
- assert true
- end
-end
diff --git a/vendor/plugins/interlock/test/integration/app/test/test_helper.rb b/vendor/plugins/interlock/test/integration/app/test/test_helper.rb
deleted file mode 100644
index b225a3349..000000000
--- a/vendor/plugins/interlock/test/integration/app/test/test_helper.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-ENV["RAILS_ENV"] = "test"
-require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
-require 'test_help'
-
-class Test::Unit::TestCase
- self.use_transactional_fixtures = true
- self.use_instantiated_fixtures = false
- fixtures :all
-end
diff --git a/vendor/plugins/interlock/test/integration/app/test/unit/item_test.rb b/vendor/plugins/interlock/test/integration/app/test/unit/item_test.rb
deleted file mode 100644
index fbb5b711f..000000000
--- a/vendor/plugins/interlock/test/integration/app/test/unit/item_test.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require File.dirname(__FILE__) + '/../test_helper'
-
-class ItemTest < ActiveSupport::TestCase
- # Replace this with your real tests.
- def test_truth
- assert true
- end
-end
diff --git a/vendor/plugins/interlock/test/integration/app/vendor/plugins/interlock b/vendor/plugins/interlock/test/integration/app/vendor/plugins/interlock
deleted file mode 120000
index 3efed95be..000000000
--- a/vendor/plugins/interlock/test/integration/app/vendor/plugins/interlock
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../ \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/integration/server_test.rb b/vendor/plugins/interlock/test/integration/server_test.rb
deleted file mode 100644
index 67335dd82..000000000
--- a/vendor/plugins/interlock/test/integration/server_test.rb
+++ /dev/null
@@ -1,230 +0,0 @@
-
-require "#{File.dirname(__FILE__)}/../test_helper"
-require 'open-uri'
-require 'cgi'
-require 'fileutils'
-
-class ServerTest < Test::Unit::TestCase
-
- PORT = 43041
- URL = "http://localhost:#{PORT}/"
- LOG = "#{HERE}/integration/app/log/development.log"
-
- Dir.chdir RAILS_ROOT do
- COVERAGE = "coverage"
- RCOV = "#{COVERAGE}/cache-#{PORT}" # The port number must appear in `ps awx`
- FileUtils.rm_rf(COVERAGE) if File.exist? COVERAGE
- Dir.mkdir COVERAGE
- end
-
-
- ### Fragment caching tests
-
- def test_render
- assert_match(/Welcome/, browse)
- assert_match(/Artichoke/, browse("items"))
- end
-
- def test_caching
- browse("items")
- assert_match(/cleared interlock local cache/, log)
- assert_match(/all:untagged is running the controller block/, log)
- assert_match(/all:untagged wrote/, log)
-
- truncate
- browse("items")
- assert_match(/cleared interlock local cache/, log)
- assert_no_match(/all:untagged is running the controller block/, log)
- assert_no_match(/all:untagged wrote/, log)
- assert_match(/all:untagged read from memcached/, log)
- end
-
- def test_controller_respects_log_level
- remote = <<-CODE
- RAILS_DEFAULT_LOGGER.level = Logger::INFO;
- Interlock.config[:log_level] = 'info'
- CODE
- remote_eval(remote)
-
- truncate
- browse("items")
- assert_match(/cleared interlock local cache/, log)
-
- remote_eval("Interlock.config[:log_level] = 'debug'")
- truncate
- browse("items")
- assert_no_match(/cleared interlock local cache/, log)
- end
-
- def test_broad_invalidation
- browse("items")
- assert_match(/all:untagged is running the controller block/, log)
- assert_match(/all:untagged wrote/, log)
-
- truncate
- assert_equal "true", remote_eval("Item.find(:first).save!")
- assert_match(/all:untagged invalidated by rule Item \-\> .all/, log)
- browse("items")
- assert_match(/all:untagged is running the controller block/, log)
- assert_match(/all:untagged wrote/, log)
- end
-
- def test_narrow_invalidation
- browse("items/show/1")
- assert_match(/show:1:untagged is running the controller block/, log)
-
- truncate
- assert_equal "true", remote_eval("Item.find(2).save!")
- assert_no_match(/show:1:untagged invalidated/, log)
- browse("items/show/1")
- assert_no_match(/show:1:untagged is running the controller block/, log)
-
- truncate
- assert_equal "true", remote_eval("Item.find(1).save!")
- assert_match(/show:1:untagged invalidated/, log)
- browse("items/show/1")
- assert_match(/show:1:untagged is running the controller block/, log)
- end
-
- def test_caching_with_tag
- # This test is a little over-complicated
- remote_eval("Item.update_all('updated_at = NULL')")
-
- assert_no_match(/Artichoke/, browse("items/recent?seconds=3"))
- assert_match(/recent:all:3 is running the controller block/, log)
-
- truncate
- assert_no_match(/Artichoke/, browse("items/recent?seconds=2"))
- assert_match(/recent:all:2 is running the controller block/, log)
- assert_no_match(/recent:all:3 is running the controller block/, log)
-
- truncate
- remote_eval("Item.find(1).update_attributes!(:description => 'Changed!')")
- assert_match(/Artichoke/, browse("items/recent?seconds=4"))
- assert_match(/recent:all:4 is running the controller block/, log)
-
- truncate
- assert_no_match(/Artichoke/, browse("items/recent?seconds=3"))
- assert_no_match(/recent:all:3 is running the controller block/, log)
- end
-
- def test_caching_with_perform_false
- browse("items/preview/1")
- assert_no_match(/preview:1:untagged registered a dependency/, log)
- assert_match(/preview:1:untagged is not cached/, log)
-
- truncate
- browse("items/preview/1")
- assert_no_match(/preview:1:untagged registered a dependency/, log)
- assert_match(/preview:1:untagged is not cached/, log)
- end
-
- def test_caching_with_ignore
- assert_match(/Delicious cake/, browse('items'))
- assert_match(/any:any:all:related is running the controller block/, log)
-
- truncate
- assert_match(/Delicious cake/, browse("items/show/2"))
- assert_no_match(/any:any:all:related is running the controller block/, log)
-
- truncate
- remote_eval("Item.find(1).save!")
- assert_match(/Delicious cake/, browse("items/show/2"))
- assert_match(/any:any:all:related invalidated/, log)
- assert_match(/any:any:all:related is running the controller block/, log)
- end
-
- unless ENV['RAILS_GEM_VERSION'] == "1.2.6"
- # This functionality not supported on 1.2.6
-
- def test_caching_of_content_for
- assert_match(/Interlock Test:\s*\d\s*Items/m, browse("items"))
- assert_match(/all:untagged is running the controller block/, log)
- assert_match(/all:untagged wrote/, log)
-
- truncate
- assert_match(/Interlock Test:\s*\d\s*Items/m, browse("items"))
- # Make sure we didn't copy the content_for too many times
- assert_no_match(/Interlock Test:\s*\d\s*Items\s*\d\s*Items/m, browse("items"))
- assert_no_match(/all:untagged is running the controller block/, log)
- assert_match(/all:untagged read from memcached/, log)
- end
-
- def test_nested_view_caches
- assert_match(/Outer: Inner<.*2 total items.*Artichoke/m, browse("items/detail/1"))
- assert_match(/detail:1:outer is running the controller block/, log)
- assert_match(/detail:1:inner is running the controller block/, log)
-
- truncate
- assert_match(/Outer: Inner<.*2 total items.*Artichoke/m, browse("items/detail/1"))
- assert_no_match(/detail:1:outer is running the controller block/, log)
- assert_no_match(/detail:1:inner is running the controller block/, log)
-
- truncate
- remote_eval("Item.find(2).save!")
- assert_match(/Outer: Inner<.*2 total items.*Artichoke/m, browse("items/detail/1"))
- assert_match(/detail:1:outer is running the controller block/, log)
- assert_no_match(/detail:1:inner is running the controller block/, log)
-
- truncate
- remote_eval("Item.find(1).save!")
- assert_match(/Outer: Inner<.*2 total items.*Artichoke/m, browse("items/detail/1"))
- assert_match(/detail:1:outer is running the controller block/, log)
- assert_match(/detail:1:inner is running the controller block/, log)
- end
- end
-
- ### Support methods
-
- def setup
- # We test against an actual running server in order to lock down the environment
- # class reloading situation
- Process.fork do
- Dir.chdir RAILS_ROOT do
- if $rcov
- exec("rcov --aggregate #{RCOV} --exclude config\/.*,app\/.*,boot\/.*,script\/server --include-file vendor\/plugins\/interlock\/lib\/.*\.rb script/server -- -p #{PORT} &> #{LOG}")
- else
- exec("script/server -p #{PORT} &> #{LOG}")
- end
- end
- end
- sleep(0.2) while log !~ /available at 0.0.0.0.#{PORT}/
- truncate
- end
-
- def teardown
- # Process.kill(9, pid) doesn't work because Mongrel has double-forked itself away
- while (pids = `ps awx | grep #{PORT} | grep -v grep | awk '{print $1}'`.split("\n")).any?
- pids.each {|pid| system("kill #{pid}")}
- sleep(0.2)
- end
- end
-
- def truncate
- system("> #{LOG}")
- end
-
- def log
- File.open(LOG, 'r') do |f|
- f.read
- end
- end
-
- def browse(url = "")
- flag = false
- begin
- open(URL + url).read
- rescue Errno::ECONNREFUSED, OpenURI::HTTPError => e
- raise "#{e.to_s}: #{URL + url}" if flag
- flag = true
- sleep 3
- retry
- end
- end
-
- def remote_eval(string)
- # Server doesn't run in our process, so invalidations here don't affect it
- browse("eval?string=#{CGI.escape(string)}")
- end
-
-end \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/setup.rb b/vendor/plugins/interlock/test/setup.rb
deleted file mode 100644
index 62d2b1ace..000000000
--- a/vendor/plugins/interlock/test/setup.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-
-# Setup integration system for the integration suite
-
-Dir.chdir "#{File.dirname(__FILE__)}/integration/app/" do
-
- `ps awx`.split("\n").grep(/4304[1-3]/).map do |process|
- system("kill -9 #{process.to_i}")
- end
-
- LOG = "/tmp/memcached.log"
-
- system "memcached -vv -p 43042 >> #{LOG} 2>&1 &"
- system "memcached -vv -p 43043 >> #{LOG} 2>&1 &"
-
- Dir.chdir "vendor/plugins" do
- system "rm interlock; ln -s ../../../../../ interlock"
- end
-
- system "rake db:create"
- system "rake db:migrate"
- system "rake db:fixtures:load"
-end
diff --git a/vendor/plugins/interlock/test/teardown.rb b/vendor/plugins/interlock/test/teardown.rb
deleted file mode 100644
index e69de29bb..000000000
--- a/vendor/plugins/interlock/test/teardown.rb
+++ /dev/null
diff --git a/vendor/plugins/interlock/test/test_helper.rb b/vendor/plugins/interlock/test/test_helper.rb
deleted file mode 100644
index 95eb1a629..000000000
--- a/vendor/plugins/interlock/test/test_helper.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-
-$VERBOSE = nil
-require 'rubygems'
-require 'test/unit'
-require 'echoe'
-require 'multi_rails_init'
-
-if defined? ENV['MULTIRAILS_RAILS_VERSION']
- ENV['RAILS_GEM_VERSION'] = ENV['MULTIRAILS_RAILS_VERSION']
-end
-
-$rcov = ENV['RCOV']
-require 'ruby-debug' unless $rcov
-
-Echoe.silence do
- HERE = File.expand_path(File.dirname(__FILE__))
- $LOAD_PATH << HERE
-end
-
-require 'integration/app/config/environment'
diff --git a/vendor/plugins/interlock/test/unit/active_record_test.rb b/vendor/plugins/interlock/test/unit/active_record_test.rb
deleted file mode 100644
index 76c218fbb..000000000
--- a/vendor/plugins/interlock/test/unit/active_record_test.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require "#{File.dirname(__FILE__)}/../test_helper"
-
-class FinderTest < Test::Unit::TestCase
- def test_reload_should_work
- item = Item.find(1)
- assert_equal Item.find(1, {}), item.reload
- end
-end \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/unit/finder_test.rb b/vendor/plugins/interlock/test/unit/finder_test.rb
deleted file mode 100644
index f73020be7..000000000
--- a/vendor/plugins/interlock/test/unit/finder_test.rb
+++ /dev/null
@@ -1,176 +0,0 @@
-
-require "#{File.dirname(__FILE__)}/../test_helper"
-require 'fileutils'
-
-class FinderTest < Test::Unit::TestCase
-
- LOG = "#{HERE}/integration/app/log/development.log"
-
- ### Finder caching tests
-
- def test_find_without_cache
- # Non-empty options hash bypasses the cache entirely, including the logging
- Item.find(1, {:conditions => "1 = 1"})
- assert_no_match(/model.*Item:find:1:default is loading from the db/, log)
- end
-
- def test_find
- assert_equal Item.find(1, {}),
- Item.find(1)
- assert_match(/model.*Item:find:1:default is loading from the db/, log)
-
- assert_equal Item.find(1, {}),
- Item.find(1)
- assert_match(/model.*Item:find:1:default is loading from memcached/, log)
- end
-
- def test_find_with_array
- assert_equal Item.find([1, 2], {}),
- Item.find([1, 2])
- assert_match(/model.*Item:find:1:default is loading from the db/, log)
- assert_match(/model.*Item:find:2:default is loading from the db/, log)
-
- assert_equal Item.find([1, 2], {}),
- Item.find([1, 2])
- assert_match(/model.*Item:find:1:default is loading from memcached/, log)
- assert_match(/model.*Item:find:2:default is loading from memcached/, log)
- end
-
- def test_single_element_array_returns_array
- assert_equal Item.find([1], {}),
- Item.find([1])
- end
-
- def test_find_raise
- assert_raises(ActiveRecord::RecordNotFound) do
- Item.find(44)
- end
- end
-
- def test_find_with_array_raise
- assert_raises(ActiveRecord::RecordNotFound) do
- # Once from the DB
- Item.find([1, 2, 44])
- end
- assert_raises(ActiveRecord::RecordNotFound) do
- # Once from Memcached
- Item.find([1, 2, 44])
- end
- end
-
- def test_find_with_array_ignores_nil
- assert_equal Item.find(1, nil, {}), Item.find(1, nil)
- assert_equal Item.find([1, nil], {}), Item.find([1, nil])
- end
-
- def test_invalidate
- Item.find(1).save!
- truncate
- Item.find(1)
- assert_match(/model.*Item:find:1:default is loading from the db/, log)
- Item.find(1)
- assert_match(/model.*Item:find:1:default is loading from memcached/, log)
- end
-
- def test_reload_should_invalidate
- item = Item.find(1)
- item.reload
- assert_match(/model.*Item:find:1:default invalidated with finders/, log)
- truncate
- Item.find(1)
- assert_match(/model.*Item:find:1:default is loading from memcached/, log)
- end
-
- def test_update_attributes_should_invalidate
- item = Item.find(1)
- name = item.name
-
- item.update_attributes!(:name => 'Updated')
- updated_item = Item.find(1)
- assert_equal 'Updated', item.name
-
- # Restore name for further tests
- item.update_attributes!(:name => name)
- end
-
- def test_update_all_should_invalidate
- # TODO
- end
-
- def test_update_counters_should_invalidate
- item = Item.find(1)
- Item.update_counters(1, :counting_something => 1)
- updated_item = Item.find(1)
- assert_equal updated_item.counting_something, item.counting_something + 1
- end
-
- def test_find_all_by_id
- assert_equal Item.find_all_by_id(44, {}),
- Item.find_all_by_id(44)
- assert_equal Item.find_all_by_id([1,2], {}),
- Item.find_all_by_id([1,2])
- assert_equal Item.find_all_by_id(1, 2, {}),
- Item.find_all_by_id(1, 2)
- end
-
- def test_invalidate_sti
- # XXX Need a regression test
- end
-
- def test_find_by_id
- assert_equal Item.find_by_id(44, {}),
- Item.find_by_id(44)
- assert_equal Item.find_by_id([1,2], {}),
- Item.find_by_id([1,2])
- assert_equal Item.find_by_id(1, 2, {}),
- Item.find_by_id(1, 2)
- end
-
- def test_custom_log_level
- old_level = RAILS_DEFAULT_LOGGER.level
- RAILS_DEFAULT_LOGGER.level = Logger::INFO
-
- Interlock.config[:log_level] = 'info'
- truncate
- Item.find(1)
- assert_match(/model.*Item:find:1:default is loading/, log)
-
- Interlock.config[:log_level] = 'debug'
- truncate
- Item.find(1)
- assert_no_match(/model.*Item:find:1:default is loading/, log)
- ensure
- RAILS_DEFAULT_LOGGER.level = old_level
- end
-
- def test_find_with_nonstandard_primary_key
- db = Book.find_via_db(1137)
- cache = Book.find(1137)
- assert_equal db, cache
- assert_equal Book.find_via_db(1137, 2001, :order => "guid ASC"), Book.find(1137, 2001)
- end
-
- ### Support methods
-
- def setup
- # Change the asset ID; has a similar effect to flushing memcached
- @old_asset_id = ENV['RAILS_ASSET_ID']
- ENV['RAILS_ASSET_ID'] = rand.to_s
- truncate
- end
-
- def teardown
- # Restore the asset id
- ENV['RAILS_ASSET_ID'] = @old_asset_id
- end
-
- def truncate
- system("> #{LOG}")
- end
-
- def log
- File.open(LOG, 'r') do |f|
- f.read
- end
- end
-end \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/unit/interlock_test.rb b/vendor/plugins/interlock/test/unit/interlock_test.rb
deleted file mode 100644
index 3ffb8128b..000000000
--- a/vendor/plugins/interlock/test/unit/interlock_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require "#{File.dirname(__FILE__)}/../test_helper"
-
-class InterlockTest < Test::Unit::TestCase
- def test_caching_key_requires_controller_and_action
- assert_raises ArgumentError do
- Interlock.caching_key nil, nil, nil, nil
- end
- end
-
- def test_caching_key_prevents_too_long_keys
- assert_equal Interlock::KEY_LENGTH_LIMIT,
- Interlock.caching_key('controller', 'action', 'id', 'x'*Interlock::KEY_LENGTH_LIMIT).size,
- "keys longer than #{Interlock::KEY_LENGTH_LIMIT} will result in errors from memcache-client"
- end
-
- def test_caching_key_strips_whitespace
- assert_no_match Interlock::ILLEGAL_KEY_CHARACTERS_PATTERN,
- Interlock.caching_key('controller', 'action', 'id', 'tag with illegal characters')
- 'generated keys should not contain illegal characters'
- end
-
- def disabled_test_register_dependencies_with_many_keys_one_dependency
- assert_nothing_raised do
- (1..5000).each do |i|
- Interlock.register_dependencies({Item=>:id}, Interlock.caching_key("a"*200, "show", i, nil))
- end
- end
- end
-
-end \ No newline at end of file
diff --git a/vendor/plugins/interlock/test/unit/lock_test.rb b/vendor/plugins/interlock/test/unit/lock_test.rb
deleted file mode 100644
index bd75ddfee..000000000
--- a/vendor/plugins/interlock/test/unit/lock_test.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-
-require "#{File.dirname(__FILE__)}/../test_helper"
-
-class LockTest < Test::Unit::TestCase
-
- KEY = "memcached_test"
- LOCK = "lock:#{KEY}"
-
- def setup
- CACHE.delete KEY
- CACHE.delete LOCK
- end
-
- def test_unlocked
- assert_nil CACHE.get(KEY)
- assert_nil CACHE.get(LOCK)
-
- assert_nothing_raised do
- CACHE.lock(KEY) { "A" }
- end
-
- assert_nil CACHE.get(LOCK)
- assert_equal("A", CACHE.get(KEY))
- end
-
- def test_locked
- CACHE.set LOCK, "Bogus"
-
- assert_raises Interlock::LockAcquisitionError do
- CACHE.lock(KEY, 30, 2) { "A" }
- end
-
- assert_equal("Bogus", CACHE.get(LOCK))
- assert_nil CACHE.get(KEY)
-end
-
- def test_ensure_lock_release
- assert_raises RuntimeError do
- CACHE.lock(KEY) { raise }
- end
-
- assert_nil CACHE.get(LOCK)
- end
-
-end