From e8b7b45eb3bf316446d33f39e4dbb528abf6bd24 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Mon, 8 Oct 2012 19:20:20 +0100 Subject: [PATCH 01/55] remove examples --- example/.gitignore | 4 - example/rails2.3/Gemfile | 27 - example/rails2.3/Gemfile.lock | 38 - example/rails2.3/README | 243 - example/rails2.3/Rakefile | 10 - .../app/controllers/application_controller.rb | 6 - .../app/controllers/page_controller.rb | 18 - .../app/helpers/application_helper.rb | 3 - .../app/views/layouts/application.html.erb | 16 - .../rails2.3/app/views/page/index.html.erb | 6 - .../rails2.3/app/views/page/test_a.html.erb | 11 - .../rails2.3/app/views/page/test_b.html.erb | 7 - example/rails2.3/config/boot.rb | 114 - example/rails2.3/config/database.yml | 22 - example/rails2.3/config/environment.rb | 41 - .../config/environments/development.rb | 17 - .../config/environments/production.rb | 28 - example/rails2.3/config/environments/test.rb | 28 - .../initializers/backtrace_silencers.rb | 7 - .../cookie_verification_secret.rb | 7 - .../config/initializers/inflections.rb | 10 - .../config/initializers/mime_types.rb | 5 - .../config/initializers/new_rails_defaults.rb | 21 - .../config/initializers/session_store.rb | 15 - example/rails2.3/config/locales/en.yml | 5 - example/rails2.3/config/routes.rb | 43 - example/rails2.3/db/development.sqlite3 | 0 example/rails2.3/db/seeds.rb | 7 - example/rails2.3/doc/README_FOR_APP | 2 - example/rails2.3/public/404.html | 30 - example/rails2.3/public/422.html | 30 - example/rails2.3/public/500.html | 30 - example/rails2.3/public/favicon.ico | 0 example/rails2.3/public/images/rails.png | Bin 6646 -> 0 bytes .../public/javascripts/application.js | 2 - .../rails2.3/public/javascripts/controls.js | 963 ---- .../rails2.3/public/javascripts/dragdrop.js | 973 ---- .../rails2.3/public/javascripts/effects.js | 1128 ---- .../rails2.3/public/javascripts/prototype.js | 4320 --------------- example/rails2.3/public/robots.txt | 5 - example/rails2.3/script/about | 4 - example/rails2.3/script/console | 3 - example/rails2.3/script/dbconsole | 3 - example/rails2.3/script/destroy | 3 - example/rails2.3/script/generate | 3 - .../rails2.3/script/performance/benchmarker | 3 - example/rails2.3/script/performance/profiler | 3 - example/rails2.3/script/plugin | 3 - example/rails2.3/script/runner | 3 - example/rails2.3/script/server | 3 - .../test/performance/browsing_test.rb | 9 - example/rails2.3/test/test_helper.rb | 38 - example/rails3.1/Gemfile | 33 - example/rails3.1/Gemfile.lock | 123 - example/rails3.1/README | 261 - example/rails3.1/Rakefile | 7 - example/rails3.1/app/assets/images/rails.png | Bin 6646 -> 0 bytes .../app/assets/javascripts/application.js | 9 - .../app/assets/stylesheets/application.css | 7 - .../app/controllers/application_controller.rb | 5 - .../app/controllers/page_controller.rb | 18 - .../app/helpers/application_helper.rb | 2 - example/rails3.1/app/mailers/.gitkeep | 0 example/rails3.1/app/models/.gitkeep | 0 .../app/views/layouts/application.html.erb | 17 - .../rails3.1/app/views/page/index.html.erb | 6 - .../rails3.1/app/views/page/test_a.html.erb | 11 - .../rails3.1/app/views/page/test_b.html.erb | 7 - example/rails3.1/config.ru | 4 - example/rails3.1/config/application.rb | 48 - example/rails3.1/config/boot.rb | 6 - example/rails3.1/config/database.yml | 25 - example/rails3.1/config/environment.rb | 5 - .../config/environments/development.rb | 30 - .../config/environments/production.rb | 60 - example/rails3.1/config/environments/test.rb | 42 - .../initializers/backtrace_silencers.rb | 7 - .../config/initializers/inflections.rb | 10 - .../config/initializers/mime_types.rb | 5 - .../config/initializers/secret_token.rb | 7 - .../config/initializers/session_store.rb | 8 - .../config/initializers/wrap_parameters.rb | 14 - example/rails3.1/config/locales/en.yml | 5 - example/rails3.1/config/routes.rb | 67 - example/rails3.1/db/development.sqlite3 | 0 example/rails3.1/db/seeds.rb | 7 - example/rails3.1/doc/README_FOR_APP | 2 - example/rails3.1/lib/assets/.gitkeep | 0 example/rails3.1/lib/tasks/.gitkeep | 0 example/rails3.1/public/404.html | 26 - example/rails3.1/public/422.html | 26 - example/rails3.1/public/500.html | 26 - example/rails3.1/public/favicon.ico | 0 example/rails3.1/public/robots.txt | 5 - example/rails3.1/script/rails | 6 - example/rails3.1/test/fixtures/.gitkeep | 0 example/rails3.1/test/functional/.gitkeep | 0 example/rails3.1/test/integration/.gitkeep | 0 .../test/performance/browsing_test.rb | 12 - example/rails3.1/test/test_helper.rb | 13 - example/rails3.1/test/unit/.gitkeep | 0 .../vendor/assets/stylesheets/.gitkeep | 0 example/rails3.1/vendor/plugins/.gitkeep | 0 example/rails3/Gemfile | 27 - example/rails3/Gemfile.lock | 84 - example/rails3/README | 244 - example/rails3/Rakefile | 10 - .../app/controllers/application_controller.rb | 6 - .../rails3/app/controllers/page_controller.rb | 18 - .../rails3/app/helpers/application_helper.rb | 2 - example/rails3/app/helpers/page_helper.rb | 2 - .../app/views/layouts/application.html.erb | 17 - example/rails3/app/views/page/index.html.erb | 6 - example/rails3/app/views/page/test_a.html.erb | 11 - example/rails3/app/views/page/test_b.html.erb | 7 - example/rails3/config.ru | 4 - example/rails3/config/analytical.yml | 30 - example/rails3/config/application.rb | 46 - example/rails3/config/boot.rb | 6 - example/rails3/config/database.yml | 22 - example/rails3/config/environment.rb | 7 - .../rails3/config/environments/development.rb | 21 - .../rails3/config/environments/production.rb | 42 - example/rails3/config/environments/test.rb | 32 - .../initializers/backtrace_silencers.rb | 7 - .../rails3/config/initializers/inflections.rb | 10 - .../rails3/config/initializers/mime_types.rb | 5 - .../config/initializers/secret_token.rb | 7 - .../config/initializers/session_store.rb | 8 - example/rails3/config/locales/en.yml | 5 - example/rails3/config/routes.rb | 63 - example/rails3/db/development.sqlite3 | 0 example/rails3/db/production.sqlite3 | 0 example/rails3/db/seeds.rb | 7 - example/rails3/doc/README_FOR_APP | 2 - example/rails3/lib/tasks/.gitkeep | 0 example/rails3/public/404.html | 26 - example/rails3/public/422.html | 26 - example/rails3/public/500.html | 26 - example/rails3/public/favicon.ico | 0 example/rails3/public/images/rails.png | Bin 6646 -> 0 bytes .../rails3/public/javascripts/application.js | 2 - example/rails3/public/javascripts/controls.js | 965 ---- example/rails3/public/javascripts/dragdrop.js | 974 ---- example/rails3/public/javascripts/effects.js | 1123 ---- .../rails3/public/javascripts/prototype.js | 4874 ----------------- example/rails3/public/javascripts/rails.js | 118 - example/rails3/public/robots.txt | 5 - example/rails3/public/stylesheets/.gitkeep | 0 example/rails3/script/rails | 9 - .../test/functional/page_controller_test.rb | 19 - .../rails3/test/performance/browsing_test.rb | 9 - example/rails3/test/test_helper.rb | 13 - .../test/unit/helpers/page_helper_test.rb | 4 - example/rails3/vendor/plugins/.gitkeep | 0 155 files changed, 18243 deletions(-) delete mode 100644 example/.gitignore delete mode 100644 example/rails2.3/Gemfile delete mode 100644 example/rails2.3/Gemfile.lock delete mode 100644 example/rails2.3/README delete mode 100644 example/rails2.3/Rakefile delete mode 100644 example/rails2.3/app/controllers/application_controller.rb delete mode 100644 example/rails2.3/app/controllers/page_controller.rb delete mode 100644 example/rails2.3/app/helpers/application_helper.rb delete mode 100644 example/rails2.3/app/views/layouts/application.html.erb delete mode 100644 example/rails2.3/app/views/page/index.html.erb delete mode 100644 example/rails2.3/app/views/page/test_a.html.erb delete mode 100644 example/rails2.3/app/views/page/test_b.html.erb delete mode 100644 example/rails2.3/config/boot.rb delete mode 100644 example/rails2.3/config/database.yml delete mode 100644 example/rails2.3/config/environment.rb delete mode 100644 example/rails2.3/config/environments/development.rb delete mode 100644 example/rails2.3/config/environments/production.rb delete mode 100644 example/rails2.3/config/environments/test.rb delete mode 100644 example/rails2.3/config/initializers/backtrace_silencers.rb delete mode 100644 example/rails2.3/config/initializers/cookie_verification_secret.rb delete mode 100644 example/rails2.3/config/initializers/inflections.rb delete mode 100644 example/rails2.3/config/initializers/mime_types.rb delete mode 100644 example/rails2.3/config/initializers/new_rails_defaults.rb delete mode 100644 example/rails2.3/config/initializers/session_store.rb delete mode 100644 example/rails2.3/config/locales/en.yml delete mode 100644 example/rails2.3/config/routes.rb delete mode 100644 example/rails2.3/db/development.sqlite3 delete mode 100644 example/rails2.3/db/seeds.rb delete mode 100644 example/rails2.3/doc/README_FOR_APP delete mode 100644 example/rails2.3/public/404.html delete mode 100644 example/rails2.3/public/422.html delete mode 100644 example/rails2.3/public/500.html delete mode 100644 example/rails2.3/public/favicon.ico delete mode 100644 example/rails2.3/public/images/rails.png delete mode 100644 example/rails2.3/public/javascripts/application.js delete mode 100644 example/rails2.3/public/javascripts/controls.js delete mode 100644 example/rails2.3/public/javascripts/dragdrop.js delete mode 100644 example/rails2.3/public/javascripts/effects.js delete mode 100644 example/rails2.3/public/javascripts/prototype.js delete mode 100644 example/rails2.3/public/robots.txt delete mode 100755 example/rails2.3/script/about delete mode 100755 example/rails2.3/script/console delete mode 100755 example/rails2.3/script/dbconsole delete mode 100755 example/rails2.3/script/destroy delete mode 100755 example/rails2.3/script/generate delete mode 100755 example/rails2.3/script/performance/benchmarker delete mode 100755 example/rails2.3/script/performance/profiler delete mode 100755 example/rails2.3/script/plugin delete mode 100755 example/rails2.3/script/runner delete mode 100755 example/rails2.3/script/server delete mode 100644 example/rails2.3/test/performance/browsing_test.rb delete mode 100644 example/rails2.3/test/test_helper.rb delete mode 100644 example/rails3.1/Gemfile delete mode 100644 example/rails3.1/Gemfile.lock delete mode 100644 example/rails3.1/README delete mode 100644 example/rails3.1/Rakefile delete mode 100644 example/rails3.1/app/assets/images/rails.png delete mode 100644 example/rails3.1/app/assets/javascripts/application.js delete mode 100644 example/rails3.1/app/assets/stylesheets/application.css delete mode 100644 example/rails3.1/app/controllers/application_controller.rb delete mode 100644 example/rails3.1/app/controllers/page_controller.rb delete mode 100644 example/rails3.1/app/helpers/application_helper.rb delete mode 100644 example/rails3.1/app/mailers/.gitkeep delete mode 100644 example/rails3.1/app/models/.gitkeep delete mode 100644 example/rails3.1/app/views/layouts/application.html.erb delete mode 100644 example/rails3.1/app/views/page/index.html.erb delete mode 100644 example/rails3.1/app/views/page/test_a.html.erb delete mode 100644 example/rails3.1/app/views/page/test_b.html.erb delete mode 100644 example/rails3.1/config.ru delete mode 100644 example/rails3.1/config/application.rb delete mode 100644 example/rails3.1/config/boot.rb delete mode 100644 example/rails3.1/config/database.yml delete mode 100644 example/rails3.1/config/environment.rb delete mode 100644 example/rails3.1/config/environments/development.rb delete mode 100644 example/rails3.1/config/environments/production.rb delete mode 100644 example/rails3.1/config/environments/test.rb delete mode 100644 example/rails3.1/config/initializers/backtrace_silencers.rb delete mode 100644 example/rails3.1/config/initializers/inflections.rb delete mode 100644 example/rails3.1/config/initializers/mime_types.rb delete mode 100644 example/rails3.1/config/initializers/secret_token.rb delete mode 100644 example/rails3.1/config/initializers/session_store.rb delete mode 100644 example/rails3.1/config/initializers/wrap_parameters.rb delete mode 100644 example/rails3.1/config/locales/en.yml delete mode 100644 example/rails3.1/config/routes.rb delete mode 100644 example/rails3.1/db/development.sqlite3 delete mode 100644 example/rails3.1/db/seeds.rb delete mode 100644 example/rails3.1/doc/README_FOR_APP delete mode 100644 example/rails3.1/lib/assets/.gitkeep delete mode 100644 example/rails3.1/lib/tasks/.gitkeep delete mode 100644 example/rails3.1/public/404.html delete mode 100644 example/rails3.1/public/422.html delete mode 100644 example/rails3.1/public/500.html delete mode 100644 example/rails3.1/public/favicon.ico delete mode 100644 example/rails3.1/public/robots.txt delete mode 100755 example/rails3.1/script/rails delete mode 100644 example/rails3.1/test/fixtures/.gitkeep delete mode 100644 example/rails3.1/test/functional/.gitkeep delete mode 100644 example/rails3.1/test/integration/.gitkeep delete mode 100644 example/rails3.1/test/performance/browsing_test.rb delete mode 100644 example/rails3.1/test/test_helper.rb delete mode 100644 example/rails3.1/test/unit/.gitkeep delete mode 100644 example/rails3.1/vendor/assets/stylesheets/.gitkeep delete mode 100644 example/rails3.1/vendor/plugins/.gitkeep delete mode 100644 example/rails3/Gemfile delete mode 100644 example/rails3/Gemfile.lock delete mode 100644 example/rails3/README delete mode 100644 example/rails3/Rakefile delete mode 100644 example/rails3/app/controllers/application_controller.rb delete mode 100644 example/rails3/app/controllers/page_controller.rb delete mode 100644 example/rails3/app/helpers/application_helper.rb delete mode 100644 example/rails3/app/helpers/page_helper.rb delete mode 100644 example/rails3/app/views/layouts/application.html.erb delete mode 100644 example/rails3/app/views/page/index.html.erb delete mode 100644 example/rails3/app/views/page/test_a.html.erb delete mode 100644 example/rails3/app/views/page/test_b.html.erb delete mode 100644 example/rails3/config.ru delete mode 100644 example/rails3/config/analytical.yml delete mode 100644 example/rails3/config/application.rb delete mode 100644 example/rails3/config/boot.rb delete mode 100644 example/rails3/config/database.yml delete mode 100644 example/rails3/config/environment.rb delete mode 100644 example/rails3/config/environments/development.rb delete mode 100644 example/rails3/config/environments/production.rb delete mode 100644 example/rails3/config/environments/test.rb delete mode 100644 example/rails3/config/initializers/backtrace_silencers.rb delete mode 100644 example/rails3/config/initializers/inflections.rb delete mode 100644 example/rails3/config/initializers/mime_types.rb delete mode 100644 example/rails3/config/initializers/secret_token.rb delete mode 100644 example/rails3/config/initializers/session_store.rb delete mode 100644 example/rails3/config/locales/en.yml delete mode 100644 example/rails3/config/routes.rb delete mode 100644 example/rails3/db/development.sqlite3 delete mode 100644 example/rails3/db/production.sqlite3 delete mode 100644 example/rails3/db/seeds.rb delete mode 100644 example/rails3/doc/README_FOR_APP delete mode 100644 example/rails3/lib/tasks/.gitkeep delete mode 100644 example/rails3/public/404.html delete mode 100644 example/rails3/public/422.html delete mode 100644 example/rails3/public/500.html delete mode 100644 example/rails3/public/favicon.ico delete mode 100644 example/rails3/public/images/rails.png delete mode 100644 example/rails3/public/javascripts/application.js delete mode 100644 example/rails3/public/javascripts/controls.js delete mode 100644 example/rails3/public/javascripts/dragdrop.js delete mode 100644 example/rails3/public/javascripts/effects.js delete mode 100644 example/rails3/public/javascripts/prototype.js delete mode 100644 example/rails3/public/javascripts/rails.js delete mode 100644 example/rails3/public/robots.txt delete mode 100644 example/rails3/public/stylesheets/.gitkeep delete mode 100755 example/rails3/script/rails delete mode 100644 example/rails3/test/functional/page_controller_test.rb delete mode 100644 example/rails3/test/performance/browsing_test.rb delete mode 100644 example/rails3/test/test_helper.rb delete mode 100644 example/rails3/test/unit/helpers/page_helper_test.rb delete mode 100644 example/rails3/vendor/plugins/.gitkeep diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index af64fae5..00000000 --- a/example/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.bundle -db/*.sqlite3 -log/*.log -tmp/**/* diff --git a/example/rails2.3/Gemfile b/example/rails2.3/Gemfile deleted file mode 100644 index 04b70adc..00000000 --- a/example/rails2.3/Gemfile +++ /dev/null @@ -1,27 +0,0 @@ -source 'http://rubygems.org' - -gem 'rails', '~> 2.3.0' - -# Bundle edge Rails instead: -# gem 'rails', :git => 'git://github.com/rails/rails.git' - -gem 'sqlite3-ruby', :require => 'sqlite3' -gem 'analytical', :path => '../..' - -# Use unicorn as the web server -# gem 'unicorn' - -# Deploy with Capistrano -# gem 'capistrano' - -# Bundle the extra gems: -# gem 'bj' -# gem 'nokogiri', '1.4.1' -# gem 'sqlite3-ruby', :require => 'sqlite3' -# gem 'aws-s3', :require => 'aws/s3' - -# Bundle gems for certain environments: -# gem 'rspec', :group => :test -# group :test do -# gem 'webrat' -# end diff --git a/example/rails2.3/Gemfile.lock b/example/rails2.3/Gemfile.lock deleted file mode 100644 index b4165e00..00000000 --- a/example/rails2.3/Gemfile.lock +++ /dev/null @@ -1,38 +0,0 @@ -PATH - remote: ../.. - specs: - analytical (3.0.8) - -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) - rack (1.1.2) - 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) - rake (0.9.2) - sqlite3 (1.3.4) - sqlite3-ruby (1.3.3) - sqlite3 (>= 1.3.3) - -PLATFORMS - ruby - -DEPENDENCIES - analytical! - rails (~> 2.3.0) - sqlite3-ruby diff --git a/example/rails2.3/README b/example/rails2.3/README deleted file mode 100644 index 37ec8ea2..00000000 --- a/example/rails2.3/README +++ /dev/null @@ -1,243 +0,0 @@ -== Welcome to Rails - -Rails is a web-application framework that includes everything needed to create -database-backed web applications according to the Model-View-Control pattern. - -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 -2. Change directory into myapp and start the web server: script/server (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 if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails -with a variety of other web servers. - -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: gem install mongrel. -More info at: http://mongrel.rubyforge.org - -Say other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or -Lighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use -FCGI or proxy to a pack of Mongrels/Thin/Ebb servers. - -== Apache .htaccess example for FCGI/CGI - -# 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 "

Application error

Rails application failed to start properly" - - -== 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! -You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug' -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 - => "[#nil, \"body\"=>nil, \"id\"=>\"1\"}>, - #\"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 - => #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 script/console. -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 script/console production. - -To reload your controllers and models after launching the console run reload! - -== dbconsole - -You can go to the command line of your database directly through script/dbconsole. -You would be connected to the database with the credentials defined in database.yml. -Starting the script without arguments will connect you to the development database. Passing an -argument will connect you to a different database, like script/dbconsole production. -Currently works for mysql, postgresql and sqlite. - -== 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.html.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 - layout :default and create a file named default.html.erb. Inside default.html.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 rake doc:app - -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. - If the app has frozen rails, those gems also go here, under vendor/rails/. - This directory is in the load path. diff --git a/example/rails2.3/Rakefile b/example/rails2.3/Rakefile deleted file mode 100644 index 3bb0e859..00000000 --- a/example/rails2.3/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/example/rails2.3/app/controllers/application_controller.rb b/example/rails2.3/app/controllers/application_controller.rb deleted file mode 100644 index 8b139794..00000000 --- a/example/rails2.3/app/controllers/application_controller.rb +++ /dev/null @@ -1,6 +0,0 @@ -class ApplicationController < ActionController::Base - helper :all - protect_from_forgery - - analytical :modules=>[:console, :google, :clicky], :disable_if=>lambda{|controller| false}, :use_session_store=>true -end diff --git a/example/rails2.3/app/controllers/page_controller.rb b/example/rails2.3/app/controllers/page_controller.rb deleted file mode 100644 index 1e9184b3..00000000 --- a/example/rails2.3/app/controllers/page_controller.rb +++ /dev/null @@ -1,18 +0,0 @@ -class PageController < ApplicationController - def index - end - - def test_a - end - - def test_b - analytical.clicky.track('track link in A') - analytical.track 'track in controller' - end - - def test_c - analytical.track 'track in controller that redirects' - redirect_to root_path - end - -end diff --git a/example/rails2.3/app/helpers/application_helper.rb b/example/rails2.3/app/helpers/application_helper.rb deleted file mode 100644 index 22a7940e..00000000 --- a/example/rails2.3/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/example/rails2.3/app/views/layouts/application.html.erb b/example/rails2.3/app/views/layouts/application.html.erb deleted file mode 100644 index d1d537e0..00000000 --- a/example/rails2.3/app/views/layouts/application.html.erb +++ /dev/null @@ -1,16 +0,0 @@ - - - - Example - <%= stylesheet_link_tag :all %> - <%= javascript_include_tag :defaults %> - - <% analytical.identify '5', :email=>'josh@feefighters.com' %> - <%= analytical.head_javascript %> - - - <%= analytical.body_prepend_javascript %> - <%= yield %> - <%= analytical.body_append_javascript %> - - diff --git a/example/rails2.3/app/views/page/index.html.erb b/example/rails2.3/app/views/page/index.html.erb deleted file mode 100644 index cdaec125..00000000 --- a/example/rails2.3/app/views/page/index.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -

Page#index

-

- <%= link_to 'A', '/page/test_a' %>
- <%= link_to 'B', '/page/test_b' %>
- <%= link_to 'C', '/page/test_c' %>
-

\ No newline at end of file diff --git a/example/rails2.3/app/views/page/test_a.html.erb b/example/rails2.3/app/views/page/test_a.html.erb deleted file mode 100644 index 19b6c223..00000000 --- a/example/rails2.3/app/views/page/test_a.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -<% analytical.track 'track in view A' %> -<% analytical.event 'Something Important', {:some=>'data'} %> -

Page#test_a

-

- <%= link_to 'B', '/page/test_b' %>
- <%= link_to 'C', '/page/test_c' %>
- <%= link_to 'index', '/' %>
- click me to track all modules
-

- - diff --git a/example/rails2.3/app/views/page/test_b.html.erb b/example/rails2.3/app/views/page/test_b.html.erb deleted file mode 100644 index 9b4f03ab..00000000 --- a/example/rails2.3/app/views/page/test_b.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -

Page#test_b

-

- <%= link_to 'A', '/page/test_a' %>
- <%= link_to 'C', '/page/test_c' %>
- <%= link_to 'index', '/' %>
- track console only
-

diff --git a/example/rails2.3/config/boot.rb b/example/rails2.3/config/boot.rb deleted file mode 100644 index 6686664c..00000000 --- a/example/rails2.3/config/boot.rb +++ /dev/null @@ -1,114 +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.exist?(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" - 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! diff --git a/example/rails2.3/config/database.yml b/example/rails2.3/config/database.yml deleted file mode 100644 index 025d62a8..00000000 --- a/example/rails2.3/config/database.yml +++ /dev/null @@ -1,22 +0,0 @@ -# SQLite version 3.x -# gem install sqlite3-ruby (not necessary on OS X Leopard) -development: - adapter: sqlite3 - database: db/development.sqlite3 - pool: 5 - timeout: 5000 - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - adapter: sqlite3 - database: db/test.sqlite3 - pool: 5 - timeout: 5000 - -production: - adapter: sqlite3 - database: db/production.sqlite3 - pool: 5 - timeout: 5000 diff --git a/example/rails2.3/config/environment.rb b/example/rails2.3/config/environment.rb deleted file mode 100644 index f47528a2..00000000 --- a/example/rails2.3/config/environment.rb +++ /dev/null @@ -1,41 +0,0 @@ -# Be sure to restart your server when you modify this file - -# 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') - -Rails::Initializer.run do |config| - # 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. - - # Add additional load paths for your own custom dirs - # config.autoload_paths += %W( #{RAILS_ROOT}/extras ) - - # Specify gems that this application depends on and have them installed with rake gems:install - # config.gem "bj" - # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net" - # config.gem "sqlite3-ruby", :lib => "sqlite3" - # config.gem "aws-s3", :lib => "aws/s3" - - # 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 ] - - # Skip frameworks you're not going to use. To use Rails without a database, - # you must remove the Active Record framework. - # config.frameworks -= [ :active_record, :active_resource, :action_mailer ] - - # 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. - config.time_zone = 'UTC' - - # 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}')] - # config.i18n.default_locale = :de -end \ No newline at end of file diff --git a/example/rails2.3/config/environments/development.rb b/example/rails2.3/config/environments/development.rb deleted file mode 100644 index 85c9a608..00000000 --- a/example/rails2.3/config/environments/development.rb +++ /dev/null @@ -1,17 +0,0 @@ -# Settings specified here will take precedence over those in config/environment.rb - -# 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 - -# Show full error reports and disable caching -config.action_controller.consider_all_requests_local = true -config.action_view.debug_rjs = true -config.action_controller.perform_caching = false - -# Don't care if the mailer can't send -config.action_mailer.raise_delivery_errors = false \ No newline at end of file diff --git a/example/rails2.3/config/environments/production.rb b/example/rails2.3/config/environments/production.rb deleted file mode 100644 index 27119d2d..00000000 --- a/example/rails2.3/config/environments/production.rb +++ /dev/null @@ -1,28 +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 - -# Full error reports are disabled and caching is turned on -config.action_controller.consider_all_requests_local = false -config.action_controller.perform_caching = true -config.action_view.cache_template_loading = true - -# See everything in the log (default is :info) -# config.log_level = :debug - -# Use a different logger for distributed setups -# config.logger = SyslogLogger.new - -# Use a different cache store in production -# config.cache_store = :mem_cache_store - -# 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 - -# Enable threaded mode -# config.threadsafe! \ No newline at end of file diff --git a/example/rails2.3/config/environments/test.rb b/example/rails2.3/config/environments/test.rb deleted file mode 100644 index d6f80a40..00000000 --- a/example/rails2.3/config/environments/test.rb +++ /dev/null @@ -1,28 +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 -config.action_view.cache_template_loading = true - -# Disable request forgery protection in test environment -config.action_controller.allow_forgery_protection = false - -# Tell Action Mailer 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 - -# 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 \ No newline at end of file diff --git a/example/rails2.3/config/initializers/backtrace_silencers.rb b/example/rails2.3/config/initializers/backtrace_silencers.rb deleted file mode 100644 index c2169ed0..00000000 --- a/example/rails2.3/config/initializers/backtrace_silencers.rb +++ /dev/null @@ -1,7 +0,0 @@ -# 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 do debug a problem that might steem from framework code. -# Rails.backtrace_cleaner.remove_silencers! \ No newline at end of file diff --git a/example/rails2.3/config/initializers/cookie_verification_secret.rb b/example/rails2.3/config/initializers/cookie_verification_secret.rb deleted file mode 100644 index ef7221c5..00000000 --- a/example/rails2.3/config/initializers/cookie_verification_secret.rb +++ /dev/null @@ -1,7 +0,0 @@ -# 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. -ActionController::Base.cookie_verifier_secret = '9f8775eb823fb91b025741105b96651584bd80dca1c1b53e93ddb30e0c093bf3a2fed5326d90f516903dc2b32dc29872b2e380e706cdff5972d6820a7ce3f1fb'; diff --git a/example/rails2.3/config/initializers/inflections.rb b/example/rails2.3/config/initializers/inflections.rb deleted file mode 100644 index d531b8bb..00000000 --- a/example/rails2.3/config/initializers/inflections.rb +++ /dev/null @@ -1,10 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Add new inflection rules using the following format -# (all these examples are active by default): -# ActiveSupport::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 diff --git a/example/rails2.3/config/initializers/mime_types.rb b/example/rails2.3/config/initializers/mime_types.rb deleted file mode 100644 index 72aca7e4..00000000 --- a/example/rails2.3/config/initializers/mime_types.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Add new mime types for use in respond_to blocks: -# Mime::Type.register "text/richtext", :rtf -# Mime::Type.register_alias "text/html", :iphone diff --git a/example/rails2.3/config/initializers/new_rails_defaults.rb b/example/rails2.3/config/initializers/new_rails_defaults.rb deleted file mode 100644 index c94db0a6..00000000 --- a/example/rails2.3/config/initializers/new_rails_defaults.rb +++ /dev/null @@ -1,21 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# These settings change the behavior of Rails 2 apps and will be defaults -# for Rails 3. You can remove this initializer when Rails 3 is released. - -if defined?(ActiveRecord) - # Include Active Record class name as root for JSON serialized output. - ActiveRecord::Base.include_root_in_json = true - - # Store the full class name (including module namespace) in STI type column. - ActiveRecord::Base.store_full_sti_class = true -end - -ActionController::Routing.generate_best_match = false - -# Use ISO 8601 format for JSON serialized times and dates. -ActiveSupport.use_standard_json_time_format = true - -# Don't escape HTML entities in JSON, leave that for the #json_escape helper. -# if you're including raw json in an HTML page. -ActiveSupport.escape_html_entities_in_json = false \ No newline at end of file diff --git a/example/rails2.3/config/initializers/session_store.rb b/example/rails2.3/config/initializers/session_store.rb deleted file mode 100644 index 33a26949..00000000 --- a/example/rails2.3/config/initializers/session_store.rb +++ /dev/null @@ -1,15 +0,0 @@ -# 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 => '_example_session', - :secret => '857713e3e2406c40a81798264d0523a082862e124f5b7998ff6c8af1358f58b54e86f0a0793d252d596c9cbbba2c38fd7c08c475977684aaeb58f75872c51850' -} - -# Use the database for sessions instead of the cookie-based default, -# which shouldn't be used to store highly confidential information -# (create the session table with "rake db:sessions:create") -# ActionController::Base.session_store = :active_record_store diff --git a/example/rails2.3/config/locales/en.yml b/example/rails2.3/config/locales/en.yml deleted file mode 100644 index f265c068..00000000 --- a/example/rails2.3/config/locales/en.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Sample localization file for English. Add more files in this directory for other locales. -# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. - -en: - hello: "Hello world" \ No newline at end of file diff --git a/example/rails2.3/config/routes.rb b/example/rails2.3/config/routes.rb deleted file mode 100644 index 3eed029b..00000000 --- a/example/rails2.3/config/routes.rb +++ /dev/null @@ -1,43 +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 with more complex sub-resources - # map.resources :products do |products| - # products.resources :comments - # products.resources :sales, :collection => { :recent => :get } - # end - - # 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 => "page" - - # See how all your routes lay out with "rake routes" - - # Install the default routes as the lowest priority. - # Note: These default routes make all actions in every controller accessible via GET requests. You should - # consider removing or commenting them out if you're using named routes and resources. - map.connect ':controller/:action/:id' - map.connect ':controller/:action/:id.:format' -end diff --git a/example/rails2.3/db/development.sqlite3 b/example/rails2.3/db/development.sqlite3 deleted file mode 100644 index e69de29b..00000000 diff --git a/example/rails2.3/db/seeds.rb b/example/rails2.3/db/seeds.rb deleted file mode 100644 index 3174d0cb..00000000 --- a/example/rails2.3/db/seeds.rb +++ /dev/null @@ -1,7 +0,0 @@ -# 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' }]) -# Major.create(:name => 'Daley', :city => cities.first) diff --git a/example/rails2.3/doc/README_FOR_APP b/example/rails2.3/doc/README_FOR_APP deleted file mode 100644 index fe41f5cc..00000000 --- a/example/rails2.3/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/example/rails2.3/public/404.html b/example/rails2.3/public/404.html deleted file mode 100644 index eff660b9..00000000 --- a/example/rails2.3/public/404.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - The page you were looking for doesn't exist (404) - - - - - -
-

The page you were looking for doesn't exist.

-

You may have mistyped the address or the page may have moved.

-
- - \ No newline at end of file diff --git a/example/rails2.3/public/422.html b/example/rails2.3/public/422.html deleted file mode 100644 index b54e4a3c..00000000 --- a/example/rails2.3/public/422.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - The change you wanted was rejected (422) - - - - - -
-

The change you wanted was rejected.

-

Maybe you tried to change something you didn't have access to.

-
- - \ No newline at end of file diff --git a/example/rails2.3/public/500.html b/example/rails2.3/public/500.html deleted file mode 100644 index ec3bbf02..00000000 --- a/example/rails2.3/public/500.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - We're sorry, but something went wrong (500) - - - - - -
-

We're sorry, but something went wrong.

-

We've been notified about this issue and we'll take a look at it shortly.

-
- - diff --git a/example/rails2.3/public/favicon.ico b/example/rails2.3/public/favicon.ico deleted file mode 100644 index e69de29b..00000000 diff --git a/example/rails2.3/public/images/rails.png b/example/rails2.3/public/images/rails.png deleted file mode 100644 index d5edc04e65f555e3ba4dcdaad39dc352e75b575e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6646 zcmVpVcQya!6@Dsmj@#jv7C*qh zIhOJ6_K0n?*d`*T7TDuW-}m`9Kz3~>+7`DUkbAraU%yi+R{N~~XA2B%zt-4=tLimUer9!2M~N{G5bftFij_O&)a zsHnOppFIzebQ`RA0$!yUM-lg#*o@_O2wf422iLnM6cU(ktYU8#;*G!QGhIy9+ZfzKjLuZo%@a z-i@9A`X%J{^;2q&ZHY3C(B%gqCPW!8{9C0PMcNZccefK){s|V5-xxtHQc@uf>XqhD z7#N^siWqetgq29aX>G^olMf=bbRF6@Y(}zYxw6o!9WBdG1unP}<(V;zKlcR2p86fq zYjaqB^;Ycq>Wy@5T1xOzG3tucG3e%nPvajaN{CrFbnzv^9&K3$NrDm*eQe4`BGQ2bI;dFEwyt>hK%X!L6)82aOZp zsrGcJ#7PoX7)s|~t6is?FfX*7vWdREi58tiY4S)t6u*|kv?J)d_$r+CH#eZ?Ef+I_ z(eVlX8dh~4QP?o*E`_MgaNFIKj*rtN(0Raj3ECjSXcWfd#27NYs&~?t`QZFT}!Zaf=ldZIhi}LhQlqLo+o5(Pvui&{7PD__^53f9j>HW`Q z_V8X5j~$|GP9qXu0C#!@RX2}lXD35@3N5{BkUi%jtaPQ*H6OX2zIz4QPuqmTv3`vG{zc>l3t0B9E75h< z8&twGh%dp7WPNI+tRl%#gf2}Epg8st+~O4GjtwJsXfN;EjAmyr6z5dnaFU(;IV~QK zW62fogF~zA``(Q>_SmD!izc6Y4zq*97|NAPHp1j5X7Op2%;GLYm>^HEMyObo6s7l) zE3n|aOHi5~B84!}b^b*-aL2E)>OEJX_tJ~t<#VJ?bT?lDwyDB&5SZ$_1aUhmAY}#* zs@V1I+c5md9%R-o#_DUfqVtRk>59{+Opd5Yu%dAU#VQW}^m}x-30ftBx#527{^pI4 z6l2C6C7QBG$~NLYb3rVdLD#Z{+SleOp`(Lg5J}`kxdTHe(nV5BdpLrD=l|)e$gEqA zwI6vuX-PFCtcDIH>bGY2dwq&^tf+&R?)nY-@7_j%4CMRAF}C9w%p86W<2!aSY$p+k zrkFtG=cGo38RnrG28;?PNk%7a@faaXq&MS*&?1Z`7Ojw7(#>}ZG4nMAs3VXxfdW>i zY4VX02c5;f7jDPY_7@Oa)CHH}cH<3y#}_!nng^W+h1e-RL*YFYOteC@h?BtJZ+?sE zy)P5^8Mregx{nQaw1NY-|3>{Z)|0`?zc?G2-acYiSU`tj#sSGfm7k86ZQ0SQgPevcklHxM9<~4yW zR796sisf1|!#{Z=e^)0;_8iUhL8g(;j$l=02FTPZ(dZV@s#aQ`DHkLM6=YsbE4iQ!b#*374l0Jw5;jD%J;vQayq=nD8-kHI~f9Ux|32SJUM`> zGp2UGK*4t?cRKi!2he`zI#j0f${I#f-jeT?u_C7S4WsA0)ryi-1L0(@%pa^&g5x=e z=KW9+Nn(=)1T&S8g_ug%dgk*~l2O-$r9#zEGBdQsweO%t*6F4c8JC36JtTizCyy+E4h%G(+ z5>y$%0txMuQ$e~wjFgN(xrAndHQo`Za+K*?gUVDTBV&Ap^}|{w#CIq{DRe}+l@(Ec zCCV6f_?dY_{+f{}6XGn!pL_up?}@>KijT^$w#Lb6iHW&^8RP~g6y=vZBXx~B9nI^i zGexaPjcd(%)zGw!DG_dDwh-7x6+ST#R^${iz_M$uM!da8SxgB_;Z0G%Y*HpvLjKw; zX=ir7i1O$-T|*TBoH$dlW+TLf5j5sep^DlDtkox;Kg{Q%EXWedJq@J@%VAcK)j3y1 zShM!CS#qax;D@RND%2t3W6kv+#Ky0F9<3YKDbV^XJ=^$s(Vtza8V72YY)577nnldI zHMA0PUo!F3j(ubV*CM@PiK<^|RM2(DuCbG7`W}Rg(xdYC>C~ z;1KJGLN&$cRxSZunjXcntykmpFJ7;dk>shY(DdK&3K_JDJ6R%D`e~6Qv67@Rwu+q9 z*|NG{r}4F8f{Dfzt0+cZMd$fvlX3Q`dzM46@r?ISxr;9gBTG2rmfiGOD*#c*3f)cc zF+PFZobY$-^}J8 z%n=h4;x2}cP!@SiVd!v;^Wwo0(N??-ygDr7gG^NKxDjSo{5T{?$|Qo5;8V!~D6O;F*I zuY!gd@+2j_8Rn=UWDa#*4E2auWoGYDddMW7t0=yuC(xLWky?vLimM~!$3fgu!dR>p z?L?!8z>6v$|MsLb&dU?ob)Zd!B)!a*Z2eTE7 zKCzP&e}XO>CT%=o(v+WUY`Az*`9inbTG& z_9_*oQKw;sc8{ipoBC`S4Tb7a%tUE)1fE+~ib$;|(`|4QbXc2>VzFi%1nX%ti;^s3~NIL0R}!!a{0A zyCRp0F7Y&vcP&3`&Dzv5!&#h}F2R-h&QhIfq*ts&qO13{_CP}1*sLz!hI9VoTSzTu zok5pV0+~jrGymE~{TgbS#nN5+*rF7ij)cnSLQw0Ltc70zmk|O!O(kM<3zw-sUvkx~ z2`y+{xAwKSa-0}n7{$I@Zop7CWy%_xIeN1e-7&OjQ6vZZPbZ^3_ z(~=;ZSP98S2oB#35b1~_x`2gWiPdIVddEf`AD9<@c_s)TM;3J$T_l?pr{<7PTgdiy zBc5IGx)g~n=s+Z$RzYCmv8PlJu%gkh^;%mTGMc)UwRINVD~K;`Rl!5@hhGg;y>5qj zq|u-Yf0q_~Y+Mbivkkfa0nAOzB1acnytogsj_m7FB(-FjihMek#GAU4M!iXCgdK8a zjoKm?*|iz7;dHm4$^hh(`Ufl>yb>$hjIA-;>{>C}G0Di%bGvUsJkfLAV|xq32c>RqJqTBJ3Dx zYC;*Dt|S$b6)aCJFnK(Eey$M1DpVV~_MIhwK> zygo(jWC|_IRw|456`roEyXtkNLWNAt-4N1qyN$I@DvBzt;e|?g<*HK1%~cq|^u*}C zmMrwh>{QAq?Ar~4l^DqT%SQ)w)FA(#7#u+N;>E975rYML>)LgE`2<7nN=C1pC{IkV zVw}_&v6j&S?QVh*)wF3#XmE@0($^BVl1969csLKUBNer{suVd!a~B!0MxWY?=(GD6 zy$G&ERFR#i6G4=2F?R4}Mz3B?3tnpoX3)qFF2sh9-Jn*e%9F>i{WG7$_~XyOO2!+@ z6k+38KyD@-0=uee54D0!Z1@B^ilj~StchdOn(*qvg~s5QJpWGc!6U^Aj!xt-HZn_V zS%|fyQ5YS@EP2lBIodXCLjG_+a)%En+7jzngk@J>6D~^xbxKkvf-R0-c%mX+o{?&j zZZ%RxFeav8Y0gkwtdtrwUb-i0Egd2C=ADu%w5VV-hNJvl)GZ?M;y$!?b=S+wKRK7Q zcOjPT!p<*#8m;TsBih=@Xc&c)?Vy`Ys>IvK@|1%N+M6J-^RCRaZcPP2eQh9DEGZr+ z?8B~wF14mk4Xkuen{wY^CWwS1PI<8gikY*)3?RSo5l8es4*J z43k_BIwc}of=6Pfs%xIxlMDGOJN zvl!a>G)52XMqA%fbgkZi%)%bN*ZzZw2!rn4@+J)2eK#kWuEW{)W~-`y1vhA5-7p%R z&f5N!a9f8cK1Xa=O}=9{wg%}Ur^+8Y(!UCeqw>%wj@|bYHD-bZO~mk3L$9_^MmF3G zvCiK^e@q6G?tHkM8%GqsBMZaB20W$UEt_5r~jc#WlR>Bv{6W>A=!#InoY zLOd04@Rz?*7PpW8u|+}bt`?+Z(GsX{Br4A2$ZZ(26Degmr9`O=t2KgHTL*==R3xcP z&Y(J7hC@6_x8zVz!CX3l4Xtss6i7r#E6kXMNN1~>9KTRzewfp))ij%)SBBl0fZdYP zd!zzQD5u8yk-u|41|Rqz7_tCFUMThZJVj)yQf6^Cwtn|Ew6cm5J|u1Bq>MWX-AfB&NE;C z62@=-0le`E6-CurMKjoIy)BuUmhMGJb}pPx!@GLWMT+wH2R?wA=MEy)o57~feFp8P zY@YXAyt4<1FD<|iw{FGQu~GEI<4C64)V*QiVk+VzOV^9GWf4ir#oYgHJz!wq>iZV#_6@_{)&lum)4x z_Of*CLVQ7wdT#XT-(h0qH%mcIF7yzMIvvTN3bPceK>PpJi(=3Nny zbSn}p$dGKQUlX&-t~RR)#F7I<8NCD^yke(vdf#4^aAh}M-{tS9-&^tC4`KU_pToXy z+|K8sx}a)Kh{h{;*V1#hs1xB%(?j>)g~`Wv(9F)f=Qn)(daVB7hZtcp^#LrEr1T1J zZSJ*lVyVVjhy)mkex9Whn=EinKDHe@KlfQI-Fl7M?-c~HnW0;C;+MbUY8?FToy;A+ zs&Nc7VZ=Of+e!G6s#+S5WBU)kgQq_I1@!uH74GJ-+O|%0HXm9Mqlvp|j%0`T>fr9^ zK;qo>XdwZW<>%tTA+<(1^6(>=-2N;hRgBnjvEjN;VbKMbFg--WrGy|XESoH1p|M4` z86(gC^vB4qScASZ&cdpT{~QDN-jC|GJ(RYoW1VW4!SSn- zhQds9&RBKn6M&GVK_Aayt(Hekbnw=tr>f z^o@v9_*iQO1*zeOrts9Q-$pc@!StS&kz$cF`s@pM`rmJXTP&h5G)A74!0e%ZJbl}( zssI|_!%~_hZFypv*S^JE5N&Kvmx7KiG<|fGMO=WrH+@Yhuj+KwiS#l4>@%2nl zS)mDikfmokO4q2A)hRVZBq2-5q&XC>%HOLkOYxZ66(s86?=0s4z5xbiOV)}L-&6b)h6(~CIaR#JNw~46+WBiU7IhB zq!NuR4!TsYnyBg>@G=Ib*cMq^k<}AMpCeYEf&dzfiGI-wOQ7hb+nA zkN7_){y&c3xC0 AQ~&?~ diff --git a/example/rails2.3/public/javascripts/application.js b/example/rails2.3/public/javascripts/application.js deleted file mode 100644 index fe457769..00000000 --- a/example/rails2.3/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/example/rails2.3/public/javascripts/controls.js b/example/rails2.3/public/javascripts/controls.js deleted file mode 100644 index ca29aefd..00000000 --- a/example/rails2.3/public/javascripts/controls.js +++ /dev/null @@ -1,963 +0,0 @@ -// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan) -// (c) 2005-2008 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, 'keydown', 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, - ''); - 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(); - Event.stop(event); - return; - case Event.KEY_DOWN: - this.markNext(); - this.render(); - 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("
  • " + elem.substr(0, entry.length) + "" + - elem.substr(entry.length) + "
  • "); - 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("
  • " + elem.substr(0, foundPos) + "" + - elem.substr(foundPos, entry.length) + "" + elem.substr( - foundPos + entry.length) + "
  • "); - 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 "
      " + ret.join('') + "
    "; - } - }, options || { }); - } -}); - -// AJAX in-place editor and collection editor -// Full rewrite by Christophe Porteneuve (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.unescapeHTML(); - }, - 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)); - } -}); \ No newline at end of file diff --git a/example/rails2.3/public/javascripts/dragdrop.js b/example/rails2.3/public/javascripts/dragdrop.js deleted file mode 100644 index 07229f98..00000000 --- a/example/rails2.3/public/javascripts/dragdrop.js +++ /dev/null @@ -1,973 +0,0 @@ -// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// (c) 2005-2008 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._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); - 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._originallyAbsolute) - Position.relativize(this.element); - 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; - } - 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){ - 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({ - 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')]; -}; \ No newline at end of file diff --git a/example/rails2.3/public/javascripts/effects.js b/example/rails2.3/public/javascripts/effects.js deleted file mode 100644 index 5a639d2d..00000000 --- a/example/rails2.3/public/javascripts/effects.js +++ /dev/null @@ -1,1128 +0,0 @@ -// Copyright (c) 2005-2008 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) + .5; - }, - reverse: function(pos) { - return 1-pos; - }, - flicker: function(pos) { - 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) + .5; - }, - 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)); - }, - 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= 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.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(); - - if (options.offset) elementOffsets[1] += options.offset; - - return new Effect.Tween(null, - scrollOffsets.top, - 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] || { }, - 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})); -}; - -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 = '
    '; - 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(results, property) { - results[property] = css[property]; - return results; - }); - if (!styles.opacity) styles.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); \ No newline at end of file diff --git a/example/rails2.3/public/javascripts/prototype.js b/example/rails2.3/public/javascripts/prototype.js deleted file mode 100644 index dfe8ab4e..00000000 --- a/example/rails2.3/public/javascripts/prototype.js +++ /dev/null @@ -1,4320 +0,0 @@ -/* Prototype JavaScript framework, version 1.6.0.3 - * (c) 2005-2008 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.3', - - Browser: { - IE: !!(window.attachEvent && - navigator.userAgent.indexOf('Opera') === -1), - Opera: navigator.userAgent.indexOf('Opera') > -1, - 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, - SelectorsAPI: !!document.querySelector, - ElementExtensions: !!window.HTMLElement, - SpecificElementExtensions: - document.createElement('div')['__proto__'] && - document.createElement('div')['__proto__'] !== - document.createElement('form')['__proto__'] - }, - - ScriptFragment: ']*>([\\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 = (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 = { }; - -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() : 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(); - } - - 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 != null && typeof object == "object" && - 'splice' in object && 'join' in object; - }, - - 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] - .replace(/\s+/g, '').split(','); - 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); - }, - - defer: function() { - var args = [0.01].concat($A(arguments)); - return this.delay.apply(this, args); - }, - - 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))); - }; - } -}); - -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,'&').replace(//g,'>'); - }, - unescapeHTML: function() { - return this.stripTags().replace(/&/g,'&').replace(/</g,'<').replace(/>/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('') -}); - -String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.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); - }); - } -}); -Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; - -var $break = { }; - -var Enumerable = { - each: function(iterator, context) { - var index = 0; - try { - this._each(function(value) { - iterator.call(context, value, index++); - }); - } catch (e) { - if (e != $break) throw e; - } - return this; - }, - - eachSlice: function(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 || Prototype.K; - var result = true; - this.each(function(value, index) { - result = result && !!iterator.call(context, value, index); - if (!result) throw $break; - }); - return result; - }, - - any: function(iterator, context) { - iterator = iterator || Prototype.K; - var result = false; - this.each(function(value, index) { - if (result = !!iterator.call(context, value, index)) - throw $break; - }); - return result; - }, - - collect: function(iterator, context) { - iterator = iterator || Prototype.K; - var results = []; - this.each(function(value, index) { - results.push(iterator.call(context, value, index)); - }); - return results; - }, - - detect: function(iterator, context) { - var result; - this.each(function(value, index) { - if (iterator.call(context, value, index)) { - result = value; - throw $break; - } - }); - return result; - }, - - findAll: function(iterator, context) { - var results = []; - this.each(function(value, index) { - if (iterator.call(context, value, index)) - results.push(value); - }); - return results; - }, - - grep: function(filter, iterator, context) { - iterator = iterator || Prototype.K; - var results = []; - - if (Object.isString(filter)) - filter = new RegExp(filter); - - this.each(function(value, index) { - if (filter.match(value)) - results.push(iterator.call(context, 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) { - this.each(function(value, index) { - memo = iterator.call(context, 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 || Prototype.K; - var result; - this.each(function(value, index) { - value = iterator.call(context, value, index); - if (result == null || value >= result) - result = value; - }); - return result; - }, - - min: function(iterator, context) { - iterator = iterator || Prototype.K; - var result; - this.each(function(value, index) { - value = iterator.call(context, value, index); - if (result == null || value < result) - result = value; - }); - return result; - }, - - partition: function(iterator, context) { - iterator = iterator || Prototype.K; - var trues = [], falses = []; - this.each(function(value, index) { - (iterator.call(context, 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) { - var results = []; - this.each(function(value, index) { - if (!iterator.call(context, value, index)) - results.push(value); - }); - return results; - }, - - sortBy: function(iterator, context) { - return this.map(function(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() { - 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 '#'; - } -}; - -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 || 0, results = new Array(length); - while (length--) results[length] = iterable[length]; - return results; -} - -if (Prototype.Browser.WebKit) { - $A = function(iterable) { - if (!iterable) return []; - // In Safari, only use the `toArray` method if it's not a NodeList. - // A NodeList is a function, has an function `item` property, and a numeric - // `length` property. Adapted from Google Doctype. - if (!(typeof iterable === 'function' && typeof iterable.length === - 'number' && typeof iterable.item === 'function') && iterable.toArray) - return iterable.toArray(); - var length = iterable.length || 0, 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, context) { - $R(0, this, true).each(iterator, context); - 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) { - // simulating poorly supported hasOwnProperty - 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; - }, - - 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.inject([], function(results, pair) { - var key = encodeURIComponent(pair.key), values = pair.value; - - 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('&'); - }, - - inspect: function() { - return '#'; - }, - - 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 && this.isSameOrigin() && 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; - } - }, - - 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) || null; - } 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 || - !this.request.isSameOrigin()); - } 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 || - !this.request.isSameOrigin()); - } 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 || { }); - if (element) this.Element.prototype = element.prototype; -}).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 = $(element); - element.style.display = 'none'; - return element; - }, - - show: function(element) { - element = $(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, insert, tagName, childNodes; - - for (var position in insertions) { - content = insertions[position]; - position = position.toLowerCase(); - insert = Element._insertionTranslations[position]; - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - insert(element, content); - continue; - } - - content = Object.toHTML(content); - - 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(); - } - - 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).select("*"); - }, - - 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 Object.isNumber(expression) ? ancestors[expression] : - Selector.findElement(ancestors, expression, index); - }, - - down: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return element.firstDescendant(); - return Object.isNumber(expression) ? element.descendants()[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 Object.isNumber(expression) ? previousSiblings[expression] : - Selector.findElement(previousSiblings, expression, index); - }, - - next: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); - var nextSiblings = element.nextSiblings(); - return Object.isNumber(expression) ? nextSiblings[expression] : - Selector.findElement(nextSiblings, expression, index); - }, - - 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); - - if (element.compareDocumentPosition) - return (element.compareDocumentPosition(ancestor) & 8) === 8; - - if (ancestor.contains) - return ancestor.contains(element) && ancestor !== element; - - while (element = element.parentNode) - if (element == ancestor) 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 || value == 'auto') { - 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 (Prototype.Browser.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.toUpperCase() == 'BODY') break; - var p = Element.getStyle(element, 'position'); - if (p !== 'static') break; - } - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - absolutize: function(element) { - element = $(element); - if (element.getStyle('position') == 'absolute') return element; - // 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 element; - // 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 && (element.tagName.toUpperCase() == '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 (Prototype.Browser.Opera) { - Element.Methods.getStyle = Element.Methods.getStyle.wrap( - function(proceed, element, style) { - switch (style) { - 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()]) - return dim + 'px'; - - var properties; - if (style === 'height') { - properties = ['border-top-width', 'padding-top', - 'padding-bottom', 'border-bottom-width']; - } - else { - properties = ['border-left-width', 'padding-left', - 'padding-right', 'border-right-width']; - } - return properties.inject(dim, function(memo, property) { - var val = proceed(element, property); - return val === null ? memo : memo - parseInt(val, 10); - }) + 'px'; - default: return proceed(element, style); - } - } - ); - - Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( - function(proceed, element, attribute) { - if (attribute === 'title') return element.title; - return proceed(element, attribute); - } - ); -} - -else if (Prototype.Browser.IE) { - // IE doesn't report offsets correctly for static elements, so we change them - // to "relative" to get the values, then change them back. - Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( - function(proceed, element) { - element = $(element); - // IE throws an error if element is not in document - try { element.offsetParent } - catch(e) { 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); - try { element.offsetParent } - catch(e) { return Element._returnOffset(0,0) } - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - // Trigger hasLayout on the offset parent so that IE6 reports - // accurate offsetTop and offsetLeft values for position: fixed. - 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 }); - return value; - } - ); - }); - - Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( - function(proceed, element) { - try { element.offsetParent } - catch(e) { return Element._returnOffset(0,0) } - return proceed(element); - } - ); - - 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.extend({ - cellpadding: 'cellPadding', - cellspacing: 'cellSpacing' - }, 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 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, - 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.toUpperCase() == '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 Element#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 ('outerHTML' in document.createElement('div')) { - 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]; - if (t) { - div.innerHTML = t[0] + html + t[1]; - t[2].times(function() { div = div.firstChild }); - } else div.innerHTML = html; - return $A(div.childNodes); -}; - -Element._insertionTranslations = { - before: function(element, node) { - element.parentNode.insertBefore(node, element); - }, - top: function(element, node) { - element.insertBefore(node, element.firstChild); - }, - bottom: function(element, node) { - element.appendChild(node); - }, - after: function(element, node) { - element.parentNode.insertBefore(node, element.nextSibling); - }, - tags: { - TABLE: ['', '
    ', 1], - TBODY: ['', '
    ', 2], - TR: ['', '
    ', 3], - TD: ['
    ', '
    ', 4], - SELECT: ['', 1] - } -}; - -(function() { - 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.toUpperCase(), 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 = { }, B = Prototype.Browser; - $w('width height').each(function(d) { - var D = d.capitalize(); - if (B.WebKit && !document.evaluate) { - // Safari <3.0 needs self.innerWidth/Height - dimensions[d] = self['inner' + D]; - } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) { - // Opera <9.5 needs document.body.clientWidth/Height - dimensions[d] = document.body['client' + D] - } else { - dimensions[d] = document.documentElement['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(); - - if (this.shouldUseSelectorsAPI()) { - this.mode = 'selectorsAPI'; - } else if (this.shouldUseXPath()) { - this.mode = 'xpath'; - this.compileXPathMatcher(); - } else { - this.mode = "normal"; - 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(e)) - return false; - - return true; - }, - - shouldUseSelectorsAPI: function() { - if (!Prototype.BrowserFeatures.SelectorsAPI) return false; - - if (!Selector._div) Selector._div = new Element('div'); - - // Make sure the browser treats the selector as valid. Test on an - // isolated element to minimize cost of this check. - try { - Selector._div.querySelector(this.expression); - } catch(e) { - return false; - } - - return true; - }, - - compileMatcher: function() { - 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; - var e = this.expression, results; - - switch (this.mode) { - case 'selectorsAPI': - // querySelectorAll queries document-wide, then filters to descendants - // of the context element. That's not what we want. - // Add an explicit context to the selector if necessary. - if (root !== document) { - var oldId = root.id, id = $(root).identify(); - e = "#" + id + " " + e; - } - - results = $A(root.querySelectorAll(e)).map(Element.extend); - root.id = oldId; - - return results; - case 'xpath': - return document._getElementsByXPath(this.xpath, root); - default: - 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 "#"; - } -}); - -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)]", - 'checked': "[@checked]", - 'disabled': "[(@disabled) and (@type!='hidden')]", - 'enabled': "[not(@disabled) and (@type!='hidden')]", - '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); c = false;', - attr: function(m) { - m[3] = (m[5] || m[6]); - return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); 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]+:)?[\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 nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); - } - }, - - 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) { - var _true = Prototype.emptyFunction; - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = _true; - return nodes; - }, - - unmark: function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = 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._countedByPrototype = Prototype.emptyFunction; - 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._countedByPrototype)) node.nodeIndex = j++; - } - } else { - for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) 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])._countedByPrototype) { - n._countedByPrototype = Prototype.emptyFunction; - 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) { - var uTagName = 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() === uTagName) 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, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - 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, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - 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._countedByPrototype) { - 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) 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._countedByPrototype) 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 && (!node.type || node.type !== 'hidden')) - 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 == v || nv && nv.startsWith(v); }, - '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, - '*=': function(nv, v) { return nv == v || nv && nv.include(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() + '-'); } - }, - - split: function(expression) { - var expressions = []; - expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { - expressions.push(m[1].strip()); - }); - return expressions; - }, - - matchElements: function(elements, expression) { - var matches = $$(expression), h = Selector.handlers; - h.mark(matches); - for (var i = 0, results = [], element; element = elements[i]; i++) - if (element._countedByPrototype) 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) { - expressions = Selector.split(expressions.join(',')); - 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) { - Object.extend(Selector.handlers, { - // IE returns comment nodes on getElementsByTagName("*"). - // Filter them out. - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - if (node.tagName !== "!") a.push(node); - return a; - }, - - // IE improperly serializes _countedByPrototype in (inner|outer)HTML. - unmark: function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node.removeAttribute('_countedByPrototype'); - return nodes; - } - }); -} - -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 != '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); - } - 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.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, value) { - if (Object.isUndefined(value)) - return this[element.type == 'select-one' ? - 'selectOne' : 'selectMany'](element); - else { - var opt, currentValue, single = !Object.isArray(value); - for (var i = 0, length = element.length; i < length; i++) { - opt = element.options[i]; - currentValue = this.optionValue(opt); - if (single) { - if (currentValue == value) { - opt.selected = true; - return; - } - } - else opt.selected = value.include(currentValue); - } - } - }, - - 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) { - event = Event.extend(event); - - var node = event.target, - type = event.type, - currentTarget = event.currentTarget; - - if (currentTarget && currentTarget.tagName) { - // Firefox screws up the "click" event when moving between radio buttons - // via arrow keys. It also screws up the "load" and "error" events on images, - // reporting the document as the target instead of the original image. - if (type === 'load' || type === 'error' || - (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' - && currentTarget.type === 'radio')) - node = currentTarget; - } - if (node.nodeType == Node.TEXT_NODE) node = node.parentNode; - return Element.extend(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) { - var docElement = document.documentElement, - body = document.body || { scrollLeft: 0, scrollTop: 0 }; - return { - x: event.pageX || (event.clientX + - (docElement.scrollLeft || body.scrollLeft) - - (docElement.clientLeft || 0)), - y: event.pageY || (event.clientY + - (docElement.scrollTop || body.scrollTop) - - (docElement.clientTop || 0)) - }; - }, - - 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._prototypeEventID) return element._prototypeEventID[0]; - arguments.callee.id = arguments.callee.id || 1; - return element._prototypeEventID = [++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; - } - - - // Internet Explorer needs to remove event handlers on page unload - // in order to avoid memory leaks. - if (window.attachEvent) { - window.attachEvent("onunload", destroyCache); - } - - // Safari has a dummy event handler on page unload so that it won't - // use its bfcache. Safari <= 3.1 has an issue with restoring the "document" - // object when page is returned to via the back button using its bfcache. - if (Prototype.Browser.WebKit) { - window.addEventListener('unload', Prototype.emptyFunction, false); - } - - 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; - - var event; - if (document.createEvent) { - event = document.createEvent("HTMLEvents"); - event.initEvent("dataavailable", true, true); - } else { - 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(), - loaded: false -}); - -(function() { - /* Support for the DOMContentLoaded event is based on work by Dan Webb, - Matthias Miller, Dean Edwards and John Resig. */ - - var timer; - - function fireContentLoadedEvent() { - if (document.loaded) return; - if (timer) window.clearInterval(timer); - document.fire("dom:loaded"); - document.loaded = 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(" diff --git a/lib/analytical.rb b/lib/analytical.rb index f936300f..9a272cca 100644 --- a/lib/analytical.rb +++ b/lib/analytical.rb @@ -9,6 +9,10 @@ module Analytical def analytical(options = {}) config = Analytical.config(options[:config]) self.analytical_options = options.reverse_merge(config) + + after_filter do |controller| + flash[:analytical] = controller.analytical.flash.to_json.html_safe + end end def self.config(path = nil) diff --git a/lib/analytical/api.rb b/lib/analytical/api.rb index 3810f37a..32fcd967 100644 --- a/lib/analytical/api.rb +++ b/lib/analytical/api.rb @@ -20,6 +20,10 @@ def initialize(options={}) @dummy_module = Analytical::Modules::DummyModule.new end + def flash + @modules.values.map{ |m| m.events_options }.flatten(1) + end + def get_mod(name) name = name.to_s.camelize "Analytical::Modules::#{name}".constantize @@ -70,13 +74,12 @@ def now # These methods return the javascript that should be inserted into each section of your layout # def head_prepend_javascript - [init_javascript(:head_prepend), tracking_javascript(:head_prepend)].delete_if{|s| s.blank?}.join("\n") + [init_javascript(:head_prepend)].delete_if{|s| s.blank?}.join("\n") end def head_append_javascript js = [ - init_javascript(:head_append), - tracking_javascript(:head_append), + init_javascript(:head_append) ] if Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new('3.1.0') # Rails 3.1 lets us override views in engines @@ -92,10 +95,10 @@ def head_append_javascript alias_method :head_javascript, :head_append_javascript def body_prepend_javascript - [init_javascript(:body_prepend), tracking_javascript(:body_prepend)].delete_if{|s| s.blank?}.join("\n") + [init_javascript(:body_prepend)].delete_if{|s| s.blank?}.join("\n") end def body_append_javascript - [init_javascript(:body_append), tracking_javascript(:body_append)].delete_if{|s| s.blank?}.join("\n") + [init_javascript(:body_append)].delete_if{|s| s.blank?}.join("\n") end private @@ -106,19 +109,6 @@ def process_command(command, *args) end end - def tracking_javascript(location) - commands = [] - @modules.each do |name, m| - commands += m.process_queued_commands if m.init_location?(location) || m.initialized - end - commands = commands.delete_if{|c| c.blank? || c.empty?} - unless commands.empty? - commands.unshift "" - end - commands.join("\n") - end - def init_javascript(location) @modules.values.collect do |m| m.init_javascript(location) if m.respond_to?(:init_javascript) diff --git a/lib/analytical/modules/base.rb b/lib/analytical/modules/base.rb index 5521bc0b..fc7f4323 100644 --- a/lib/analytical/modules/base.rb +++ b/lib/analytical/modules/base.rb @@ -49,12 +49,12 @@ def queue(*args) @command_store << args end end - def process_queued_commands - command_strings = @command_store.collect do |c| - send(*c) if respond_to?(c.first) - end.compact + + # we only care about events, all other commands will be removed without having effect + def events_options + result = @command_store.commands.select{ |command| command[0] == :event }.map{ |command| [command[1], command[2]] } @command_store.flush - command_strings + result end def init_location?(location) diff --git a/spec/analytical/api_spec.rb b/spec/analytical/api_spec.rb index 27faf4e3..a6384024 100644 --- a/spec/analytical/api_spec.rb +++ b/spec/analytical/api_spec.rb @@ -79,10 +79,8 @@ before(:each) do @console.stub!(:init_location?).and_return(false) @console.stub!(:initialized).and_return(false) - @console.stub!(:process_queued_commands).and_return([]) @google.stub!(:init_location?).and_return(false) @google.stub!(:initialized).and_return(false) - @google.stub!(:process_queued_commands).and_return([]) end describe '#head_prepend_javascript' do @@ -125,12 +123,10 @@ @console.stub!(:initialized).and_return(false) @console.stub!(:track).and_return('console track called') @console.stub!(:queue) - @console.stub!(:process_queued_commands).and_return(['console track called']) @google.stub!(:init_location?).and_return(false) @google.stub!(:initialized).and_return(true) @google.stub!(:track).and_return('google track called') @google.stub!(:queue) - @google.stub!(:process_queued_commands).and_return(['google track called']) @api.track('something', {:a=>1, :b=>2}) end describe '#body_prepend_javascript' do diff --git a/spec/analytical/modules/base_spec.rb b/spec/analytical/modules/base_spec.rb index 1ee9eebe..22b982b8 100644 --- a/spec/analytical/modules/base_spec.rb +++ b/spec/analytical/modules/base_spec.rb @@ -53,31 +53,6 @@ class BaseApiDummy end end - describe '#process_queued_commands' do - before(:each) do - @api = BaseApiDummy.new(:parent=>mock('parent')) - @api.command_store.commands = [[:a, 1, 2, 3], [:b, {:some=>:args}]] - @api.stub!(:a).and_return('a') - @api.stub!(:b).and_return('b') - end - it 'should send each of the args arrays in the command list' do - @api.should_receive(:a).with(1, 2, 3).and_return('a') - @api.should_receive(:b).with({:some=>:args}).and_return('b') - @api.process_queued_commands - end - it 'should return the results as an array' do - @api.process_queued_commands.should == ['a', 'b'] - end - it 'should clear the commands list' do - @api.process_queued_commands - @api.command_store.commands == [] - end - it "should not store an unrecognized command" do - @api.command_store.commands << [:c, 1] - @api.process_queued_commands.should == ['a','b'] - end - end - describe '#init_location?' do before(:each) do @api = BaseApiDummy.new(:parent=>mock('parent')) From 7e6e30aeb61b01cfee1b40723e92e9b4d2feebfc Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Mon, 8 Oct 2012 19:30:50 +0100 Subject: [PATCH 05/55] forgot to update gemspec --- analytical.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/analytical.gemspec b/analytical.gemspec index 0bc93795..8086cd4f 100644 --- a/analytical.gemspec +++ b/analytical.gemspec @@ -43,7 +43,6 @@ Gem::Specification.new do |s| "lib/analytical/modules/quantcast.rb", "lib/analytical/modules/reinvigorate.rb", "lib/analytical/rails/engine.rb", - "lib/analytical/session_command_store.rb", "rails/init.rb" ] s.homepage = %q{http://github.com/jkrall/analytical} From 5e0df9a8a7b42dee866ef3c424f6fb70d50dbd2c Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Wed, 10 Oct 2012 15:48:17 +0100 Subject: [PATCH 06/55] add middleware --- README.rdoc | 4 +++ .../_analytical_fire_events.html.erb | 19 ++++++++++++ lib/analytical.rb | 4 --- lib/analytical/api.rb | 30 +++++++++++-------- lib/analytical/middleware.rb | 26 ++++++++++++++++ lib/analytical/modules/base.rb | 12 ++++---- 6 files changed, 71 insertions(+), 24 deletions(-) create mode 100644 app/views/application/_analytical_fire_events.html.erb create mode 100644 lib/analytical/middleware.rb diff --git a/README.rdoc b/README.rdoc index 18af2309..a996d813 100644 --- a/README.rdoc +++ b/README.rdoc @@ -27,6 +27,10 @@ Add the following to your controllers: analytical +Add the middleware configuration in config/application.rb: + + config.middleware.use Analytical::Middleware + Add a configuration file (config/analytical.yml) to declare your API keys, like so: production: diff --git a/app/views/application/_analytical_fire_events.html.erb b/app/views/application/_analytical_fire_events.html.erb new file mode 100644 index 00000000..537a413d --- /dev/null +++ b/app/views/application/_analytical_fire_events.html.erb @@ -0,0 +1,19 @@ + diff --git a/lib/analytical.rb b/lib/analytical.rb index 9a272cca..f936300f 100644 --- a/lib/analytical.rb +++ b/lib/analytical.rb @@ -9,10 +9,6 @@ module Analytical def analytical(options = {}) config = Analytical.config(options[:config]) self.analytical_options = options.reverse_merge(config) - - after_filter do |controller| - flash[:analytical] = controller.analytical.flash.to_json.html_safe - end end def self.config(path = nil) diff --git a/lib/analytical/api.rb b/lib/analytical/api.rb index 32fcd967..bd03906b 100644 --- a/lib/analytical/api.rb +++ b/lib/analytical/api.rb @@ -20,10 +20,6 @@ def initialize(options={}) @dummy_module = Analytical::Modules::DummyModule.new end - def flash - @modules.values.map{ |m| m.events_options }.flatten(1) - end - def get_mod(name) name = name.to_s.camelize "Analytical::Modules::#{name}".constantize @@ -79,16 +75,10 @@ def head_prepend_javascript def head_append_javascript js = [ - init_javascript(:head_append) + init_javascript(:head_append), + render_js_partial("analytical_javascript") ] - if Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new('3.1.0') # Rails 3.1 lets us override views in engines - js << options[:controller].send(:render_to_string, :partial=>'analytical_javascript') if options[:controller] - else # All other rails - _partial_path = Pathname.new(__FILE__).dirname.join('..', '..', 'app/views/application', '_analytical_javascript.html.erb').to_s - js << options[:controller].send(:render_to_string, :file=>_partial_path, :layout=>false) if options[:controller] - end - js.delete_if{|s| s.blank?}.join("\n") end @@ -98,11 +88,25 @@ def body_prepend_javascript [init_javascript(:body_prepend)].delete_if{|s| s.blank?}.join("\n") end def body_append_javascript - [init_javascript(:body_append)].delete_if{|s| s.blank?}.join("\n") + js = [ + init_javascript(:body_append), + render_js_partial("analytical_fire_events") + ] + + js.delete_if{|s| s.blank?}.join("\n") end private + def render_js_partial(name) + if Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new('3.1.0') # Rails 3.1 lets us override views in engines + options[:controller].send(:render_to_string, :partial=> name) if options[:controller] + else # All other rails + _partial_path = Pathname.new(__FILE__).dirname.join('..', '..', 'app/views/application', "_#{name}.html.erb").to_s + options[:controller].send(:render_to_string, :file=>_partial_path, :layout=>false) if options[:controller] + end + end + def process_command(command, *args) @modules.values.each do |m| m.queue command, *args diff --git a/lib/analytical/middleware.rb b/lib/analytical/middleware.rb new file mode 100644 index 00000000..ffb98eb3 --- /dev/null +++ b/lib/analytical/middleware.rb @@ -0,0 +1,26 @@ +module Analytical + class Middleware + + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + + new_events_data = env["analytical"] + if new_events_data + response = Rack::Response.new(body, status, headers) + cookies = env["rack.cookies"] || {} + + events_data = (JSON(cookies["analytical"]) if cookies["analytical"]) || [] rescue [] + events_data.concat(new_events_data) + response.set_cookie("analytical", { :value => events_data.to_json, :path => "/" }) + response.finish + else + [status, headers, body] + end + end + + end +end diff --git a/lib/analytical/modules/base.rb b/lib/analytical/modules/base.rb index fc7f4323..c9dcff41 100644 --- a/lib/analytical/modules/base.rb +++ b/lib/analytical/modules/base.rb @@ -46,17 +46,15 @@ def queue(*args) if args.first==:identify @command_store.unshift args else + # we only care about events, all other commands will be removed without having effect + if args[0] == :event + @options[:controller].env["analytical"] ||= [] + @options[:controller].env["analytical"] << [args[1], args[2]] + end @command_store << args end end - # we only care about events, all other commands will be removed without having effect - def events_options - result = @command_store.commands.select{ |command| command[0] == :event }.map{ |command| [command[1], command[2]] } - @command_store.flush - result - end - def init_location?(location) if @tracking_command_location.is_a?(Array) @tracking_command_location.map { |item|item.to_sym }.include?(location.to_sym) From 432106ac946aca2bd47d84b0a495aea230376f83 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Wed, 10 Oct 2012 15:48:29 +0100 Subject: [PATCH 07/55] don't log options in a separate line --- lib/analytical/modules/console.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/analytical/modules/console.rb b/lib/analytical/modules/console.rb index f28e6b58..4f4b341e 100644 --- a/lib/analytical/modules/console.rb +++ b/lib/analytical/modules/console.rb @@ -32,7 +32,7 @@ def identify(id, *args) data = args.first || {} check_for_console <<-HERE console.log("Analytical Identify: "+"#{escape id}"); - console.log(#{data.to_json}); + // console.log(#{data.to_json}); HERE end @@ -40,14 +40,14 @@ def event(name, *args) data = args.first || {} check_for_console <<-HERE console.log("Analytical Event: "+"#{escape name}"); - console.log(#{data.to_json}); + // console.log(#{data.to_json}); HERE end def set(data) check_for_console <<-HERE console.log("Analytical Set: "); - console.log(#{data.to_json}); + // console.log(#{data.to_json}); HERE end From 07f3b0a9792a6b96631aff42c851804191575967 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Wed, 10 Oct 2012 16:22:29 +0100 Subject: [PATCH 08/55] explicitly require the middleware --- lib/analytical.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/analytical.rb b/lib/analytical.rb index f936300f..61f560c2 100644 --- a/lib/analytical.rb +++ b/lib/analytical.rb @@ -3,6 +3,7 @@ Dir.glob(File.dirname(__FILE__)+'/analytical/**/*.rb').each do |f| require f end +require 'analytical/middleware' module Analytical From fc6e99c60b8202004c36750e126d84e9c2e8315a Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Wed, 10 Oct 2012 18:07:00 +0100 Subject: [PATCH 09/55] update mixpanel module --- lib/analytical/modules/mixpanel.rb | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index 18c4b4ab..e7274dad 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -12,14 +12,18 @@ def init_javascript(location) init_location(location) do js = <<-HTML - HTML js @@ -28,23 +32,23 @@ def init_javascript(location) def track(event, properties = {}) callback = properties.delete(:callback) || "function(){}" - %(mpmetrics.track("#{event}", #{properties.to_json}, #{callback});) + %(mixpanel.track("#{event}", #{properties.to_json}, #{callback});) end # Used to set "Super Properties" - http://mixpanel.com/api/docs/guides/super-properties def set(properties) - "mpmetrics.register(#{properties.to_json});" + "mixpanel.register(#{properties.to_json});" end def identify(id, *args) opts = args.first || {} name = opts.is_a?(Hash) ? opts[:name] : "" - name_str = name.blank? ? "" : " mpmetrics.name_tag('#{name}');" - %(mpmetrics.identify('#{id}');#{name_str}) + name_str = name.blank? ? "" : " mixpanel.name_tag('#{name}');" + %(mixpanel.identify('#{id}');#{name_str}) end def event(name, attributes = {}) - %(mpmetrics.track("#{name}", #{attributes.to_json});) + %(mixpanel.track("#{name}", #{attributes.to_json});) end end From 53958ba29e39fff98042b981c233412be0798767 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Thu, 11 Oct 2012 16:33:17 +0100 Subject: [PATCH 10/55] re-add support for commands other than event --- README.rdoc | 9 ++- .../_analytical_fire_events.html.erb | 29 +++---- .../_analytical_javascript.html.erb | 16 ++-- lib/analytical/api.rb | 9 ++- lib/analytical/middleware.rb | 10 +-- lib/analytical/modules/base.rb | 10 +-- lib/analytical/modules/console.rb | 77 ++++++++----------- lib/analytical/modules/kiss_metrics.rb | 31 +++++--- lib/analytical/modules/mixpanel.rb | 36 +++++---- spec/analytical/modules/base_spec.rb | 2 +- 10 files changed, 119 insertions(+), 110 deletions(-) diff --git a/README.rdoc b/README.rdoc index a996d813..0c5f829b 100644 --- a/README.rdoc +++ b/README.rdoc @@ -57,7 +57,6 @@ Then, in your template files, you'll want to add the analytical helper methods f <%= stylesheet_link_tag :all %> <%= javascript_include_tag :defaults %> <%= csrf_meta_tag %> - <% analytical.identify '5', :email=>'josh@transfs.com' %> <%= raw analytical.head_append_javascript %> @@ -67,9 +66,13 @@ Then, in your template files, you'll want to add the analytical helper methods f -Note the example above also includes an identify() command that will apply to every page. More likely, you'll want to make this identify() command conditional so that it only applies when you have a logged-in user: +You'll want to add the identify() command as a before_filter: - <% analytical.identify(current_user.id, :email=>current_user.email) if current_user %> + before_filter :analytical_identify + protected + def analytical_identify + analytical.identify(current_user.id, :email => current_user.email) if current_user + end You can sprinkle the track() and event() tracking methods throughout your views & controllers as needed. diff --git a/app/views/application/_analytical_fire_events.html.erb b/app/views/application/_analytical_fire_events.html.erb index 537a413d..23a0f9e4 100644 --- a/app/views/application/_analytical_fire_events.html.erb +++ b/app/views/application/_analytical_fire_events.html.erb @@ -1,19 +1,20 @@ diff --git a/app/views/application/_analytical_javascript.html.erb b/app/views/application/_analytical_javascript.html.erb index 50394850..f81ae24c 100644 --- a/app/views/application/_analytical_javascript.html.erb +++ b/app/views/application/_analytical_javascript.html.erb @@ -1,10 +1,16 @@ diff --git a/lib/analytical/api.rb b/lib/analytical/api.rb index bd03906b..cc996a9c 100644 --- a/lib/analytical/api.rb +++ b/lib/analytical/api.rb @@ -40,7 +40,7 @@ def method_missing(method, *args, &block) elsif available_modules.include?(method) @dummy_module else - process_command method, *args + process_command(method, *args) end end @@ -52,17 +52,18 @@ class ImmediateDelegateHelper def initialize(_parent) @parent = _parent end + def method_missing(method, *args, &block) @parent.modules.values.collect do |m| m.send(method, *args) if m.respond_to?(method) end.delete_if{|c| c.blank?}.join("\n") end end - + # # Returns a new delegation object for immediate processing of a command # - def now + def javascript_for ImmediateDelegateHelper.new(self) end @@ -109,7 +110,7 @@ def render_js_partial(name) def process_command(command, *args) @modules.values.each do |m| - m.queue command, *args + m.queue(command, *args) end end diff --git a/lib/analytical/middleware.rb b/lib/analytical/middleware.rb index ffb98eb3..52ef7768 100644 --- a/lib/analytical/middleware.rb +++ b/lib/analytical/middleware.rb @@ -8,14 +8,14 @@ def initialize(app) def call(env) status, headers, body = @app.call(env) - new_events_data = env["analytical"] - if new_events_data + new_commands = env["analytical"] + if new_commands response = Rack::Response.new(body, status, headers) cookies = env["rack.cookies"] || {} - events_data = (JSON(cookies["analytical"]) if cookies["analytical"]) || [] rescue [] - events_data.concat(new_events_data) - response.set_cookie("analytical", { :value => events_data.to_json, :path => "/" }) + commands = (JSON(cookies["analytical"]) if cookies["analytical"]) || [] rescue [] + commands.concat(new_commands) + response.set_cookie("analytical", { :value => commands.to_json, :path => "/" }) response.finish else [status, headers, body] diff --git a/lib/analytical/modules/base.rb b/lib/analytical/modules/base.rb index c9dcff41..a0664053 100644 --- a/lib/analytical/modules/base.rb +++ b/lib/analytical/modules/base.rb @@ -42,17 +42,13 @@ def protocol # def init_javascript(location) def queue(*args) - return if @options[:ignore_duplicates] && @command_store.include?(args) - if args.first==:identify + return if @command_store.include?(args) + if args.first == :identify @command_store.unshift args else - # we only care about events, all other commands will be removed without having effect - if args[0] == :event - @options[:controller].env["analytical"] ||= [] - @options[:controller].env["analytical"] << [args[1], args[2]] - end @command_store << args end + @options[:controller].env["analytical"] = @command_store.commands end def init_location?(location) diff --git a/lib/analytical/modules/console.rb b/lib/analytical/modules/console.rb index 4f4b341e..baed0544 100644 --- a/lib/analytical/modules/console.rb +++ b/lib/analytical/modules/console.rb @@ -13,7 +13,7 @@ def init_javascript(location) js = <<-HTML @@ -22,59 +22,44 @@ def init_javascript(location) end end - def track(*args) - check_for_console <<-HERE - console.log("Analytical Track: "+"#{escape args.first}"); - HERE + def identify(*args) # id, options + <<-JS.gsub(/^ {10}/, '') + if (typeof(console) !== 'undefined' && console != null) { + console.log('Analytical identify', arguments) + } + JS end - def identify(id, *args) - data = args.first || {} - check_for_console <<-HERE - console.log("Analytical Identify: "+"#{escape id}"); - // console.log(#{data.to_json}); - HERE + def track(*args) # event, properties, callback + <<-JS.gsub(/^ {10}/, '') + if (typeof(console) !== 'undefined' && console != null) { + console.log('Analytical track', arguments) + } + JS end - def event(name, *args) - data = args.first || {} - check_for_console <<-HERE - console.log("Analytical Event: "+"#{escape name}"); - // console.log(#{data.to_json}); - HERE + def event(*args) # name, options + <<-JS.gsub(/^ {10}/, '') + if (typeof(console) !== 'undefined' && console != null) { + console.log('Analytical event', arguments) + } + JS end - def set(data) - check_for_console <<-HERE - console.log("Analytical Set: "); - // console.log(#{data.to_json}); - HERE + def set(*args) # properties + <<-JS.gsub(/^ {10}/, '') + if (typeof(console) !== 'undefined' && console != null) { + console.log('Analytical set', arguments) + } + JS end - def alias_identity(old_identity,new_identity) - check_for_console <<-HERE - console.log("Analytical Alias: #{old_identity} => #{new_identity}"); - HERE - end - - private - - CONSOLE_JS_ESCAPE_MAP = { - '\\' => '\\\\', - ' '<\/', - "\r\n" => '\n', - "\n" => '\n', - "\r" => '\n', - '"' => '\\"', - "'" => "\\'" - } unless defined?(CONSOLE_JS_ESCAPE_MAP) - - def escape(js) - js.to_s.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { CONSOLE_JS_ESCAPE_MAP[$1] } - end - - def check_for_console(data) - "if(typeof(console) !== 'undefined' && console != null) { \n#{data} }" + def alias_identity(*args) # old_identity, new_identity + <<-JS.gsub(/^ {10}/, '') + if (typeof(console) !== 'undefined' && console != null) { + console.log('Analytical alias_identity', arguments) + } + JS end end diff --git a/lib/analytical/modules/kiss_metrics.rb b/lib/analytical/modules/kiss_metrics.rb index 8da04473..cf85260d 100644 --- a/lib/analytical/modules/kiss_metrics.rb +++ b/lib/analytical/modules/kiss_metrics.rb @@ -27,23 +27,32 @@ def init_javascript(location) end end - def identify(id, *args) - data = args.first || {} - "_kmq.push([\"identify\", \"#{data[:email]}\"]);" + def identify(*args) # id, options + <<-JS.gsub(/^ {10}/, '') + if (options && options.email) { + _kmq.push(['identify', options.email]); + } + JS end - def event(name, *args) - data = args.first || {} - "_kmq.push([\"record\", \"#{name}\", #{data.to_json}]);" + def event(*args) # name, options, callback + <<-JS.gsub(/^ {10}/, '') + _kmq.push(['record', name, options]); + JS end - def set(data) - return '' if data.blank? - "_kmq.push([\"set\", #{data.to_json}]);" + def set(*args) # properties + <<-JS.gsub(/^ {10}/, '') + if (properties) { + _kmq.push(['set', properties]); + } + JS end - def alias_identity(old_identity, new_identity) - "_kmq.push([\"alias\", \"#{old_identity}\", \"#{new_identity}\"]);" + def alias_identity(*args) # old_identity, new_identity + <<-JS.gsub(/^ {10}/, '') + _kmq.push(['alias', old_identity, new_identity]); + JS end private diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index e7274dad..e4c1906b 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -30,25 +30,33 @@ def init_javascript(location) end end - def track(event, properties = {}) - callback = properties.delete(:callback) || "function(){}" - %(mixpanel.track("#{event}", #{properties.to_json}, #{callback});) + def identify(*args) # id, options + <<-JS.gsub(/^ {10}/, '') + mixpanel.identify(id); + if (options && options.name) { + mixpanel.name_tag(options.name); + } + JS end - # Used to set "Super Properties" - http://mixpanel.com/api/docs/guides/super-properties - def set(properties) - "mixpanel.register(#{properties.to_json});" + def event(*args) # name, options, callback + <<-JS.gsub(/^ {10}/, '') + mixpanel.track(name, options || {}); + JS end - def identify(id, *args) - opts = args.first || {} - name = opts.is_a?(Hash) ? opts[:name] : "" - name_str = name.blank? ? "" : " mixpanel.name_tag('#{name}');" - %(mixpanel.identify('#{id}');#{name_str}) - end + # def track(*args) # event, properties, callback + # # event_name, properties, callback + # <<-JS.gsub(/^ {10}/, '') + # mixpanel.track(event, properties, callback); + # JS + # end - def event(name, attributes = {}) - %(mixpanel.track("#{name}", #{attributes.to_json});) + # Used to set "Super Properties" - http://mixpanel.com/api/docs/guides/super-properties + def set(*args) # properties + <<-JS.gsub(/^ {10}/, '') + mixpanel.register(properties); + JS end end diff --git a/spec/analytical/modules/base_spec.rb b/spec/analytical/modules/base_spec.rb index 22b982b8..676c0cb5 100644 --- a/spec/analytical/modules/base_spec.rb +++ b/spec/analytical/modules/base_spec.rb @@ -40,7 +40,7 @@ class BaseApiDummy end describe 'ignoring duplicates' do before(:each) do - @api = BaseApiDummy.new(:parent=>mock('parent'), :ignore_duplicates=>true) + @api = BaseApiDummy.new(:parent=>mock('parent')) @api.command_store.commands = [[:a]] end it 'should store only unique commands' do From 5273954c5e4675f5a5ef9ac5d0661982c9216ac6 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Thu, 11 Oct 2012 18:54:55 +0100 Subject: [PATCH 11/55] support google in the new style commands --- lib/analytical/modules/google.rb | 160 +++++++++++++++---------------- 1 file changed, 79 insertions(+), 81 deletions(-) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index 866981db..8115e321 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -29,91 +29,89 @@ def init_javascript(location) end end - def track(*args) - "_gaq.push(['_trackPageview'#{args.empty? ? ']' : ', "' + args.first + '"]'});" - end - - def event(name, *args) - data = args.first || {} - data = data[:value] if data.is_a?(Hash) - data_string = !data.nil? ? ", #{data}" : "" - "_gaq.push(['_trackEvent', \"Event\", \"#{name}\"" + data_string + "]);" - end - - def custom_event(category, action, opt_label=nil, opt_value=nil) - args = [category, action, opt_label, opt_value].compact - "_gaq.push(" + [ "_trackEvent", *args].to_json + ");" - end - - - # http://code.google.com/apis/analytics/docs/tracking/gaTrackingCustomVariables.html - # - #_setCustomVar(index, name, value, opt_scope) - # - # index — The slot for the custom variable. Required. This is a number whose value can range from 1 - 5, inclusive. - # - # name — The name for the custom variable. Required. This is a string that identifies the custom variable and appears in the top-level Custom Variables report of the Analytics reports. - # - # value — The value for the custom variable. Required. This is a string that is paired with a name. - # - # opt_scope — The scope for the custom variable. Optional. As described above, the scope defines the level of user engagement with your site. - # It is a number whose possible values are 1 (visitor-level), 2 (session-level), or 3 (page-level). - # When left undefined, the custom variable scope defaults to page-level interaction. - def set(data) - if data.is_a?(Hash) && data.keys.any? - index = data[:index].to_i - name = data[:name ] - value = data[:value] - scope = data[:scope] - if (1..5).to_a.include?(index) && !name.nil? && !value.nil? - data = "#{index}, '#{name}', '#{value}'" - data += (1..3).to_a.include?(scope) ? ", #{scope}" : "" - return "_gaq.push(['_setCustomVar', #{ data }]);" - end - end + def event(*args) # name, options, callback + <<-JS.gsub(/^ {10}/, '') + _gaq.push(['_trackEvent', 'Event', name, options && options.value]); + JS end - # http://code.google.com/apis/analytics/docs/gaJS/gaJSApiEcommerce.html#_gat.GA_Tracker_._addTrans - # String orderId Required. Internal unique order id number for this transaction. - # String affiliation Optional. Partner or store affiliation (undefined if absent). - # String total Required. Total dollar amount of the transaction. - # String tax Optional. Tax amount of the transaction. - # String shipping Optional. Shipping charge for the transaction. - # String city Optional. City to associate with transaction. - # String state Optional. State to associate with transaction. - # String country Optional. Country to associate with transaction. - def add_trans(order_id, affiliation=nil, total=nil, tax=nil, shipping=nil, city=nil, state=nil, country=nil) - data = [] - data << "'#{order_id}'" - data << "'#{affiliation}'" - data << "'#{total}'" - data << "'#{tax}'" - data << "'#{shipping}'" - data << "'#{city}'" - data << "'#{state}'" - data << "'#{country}'" + # def track(*args) + # "_gaq.push(['_trackPageview'#{args.empty? ? ']' : ', "' + args.first + '"]'});" + # end - "_gaq.push(['_addTrans', #{data.join(', ')}]);" - end + # def custom_event(category, action, opt_label=nil, opt_value=nil) + # args = [category, action, opt_label, opt_value].compact + # "_gaq.push(" + [ "_trackEvent", *args].to_json + ");" + # end - # http://code.google.com/apis/analytics/docs/gaJS/gaJSApiEcommerce.html#_gat.GA_Tracker_._addItem - # String orderId Optional Order ID of the transaction to associate with item. - # String sku Required. Item's SKU code. - # String name Required. Product name. Required to see data in the product detail report. - # String category Optional. Product category. - # String price Required. Product price. - # String quantity Required. Purchase quantity. - def add_item(order_id, sku, name, category, price, quantity) - data = "'#{order_id}', '#{sku}', '#{name}', '#{category}', '#{price}', '#{quantity}'" - "_gaq.push(['_addItem', #{data}]);" - end - - # http://code.google.com/apis/analytics/docs/gaJS/gaJSApiEcommerce.html#_gat.GA_Tracker_._trackTrans - # Sends both the transaction and item data to the Google Analytics server. - # This method should be used in conjunction with the add_item and add_trans methods. - def track_trans - "_gaq.push(['_trackTrans']);" - end + # # http://code.google.com/apis/analytics/docs/tracking/gaTrackingCustomVariables.html + # # + # #_setCustomVar(index, name, value, opt_scope) + # # + # # index — The slot for the custom variable. Required. This is a number whose value can range from 1 - 5, inclusive. + # # + # # name — The name for the custom variable. Required. This is a string that identifies the custom variable and appears in the top-level Custom Variables report of the Analytics reports. + # # + # # value — The value for the custom variable. Required. This is a string that is paired with a name. + # # + # # opt_scope — The scope for the custom variable. Optional. As described above, the scope defines the level of user engagement with your site. + # # It is a number whose possible values are 1 (visitor-level), 2 (session-level), or 3 (page-level). + # # When left undefined, the custom variable scope defaults to page-level interaction. + # def set(data) + # if data.is_a?(Hash) && data.keys.any? + # index = data[:index].to_i + # name = data[:name ] + # value = data[:value] + # scope = data[:scope] + # if (1..5).to_a.include?(index) && !name.nil? && !value.nil? + # data = "#{index}, '#{name}', '#{value}'" + # data += (1..3).to_a.include?(scope) ? ", #{scope}" : "" + # return "_gaq.push(['_setCustomVar', #{ data }]);" + # end + # end + # end + # + # # http://code.google.com/apis/analytics/docs/gaJS/gaJSApiEcommerce.html#_gat.GA_Tracker_._addTrans + # # String orderId Required. Internal unique order id number for this transaction. + # # String affiliation Optional. Partner or store affiliation (undefined if absent). + # # String total Required. Total dollar amount of the transaction. + # # String tax Optional. Tax amount of the transaction. + # # String shipping Optional. Shipping charge for the transaction. + # # String city Optional. City to associate with transaction. + # # String state Optional. State to associate with transaction. + # # String country Optional. Country to associate with transaction. + # def add_trans(order_id, affiliation=nil, total=nil, tax=nil, shipping=nil, city=nil, state=nil, country=nil) + # data = [] + # data << "'#{order_id}'" + # data << "'#{affiliation}'" + # data << "'#{total}'" + # data << "'#{tax}'" + # data << "'#{shipping}'" + # data << "'#{city}'" + # data << "'#{state}'" + # data << "'#{country}'" + # + # "_gaq.push(['_addTrans', #{data.join(', ')}]);" + # end + # + # # http://code.google.com/apis/analytics/docs/gaJS/gaJSApiEcommerce.html#_gat.GA_Tracker_._addItem + # # String orderId Optional Order ID of the transaction to associate with item. + # # String sku Required. Item's SKU code. + # # String name Required. Product name. Required to see data in the product detail report. + # # String category Optional. Product category. + # # String price Required. Product price. + # # String quantity Required. Purchase quantity. + # def add_item(order_id, sku, name, category, price, quantity) + # data = "'#{order_id}', '#{sku}', '#{name}', '#{category}', '#{price}', '#{quantity}'" + # "_gaq.push(['_addItem', #{data}]);" + # end + # + # # http://code.google.com/apis/analytics/docs/gaJS/gaJSApiEcommerce.html#_gat.GA_Tracker_._trackTrans + # # Sends both the transaction and item data to the Google Analytics server. + # # This method should be used in conjunction with the add_item and add_trans methods. + # def track_trans + # "_gaq.push(['_trackTrans']);" + # end end end From 6cc6676d5af394a5b2b88c0874d20eb4a146b812 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Thu, 11 Oct 2012 18:56:15 +0100 Subject: [PATCH 12/55] fix middleware cookie handling --- lib/analytical/middleware.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/analytical/middleware.rb b/lib/analytical/middleware.rb index 52ef7768..fd207659 100644 --- a/lib/analytical/middleware.rb +++ b/lib/analytical/middleware.rb @@ -10,16 +10,16 @@ def call(env) new_commands = env["analytical"] if new_commands - response = Rack::Response.new(body, status, headers) - cookies = env["rack.cookies"] || {} + new_commands = JSON(new_commands.to_json) rescue [] # stringify symbols so that the uniq below works. + + cookies = Rack::Request.new(env).cookies commands = (JSON(cookies["analytical"]) if cookies["analytical"]) || [] rescue [] - commands.concat(new_commands) - response.set_cookie("analytical", { :value => commands.to_json, :path => "/" }) - response.finish - else - [status, headers, body] + commands = (commands + new_commands).uniq + Rack::Utils.set_cookie_header!(headers, "analytical", :value => commands.to_json, :path => "/") end + + [status, headers, body] end end From d489ab5d38906550f8e0a34268a5de2c007a5bfc Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Tue, 16 Oct 2012 10:51:20 +0100 Subject: [PATCH 13/55] modernize crazy_egg include --- lib/analytical/modules/crazy_egg.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/analytical/modules/crazy_egg.rb b/lib/analytical/modules/crazy_egg.rb index 9a55ae2b..4303b764 100644 --- a/lib/analytical/modules/crazy_egg.rb +++ b/lib/analytical/modules/crazy_egg.rb @@ -13,7 +13,12 @@ def init_javascript(location) code_url = "#{options[:key].to_s[0,4]}/#{options[:key].to_s[4,4]}" js = <<-HTML - + HTML js end From 6c24f75f7309ec4b414fca8de39574b69d42bbec Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Fri, 25 Jan 2013 10:37:53 +0000 Subject: [PATCH 14/55] Fix google event tracking value --- lib/analytical/modules/google.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index 8115e321..a36b7526 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -31,7 +31,7 @@ def init_javascript(location) def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') - _gaq.push(['_trackEvent', 'Event', name, options && options.value]); + _gaq.push(['_trackEvent', 'Event', name, '', options && options.value]); JS end From e30354df93b1954088486dce4837da9528a86256 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Wed, 27 Feb 2013 10:18:44 +0000 Subject: [PATCH 15/55] fix js decoding --- app/views/application/_analytical_fire_events.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/application/_analytical_fire_events.html.erb b/app/views/application/_analytical_fire_events.html.erb index 23a0f9e4..5ac358cc 100644 --- a/app/views/application/_analytical_fire_events.html.erb +++ b/app/views/application/_analytical_fire_events.html.erb @@ -2,7 +2,7 @@ var commands; try { - commands = $.parseJSON(($.cookie('analytical') || '').replace(/\+/g, ' ')) || []; + commands = $.parseJSON(decodeURIComponent(($.cookie('analytical') || '').replace(/\+/g, '%20'))) || []; } catch(e) { commands = []; } From b54806bb7db22b33f2f4b3d7747513f1e170be76 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Tue, 26 Nov 2013 19:16:24 +0000 Subject: [PATCH 16/55] Support Display Advertiser --- lib/analytical/modules/google.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index a36b7526..e1df160a 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -20,7 +20,7 @@ def init_javascript(location) _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; - ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + ga.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); From 8c3b309be777db4b120138794ce6ab90aa98bf16 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Sat, 22 Feb 2014 20:00:39 +0000 Subject: [PATCH 17/55] solve jquery deprecation warning --- app/views/application/_analytical_fire_events.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/application/_analytical_fire_events.html.erb b/app/views/application/_analytical_fire_events.html.erb index 5ac358cc..523f69b8 100644 --- a/app/views/application/_analytical_fire_events.html.erb +++ b/app/views/application/_analytical_fire_events.html.erb @@ -2,7 +2,7 @@ var commands; try { - commands = $.parseJSON(decodeURIComponent(($.cookie('analytical') || '').replace(/\+/g, '%20'))) || []; + commands = $.parseJSON(decodeURIComponent(($.cookie('analytical') || '').replace(/\+/g, '%20')) || '[]'); } catch(e) { commands = []; } From 779cf9cad12686689612bd4edd2698602bea14d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Carric=CC=A7o?= Date: Tue, 25 Mar 2014 17:01:09 +0000 Subject: [PATCH 18/55] Update mixpanel implementation --- lib/analytical/modules/mixpanel.rb | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index e4c1906b..f14d16f3 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -15,14 +15,14 @@ def init_javascript(location) HTML @@ -32,10 +32,13 @@ def init_javascript(location) def identify(*args) # id, options <<-JS.gsub(/^ {10}/, '') - mixpanel.identify(id); - if (options && options.name) { - mixpanel.name_tag(options.name); + if (options) { + mixpanel.people.set(options); + if (options.name) { + mixpanel.name_tag(options.name); + } } + mixpanel.identify(id); JS end From f1eaf465efd3bb9564b6f55cbec92e9ada961543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Carric=CC=A7o?= Date: Wed, 30 Apr 2014 17:19:10 +0100 Subject: [PATCH 19/55] Update google analytics to the new Universal Analytics version --- lib/analytical/modules/google.rb | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index e1df160a..675486d3 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -12,17 +12,14 @@ def init_javascript(location) init_location(location) do js = <<-HTML - HTML js @@ -31,7 +28,7 @@ def init_javascript(location) def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') - _gaq.push(['_trackEvent', 'Event', name, '', options && options.value]); + ga('send', 'event', name, options && options.value); JS end @@ -115,4 +112,4 @@ def event(*args) # name, options, callback end end -end \ No newline at end of file +end From db380aa6862dcc30342a2a1fd1631e1670d79cfe Mon Sep 17 00:00:00 2001 From: pierreozoux Date: Thu, 8 May 2014 10:54:45 +0100 Subject: [PATCH 20/55] Add alias identity to mixpanel module --- lib/analytical/modules/mixpanel.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index f14d16f3..e3042c3a 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -42,6 +42,16 @@ def identify(*args) # id, options JS end + # See https://mixpanel.com/docs/integration-libraries/using-mixpanel-alias + # For consistency with KissMetrics this method accepts two parameters. + # However, the first parameter is ignored because Mixpanel doesn't need it; + # pass any value for the first parameter, e.g. nil. + def alias_identity(*args) # old_identity, new_identity + <<-JS.gsub(/^ {10}/, '') + mixpanel.alias(new_identity); + JS + end + def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') mixpanel.track(name, options || {}); From 9236c461f86c894b58d1ac2729546b01eff1537a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lu=C3=ADs?= Date: Thu, 19 Jun 2014 18:09:09 +0100 Subject: [PATCH 21/55] Fix arguments' order for google analytics tracking method. --- lib/analytical/modules/google.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index 675486d3..934b6542 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -28,7 +28,7 @@ def init_javascript(location) def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') - ga('send', 'event', name, options && options.value); + ga('send', 'event', name, options && options.action, options && options.label, options && options.value); JS end From 77f87ddba7807035dbb2b94ac8177d7ce7379951 Mon Sep 17 00:00:00 2001 From: Duarte Henriques Date: Mon, 30 Jun 2014 16:01:57 +0100 Subject: [PATCH 22/55] minor: correctly indent output --- lib/analytical/modules/google.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index 934b6542..5c1b895c 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -10,7 +10,7 @@ def initialize(options={}) def init_javascript(location) init_location(location) do - js = <<-HTML + js = <<-HTML.gsub(/^ {10}/, '') HTML @@ -31,6 +31,12 @@ def event(*args) # name, options, callback ga('send', 'event', name, options && options.action, options && options.label, options && options.value); JS end + + def linkid + if options[:linkid] + "\n ga('require', 'linkid', 'linkid.js');" + end + end # def track(*args) # "_gaq.push(['_trackPageview'#{args.empty? ? ']' : ', "' + args.first + '"]'});" From 8c3c2ea98ff62d1a3165aac4b477c903cc9eb36c Mon Sep 17 00:00:00 2001 From: Telmo Costa Date: Mon, 7 Jul 2014 18:54:32 +0100 Subject: [PATCH 24/55] send event action as string "undefined" when action is not defined --- lib/analytical/modules/google.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index ecd274bd..5450b41b 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -28,7 +28,7 @@ def init_javascript(location) def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') - ga('send', 'event', name, options && options.action, options && options.label, options && options.value); + ga('send', 'event', name, options && options.action || 'undefined', options && options.label, options && options.value); JS end From 1adb35074e7ef73d9f14b99f81343a14cdb25484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=CC=81rio=20=20Nzualo?= Date: Tue, 31 Mar 2015 13:04:33 +0100 Subject: [PATCH 25/55] Add sift science module --- lib/analytical/modules/sift_science.rb | 51 ++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 lib/analytical/modules/sift_science.rb diff --git a/lib/analytical/modules/sift_science.rb b/lib/analytical/modules/sift_science.rb new file mode 100644 index 00000000..ef69393e --- /dev/null +++ b/lib/analytical/modules/sift_science.rb @@ -0,0 +1,51 @@ +module Analytical + module Modules + class SiftScience + include Analytical::Modules::Base + + def initialize(options={}) + super + @tracking_command_location = :head_append + end + + def init_javascript(location) + init_location(location) do + js = <<-HTML + + + HTML + js + end + end + + def set(*args) + <<-JS.gsub(/^ {10}/, '') + if (properties) { + _sift.push(['_setUserId', properties.user_id]); + _sift.push(['_setSessionId', properties.session_id]); + _sift.push(['_trackPageview']); + } + JS + end + end + end +end From a324f41011a38f3b4cb39cec01f192df9e530b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Carric=CC=A7o?= Date: Tue, 9 Jun 2015 15:20:54 +0100 Subject: [PATCH 26/55] Upgrade jquery Cookie to js cookie --- app/views/application/_analytical_fire_events.html.erb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/application/_analytical_fire_events.html.erb b/app/views/application/_analytical_fire_events.html.erb index 523f69b8..94f6b424 100644 --- a/app/views/application/_analytical_fire_events.html.erb +++ b/app/views/application/_analytical_fire_events.html.erb @@ -2,17 +2,17 @@ var commands; try { - commands = $.parseJSON(decodeURIComponent(($.cookie('analytical') || '').replace(/\+/g, '%20')) || '[]'); + commands = $.parseJSON(decodeURIComponent((Cookies.get('analytical') || '').replace(/\+/g, '%20')) || '[]'); } catch(e) { commands = []; } -$.cookie('analytical', null, { path: '/' }); +Cookies.remove('analytical', { path: '/' }); $.each(commands, function(index, command) { var method = command.shift(), arguments = command; - + if ('function' === typeof Analytical[method]) { Analytical[method].apply(this, arguments); } From f861c5f308a056bb13a38145422b6a96a0f005bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Mon, 7 Dec 2015 15:12:59 +0000 Subject: [PATCH 27/55] Disable robot checking Robots can cause a page to be cached. And that cached page will be used by normal users with analytical disabled. --- lib/analytical.rb | 8 +++++--- spec/analytical_spec.rb | 16 ++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/analytical.rb b/lib/analytical.rb index 61f560c2..ad3604fa 100644 --- a/lib/analytical.rb +++ b/lib/analytical.rb @@ -42,9 +42,11 @@ def analytical if options[:disable_if] && options[:disable_if].call(self) options[:modules] = [] end - if analytical_is_robot?(request.user_agent) - options[:modules] = [] - end + # Don't check for robots, robots can cause a page to be cached. And that cached page will be used by normal + # users with analytical disabled. + # if analytical_is_robot?(request.user_agent) + # options[:modules] = [] + # end options[:modules] = options[:filter_modules].call(self, options[:modules]) if options[:filter_modules] Analytical::Api.new options end diff --git a/spec/analytical_spec.rb b/spec/analytical_spec.rb index 6e6acd4c..4368e0e9 100644 --- a/spec/analytical_spec.rb +++ b/spec/analytical_spec.rb @@ -52,14 +52,14 @@ def request end end - describe 'with a robot request' do - it 'should set the modules to []' do - DummyForInit.analytical - d = DummyForInit.new - d.stub!(:'analytical_is_robot?').and_return(true) - d.analytical.options[:modules].should == [] - end - end + # describe 'with a robot request' do + # it 'should set the modules to []' do + # DummyForInit.analytical + # d = DummyForInit.new + # d.stub!(:'analytical_is_robot?').and_return(true) + # d.analytical.options[:modules].should == [] + # end + # end it 'should open the initialization file' do File.should_receive(:'exists?').with("#{Rails.root}/config/analytical.yml").and_return(true) From 5fce5c315beec4eb0f0b130620d7f1137c78769f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Wed, 9 Dec 2015 09:48:16 +0000 Subject: [PATCH 28/55] Add Google Tag Manager module --- lib/analytical/modules/google_tag_manager.rb | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/analytical/modules/google_tag_manager.rb diff --git a/lib/analytical/modules/google_tag_manager.rb b/lib/analytical/modules/google_tag_manager.rb new file mode 100644 index 00000000..7227ed99 --- /dev/null +++ b/lib/analytical/modules/google_tag_manager.rb @@ -0,0 +1,40 @@ +module Analytical + module Modules + class GoogleTagManager + include Analytical::Modules::Base + + def initialize(options={}) + super + @tracking_command_location = :body_prepend + end + + def init_javascript(location) + init_location(location) do + js = <<-HTML.gsub(/^ {10}/, '') + + + + + HTML + js + end + end + + def event(*args) # name, options, callback + <<-JS.gsub(/^ {10}/, '') + var gtmVariables = {}; + for (var k in options) { + gtmVariables[k] = options[k]; + } + gtmVariables.event = name; + gtmDataLayer.push(gtmVariables); + JS + end + end + end +end From 6ca6056df847152cdd4dd1c51a0df1240a1b2109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Wed, 6 Jan 2016 10:41:40 +0000 Subject: [PATCH 29/55] Cleanup track in mixpanel --- lib/analytical/modules/mixpanel.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index e3042c3a..3974f38e 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -58,13 +58,6 @@ def event(*args) # name, options, callback JS end - # def track(*args) # event, properties, callback - # # event_name, properties, callback - # <<-JS.gsub(/^ {10}/, '') - # mixpanel.track(event, properties, callback); - # JS - # end - # Used to set "Super Properties" - http://mixpanel.com/api/docs/guides/super-properties def set(*args) # properties <<-JS.gsub(/^ {10}/, '') From 70699caf9b061489ad658569dee1b5e7dca829d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Wed, 6 Jan 2016 10:43:17 +0000 Subject: [PATCH 30/55] Enable track (page-view events) javascript --- app/views/application/_analytical_javascript.html.erb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/application/_analytical_javascript.html.erb b/app/views/application/_analytical_javascript.html.erb index f81ae24c..f86a0599 100644 --- a/app/views/application/_analytical_javascript.html.erb +++ b/app/views/application/_analytical_javascript.html.erb @@ -3,6 +3,9 @@ var Analytical = { identify: function(id, options) { <%= raw(analytical.javascript_for.identify.gsub(/^/, " " * 4).strip) %> }, + track: function(page) { + <%= raw(analytical.javascript_for.track.gsub(/^/, " " * 4).strip) %> + }, event: function(name, options, callback) { <%= raw(analytical.javascript_for.event.gsub(/^/, " " * 4).strip) %> }, From 07619ef0779fd090bb8a1a1749a80fe55398ca32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Wed, 6 Jan 2016 10:44:37 +0000 Subject: [PATCH 31/55] Configure track (page-view events) for google --- lib/analytical/modules/console.rb | 2 +- lib/analytical/modules/google.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/analytical/modules/console.rb b/lib/analytical/modules/console.rb index baed0544..ca8b45e7 100644 --- a/lib/analytical/modules/console.rb +++ b/lib/analytical/modules/console.rb @@ -30,7 +30,7 @@ def identify(*args) # id, options JS end - def track(*args) # event, properties, callback + def track(*args) # page <<-JS.gsub(/^ {10}/, '') if (typeof(console) !== 'undefined' && console != null) { console.log('Analytical track', arguments) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index 5450b41b..296eb7bc 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -26,6 +26,12 @@ def init_javascript(location) end end + def track(*args) # page + <<-JS.gsub(/^ {10}/, '') + ga('send', 'pageview', page); + JS + end + def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') ga('send', 'event', name, options && options.action || 'undefined', options && options.label, options && options.value); From 290b184e0d7dd2680c944a07fbbc944bc4eb822d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Thu, 7 Jan 2016 17:58:46 +0000 Subject: [PATCH 32/55] Enable identify option for google module --- lib/analytical/modules/google.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index 296eb7bc..b8f4d935 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -26,6 +26,14 @@ def init_javascript(location) end end + # note that "Session Unification" must be enabled in the GA User-ID feature + # because the page view event happens before the identify + def identify(*args) # id, options + <<-JS.gsub(/^ {10}/, '') + ga('set', 'userId', id); + JS + end + def track(*args) # page <<-JS.gsub(/^ {10}/, '') ga('send', 'pageview', page); From 7d659c6b6dd419255152395b2961b355e7ef3642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Tue, 19 Jan 2016 12:55:35 +0000 Subject: [PATCH 33/55] Allow to send custom dimensions with google track --- app/views/application/_analytical_javascript.html.erb | 2 +- lib/analytical/modules/google.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/application/_analytical_javascript.html.erb b/app/views/application/_analytical_javascript.html.erb index f86a0599..326a02e1 100644 --- a/app/views/application/_analytical_javascript.html.erb +++ b/app/views/application/_analytical_javascript.html.erb @@ -3,7 +3,7 @@ var Analytical = { identify: function(id, options) { <%= raw(analytical.javascript_for.identify.gsub(/^/, " " * 4).strip) %> }, - track: function(page) { + track: function(page, options) { <%= raw(analytical.javascript_for.track.gsub(/^/, " " * 4).strip) %> }, event: function(name, options, callback) { diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index b8f4d935..d7abe7dd 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -34,9 +34,9 @@ def identify(*args) # id, options JS end - def track(*args) # page + def track(*args) # page, options <<-JS.gsub(/^ {10}/, '') - ga('send', 'pageview', page); + ga('send', 'pageview', page, options || {}); JS end From 5ee4891f3b16725050dddc35d222df763a09efd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Tue, 2 Feb 2016 11:46:14 +0000 Subject: [PATCH 34/55] Properly enable identify option for google module --- lib/analytical/modules/google.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index d7abe7dd..fda66fbe 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -19,6 +19,10 @@ def init_javascript(location) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', '#{options[:key]}', 'auto');#{linkid} + var userId = document.cookie.replace(/(?:(?:^|.*;\s*)external_user_id\s*\=\s*([^;]*).*$)|^.*$/, "$1"); + if(userId.length > 0) { + ga('set', 'userId', userId); + } ga('send', 'pageview'); HTML @@ -26,14 +30,6 @@ def init_javascript(location) end end - # note that "Session Unification" must be enabled in the GA User-ID feature - # because the page view event happens before the identify - def identify(*args) # id, options - <<-JS.gsub(/^ {10}/, '') - ga('set', 'userId', id); - JS - end - def track(*args) # page, options <<-JS.gsub(/^ {10}/, '') ga('send', 'pageview', page, options || {}); From 393ccfa7a7f65d5454af0c0c66296adb5f626d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Nzualo?= Date: Wed, 13 Apr 2016 11:58:28 +0100 Subject: [PATCH 35/55] Integrate GA and Optimizely --- lib/analytical/modules/google.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index fda66fbe..9be465a3 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -23,6 +23,11 @@ def init_javascript(location) if(userId.length > 0) { ga('set', 'userId', userId); } + + // Optimizely Universal Analytics Integration Code + window.optimizely = window.optimizely || []; + window.optimizely.push("activateUniversalAnalytics"); + ga('send', 'pageview'); HTML From d0079752bf0bf7fd1b4d1748480843449436a8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Nzualo?= Date: Fri, 13 May 2016 15:18:11 +0100 Subject: [PATCH 36/55] Track events in intercom --- lib/analytical/modules/intercom.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 lib/analytical/modules/intercom.rb diff --git a/lib/analytical/modules/intercom.rb b/lib/analytical/modules/intercom.rb new file mode 100644 index 00000000..18ebb34b --- /dev/null +++ b/lib/analytical/modules/intercom.rb @@ -0,0 +1,20 @@ +module Analytical + module Modules + class Intercom + include Analytical::Modules::Base + + def initialize(options={}) + super + @tracking_command_location = :body_append + end + + # Intercom indentification is being done through the intercom-rails gem + + def event(*args) # name, options, callback + <<-JS.gsub(/^ {10}/, '') + Intercom('trackEvent', name, options || {}); + JS + end + end + end +end From 5fb5ea69adef9aa03cfb156506684322dc66c1b0 Mon Sep 17 00:00:00 2001 From: Telmo Costa Date: Tue, 14 Jun 2016 17:19:40 +0100 Subject: [PATCH 37/55] Spelling fix --- lib/analytical/modules/intercom.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/intercom.rb b/lib/analytical/modules/intercom.rb index 18ebb34b..152484cf 100644 --- a/lib/analytical/modules/intercom.rb +++ b/lib/analytical/modules/intercom.rb @@ -8,7 +8,7 @@ def initialize(options={}) @tracking_command_location = :body_append end - # Intercom indentification is being done through the intercom-rails gem + # Intercom identification is being done through the intercom-rails gem def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') From 82b8d7cc0cd61d80acf240b8102914eb3a257319 Mon Sep 17 00:00:00 2001 From: Telmo Costa Date: Tue, 14 Jun 2016 17:22:09 +0100 Subject: [PATCH 38/55] Update intercom user after event track Allow intercom to be aware of user data update. Needed for the case of auto-messaging to react on events without a page reload. --- lib/analytical/modules/intercom.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/analytical/modules/intercom.rb b/lib/analytical/modules/intercom.rb index 152484cf..3fab60a9 100644 --- a/lib/analytical/modules/intercom.rb +++ b/lib/analytical/modules/intercom.rb @@ -13,6 +13,9 @@ def initialize(options={}) def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') Intercom('trackEvent', name, options || {}); + setTimeout(function() { + Intercom('update'); + }, 2000); JS end end From 6a64ec1413cb38c675614c6ccab75442ed1044f4 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Mon, 22 Aug 2016 18:25:03 +0100 Subject: [PATCH 39/55] Update mixpanel lib --- lib/analytical/modules/mixpanel.rb | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index 3974f38e..978a3fea 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -11,20 +11,10 @@ def initialize(options={}) def init_javascript(location) init_location(location) do js = <<-HTML - - + HTML js end From a1ebeaa4a1417c643f0bba724d6db77b2e3fc90e Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Mon, 22 Aug 2016 17:03:18 +0100 Subject: [PATCH 40/55] Add reset method The reset method should be implemented when the service provides some kind of clean up task to be executed when the user logs out. --- app/views/application/_analytical_javascript.html.erb | 3 +++ lib/analytical/modules/base.rb | 4 ++++ lib/analytical/modules/mixpanel.rb | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/app/views/application/_analytical_javascript.html.erb b/app/views/application/_analytical_javascript.html.erb index 326a02e1..1129aab6 100644 --- a/app/views/application/_analytical_javascript.html.erb +++ b/app/views/application/_analytical_javascript.html.erb @@ -12,6 +12,9 @@ var Analytical = { set: function(properties) { <%= raw(analytical.javascript_for.set.gsub(/^/, " " * 4).strip) %> }, + reset: function(properties) { + <%= raw(analytical.javascript_for.reset.gsub(/^/, " " * 4).strip) %> + }, alias_identity: function(old_identity, new_identity) { <%= raw(analytical.javascript_for.alias_identity.gsub(/^/, " " * 4).strip) %> } diff --git a/lib/analytical/modules/base.rb b/lib/analytical/modules/base.rb index a0664053..1729175d 100644 --- a/lib/analytical/modules/base.rb +++ b/lib/analytical/modules/base.rb @@ -41,6 +41,10 @@ def protocol # This method generates the initialization javascript that an analytics service uses to track your site # def init_javascript(location) + # The reset method should be implemented when the service provides some kind of clean up task to be executed + # when the user logs out + # def reset + def queue(*args) return if @command_store.include?(args) if args.first == :identify diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index 978a3fea..6b383466 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -55,6 +55,13 @@ def set(*args) # properties JS end + # Clears super properties and generates a new random distinct_id for this instance + # https://mixpanel.com/help/reference/javascript-full-api-reference#mixpanel.reset + def reset + <<-JS.gsub(/^ {10}/, '') + mixpanel.reset(); + JS + end end end end From 182139e45c108a2e2bc366a3909e9103491d0769 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Tue, 23 Aug 2016 14:00:33 +0100 Subject: [PATCH 41/55] Fix mixpanel lib We need to double escape the backslash character because the heredoc results in a escaped string. We can't disable the interpolation and escaping from the heredoc because we're using interpolation to insert the mixpanel token. --- lib/analytical/modules/mixpanel.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index 6b383466..5e394d50 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -13,7 +13,7 @@ def init_javascript(location) js = <<-HTML HTML js From 16671d35a8b8701f1ee10041ab6296dff39ae9c2 Mon Sep 17 00:00:00 2001 From: JSFernandes Date: Wed, 31 Aug 2016 18:17:45 +0100 Subject: [PATCH 42/55] Update AdRoll code --- lib/analytical/modules/adroll.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/analytical/modules/adroll.rb b/lib/analytical/modules/adroll.rb index 584652a3..f2265db2 100644 --- a/lib/analytical/modules/adroll.rb +++ b/lib/analytical/modules/adroll.rb @@ -24,7 +24,7 @@ def init_javascript(location) scr.setAttribute('async', 'true'); scr.type = "text/javascript"; scr.src = host + "/j/roundtrip.js"; - document.documentElement.firstChild.appendChild(scr); + ((document.getElementsByTagName('head') || [null])[0] || document.getElementsByTagName('script')[0].parentNode).appendChild(scr); if(oldonload){oldonload()}}; }()); @@ -35,4 +35,4 @@ def init_javascript(location) end end -end \ No newline at end of file +end From 7f6ec2ef673aa24616c1d0a8b4898524f5a0e724 Mon Sep 17 00:00:00 2001 From: JSFernandes Date: Thu, 1 Sep 2016 13:04:08 +0100 Subject: [PATCH 43/55] Update adroll code again Previous update did not include changes that I thought were superficial --- lib/analytical/modules/adroll.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/analytical/modules/adroll.rb b/lib/analytical/modules/adroll.rb index f2265db2..b6c2d80a 100644 --- a/lib/analytical/modules/adroll.rb +++ b/lib/analytical/modules/adroll.rb @@ -16,16 +16,19 @@ def init_javascript(location) adroll_adv_id = "#{options[:adv_id]}"; adroll_pix_id = "#{options[:pix_id]}"; (function () { - var oldonload = window.onload; - window.onload = function(){ - __adroll_loaded=true; - var scr = document.createElement("script"); - var host = (("https:" == document.location.protocol) ? "https://s.adroll.com" : "http://a.adroll.com"); - scr.setAttribute('async', 'true'); - scr.type = "text/javascript"; - scr.src = host + "/j/roundtrip.js"; - ((document.getElementsByTagName('head') || [null])[0] || document.getElementsByTagName('script')[0].parentNode).appendChild(scr); - if(oldonload){oldonload()}}; + var _onload = function(){ + if (document.readyState && !/loaded|complete/.test(document.readyState)){setTimeout(_onload, 10);return} + if (!window.__adroll_loaded){__adroll_loaded=true;setTimeout(_onload, 50);return} + var scr = document.createElement("script"); + var host = (("https:" == document.location.protocol) ? "https://s.adroll.com" : "http://a.adroll.com"); + scr.setAttribute('async', 'true'); + scr.type = "text/javascript"; + scr.src = host + "/j/roundtrip.js"; + ((document.getElementsByTagName('head') || [null])[0] || + document.getElementsByTagName('script')[0].parentNode).appendChild(scr); + }; + if (window.addEventListener) {window.addEventListener('load', _onload, false);} + else {window.attachEvent('onload', _onload)} }()); HTML From 7445db9642d15bcf2d122882d6eddc51888da519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Patr=C3=ADcio?= Date: Fri, 28 Oct 2016 09:27:39 +0100 Subject: [PATCH 44/55] Check if intercom is defined before calling it --- lib/analytical/modules/intercom.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/analytical/modules/intercom.rb b/lib/analytical/modules/intercom.rb index 3fab60a9..5d9b78a1 100644 --- a/lib/analytical/modules/intercom.rb +++ b/lib/analytical/modules/intercom.rb @@ -12,10 +12,12 @@ def initialize(options={}) def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') - Intercom('trackEvent', name, options || {}); - setTimeout(function() { - Intercom('update'); - }, 2000); + if (typeof(Intercom) !== 'undefined') { + Intercom('trackEvent', name, options || {}); + setTimeout(function() { + Intercom('update'); + }, 2000); + } JS end end From 646c5f30a1e44ab21ade9a0b97e3499d10831406 Mon Sep 17 00:00:00 2001 From: Nick Sorros Date: Tue, 15 Nov 2016 15:23:47 +0000 Subject: [PATCH 45/55] Add segment --- lib/analytical/modules/segment.rb | 130 ++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 lib/analytical/modules/segment.rb diff --git a/lib/analytical/modules/segment.rb b/lib/analytical/modules/segment.rb new file mode 100644 index 00000000..6a387e85 --- /dev/null +++ b/lib/analytical/modules/segment.rb @@ -0,0 +1,130 @@ +module Analytical + module Modules + class Segment + include Analytical::Modules::Base + + def initialize(options={}) + super + @tracking_command_location = :body_append + end + + def init_javascript(location) + init_location(location) do + js = <<-HTML + + + HTML + js + end + end + + def identify(*args) # id, options + <<-JS.gsub(/^ {10}/, '') + analytics.identify(id); + JS + end + + def alias_identity(*args) # old_identity, new_identity + <<-JS.gsub(/^ {10}/, '') + analytics.alias(new_identity); + JS + end + + def event(*args) # name, options, callback + <<-JS.gsub(/^ {10}/, '') + analytics.track(name, options || {}); + JS + end + end + end +end From f3be42fe4946efa4116f038f8c61d405444e70e4 Mon Sep 17 00:00:00 2001 From: Nick Sorros Date: Fri, 25 Nov 2016 10:24:36 +0000 Subject: [PATCH 46/55] Add the ability to track virtual page views --- lib/analytical/modules/segment.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/analytical/modules/segment.rb b/lib/analytical/modules/segment.rb index 6a387e85..7453f715 100644 --- a/lib/analytical/modules/segment.rb +++ b/lib/analytical/modules/segment.rb @@ -120,6 +120,12 @@ def alias_identity(*args) # old_identity, new_identity JS end + def track(*args) # page, options + <<-JS.gsub(/^ {10}/, '') + analytics.page({'path': page}) + JS + end + def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') analytics.track(name, options || {}); From d23030e4da3afd683699461710e6036baf7f435f Mon Sep 17 00:00:00 2001 From: Nick Sorros Date: Fri, 25 Nov 2016 10:36:06 +0000 Subject: [PATCH 47/55] Add url in segment track method --- lib/analytical/modules/segment.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/segment.rb b/lib/analytical/modules/segment.rb index 7453f715..d784fcad 100644 --- a/lib/analytical/modules/segment.rb +++ b/lib/analytical/modules/segment.rb @@ -122,7 +122,7 @@ def alias_identity(*args) # old_identity, new_identity def track(*args) # page, options <<-JS.gsub(/^ {10}/, '') - analytics.page({'path': page}) + analytics.page({'path': page, 'url': 'https://www.seedrs.com'.concat(page)}) JS end From a976fc01e79cf5728f0408f2a7dd64e45f3b1e9f Mon Sep 17 00:00:00 2001 From: Nick Sorros Date: Fri, 16 Dec 2016 18:15:39 +0000 Subject: [PATCH 48/55] Add options as a string in GA to be able to filter --- lib/analytical/modules/google.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index 9be465a3..1f7fae80 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -43,7 +43,7 @@ def track(*args) # page, options def event(*args) # name, options, callback <<-JS.gsub(/^ {10}/, '') - ga('send', 'event', name, options && options.action || 'undefined', options && options.label, options && options.value); + ga('send', 'event', name, options && options.action || 'undefined', JSON.stringify(options), options && options.value); JS end From 3ac663d5a24862128245978fa3b88df6029a9cf5 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Tue, 10 Jan 2017 09:34:11 +0000 Subject: [PATCH 49/55] Remove intercom integration We're going to use just the intercom-rails gem to track only the events needed for intercom --- lib/analytical/modules/intercom.rb | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 lib/analytical/modules/intercom.rb diff --git a/lib/analytical/modules/intercom.rb b/lib/analytical/modules/intercom.rb deleted file mode 100644 index 5d9b78a1..00000000 --- a/lib/analytical/modules/intercom.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Analytical - module Modules - class Intercom - include Analytical::Modules::Base - - def initialize(options={}) - super - @tracking_command_location = :body_append - end - - # Intercom identification is being done through the intercom-rails gem - - def event(*args) # name, options, callback - <<-JS.gsub(/^ {10}/, '') - if (typeof(Intercom) !== 'undefined') { - Intercom('trackEvent', name, options || {}); - setTimeout(function() { - Intercom('update'); - }, 2000); - } - JS - end - end - end -end From b01d9418ad6fbcaabd6dae3b526123cac448e5dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Fri, 9 Feb 2018 11:06:11 +0000 Subject: [PATCH 50/55] Ignore referrer when cookie for that is present The way to ignore the referrer is to set our own domain as the referrer. --- lib/analytical/modules/google.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/analytical/modules/google.rb b/lib/analytical/modules/google.rb index 1f7fae80..f210d045 100644 --- a/lib/analytical/modules/google.rb +++ b/lib/analytical/modules/google.rb @@ -19,6 +19,10 @@ def init_javascript(location) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', '#{options[:key]}', 'auto');#{linkid} + var match = document.cookie.match(new RegExp('(^| )ignored_referrer=([^;]+)')); + if (match && decodeURIComponent(match[2]) === document.referrer) { + ga('set', 'referrer', window.location.origin); + } var userId = document.cookie.replace(/(?:(?:^|.*;\s*)external_user_id\s*\=\s*([^;]*).*$)|^.*$/, "$1"); if(userId.length > 0) { ga('set', 'userId', userId); From 5c7e62834a80a6c621c4a63b63434d41602840ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Patri=CC=81cio?= Date: Wed, 9 May 2018 12:13:11 +0100 Subject: [PATCH 51/55] Fix rails 5 deprecation --- lib/analytical/modules/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/base.rb b/lib/analytical/modules/base.rb index 1729175d..3dec61d6 100644 --- a/lib/analytical/modules/base.rb +++ b/lib/analytical/modules/base.rb @@ -52,7 +52,7 @@ def queue(*args) else @command_store << args end - @options[:controller].env["analytical"] = @command_store.commands + @options[:controller].request.env["analytical"] = @command_store.commands end def init_location?(location) From eb339cc6ade90637931c27fbf376f9a31411a536 Mon Sep 17 00:00:00 2001 From: brizido Date: Thu, 24 May 2018 23:07:27 +0100 Subject: [PATCH 52/55] Implementing not track on Mixpanel --- lib/analytical/modules/mixpanel.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index 5e394d50..89d163b2 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -14,7 +14,8 @@ def init_javascript(location) + var mixpanel_tracking_opt = {}; if (document.cookie.replace(/(?:(?:^|.*;\s*)performance_cookie\s*\=\s*([^;]*).*$)|^.*$/, "$1") != "true") { mixpanel_tracking = {opt_out_tracking_by_default: true} } + mixpanel.init('#{options[:key]}', mixpanel_tracking_opt); HTML js end From 11d6711ac242b69434543d32255a0cd6aa1ae506 Mon Sep 17 00:00:00 2001 From: brizido Date: Thu, 24 May 2018 23:33:24 +0100 Subject: [PATCH 53/55] Fixing typo --- lib/analytical/modules/mixpanel.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index 89d163b2..e782cd3e 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -14,7 +14,7 @@ def init_javascript(location) HTML js From d985771257d422afe75f409353fa10edaa41527f Mon Sep 17 00:00:00 2001 From: brizido Date: Thu, 31 May 2018 11:03:04 +0100 Subject: [PATCH 54/55] Fixing issue where visitors where not being tracked --- lib/analytical/modules/mixpanel.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index e782cd3e..d5e783cc 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -14,7 +14,7 @@ def init_javascript(location) HTML js From 2bb03e68cac84454a3efc895d62f6883e8e121bc Mon Sep 17 00:00:00 2001 From: brizido Date: Mon, 16 Jul 2018 14:18:51 +0100 Subject: [PATCH 55/55] Fixing mixpanel tracking --- lib/analytical/modules/mixpanel.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/analytical/modules/mixpanel.rb b/lib/analytical/modules/mixpanel.rb index d5e783cc..c4daa99f 100644 --- a/lib/analytical/modules/mixpanel.rb +++ b/lib/analytical/modules/mixpanel.rb @@ -14,8 +14,8 @@ def init_javascript(location) + var mixpanel_tracking_opt = {}; var mixpanel_opt_in = true; if ((document.cookie.replace(/(?:(?:^|.*;\s*)performance_cookie\s*\=\s*([^;]*).*$)|^.*$/, "$1") != "true") && (document.cookie.indexOf("performance_cookie") != -1)) { mixpanel_tracking_opt = {opt_out_tracking_by_default: true}; mixpanel_opt_in = false; } + mixpanel.init('#{options[:key]}', mixpanel_tracking_opt); if(mixpanel_opt_in){ mixpanel.opt_in_tracking(); } HTML js end