From 0fcd9f19c4385c6eed94df5e32ebe831e8a1b9b1 Mon Sep 17 00:00:00 2001 From: Andrew McElroy Date: Mon, 21 Mar 2011 12:25:35 -0500 Subject: [PATCH] one of a lot of updates --- TryRuby/.gitignore | 4 + TryRuby/.rspec | 1 + TryRuby/.rvmrc | 1 + TryRuby/app/controllers/classic_controller.rb | 2 + TryRuby/app/controllers/irb_controller.rb | 85 + TryRuby/app/controllers/public_controller.rb | 2 + TryRuby/app/helpers/classic_helper.rb | 2 + TryRuby/app/helpers/irb_helper.rb | 2 + TryRuby/app/helpers/public_helper.rb | 2 + TryRuby/app/views/irb/_form.html.erb | 17 + TryRuby/app/views/irb/edit.html.erb | 6 + TryRuby/app/views/irb/index.html.erb | 21 + TryRuby/app/views/irb/new.html.erb | 5 + TryRuby/app/views/irb/show.html.erb | 5 + TryRuby/app/views/layouts/tryruby_es.rhtml | 96 + TryRuby/app/views/tryruby/_donate.html.erb | 41 + TryRuby/config.ru | 4 + TryRuby/config/application.rb | 42 + TryRuby/config/cucumber.yml | 8 + TryRuby/config/initializers/secret_token.rb | 7 + .../db/migrate/20110314191710_create_irb.rb | 12 + TryRuby/db/schema.rb | 15 + TryRuby/features/lesson1.feature | 7 + TryRuby/features/lesson2.feature | 0 .../features/step_definitions/web_steps.rb | 219 +++ TryRuby/features/support/env.rb | 57 + TryRuby/features/support/paths.rb | 33 + TryRuby/lib/tasks/.gitkeep | 0 TryRuby/lib/tasks/cucumber.rake | 53 + TryRuby/public/images/ajax-loader.gif | Bin 0 -> 673 bytes TryRuby/public/images/closelabel.png | Bin 0 -> 168 bytes TryRuby/public/javascripts/facebox.js | 309 ++++ TryRuby/public/javascripts/jQuery.irb.js | 1641 +++++++++++++++++ TryRuby/public/javascripts/raphael-min.js | 113 ++ TryRuby/public/stylesheets/facebox.css | 83 + TryRuby/public/stylesheets/reset.css | 46 + TryRuby/public/stylesheets/scaffold.css | 56 + TryRuby/public/tutorials/es_intro.html | 666 +++++++ TryRuby/public/tutorials/intro.html | 680 +++++++ TryRuby/public/tutorials/intro.html.broken | 638 +++++++ TryRuby/public/tutorials/intro_files/sick.gif | Bin 0 -> 94 bytes TryRuby/script/cucumber | 10 + TryRuby/script/rails | 6 + .../controllers/classic_controller_spec.rb | 5 + .../spec/controllers/irb_controller_spec.rb | 125 ++ .../controllers/public_controller_spec.rb | 5 + TryRuby/spec/helpers/classic_helper_spec.rb | 15 + TryRuby/spec/helpers/irb_helper_spec.rb | 15 + TryRuby/spec/helpers/public_helper_spec.rb | 15 + TryRuby/spec/models/irb_spec.rb | 5 + TryRuby/spec/requests/irb_spec.rb | 11 + TryRuby/spec/routing/irb_routing_spec.rb | 35 + TryRuby/spec/spec_helper.rb | 27 + TryRuby/spec/views/irb/edit.html.erb_spec.rb | 15 + TryRuby/spec/views/irb/index.html.erb_spec.rb | 14 + TryRuby/spec/views/irb/new.html.erb_spec.rb | 15 + TryRuby/spec/views/irb/show.html.erb_spec.rb | 11 + TryRuby/vendor/plugins/.gitkeep | 0 public/images/header.png | Bin 46523 -> 51873 bytes public/index.html | 46 +- public/javascripts/application.js | 2 + public/javascripts/console.js | 288 +++ public/javascripts/controls.js | 963 ++++++++++ public/javascripts/dragdrop.js | 973 ++++++++++ public/javascripts/effects.js | 1128 +++++++++++ public/javascripts/facebox.js | 309 ++++ public/javascripts/index.html | 8 + public/javascripts/irb.js | 110 ++ public/javascripts/jQuery.irb.js | 1641 +++++++++++++++++ public/javascripts/jquery-1.3.2.min.js | 19 + public/javascripts/jquery-1.4.2.min.js | 154 ++ public/javascripts/jquery.console.js | 609 ++++++ public/javascripts/jquery.console.min.js | 1 + public/javascripts/jquery.js | 32 + public/javascripts/json2.js | 1 + public/javascripts/lib.min.js | 159 ++ public/javascripts/mouseapp_2.js | 913 +++++++++ public/javascripts/mouseirb_2.js | 179 ++ public/javascripts/raphael-min.js | 113 ++ public/stylesheets/facebox.css | 83 + public/stylesheets/reset.css | 46 + public/stylesheets/scaffold.css | 56 + public/stylesheets/site.css | 378 ++++ tryruby/Gemfile | 52 +- tryruby/README | 265 ++- tryruby/Rakefile | 7 +- .../app/controllers/application_controller.rb | 13 +- tryruby/app/controllers/tryruby_controller.rb | 13 +- tryruby/app/helpers/application_helper.rb | 17 +- tryruby/app/views/layouts/tryruby.rhtml | 82 +- tryruby/app/views/tryruby/index.rhtml | 12 +- tryruby/config/boot.rb | 126 +- tryruby/config/database.yml | 36 +- tryruby/config/environment.rb | 48 +- tryruby/config/environments/development.rb | 35 +- tryruby/config/environments/production.rb | 61 +- tryruby/config/environments/test.rb | 63 +- .../initializers/backtrace_silencers.rb | 4 +- tryruby/config/initializers/inflections.rb | 8 +- tryruby/config/initializers/session_store.rb | 13 +- tryruby/config/locales/en.yml | 2 +- tryruby/config/routes.rb | 69 +- tryruby/db/seeds.rb | 4 +- tryruby/lib/tryruby.rb | 4 +- tryruby/public/images/header.png | Bin 46523 -> 51873 bytes tryruby/public/javascripts/jquery.console.js | 347 ++-- tryruby/public/stylesheets/site.css | 193 +- tryruby/test/performance/browsing_test.rb | 4 +- tryruby/test/test_helper.rb | 53 +- tryruby/tmp/pids/server.pid | 2 +- 110 files changed, 14549 insertions(+), 505 deletions(-) create mode 100644 TryRuby/.gitignore create mode 100644 TryRuby/.rspec create mode 100644 TryRuby/.rvmrc create mode 100644 TryRuby/app/controllers/classic_controller.rb create mode 100644 TryRuby/app/controllers/irb_controller.rb create mode 100644 TryRuby/app/controllers/public_controller.rb create mode 100644 TryRuby/app/helpers/classic_helper.rb create mode 100644 TryRuby/app/helpers/irb_helper.rb create mode 100644 TryRuby/app/helpers/public_helper.rb create mode 100644 TryRuby/app/views/irb/_form.html.erb create mode 100644 TryRuby/app/views/irb/edit.html.erb create mode 100644 TryRuby/app/views/irb/index.html.erb create mode 100644 TryRuby/app/views/irb/new.html.erb create mode 100644 TryRuby/app/views/irb/show.html.erb create mode 100644 TryRuby/app/views/layouts/tryruby_es.rhtml create mode 100644 TryRuby/app/views/tryruby/_donate.html.erb create mode 100644 TryRuby/config.ru create mode 100644 TryRuby/config/application.rb create mode 100644 TryRuby/config/cucumber.yml create mode 100644 TryRuby/config/initializers/secret_token.rb create mode 100644 TryRuby/db/migrate/20110314191710_create_irb.rb create mode 100644 TryRuby/db/schema.rb create mode 100644 TryRuby/features/lesson1.feature create mode 100644 TryRuby/features/lesson2.feature create mode 100644 TryRuby/features/step_definitions/web_steps.rb create mode 100644 TryRuby/features/support/env.rb create mode 100644 TryRuby/features/support/paths.rb create mode 100644 TryRuby/lib/tasks/.gitkeep create mode 100644 TryRuby/lib/tasks/cucumber.rake create mode 100644 TryRuby/public/images/ajax-loader.gif create mode 100755 TryRuby/public/images/closelabel.png create mode 100755 TryRuby/public/javascripts/facebox.js create mode 100644 TryRuby/public/javascripts/jQuery.irb.js create mode 100644 TryRuby/public/javascripts/raphael-min.js create mode 100755 TryRuby/public/stylesheets/facebox.css create mode 100644 TryRuby/public/stylesheets/reset.css create mode 100644 TryRuby/public/stylesheets/scaffold.css create mode 100644 TryRuby/public/tutorials/es_intro.html create mode 100644 TryRuby/public/tutorials/intro.html create mode 100755 TryRuby/public/tutorials/intro.html.broken create mode 100755 TryRuby/public/tutorials/intro_files/sick.gif create mode 100755 TryRuby/script/cucumber create mode 100755 TryRuby/script/rails create mode 100644 TryRuby/spec/controllers/classic_controller_spec.rb create mode 100644 TryRuby/spec/controllers/irb_controller_spec.rb create mode 100644 TryRuby/spec/controllers/public_controller_spec.rb create mode 100644 TryRuby/spec/helpers/classic_helper_spec.rb create mode 100644 TryRuby/spec/helpers/irb_helper_spec.rb create mode 100644 TryRuby/spec/helpers/public_helper_spec.rb create mode 100644 TryRuby/spec/models/irb_spec.rb create mode 100644 TryRuby/spec/requests/irb_spec.rb create mode 100644 TryRuby/spec/routing/irb_routing_spec.rb create mode 100644 TryRuby/spec/spec_helper.rb create mode 100644 TryRuby/spec/views/irb/edit.html.erb_spec.rb create mode 100644 TryRuby/spec/views/irb/index.html.erb_spec.rb create mode 100644 TryRuby/spec/views/irb/new.html.erb_spec.rb create mode 100644 TryRuby/spec/views/irb/show.html.erb_spec.rb create mode 100644 TryRuby/vendor/plugins/.gitkeep create mode 100644 public/javascripts/application.js create mode 100644 public/javascripts/console.js create mode 100644 public/javascripts/controls.js create mode 100644 public/javascripts/dragdrop.js create mode 100644 public/javascripts/effects.js create mode 100755 public/javascripts/facebox.js create mode 100755 public/javascripts/index.html create mode 100755 public/javascripts/irb.js create mode 100644 public/javascripts/jQuery.irb.js create mode 100644 public/javascripts/jquery-1.3.2.min.js create mode 100644 public/javascripts/jquery-1.4.2.min.js create mode 100644 public/javascripts/jquery.console.js create mode 100644 public/javascripts/jquery.console.min.js create mode 100755 public/javascripts/jquery.js create mode 100644 public/javascripts/json2.js create mode 100644 public/javascripts/lib.min.js create mode 100755 public/javascripts/mouseapp_2.js create mode 100755 public/javascripts/mouseirb_2.js create mode 100644 public/javascripts/raphael-min.js create mode 100755 public/stylesheets/facebox.css create mode 100644 public/stylesheets/reset.css create mode 100644 public/stylesheets/scaffold.css create mode 100755 public/stylesheets/site.css diff --git a/TryRuby/.gitignore b/TryRuby/.gitignore new file mode 100644 index 0000000..f0fa30c --- /dev/null +++ b/TryRuby/.gitignore @@ -0,0 +1,4 @@ +.bundle +db/*.sqlite3 +log/*.log +tmp/ diff --git a/TryRuby/.rspec b/TryRuby/.rspec new file mode 100644 index 0000000..53607ea --- /dev/null +++ b/TryRuby/.rspec @@ -0,0 +1 @@ +--colour diff --git a/TryRuby/.rvmrc b/TryRuby/.rvmrc new file mode 100644 index 0000000..39b50dd --- /dev/null +++ b/TryRuby/.rvmrc @@ -0,0 +1 @@ +rvm use 1.9.2@tryruby diff --git a/TryRuby/app/controllers/classic_controller.rb b/TryRuby/app/controllers/classic_controller.rb new file mode 100644 index 0000000..019e234 --- /dev/null +++ b/TryRuby/app/controllers/classic_controller.rb @@ -0,0 +1,2 @@ +class ClassicController < ApplicationController +end diff --git a/TryRuby/app/controllers/irb_controller.rb b/TryRuby/app/controllers/irb_controller.rb new file mode 100644 index 0000000..f296b07 --- /dev/null +++ b/TryRuby/app/controllers/irb_controller.rb @@ -0,0 +1,85 @@ +class IrbController < ApplicationController + # GET /irb + # GET /irb.xml + def index + + @irb = [] + + respond_to do |format| + format.html # index.html.erb + format.to_json + format.xml { render :xml => @irb } + end + end + + # GET /irb/1 + # GET /irb/1.xml + def show + @irb = Irb.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.xml { render :xml => @irb } + end + end + + # GET /irb/new + # GET /irb/new.xml + def new + @irb = Irb.new + + respond_to do |format| + format.html # new.html.erb + format.xml { render :xml => @irb } + end + end + + # GET /irb/1/edit + def edit + @irb = Irb.find(params[:id]) + end + + # POST /irb + # POST /irb.xml + def create + @irb = Irb.new(params[:irb]) + + respond_to do |format| + if @irb.save + format.html { redirect_to(@irb, :notice => 'Irb was successfully created.') } + format.xml { render :xml => @irb, :status => :created, :location => @irb } + else + format.html { render :action => "new" } + format.xml { render :xml => @irb.errors, :status => :unprocessable_entity } + end + end + end + + # PUT /irb/1 + # PUT /irb/1.xml + def update + @irb = Irb.find(params[:id]) + + respond_to do |format| + if @irb.update_attributes(params[:irb]) + format.html { redirect_to(@irb, :notice => 'Irb was successfully updated.') } + format.xml { head :ok } + else + format.html { render :action => "edit" } + format.xml { render :xml => @irb.errors, :status => :unprocessable_entity } + end + end + end + + # DELETE /irb/1 + # DELETE /irb/1.xml + def destroy + @irb = Irb.find(params[:id]) + @irb.destroy + + respond_to do |format| + format.html { redirect_to(irb_url) } + format.xml { head :ok } + end + end +end diff --git a/TryRuby/app/controllers/public_controller.rb b/TryRuby/app/controllers/public_controller.rb new file mode 100644 index 0000000..2857026 --- /dev/null +++ b/TryRuby/app/controllers/public_controller.rb @@ -0,0 +1,2 @@ +class PublicController < ApplicationController +end diff --git a/TryRuby/app/helpers/classic_helper.rb b/TryRuby/app/helpers/classic_helper.rb new file mode 100644 index 0000000..e85110a --- /dev/null +++ b/TryRuby/app/helpers/classic_helper.rb @@ -0,0 +1,2 @@ +module ClassicHelper +end diff --git a/TryRuby/app/helpers/irb_helper.rb b/TryRuby/app/helpers/irb_helper.rb new file mode 100644 index 0000000..6c9f3c9 --- /dev/null +++ b/TryRuby/app/helpers/irb_helper.rb @@ -0,0 +1,2 @@ +module IrbHelper +end diff --git a/TryRuby/app/helpers/public_helper.rb b/TryRuby/app/helpers/public_helper.rb new file mode 100644 index 0000000..0d8e188 --- /dev/null +++ b/TryRuby/app/helpers/public_helper.rb @@ -0,0 +1,2 @@ +module PublicHelper +end diff --git a/TryRuby/app/views/irb/_form.html.erb b/TryRuby/app/views/irb/_form.html.erb new file mode 100644 index 0000000..4e3cdae --- /dev/null +++ b/TryRuby/app/views/irb/_form.html.erb @@ -0,0 +1,17 @@ +<%= form_for(@irb) do |f| %> + <% if @irb.errors.any? %> +
+

<%= pluralize(@irb.errors.count, "error") %> prohibited this irb from being saved:

+ + +
+ <% end %> + +
+ <%= f.submit %> +
+<% end %> diff --git a/TryRuby/app/views/irb/edit.html.erb b/TryRuby/app/views/irb/edit.html.erb new file mode 100644 index 0000000..9e50dc3 --- /dev/null +++ b/TryRuby/app/views/irb/edit.html.erb @@ -0,0 +1,6 @@ +

Editing irb

+ +<%= render 'form' %> + +<%= link_to 'Show', @irb %> | +<%= link_to 'Back', irb_path %> diff --git a/TryRuby/app/views/irb/index.html.erb b/TryRuby/app/views/irb/index.html.erb new file mode 100644 index 0000000..90b773b --- /dev/null +++ b/TryRuby/app/views/irb/index.html.erb @@ -0,0 +1,21 @@ +

Listing irb

+ + + + + + + + +<% @irb.each do |irb| %> + + + + + +<% end %> +
<%= link_to 'Show', irb %><%= link_to 'Edit', edit_irb_path(irb) %><%= link_to 'Destroy', irb, :confirm => 'Are you sure?', :method => :delete %>
+ +
+ +<%= link_to 'New Irb', new_irb_path %> diff --git a/TryRuby/app/views/irb/new.html.erb b/TryRuby/app/views/irb/new.html.erb new file mode 100644 index 0000000..9a7b635 --- /dev/null +++ b/TryRuby/app/views/irb/new.html.erb @@ -0,0 +1,5 @@ +

New irb

+ +<%= render 'form' %> + +<%= link_to 'Back', irb_path %> diff --git a/TryRuby/app/views/irb/show.html.erb b/TryRuby/app/views/irb/show.html.erb new file mode 100644 index 0000000..c271c01 --- /dev/null +++ b/TryRuby/app/views/irb/show.html.erb @@ -0,0 +1,5 @@ +

<%= notice %>

+ + +<%= link_to 'Edit', edit_irb_path(@irb) %> | +<%= link_to 'Back', irb_path %> diff --git a/TryRuby/app/views/layouts/tryruby_es.rhtml b/TryRuby/app/views/layouts/tryruby_es.rhtml new file mode 100644 index 0000000..9ab71c1 --- /dev/null +++ b/TryRuby/app/views/layouts/tryruby_es.rhtml @@ -0,0 +1,96 @@ + + + + + try ruby! (en tu navegador) + + + + + + + + + +
+ +
+
+
+ +

A Popup Browser

+

[x]

+
+ +
+
+ +
+
+
+
+
+
+

¿Tienes 15 minutos? ¡Prueba Ruby ahora mismo!

+

Ruby es un lenguaje de programación de Japón + (disponible en ruby-lang.org) + que está revolucionando la web. + La belleza de Ruby se encuentra en su balance entre la simplicidad y el poder.

+ +

Prueba código Ruby en el prompt de arriba. Además de los métodos + originales de Ruby, los siguientes comandos están disponibles:

+
    +
  • help + Empieza el tutorial interactivo de 15 minutos. ¡Creeme, es muy básico!
  • +
  • help 2 + Salta al capítulo 2.
  • + +
  • clear + Limpia la pantalla. Útil si tu navegador empieza a alerdarce. + Tu historial de comandos será recordado. +
  • back + Retrocede una pantalla en el tutorial.
  • +
  • reset + Resetea el interprete. (o Ctrl-D!)
  • +
  • next + Te permite saltear la siguiente lección
  • + +
  • time + Detiene el reloj. Imprime cuanto tiempo tu sesión estuvo abierta.
  • +
+

Si te pasa de dejar o refrescar la página, tu sesión seguirá aquí a menos que + se deje inactiva por diez minutos.

+
+
+
+ +
+
¿Atrapado en los dos puntos? Unas comillas o algo fue dejado abierto. Escribe: reset o aprieta Ctrl-D.
+
+ +

This place was sired by why the lucky stiff. + Please contact me using the email address at that link.is maintained by Andrew McElroy and David Miani. For support issues, please post a ticket or contact Sophrinix on github.
Por asuntos de traducción, mandar un ticket o contactarse con Cristian Re (leizzer) en Github.
+

+ +
+ + + + + + + + + + diff --git a/TryRuby/app/views/tryruby/_donate.html.erb b/TryRuby/app/views/tryruby/_donate.html.erb new file mode 100644 index 0000000..f7095b1 --- /dev/null +++ b/TryRuby/app/views/tryruby/_donate.html.erb @@ -0,0 +1,41 @@ + \ No newline at end of file diff --git a/TryRuby/config.ru b/TryRuby/config.ru new file mode 100644 index 0000000..df742d3 --- /dev/null +++ b/TryRuby/config.ru @@ -0,0 +1,4 @@ +# This file is used by Rack-based servers to start the application. + +require ::File.expand_path('../config/environment', __FILE__) +run TryRuby::Application diff --git a/TryRuby/config/application.rb b/TryRuby/config/application.rb new file mode 100644 index 0000000..a8f1bd9 --- /dev/null +++ b/TryRuby/config/application.rb @@ -0,0 +1,42 @@ +require File.expand_path('../boot', __FILE__) + +require 'rails/all' + +# If you have a Gemfile, require the gems listed there, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(:default, Rails.env) if defined?(Bundler) + +module TryRuby + class Application < Rails::Application + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + + # Custom directories with classes and modules you want to be autoloadable. + # config.autoload_paths += %W(#{config.root}/extras) + + # Only load the plugins named here, in the order given (default is alphabetical). + # :all can be used as a placeholder for all plugins not explicitly named. + # config.plugins = [ :exception_notification, :ssl_requirement, :all ] + + # Activate observers that should always be running. + # config.active_record.observers = :cacher, :garbage_collector, :forum_observer + + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. + # config.time_zone = 'Central Time (US & Canada)' + + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] + # config.i18n.default_locale = :de + + # JavaScript files you want as :defaults (application.js is always included). + # config.action_view.javascript_expansions[:defaults] = %w(jquery rails) + + # Configure the default encoding used in templates for Ruby 1.9. + config.encoding = "utf-8" + + # Configure sensitive parameters which will be filtered from the log file. + config.filter_parameters += [:password] + end +end diff --git a/TryRuby/config/cucumber.yml b/TryRuby/config/cucumber.yml new file mode 100644 index 0000000..621a14c --- /dev/null +++ b/TryRuby/config/cucumber.yml @@ -0,0 +1,8 @@ +<% +rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" +rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}" +std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} --strict --tags ~@wip" +%> +default: <%= std_opts %> features +wip: --tags @wip:3 --wip features +rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip diff --git a/TryRuby/config/initializers/secret_token.rb b/TryRuby/config/initializers/secret_token.rb new file mode 100644 index 0000000..5e9b98f --- /dev/null +++ b/TryRuby/config/initializers/secret_token.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +TryRuby::Application.config.secret_token = 'f256893c2715b8fab149294c7f3b79fbc7a725534be51332c220c2e19965e631cc72c360acfec77dcc0469f58b81b5023e7c93a800626520f9420816639948d5' diff --git a/TryRuby/db/migrate/20110314191710_create_irb.rb b/TryRuby/db/migrate/20110314191710_create_irb.rb new file mode 100644 index 0000000..b628478 --- /dev/null +++ b/TryRuby/db/migrate/20110314191710_create_irb.rb @@ -0,0 +1,12 @@ +class CreateIrb < ActiveRecord::Migration + def self.up + create_table :irb do |t| + + t.timestamps + end + end + + def self.down + drop_table :irb + end +end diff --git a/TryRuby/db/schema.rb b/TryRuby/db/schema.rb new file mode 100644 index 0000000..671c0da --- /dev/null +++ b/TryRuby/db/schema.rb @@ -0,0 +1,15 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended to check this file into your version control system. + +ActiveRecord::Schema.define(:version => 0) do + +end diff --git a/TryRuby/features/lesson1.feature b/TryRuby/features/lesson1.feature new file mode 100644 index 0000000..409a21f --- /dev/null +++ b/TryRuby/features/lesson1.feature @@ -0,0 +1,7 @@ +Feature: Successfully execute lesson 1 + Scenario: starting into try ruby + Given I am on index + When I type help + Then I should see Using The Prompt + + diff --git a/TryRuby/features/lesson2.feature b/TryRuby/features/lesson2.feature new file mode 100644 index 0000000..e69de29 diff --git a/TryRuby/features/step_definitions/web_steps.rb b/TryRuby/features/step_definitions/web_steps.rb new file mode 100644 index 0000000..0f0af8a --- /dev/null +++ b/TryRuby/features/step_definitions/web_steps.rb @@ -0,0 +1,219 @@ +# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. +# It is recommended to regenerate this file in the future when you upgrade to a +# newer version of cucumber-rails. Consider adding your own code to a new file +# instead of editing this one. Cucumber will automatically load all features/**/*.rb +# files. + + +require 'uri' +require 'cgi' +require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths")) + +module WithinHelpers + def with_scope(locator) + locator ? within(locator) { yield } : yield + end +end +World(WithinHelpers) + +Given /^(?:|I )am on (.+)$/ do |page_name| + visit path_to(page_name) +end + +When /^(?:|I )go to (.+)$/ do |page_name| + visit path_to(page_name) +end + +When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector| + with_scope(selector) do + click_button(button) + end +end + +When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector| + with_scope(selector) do + click_link(link) + end +end + +When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field, value, selector| + with_scope(selector) do + fill_in(field, :with => value) + end +end + +When /^(?:|I )fill in "([^"]*)" for "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector| + with_scope(selector) do + fill_in(field, :with => value) + end +end + +# Use this to fill in an entire form with data from a table. Example: +# +# When I fill in the following: +# | Account Number | 5002 | +# | Expiry date | 2009-11-01 | +# | Note | Nice guy | +# | Wants Email? | | +# +# TODO: Add support for checkbox, select og option +# based on naming conventions. +# +When /^(?:|I )fill in the following(?: within "([^"]*)")?:$/ do |selector, fields| + with_scope(selector) do + fields.rows_hash.each do |name, value| + When %{I fill in "#{name}" with "#{value}"} + end + end +end + +When /^(?:|I )select "([^"]*)" from "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector| + with_scope(selector) do + select(value, :from => field) + end +end + +When /^(?:|I )check "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector| + with_scope(selector) do + check(field) + end +end + +When /^(?:|I )uncheck "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector| + with_scope(selector) do + uncheck(field) + end +end + +When /^(?:|I )choose "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector| + with_scope(selector) do + choose(field) + end +end + +When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"(?: within "([^"]*)")?$/ do |path, field, selector| + with_scope(selector) do + attach_file(field, path) + end +end + +Then /^(?:|I )should see JSON:$/ do |expected_json| + require 'json' + expected = JSON.pretty_generate(JSON.parse(expected_json)) + actual = JSON.pretty_generate(JSON.parse(response.body)) + expected.should == actual +end + +Then /^(?:|I )should see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector| + with_scope(selector) do + if page.respond_to? :should + page.should have_content(text) + else + assert page.has_content?(text) + end + end +end + +Then /^(?:|I )should see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector| + regexp = Regexp.new(regexp) + with_scope(selector) do + if page.respond_to? :should + page.should have_xpath('//*', :text => regexp) + else + assert page.has_xpath?('//*', :text => regexp) + end + end +end + +Then /^(?:|I )should not see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector| + with_scope(selector) do + if page.respond_to? :should + page.should have_no_content(text) + else + assert page.has_no_content?(text) + end + end +end + +Then /^(?:|I )should not see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector| + regexp = Regexp.new(regexp) + with_scope(selector) do + if page.respond_to? :should + page.should have_no_xpath('//*', :text => regexp) + else + assert page.has_no_xpath?('//*', :text => regexp) + end + end +end + +Then /^the "([^"]*)" field(?: within "([^"]*)")? should contain "([^"]*)"$/ do |field, selector, value| + with_scope(selector) do + field = find_field(field) + field_value = (field.tag_name == 'textarea') ? field.text : field.value + if field_value.respond_to? :should + field_value.should =~ /#{value}/ + else + assert_match(/#{value}/, field_value) + end + end +end + +Then /^the "([^"]*)" field(?: within "([^"]*)")? should not contain "([^"]*)"$/ do |field, selector, value| + with_scope(selector) do + field = find_field(field) + field_value = (field.tag_name == 'textarea') ? field.text : field.value + if field_value.respond_to? :should_not + field_value.should_not =~ /#{value}/ + else + assert_no_match(/#{value}/, field_value) + end + end +end + +Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should be checked$/ do |label, selector| + with_scope(selector) do + field_checked = find_field(label)['checked'] + if field_checked.respond_to? :should + field_checked.should be_true + else + assert field_checked + end + end +end + +Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should not be checked$/ do |label, selector| + with_scope(selector) do + field_checked = find_field(label)['checked'] + if field_checked.respond_to? :should + field_checked.should be_false + else + assert !field_checked + end + end +end + +Then /^(?:|I )should be on (.+)$/ do |page_name| + current_path = URI.parse(current_url).path + if current_path.respond_to? :should + current_path.should == path_to(page_name) + else + assert_equal path_to(page_name), current_path + end +end + +Then /^(?:|I )should have the following query string:$/ do |expected_pairs| + query = URI.parse(current_url).query + actual_params = query ? CGI.parse(query) : {} + expected_params = {} + expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')} + + if actual_params.respond_to? :should + actual_params.should == expected_params + else + assert_equal expected_params, actual_params + end +end + +Then /^show me the page$/ do + save_and_open_page +end diff --git a/TryRuby/features/support/env.rb b/TryRuby/features/support/env.rb new file mode 100644 index 0000000..3333139 --- /dev/null +++ b/TryRuby/features/support/env.rb @@ -0,0 +1,57 @@ +# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. +# It is recommended to regenerate this file in the future when you upgrade to a +# newer version of cucumber-rails. Consider adding your own code to a new file +# instead of editing this one. Cucumber will automatically load all features/**/*.rb +# files. + +ENV["RAILS_ENV"] ||= "test" +require File.expand_path(File.dirname(__FILE__) + '/../../config/environment') + +require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support +require 'cucumber/rails/world' +require 'cucumber/rails/active_record' +require 'cucumber/web/tableish' + +require 'capybara/rails' +require 'capybara/cucumber' +require 'capybara/session' +require 'cucumber/rails/capybara_javascript_emulation' # Lets you click links with onclick javascript handlers without using @culerity or @javascript +# Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In +# order to ease the transition to Capybara we set the default here. If you'd +# prefer to use XPath just remove this line and adjust any selectors in your +# steps to use the XPath syntax. +Capybara.default_selector = :css + +# If you set this to false, any error raised from within your app will bubble +# up to your step definition and out to cucumber unless you catch it somewhere +# on the way. You can make Rails rescue errors and render error pages on a +# per-scenario basis by tagging a scenario or feature with the @allow-rescue tag. +# +# If you set this to true, Rails will rescue all errors and render error +# pages, more or less in the same way your application would behave in the +# default production environment. It's not recommended to do this for all +# of your scenarios, as this makes it hard to discover errors in your application. +ActionController::Base.allow_rescue = false + +# If you set this to true, each scenario will run in a database transaction. +# You can still turn off transactions on a per-scenario basis, simply tagging +# a feature or scenario with the @no-txn tag. If you are using Capybara, +# tagging with @culerity or @javascript will also turn transactions off. +# +# If you set this to false, transactions will be off for all scenarios, +# regardless of whether you use @no-txn or not. +# +# Beware that turning transactions off will leave data in your database +# after each scenario, which can lead to hard-to-debug failures in +# subsequent scenarios. If you do this, we recommend you create a Before +# block that will explicitly put your database in a known state. +Cucumber::Rails::World.use_transactional_fixtures = true +# How to clean your database when transactions are turned off. See +# http://github.com/bmabey/database_cleaner for more info. +if defined?(ActiveRecord::Base) + begin + require 'database_cleaner' + DatabaseCleaner.strategy = :truncation + rescue LoadError => ignore_if_database_cleaner_not_present + end +end diff --git a/TryRuby/features/support/paths.rb b/TryRuby/features/support/paths.rb new file mode 100644 index 0000000..06b3efb --- /dev/null +++ b/TryRuby/features/support/paths.rb @@ -0,0 +1,33 @@ +module NavigationHelpers + # Maps a name to a path. Used by the + # + # When /^I go to (.+)$/ do |page_name| + # + # step definition in web_steps.rb + # + def path_to(page_name) + case page_name + + when /the home\s?page/ + '/' + + # Add more mappings here. + # Here is an example that pulls values out of the Regexp: + # + # when /^(.*)'s profile page$/i + # user_profile_path(User.find_by_login($1)) + + else + begin + page_name =~ /the (.*) page/ + path_components = $1.split(/\s+/) + self.send(path_components.push('path').join('_').to_sym) + rescue Object => e + raise "Can't find mapping from \"#{page_name}\" to a path.\n" + + "Now, go and add a mapping in #{__FILE__}" + end + end + end +end + +World(NavigationHelpers) diff --git a/TryRuby/lib/tasks/.gitkeep b/TryRuby/lib/tasks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/TryRuby/lib/tasks/cucumber.rake b/TryRuby/lib/tasks/cucumber.rake new file mode 100644 index 0000000..7db1a55 --- /dev/null +++ b/TryRuby/lib/tasks/cucumber.rake @@ -0,0 +1,53 @@ +# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. +# It is recommended to regenerate this file in the future when you upgrade to a +# newer version of cucumber-rails. Consider adding your own code to a new file +# instead of editing this one. Cucumber will automatically load all features/**/*.rb +# files. + + +unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks + +vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first +$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? + +begin + require 'cucumber/rake/task' + + namespace :cucumber do + Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t| + t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. + t.fork = true # You may get faster startup if you set this to false + t.profile = 'default' + end + + Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t| + t.binary = vendored_cucumber_bin + t.fork = true # You may get faster startup if you set this to false + t.profile = 'wip' + end + + Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t| + t.binary = vendored_cucumber_bin + t.fork = true # You may get faster startup if you set this to false + t.profile = 'rerun' + end + + desc 'Run all features' + task :all => [:ok, :wip] + end + desc 'Alias for cucumber:ok' + task :cucumber => 'cucumber:ok' + + task :default => :cucumber + + task :features => :cucumber do + STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" + end +rescue LoadError + desc 'cucumber rake task not available (cucumber not installed)' + task :cucumber do + abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' + end +end + +end diff --git a/TryRuby/public/images/ajax-loader.gif b/TryRuby/public/images/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..f2a1bc0c6f545e20e631a96e8e92f9822e75d046 GIT binary patch literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nnmm28Kh24mmkF0U1e2Nli^nlO|14{3qpHl$uzQnxasi zS(2fUn3Y(Olb@KPmzkHA&!G5|g@FsGT=74*pKD04vtxj(k)8oFBTz^Oh=E26FfcG1 zbL_hF&)}42ws10s6^G;;cE1^EoUR)U5A70}d2pLv!jVIT7j&Z~EblI3x0K*v_sV|m z0W=b9G$XP(CLnYCdK49;TX=SFc-G}o=oA=|U?{1O;Nu!CwW3C5Yw7*Bi4yD$3fCnb zwK+>}QdQ9sf*QnxY>*kpE+b{_Q;sJloS71)&(@kO!}mqf@1v(v;*8Y=G9S3kY~Cw# zY=t&c z;3~JK4HxB^lY(MD+sYeQ=t%XSSW;x^1M?dTvN=W^yNcAcy`HCte31C;)5xP%b~qs> zDP&4(%TBqBNGHwnryK;BdMI$fEg xd0mc!C@j^ZpLxYv4HmnPfI0THYuv<%+6iSmMn&w3dPGDfL1|=LY008wP(boU~ literal 0 HcmV?d00001 diff --git a/TryRuby/public/images/closelabel.png b/TryRuby/public/images/closelabel.png new file mode 100755 index 0000000000000000000000000000000000000000..c339e59333e4c3b5bc5fd910796fda5469a2515f GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XygXeTLn>}1B_t#Wus+)Bu;yq3!!CzC+*<6@{_r1Jzd&eL zz_O}6Q6nrOf&;;F`V=YhW2-H5dcFFaTerms + * Loads the #terms div in the box + * + * Terms + * Loads the terms.html page in the box + * + * Terms + * Loads the terms.png image in the box + * + * + * You can also use it programmatically: + * + * jQuery.facebox('some html') + * jQuery.facebox('some html', 'my-groovy-style') + * + * The above will open a facebox with "some html" as the content. + * + * jQuery.facebox(function($) { + * $.get('blah.html', function(data) { $.facebox(data) }) + * }) + * + * The above will show a loading screen before the passed function is called, + * allowing for a better ajaxy experience. + * + * The facebox function can also display an ajax page, an image, or the contents of a div: + * + * jQuery.facebox({ ajax: 'remote.html' }) + * jQuery.facebox({ ajax: 'remote.html' }, 'my-groovy-style') + * jQuery.facebox({ image: 'stairs.jpg' }) + * jQuery.facebox({ image: 'stairs.jpg' }, 'my-groovy-style') + * jQuery.facebox({ div: '#box' }) + * jQuery.facebox({ div: '#box' }, 'my-groovy-style') + * + * Want to close the facebox? Trigger the 'close.facebox' document event: + * + * jQuery(document).trigger('close.facebox') + * + * Facebox also has a bunch of other hooks: + * + * loading.facebox + * beforeReveal.facebox + * reveal.facebox (aliased as 'afterReveal.facebox') + * init.facebox + * afterClose.facebox + * + * Simply bind a function to any of these hooks: + * + * $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... }) + * + */ +(function($) { + $.facebox = function(data, klass) { + $.facebox.loading() + + if (data.ajax) fillFaceboxFromAjax(data.ajax, klass) + else if (data.image) fillFaceboxFromImage(data.image, klass) + else if (data.div) fillFaceboxFromHref(data.div, klass) + else if ($.isFunction(data)) data.call($) + else $.facebox.reveal(data, klass) + } + + /* + * Public, $.facebox methods + */ + + $.extend($.facebox, { + settings: { + opacity : 0.2, + overlay : true, + loadingImage : '/facebox/loading.gif', + closeImage : '/facebox/closelabel.png', + imageTypes : [ 'png', 'jpg', 'jpeg', 'gif' ], + faceboxHtml : '\ + ' + }, + + loading: function() { + init() + if ($('#facebox .loading').length == 1) return true + showOverlay() + + $('#facebox .content').empty() + $('#facebox .body').children().hide().end(). + append('
') + + $('#facebox').css({ + top: getPageScroll()[1] + (getPageHeight() / 10), + left: $(window).width() / 2 - 205 + }).show() + + $(document).bind('keydown.facebox', function(e) { + if (e.keyCode == 27) $.facebox.close() + return true + }) + $(document).trigger('loading.facebox') + }, + + reveal: function(data, klass) { + $(document).trigger('beforeReveal.facebox') + if (klass) $('#facebox .content').addClass(klass) + $('#facebox .content').append(data) + $('#facebox .loading').remove() + $('#facebox .body').children().fadeIn('normal') + $('#facebox').css('left', $(window).width() / 2 - ($('#facebox .popup').width() / 2)) + $(document).trigger('reveal.facebox').trigger('afterReveal.facebox') + }, + + close: function() { + $(document).trigger('close.facebox') + return false + } + }) + + /* + * Public, $.fn methods + */ + + $.fn.facebox = function(settings) { + if ($(this).length == 0) return + + init(settings) + + function clickHandler() { + $.facebox.loading(true) + + // support for rel="facebox.inline_popup" syntax, to add a class + // also supports deprecated "facebox[.inline_popup]" syntax + var klass = this.rel.match(/facebox\[?\.(\w+)\]?/) + if (klass) klass = klass[1] + + fillFaceboxFromHref(this.href, klass) + return false + } + + return this.bind('click.facebox', clickHandler) + } + + /* + * Private methods + */ + + // called one time to setup facebox on this page + function init(settings) { + if ($.facebox.settings.inited) return true + else $.facebox.settings.inited = true + + $(document).trigger('init.facebox') + makeCompatible() + + var imageTypes = $.facebox.settings.imageTypes.join('|') + $.facebox.settings.imageTypesRegexp = new RegExp('\.(' + imageTypes + ')$', 'i') + + if (settings) $.extend($.facebox.settings, settings) + $('body').append($.facebox.settings.faceboxHtml) + + var preload = [ new Image(), new Image() ] + preload[0].src = $.facebox.settings.closeImage + preload[1].src = $.facebox.settings.loadingImage + + $('#facebox').find('.b:first, .bl').each(function() { + preload.push(new Image()) + preload.slice(-1).src = $(this).css('background-image').replace(/url\((.+)\)/, '$1') + }) + + $('#facebox .close').click($.facebox.close) + $('#facebox .close_image').attr('src', $.facebox.settings.closeImage) + } + + // getPageScroll() by quirksmode.com + function getPageScroll() { + var xScroll, yScroll; + if (self.pageYOffset) { + yScroll = self.pageYOffset; + xScroll = self.pageXOffset; + } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict + yScroll = document.documentElement.scrollTop; + xScroll = document.documentElement.scrollLeft; + } else if (document.body) {// all other Explorers + yScroll = document.body.scrollTop; + xScroll = document.body.scrollLeft; + } + return new Array(xScroll,yScroll) + } + + // Adapted from getPageSize() by quirksmode.com + function getPageHeight() { + var windowHeight + if (self.innerHeight) { // all except Explorer + windowHeight = self.innerHeight; + } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode + windowHeight = document.documentElement.clientHeight; + } else if (document.body) { // other Explorers + windowHeight = document.body.clientHeight; + } + return windowHeight + } + + // Backwards compatibility + function makeCompatible() { + var $s = $.facebox.settings + + $s.loadingImage = $s.loading_image || $s.loadingImage + $s.closeImage = $s.close_image || $s.closeImage + $s.imageTypes = $s.image_types || $s.imageTypes + $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml + } + + // Figures out what you want to display and displays it + // formats are: + // div: #id + // image: blah.extension + // ajax: anything else + function fillFaceboxFromHref(href, klass) { + // div + if (href.match(/#/)) { + var url = window.location.href.split('#')[0] + var target = href.replace(url,'') + if (target == '#') return + $.facebox.reveal($(target).html(), klass) + + // image + } else if (href.match($.facebox.settings.imageTypesRegexp)) { + fillFaceboxFromImage(href, klass) + // ajax + } else { + fillFaceboxFromAjax(href, klass) + } + } + + function fillFaceboxFromImage(href, klass) { + var image = new Image() + image.onload = function() { + $.facebox.reveal('
', klass) + } + image.src = href + } + + function fillFaceboxFromAjax(href, klass) { + $.get(href, function(data) { $.facebox.reveal(data, klass) }) + } + + function skipOverlay() { + return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null + } + + function showOverlay() { + if (skipOverlay()) return + + if ($('#facebox_overlay').length == 0) + $("body").append('
') + + $('#facebox_overlay').hide().addClass("facebox_overlayBG") + .css('opacity', $.facebox.settings.opacity) + .click(function() { $(document).trigger('close.facebox') }) + .fadeIn(200) + return false + } + + function hideOverlay() { + if (skipOverlay()) return + + $('#facebox_overlay').fadeOut(200, function(){ + $("#facebox_overlay").removeClass("facebox_overlayBG") + $("#facebox_overlay").addClass("facebox_hide") + $("#facebox_overlay").remove() + }) + + return false + } + + /* + * Bindings + */ + + $(document).bind('close.facebox', function() { + $(document).unbind('keydown.facebox') + $('#facebox').fadeOut(function() { + $('#facebox .content').removeClass().addClass('content') + $('#facebox .loading').remove() + $(document).trigger('afterClose.facebox') + }) + hideOverlay() + }) + +})(jQuery); diff --git a/TryRuby/public/javascripts/jQuery.irb.js b/TryRuby/public/javascripts/jQuery.irb.js new file mode 100644 index 0000000..765858d --- /dev/null +++ b/TryRuby/public/javascripts/jQuery.irb.js @@ -0,0 +1,1641 @@ +//Try Ruby 1.5 (defacto version Number) +// March 14 2011 + +// large parts of this file are lifted from Try Haskell ( which lifted parts from try ruby without copyright notice >:-/ ) + +// Thus it is fair to include the following banner if/until there is no try haskell code left/ only code that originally existed in try ruby in the +//first place. + + // Try Haskell 1.0.1 + // Tue Feb 23 18:34:48 GMT 2010 + // + // Copyright 2010 Chris Done. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions + // are met: + // + // 1. Redistributions of source code must retain the above + // copyright notice, this list of conditions and the following + // disclaimer. + // 2. Redistributions in binary form must reproduce the above + // copyright notice, this list of conditions and the following + // disclaimer in the documentation and/or other materials + // provided with the distribution. + // + // THIS SOFTWARE IS PROVIDED BY CHRIS DONE ``AS IS'' AND ANY EXPRESS + // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + // ARE DISCLAIMED. IN NO EVENT SHALL CHRIS DONE OR CONTRIBUTORS BE + // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + // BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + // DAMAGE. + // The views and conclusions contained in the software and + // documentation are those of the authors and should not be + // interpreted as representing official policies, either expressed or + // implied, of Chris Done. + // + // TESTED ON + // Internet Explorer 6 + // Opera 10.01 + // Chromium 4.0.237.0 (Ubuntu build 31094) + // Firefox 3.5.8 + + //IOS 4.3 (all devices) + +// Temporary fix +function opera() { + return navigator.userAgent.indexOf("Opera") == 0; +} + +function encodeHex(str) { + var result = ""; + for (var i = 0; i < str.length; i++) { + result += "%" + pad(toHex(str.charCodeAt(i) & 0xff), 2, '0'); + } + return result; +} + +//var handleJSON = function(a){ alert('Unassigned JSONP: ' + a); } +var hexv = { + "00": 0, + "01": 1, + "02": 2, + "03": 3, + "04": 4, + "05": 5, + "06": 6, + "07": 7, + "08": 8, + "09": 9, + "0A": 10, + "0B": 11, + "0C": 12, + "0D": 13, + "0E": 14, + "0F": 15, + "10": 16, + "11": 17, + "12": 18, + "13": 19, + "14": 20, + "15": 21, + "16": 22, + "17": 23, + "18": 24, + "19": 25, + "1A": 26, + "1B": 27, + "1C": 28, + "1D": 29, + "1E": 30, + "1F": 31, + "20": 32, + "21": 33, + "22": 34, + "23": 35, + "24": 36, + "25": 37, + "26": 38, + "27": 39, + "28": 40, + "29": 41, + "2A": 42, + "2B": 43, + "2C": 44, + "2D": 45, + "2E": 46, + "2F": 47, + "30": 48, + "31": 49, + "32": 50, + "33": 51, + "34": 52, + "35": 53, + "36": 54, + "37": 55, + "38": 56, + "39": 57, + "3A": 58, + "3B": 59, + "3C": 60, + "3D": 61, + "3E": 62, + "3F": 63, + "40": 64, + "41": 65, + "42": 66, + "43": 67, + "44": 68, + "45": 69, + "46": 70, + "47": 71, + "48": 72, + "49": 73, + "4A": 74, + "4B": 75, + "4C": 76, + "4D": 77, + "4E": 78, + "4F": 79, + "50": 80, + "51": 81, + "52": 82, + "53": 83, + "54": 84, + "55": 85, + "56": 86, + "57": 87, + "58": 88, + "59": 89, + "5A": 90, + "5B": 91, + "5C": 92, + "5D": 93, + "5E": 94, + "5F": 95, + "60": 96, + "61": 97, + "62": 98, + "63": 99, + "64": 100, + "65": 101, + "66": 102, + "67": 103, + "68": 104, + "69": 105, + "6A": 106, + "6B": 107, + "6C": 108, + "6D": 109, + "6E": 110, + "6F": 111, + "70": 112, + "71": 113, + "72": 114, + "73": 115, + "74": 116, + "75": 117, + "76": 118, + "77": 119, + "78": 120, + "79": 121, + "7A": 122, + "7B": 123, + "7C": 124, + "7D": 125, + "7E": 126, + "7F": 127, + "80": 128, + "81": 129, + "82": 130, + "83": 131, + "84": 132, + "85": 133, + "86": 134, + "87": 135, + "88": 136, + "89": 137, + "8A": 138, + "8B": 139, + "8C": 140, + "8D": 141, + "8E": 142, + "8F": 143, + "90": 144, + "91": 145, + "92": 146, + "93": 147, + "94": 148, + "95": 149, + "96": 150, + "97": 151, + "98": 152, + "99": 153, + "9A": 154, + "9B": 155, + "9C": 156, + "9D": 157, + "9E": 158, + "9F": 159, + "A0": 160, + "A1": 161, + "A2": 162, + "A3": 163, + "A4": 164, + "A5": 165, + "A6": 166, + "A7": 167, + "A8": 168, + "A9": 169, + "AA": 170, + "AB": 171, + "AC": 172, + "AD": 173, + "AE": 174, + "AF": 175, + "B0": 176, + "B1": 177, + "B2": 178, + "B3": 179, + "B4": 180, + "B5": 181, + "B6": 182, + "B7": 183, + "B8": 184, + "B9": 185, + "BA": 186, + "BB": 187, + "BC": 188, + "BD": 189, + "BE": 190, + "BF": 191, + "C0": 192, + "C1": 193, + "C2": 194, + "C3": 195, + "C4": 196, + "C5": 197, + "C6": 198, + "C7": 199, + "C8": 200, + "C9": 201, + "CA": 202, + "CB": 203, + "CC": 204, + "CD": 205, + "CE": 206, + "CF": 207, + "D0": 208, + "D1": 209, + "D2": 210, + "D3": 211, + "D4": 212, + "D5": 213, + "D6": 214, + "D7": 215, + "D8": 216, + "D9": 217, + "DA": 218, + "DB": 219, + "DC": 220, + "DD": 221, + "DE": 222, + "DF": 223, + "E0": 224, + "E1": 225, + "E2": 226, + "E3": 227, + "E4": 228, + "E5": 229, + "E6": 230, + "E7": 231, + "E8": 232, + "E9": 233, + "EA": 234, + "EB": 235, + "EC": 236, + "ED": 237, + "EE": 238, + "EF": 239, + "F0": 240, + "F1": 241, + "F2": 242, + "F3": 243, + "F4": 244, + "F5": 245, + "F6": 246, + "F7": 247, + "F8": 248, + "F9": 249, + "FA": 250, + "FB": 251, + "FC": 252, + "FD": 253, + "FE": 254, + "FF": 255 +}; + +function pad(str, len, pad) { + var result = str; + for (var i = str.length; i < len; i++) { + result = pad + result; + } + return result; +} + +var digitArray = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); + +function toHex(n) { + var result = '' + var start = true; + for (var i = 32; i > 0;) { + i -= 4; + var digit = (n >> i) & 0xf; + if (!start || digit != 0) { + start = false; + result += digitArray[digit]; + } + } + return (result == '' ? '0': result); +} + + (function($) { + var raphaelPaper; + var raphaelObjs; + var tutorialGuide; + + // I don't like this at all... I intend to make this pull via ajax from a key value data store. + var pages = + [ + //////////////////////////////////////////////////////////////////////// + // Lesson 1 + // Simple addition + { + lesson: 1, + title: 'Basics; numbers, strings, etc.', + guide: + '

' + rmsg(['Learning By Numbers', 'Music is Math', 'Back to Basics']) + + '

' + + "

To kick off let's try some maths out. Up there you can" + + " type in Haskell expressions. Try this out: 5 + 7

" + }, + { + guide: function(result) { + if (!result) result = { + expr: '5+7', + result: 12 + }; + var complied = result.expr.replace(/ /g, '') == "5+7"; + var who = complied ? 'we': 'you'; + return '

' + rmsg(['Your first Haskell expression', + "First Time's a Charm"]) + '

' + + '

Well done, you typed it perfect! You got back the number' + + ' ' + result.result + '. Just what ' + who + ' wanted. ' + + "

Let's try something completely different." + + " Type in your name like this:" + + ' "chris"

' + }, + trigger: function(result) { + return result.type == "(Num t) => t" || + result.type == "Integer" || + result.type == "Int"; + } + }, + // Strings & types + { + guide: function(result) { + if (!result) result = { + expr: '"chris"', + result: "\"chris\"" + }; + var n = unString(result.result); + if (n) n = ", " + n; + n += "!"; + return '

' + rmsg(['Types of values', "What's in a name?"]) + + '

' + + '

Hi there' + htmlEncode(n) + + (n != "!" ? " That's a pretty name. Honest.": "") + + " You're getting the hang of this!

" + + "

Note: You can chat to Haskell programmers while learning here, enter chat to start it." + + " You will join the official IRC channel of the Haskell community!

" + + "

Each time, you're getting back the value of the expression. So " + + "far, just a number and a list of characters.

" + + "

You can have lists of other stuff, too. Let's see your " + + " lottery numbers: [42,13,22]

" + }, + trigger: function(result) { + return result.type == "[Char]" + || result.type == "String"; + } + }, + // Overview of lesson 1 + { + guide: function(result) { + if (!result) result = { + result: "[42,13,22]" + }; + return '

' + rmsg(["Lesson 1 done already!"]) + + '

' + + "

Great, you made a list of numbers! If you win we'll split" + + " the winnings, right?

" + + "

Let's see what you've learned so far:

" + + "
    " + + "
  1. How to write maths and lists of things.
  2. " + + "
" + + "

You can do stuff with lists. Maybe you want the lottery " + + "numbers sorted in the right order, try this: " + + "sort " + result.result + "

" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*\[[0-9, ]+\][ ]*$/) && + result.type == "(Num t) => [t]"; + } + }, + /////////////////////////////////////////////////////////////////////// + // Lesson 2 - Functions + // Functions on lists + { + lesson: 2, + title: 'Simple Functions', + guide: function(result) { + if (!result) result = { + result: "[13,23,30]" + }; + return '

' + rmsg(["We put the funk in function"]) + + '

' + + "

Congratulations, you just used a function." + + " They're how you get things done in Haskell." + + "

As you might've guessed, we got back " + + htmlEncode(result.result) + + ".

Ever wanted an evil twin nemesis? Me too. " + + "Luckily, you can sort lists of characters, or " + + "strings" + + ", in the same way as numbers! sort \"chris\"

" + }, + trigger: function(result) { + return result.expr.match(/sort/) && + result.type == "(Num t, Ord t) => [t]"; + } + }, + // Tuples + { + guide: function(result) { + if (!result) result = { + result: "\"chirs\"" + }; + nemesis = htmlEncode(unString(result.result)); + return '

' + + rmsg(["Tuples, because sometimes one value ain't enough!"]) + + '

' + + "

Watch out for " + nemesis + "! You should keep their credentials for the police.

" + + "

My nemesis is 28 years of age: " + + "(28,\"chirs\")

" + }, + trigger: function(result) { + return result.expr.match(/sort/) && + result.type == "[Char]"; + } + }, + // Functions on tuples + { + guide: function(result) { + if (!result) result = { + result: "(28,\"chirs\")" + }; + var age = result.result.match(/^\(([0-9]+)+/); + var villain = htmlEncode(result.result.replace(/\\"/g, '"')); + return '

' + + rmsg(["We'll keep them safe, don't worry about it."]) + + '

' + + "

Is " + (age ? age[1] : "that") + " a normal age for a " + + "super-villain?

" + + "

You just wrote a tuple. It's a way to keep a bunch of values together in Haskell. " + + "You can put as many as you like in there:

" + + "
  • (1,\"hats\",23/35)
  • (\"Shaggy\",\"Daphnie\",\"Velma\")
" + + "

Actually, let's say our villain is " + + "" + villain + "" + + ", how do you get their age?

" + + "fst " + villain + "" + }, + trigger: function(result) { + return result.expr.match(/\([0-9]+,[ ]*"[^"]+"\)/) && + result.type == "(Num t) => (t, [Char])"; + } + }, + // Summary of lesson 2 + { + guide: function(result) { + return '

' + + rmsg(["Lesson 2 done! Wow, great job!", + "Lesson 2 completo!"]) + + '

' + + + "

Good job! You got the age back from the tuple! Didn't " + + " even break a sweat, did you? The fst function " + + "just gets the first value. It's called \"fst\" because " + + "it's used a lot in Haskell so it really needs to be short!

" + + + "

Time to take a rest and see what you learned:

" + + "
    " + + "
  1. Functions can be used on lists of any type.
  2. " + + "
  3. We can stuff values into tuples.
  4. " + + "
  5. Getting the values back from tuples is easy.
  6. " + + "
" + + + "

Now let's say you want " + + " to use a value more than once, how would you do it? " + + "To make our lives easier, we can say:

" + + + "let x = 4 in x * x" + }, + trigger: function(result) { + return result.expr.match(/fst/) && + result.type == "(Num t) => t"; + } + }, + { + guide: function(result) { + return "

Let them eat cake

" + + + "

You just bound a variable. " + + "That is, you bound x to the expression 4, " + + " and then you can write x in some code (the body) and " + + " it will mean the same as if you'd written 4.

" + + + "

It's like this: let var = expression in body

" + + + "The in part just separates the expression from the body.

" + + + "

For example try: " + + "let x = 8 * 10 in x + x

" + + + "

So if we wanted to get the age of our villain, we could do:

" + + + "let villain = (28,\"chirs\") in fst villain" + + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+x[ ]*=[ ]*[0-9]+[ ]*in[ ]*x[ ]*\*[ ]*x/) && + result.type == "(Num t) => t"; + } + }, + { + guide: function(result) { + return "

Basics over, let's go!

" + + "

Next, let's take a short detour to learn about " + + "syntactic sugar. " + + "Try typing this out:

" + + "

'a' : []

" + + "

Or skip to lesson4 to learn about functions," + + " the meat of Haskell!"; + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+villain[ ]*=[ ]*\([0-9]+,[ ]*"[^"]+"\)[ ]*in[ ]+fst[ ]+villain[ ]*/) && + result.type == "(Num t) => t"; + } + }, + // Lesson 3: Syntactic sugar + { + lesson: 3, + title: 'Syntactic Sugar', + guide: function(result) { + return '

' + + rmsg(["You constructed a list!"]) + + '

' + + "

Well done, that was tricky syntax. You used the (:) " + + "function. It takes two values, some value and a list, and " + + " constructs a new list" + + " out of them. We call it 'cons' for short.

" + + "

'a' is " + + "the character 'a', [] is an empty list. So " + + "tacking 'a' at the start of an empty list just " + + "makes a list ['a']!

" + + "

But thankfully we don't have to type out " + + "'a' : 'b' : [] every time to we want to make a " + + "list of characters; we can use " + + "syntactic sugar and just write" + + " ['a','b']. Don't believe me, check this!

" + + "'a' : 'b' : [] == ['a','b']" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*'a'[ ]*:[ ]*\[\][ ]*/) && + result.type == "[Char]"; + } + }, + // Booleans and string syntactic sugar + { + guide: function(result) { + return '

' + + rmsg(["You're on fire!"]) + + '

' + + "

You're handling this syntax really well, nice!

" + + "

You just got a boolean value back, and it said " + + "True. That means they're equal!

" + + "

One final demonstration on syntactic sugar for now:

" + + "['a','b','c'] == \"abc\"" + }, + trigger: function(result) { + return result.type == "Bool" && + result.expr.replace(/[^':\[\]\=,]/g, '') == "'':'':[]==['','']"; + } + }, + // Summary of syntactic sugar section + { + guide: function(result) { + return '

' + + rmsg(["Lesson 3 over! Syntactic sugar is sweet"]) + + '

' + + "

Let's have a gander at what you learned:

" + + "
    " + + "
  1. In 'a' : [], : is really just " + + " another function, just clever looking.
  2. " + + "
  3. Pretty functions like this are written like (:) when " + + " you talk about them.
  4. " + + "
  5. A list of characters ['a','b'] can just be written " + + "\"ab\". Much easier!
  6. " + + "
" + + "

Phew! You're getting pretty deep! Your arch nemesis, " + + nemesis + ", is gonna try to steal your " + rmsg(['mojo', + 'pizza']) + + "! Let's learn a bit more about functions and passing " + + "them around. Try this:

map (+1) [1..5]

"; + }, + trigger: function(result) { + return result.expr.replace(/[^\]\[',=\"]?/g, '') == "['','','']==\"\"" && + result.type == "Bool"; + } + }, + { + lesson: 4, + title: 'Functions, reloaded; passing, defining, etc.', + guide: function() { + var title = + rmsg(["Functions [of a Geisha]", + "Functions, functors, functoids, funky", + "Functions: Expanded fo' real"]); + return "

" + title + "

" + + + "

Here's where the magic begins!

" + + + "

You just passed the (+1) " + + "function to the map function.

" + + + "

You can try other things like (remember: click to insert them):

" + + + "
    " + + "
  • map (*99) [1..10]
  • " + + "
  • map (/5) [13,24,52,42]
  • " + + "
  • filter (>5) [62,3,25,7,1,9]
  • " + + "
" + + + "

Note that a tuple is different to a list because you can do this:

" + + "(1,\"George\")" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*map[ ]+\(\+1\)[ ]*\[1..5\][ ]*$/) && + result.type == "(Num a, Enum a) => [a]"; + } + }, + { + guide: function(result) { + return "

Lists and Tuples

" + + + "

You can only " + + " have a list of numbers or a list of characters, whereas in a tuple you can throw anything in!

" + + + "

We've also seen that you can make a new list with (:) that joins two values together, like:

" + + "

1 : [2,3]

" + + + "

But we can't do this with tuples! You can only write a tuple and then look at what's inside. You can't make new ones on the fly like a list." + + + "

Let's write our own functions! It's really easy. How about something simple:

" + + "let square x = x * x in square " + rmsg([52, 10, 3]) + "" + + }, + trigger: function(result) { + return result.expr.match(/^[ ]*\(1,"[^"]+"\)[ ]*$/) && + result.type == "(Num t) => (t, [Char])"; + } + }, + { + guide: function(result) { + return "

Let there be functions

" + + "

Nice one! I think you're getting used to the let syntax.

" + + "

You defined a function. You can read it as, as for a given " + + "parameter called x, square of " + + "x is x * x." + + "

Some others you can try are:

" + + "
  • let add1 x = x + 1 in add1 5
  • " + + "
  • let second x = snd x in second (3,4)
  • " + + "
" + + "

Let's go crazy and use our square function with map:

" + + "let square x = x * x in map square [1..10]" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]*square[ ]+x[ ]*=[ ]*x[ ]*\*[ ]*x[ ]*in[ ]*square[ ]+[0-9]+/) && + result.type == "(Num t) => t"; + } + }, + { + guide: function(result) { + if (!result || !result.value) result = { + value: "[1,4,9,16,25,36,49,64,81,100]" + }; + return "

Let there be functions

" + + + "

That's so cool! You described a simple function square and then " + + "you just passed it to another function (map) and got back " + + htmlEncode(result.value) + ", exactly what you expected!

" + + + "

Haskell is pretty good at composing things together like this. " + + "Some other things you can try are:

" + + + "
    " + + "
  • let add1 x = x + 1 in map add1 [1,5,7]
  • " + + "
  • let take5s = filter (==5) in take5s [1,5,2,5,3,5]
  • " + + "
  • let take5s = filter (==5) in map take5s [[1,5],[5],[1,1]]
  • " + + "
" + + + "

Did you get back what you expected?

" + + + "

One more example for text; how do you upcase a letter?

" + + + "

toUpper 'a'

" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+square[ ]+x[ ]*=[ ]*x[ ]*\*[ ]*x[ ]*in[ ]+map[ ]+square[ ]*\[1..10\][ ]*$/) && + result.type == "(Num a, Enum a) => [a]"; + } + }, + { + guide: function(result) { + return "

Exercise time!

" + + + "

Easy! Remember: characters are written like 'a' and " + + "strings (lists of characters) are written like \"a\"." + + + "

I need you to use toUpper capitalise my whole name, " + + "\"Chris\". Give it a try." + + " You can do it, I believe in you!

" + + + '

Spoiler: map toUpper "Chris"

' + }, + trigger: function(result) { + return result.expr.match(/^toUpper 'a'$/) && + result.type == "Char"; + } + }, + { + guide: function(result) { + return "

Lesson 4 complete!

" + + + "

Brilliant! You're making excellent progress! " + + "You just passed toUpper to map. No problem.

" + + + "

Let's go over what you've learned in this lesson:

" + + + "
    " + + "
  1. Functions like map take other functions as parameters.
  2. " + + "
  3. Functions like (+1), (>5) and " + + "square can be passed to other functions.
  4. " + + "
  5. Defining functions is just a case of writing what " + + "to do with the parameters.
  6. " + "
" + + + "

Let's check out pattern matching; a way to " + + "get values from other values using patterns. Try this:

" + + "

let (a,b) = (10,12) in a * 2

" + + + "

Or you can skip this section and go to straight to lesson6; types!

" + }, + trigger: function(result) { + return result.type == "[Char]" && + result.expr.match(/^map[ ]+toUpper/); + } + }, + { + lesson: 5, + title: 'Pattern Matching', + guide: function(result) { + var title = + rmsg(["And therefore, patterns emerge in nature.", + "And Then Patterns", + "Pattern matching!"]) + return "

" + title + "

" + + + "

Jolly good show!

" + + "

So you had a value (10,12) and matched " + + "it against a pattern (a,b), then you were able" + + " to do stuff with the a and b!" + + + "

Note: Pattern matching (a,b) against " + + "(1,2) to get the a is the same as" + + " doing fst (1,2), like you did in step7!

" + + + "

A pattern always matches the way the " + + "value was originally constructed. Remember that \"abc\" is " + + "syntactic sugar for 'a' : 'b' : 'c' : [].

" + + + "

So you can get the characters from a string with patterns:

" + + + "let (a:b:c:[]) = \"xyz\" in a" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+\(a,b\)[ ]+=[ ]+\(10,12\)[ ]+in[ ]+a[ ]*\*[ ]*2[ ]*$/) && + result.type == "(Num t) => t"; + } + }, + { + guide: function(result) { + return "

" + rmsg(["Ignorance is bliss", "Ignoring values"]) + "

" + + + "

You're getting into tricky syntax, huh? I know you can handle it!

" + + + "

If you just want some of the values, you can ignore the others with _ (underscore) like this:

" + + + "

let (a:_:_:_) = \"xyz\" in a

" + + + "

In fact, (a:b:c:d) is short-hand for " + + "(a:(b:(c:d))), so you can just ignore the rest in one go:

" + + + "let (a:_) = \"xyz\" in a" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+\(a:b:c:\[\]\)[ ]*=[ ]*\"xyz\"[ ]*in[ ]+a[ ]*$/) && + result.type == "Char"; + } + }, + { + guide: function(result) { + return "

" + rmsg(["Exercise!", "Show me the money!"]) + "

" + + + "

Try to get the 'a' value from this value using pattern matching:

" + + "

(10,\"abc\")

" + + + "

Spoiler: let (_,(a:_)) = (10,\"abc\") in a

" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]*\(a:_\)[ ]*=[ ]*"xyz"[ ]*in[ ]*a[ ]*$/) && + result.type == "Char"; + } + }, + { + guide: function(result) { + return "

" + rmsg(["Well done!", "Brilliant!", "Perfetto!"]) + "

" + + + "

Wizard! I think you've got pattern-matching down.

" + + + "

If you're still a bit unsure, here are some other things you can try:

" + + + "
    " + + "
  • let _:_:c:_ = \"abcd\" in c
  • " + + "
  • let [a,b,c] = \"cat\" in (a,b,c)
  • " + + "
" + + + "

You can also grab a whole value and pattern match on it (have your cake and eat it too):

" + + + "let abc@(a,b,c) = (10,20,30) in (abc,a,b,c)" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]*\(_,\(?a:_\)?\)[ ]*=[ ]*\(10,\"abc\"\)[ ]*in[ ]*a[ ]*$/) && + result.type == "Char"; + } + }, + { + guide: function(result) { + return "

" + rmsg(["And that's the end of that chapter"]) + "

" + + + "

That was easy, right?

" + + + "

Let's go over what you've learned in this lesson:

" + + + "
    " + + "
  1. Values are pattern matched, or deconstructed, by writing however they were constructed.
  2. " + + "
  3. Patterns let you use the values that you match.
  4. " + + "
  5. You can ignore whichever values you want.
  6. " + + "
  7. You can pattern match and keep hold of the original value too.
  8. " + + "
" + + + "

Now we get to the Deep Ones. Types!

" + + + "

Consider the following value: 'a'

" + + }, + trigger: function(result) { + return result.type == "(Num t, Num t1, Num t2) => ((t, t1, t2), t, t1, t2)"; + } + }, + { + lesson: 6, + title: 'Types', + guide: function(result) { + showTypes = true; + return "

" + rmsg(["Types", "What's in a Type?", "Types & Values"]) + "

" + + "

What's this? Something new!

" + + + "

In Haskell there are types of values. Every value belongs to a type. To demonstrate this fact, I've sneakily enabled types to be " + + "shown of every value in the console from now on.

" + + + "

The type of the value 'a' is Char (short for 'character', but you guessed that, right?).

" + + + "

You've seen the type of a character, now what about" + + " a list of characters?

" + + "\"Spartacus\"" + }, + trigger: function(result) { + return result.type == 'Char'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

" + rmsg(["Lists of stuff, types"]) + "

" + + + "

I'm Spartacus!

" + + + "

Okay, so a list of characters has type [Char].

" + + + "

Notice that when we write a :: X it means the value a has type X. It's just a short-hand called a signature.

" + + + "

If you just want the type of a value, without actually evaluating it, you can just type:

" + + ":t toUpper" + }, + trigger: function(result) { + return result.expr.match(/"[^"]+"/) && + result.type == '[Char]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

" + rmsg(["Function types"]) + "

" + + + "

Woah! Hold your blinkin' 'orses! The type of toUpper reads: Char -> Char

" + + + "

It's pretty easy; a -> b means function from a to b. " + + "So

toUpper :: Char -> Char means: for a" + + " given character (Char value) a, toUpper a has type Char.

" + + + "

Some other things you can try are:

" + + + "
  • :t words
  • " + + "
  • :t unwords
  • " + + "
  • :t True
  • " + + "
  • :t not
  • " + + "
" + + + "

The words function is pretty handy. Want to get a list of words from a sentence?

" + + "words \"There's jam in my pants.\"" + }, + trigger: function(result) { + return result.type == 'Char -> Char'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

" + rmsg(["Mid-way review"]) + "

" + + + "

The type of words was String -> [String]. You got a list of strings back! Just what you expected, right?

" + + + "

Let's take a rest in the middle of this lesson and go over what we've learned:

" + + + "
    " + + "
  1. All values in Haskell have a type. We describe the types of values with signatures, like True :: Bool.
  2. " + + "
  3. Functions are values too, and they have types, notated a -> b.
  4. " + + "
  5. Functions can be defined for any type to any other type.
  6. " + + "
  7. Humble reader has a thing for jammy pants.
  8. " + + "
" + + + "

But what if you have a type that can contain values of any type, like a tuple?

" + + ":t fst" + + }, + trigger: function(result) { + return result.expr.match(/^[ ]*words[ ]*\"[^"]+\"[ ]*$/) && + result.type == '[String]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

" + rmsg(["Polymorphic functions"]) + "

" + + + "

Remember this one? I know you do! fst (1,2) is 1, right?

" + + "

We read its type

" + + "

fst :: (a, b) -> a

" + + "

as: for all types a and b, the fst has type (a,b) to a. So the fst " + + "function works on a pair of values of any types! We call such a function polymorphic." + + "

" + + "

Remember the drop function? Maybe you don't. I don't! Let's check out its type:

" + + "

:t drop

" + }, + trigger: function(result) { + return result.type == '(a, b) -> a'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

" + rmsg(["Multi parameter functions"]) + "

" + + + "

So the drop function has type

Int -> [a] -> [a].

" + + + "

This is something new. You've got two arrows! Relax. You can read

" + + "

a -> b -> c as a -> (b -> c)

" + + + "

In other words, drop is a function from integers (Int values) to functions of lists to lists ([a] -> [a] values). Drop is a function to another function.

" + + + "

Check for yourself! :t drop 3

" + }, + trigger: function(result) { + return result.type == 'Int -> [a] -> [a]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

" + rmsg(["Partial application"]) + "

" + + + "

You've got a function of type [a] -> [a]! The drop function is considered a multi-parameter function. Remember the map function? Its parameters were a function and a list. Just another multi-parameter function.

" + + + "

You can add another parameter and, hey presto, you get a list!

" + + + "drop 3 \"hello!\"" + + }, + trigger: function(result) { + return result.type == '[a] -> [a]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

" + rmsg(["Higher order functions"]) + "

" + + + "

'Lo bob! You've already used the map function loads. I wonder if you can guess its type?

" + + + "

map :: (a -> b) -> [a] -> [b] (spoiler)

" + + + "

It's okay to peek! Have a go at guessing these: filter, take

" + + + "

Tip: You can use parantheses to use more than one function. You want to double all the numbers over five? Psch!

" + + "map (*2) (filter (>5) [10,2,16,9,4])" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*drop[ ]*[0-9]+[ ]*"[^"]+"[ ]*$/) && + result.type == '[Char]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

" + rmsg(["Phew! Rest time!"]) + "

" + + + "

Wow! You're doing so great! Have a look at what you know now!

" + + + "
    " + + "
  1. Function parameters can be polymorphic; any type!
  2. " + + "
  3. Functions can have multiple parameters by returning more functions.
  4. " + + "
  5. You can wrap expressions in parentheses and apply functions to them as a whole value.
  6. " + + "
" + + + "

You're really making great progress. Don't hesitate to sit and play in the console between chapters to get a good feel of it!

" + + + "

Stay tuned for more chapters on type classes and the meaning of :t 1, :t (*), etc.

" + + learnMore + }, + trigger: function(result) { + return result.type == '(Num a, Ord a) => [a]'; + } + } + ]; + // + // var webchat; + // + // function runWebchat() { + // if (!webchat) { + // // Create webchat frame + // var webchat = + // $(''); + // webchat.attr('width', 635); + // webchat.attr('height', 500); + // webchat.css('float', 'left'); + // webchat.css('webkit-border-radius', '3px'); + // webchat.css('moz-border-radius', '3px'); + // webchat.css('border-radius', '3px'); + // webchat.css('border', '5px solid #eeeeee'); + // + // // Extend page wrap to fit console and chat + // $('.page-wrap').css({ + // width: '1250px' + // }); + // $('.primary-content').css('margin-left', 0); + // $('.page-wrap').append(webchat.css('margin-left', '5px')); + // } + // } + + var pageTrigger = -1; + var notices = []; + var controller; + // Console controller + var learnMore; + + //////////////////////////////////////////////////////////////////////// + // Unshow a string + function unString(str) { + return str.replace(/^"(.*)"$/, "$1").replace(/\\"/, '"'); + } + + //////////////////////////////////////////////////////////////////////// + // Random message from a list of messages + function rmsg(choices) { + return choices[Math.floor((Math.random() * 100) % choices.length)]; + } + + // Simple HTML encoding + // Simply replace '<', '>' and '&' + // TODO: Use jQuery's .html() trick, or grab a proper, fast + // HTML encoder. + function htmlEncode(text, shy) { + return ( + ('' + text).replace(/&/g, '&') + .replace(/'); + handleJSON = function(r) { + script.remove(); + func(r); + }; + script.attr('src', url); + $('body').append(script); + } + + + var console = $('.console'); + controller = console.console({ + promptLabel: '> ', + continuedPromptLabel: ' .. ', /*not quiet right but good enough*/ + cancelHandle: function() { + controller.commandRef.ignore = true; + controller.finishCommand(); + controller.report(); + }, + + commandHandle: function(line, report) { + + controller.ajaxloader = $('

Loading...

'); + var commandRef = {}; + controller.currentLine = line; + controller.commandRef = commandRef; + controller.report = report; + if (tellAboutRet) tellAboutRet.fadeOut(function() { + $(this).remove(); + }); + + if (libTrigger(line, report)) return; + controller.inner.append(controller.ajaxloader); + controller.scrollToBottom(); + + jsonp("http://localhost:3030/tryruby/run/?cmd=" + encodeHex(line), + function(resp) { + if (commandRef.ignore) { + return; + } + controller.finishCommand(); + var result = resp; + if (pageTrigger > -1 && result.expr) { + triggerTutorialPage(pageTrigger, result); + } + if (result.type) { + if (pageTrigger == 24) showTypes = false; + handleSuccess(report, result); + } else if (result.error) { + report( + [{ + msg: result.error, + className: "jquery-console-message-error jquery-console-message-compile-error" + }] + ); + notice('compile-error', + "A compile-time error! " + + "It just means the expression wasn't quite right. " + + "Try again.", + 'prompt'); + } else if (result.exception) { + var err = limitsError(result.exception); + report( + [{ + msg: err, + className: "jquery-console-message-error jquery-console-message-exception" + }] + ); + if (err == result.exception) { + notice('compile-error', + "A run-time error! The expression was right but the" + + " result didn't make sense. Check your expression and try again.", + 'prompt'); + } + } else if (result.internal) { + report( + [{ + msg: limitsError(result.internal), + className: "jquery-console-message-error jquery-console-message-internal" + }] + ); + } else if (result.bind) { + report(); + } else if (result.result) { + if (result.expr.match(/^:modules/)) { + report( + [{ + msg: result.result.replace(/[\["\]]/g, '') + .replace(/,/g, ', '), + className: "jquery-console-message-type" + }]); + } + } + }); + + }, + + charInsertTrigger: function() { + var t = notice('tellaboutreturn', + "Hit Return when you're " + + "finished typing your expression."); + if (t) tellAboutRet = t; + return true; + }, + autofocus: true, + promptHistory: true, + historyPreserveColumn: true, + welcomeMessage: 'Interactive ruby ready' + }); + + controller.finishCommand = function() { + controller.ajaxloader.remove(); + $('.jquery-console-prompt :last').each(function() { + lastLine = controller.currentLine; + if (!$(this).hasClass('prompt-done')) { + $(this).addClass('prompt-done'); + $(this).click(function() { + controller.promptText(controller.currentLine); + }); + } + }); + } + + makeGuidSamplesClickable(); + + var match = window.location.href.match(/#([0-9]+)$/); + if (match) { + pageTrigger = match[1] - 1; + setTutorialPage(undefined, match[1] - 1); + } + + var match = window.location.href.match(/\?input=([^&]+)/); + if (match) { + controller.promptText(urlDecode(match[1])); + controller.inner.click(); + controller.typer.consoleControl(13); + } + }); + + function urlDecode(encodedString) { + var output = encodedString; + var binVal, + thisString; + var myregexp = /(%[^%]{2})/; + while ((match = myregexp.exec(output)) != null + && match.length > 1 + && match[1] != '') { + binVal = parseInt(match[1].substr(1), 16); + thisString = String.fromCharCode(binVal); + output = output.replace(match[1], thisString); + } + return output; + } + + function makeGuidSamplesClickable() { + $('.chapmark code').each(function() { + $(this).css('cursor', 'pointer'); + $(this).attr('title', 'Click me to insert "' + + $(this).text() + '" into the console.'); + $(this).click(function() { + controller.promptText($(this).text()); + controller.inner.click(); + }); + }); + } + + String.prototype.trim = function() { + return this.replace(/^[\t ]*(.*)[\t ]*$/, '$1'); + }; + + //////////////////////////////////////////////////////////////////////// + // Trigger console commands + function libTrigger(line, report) { + switch (line.trim()) { + case 'help': + { + setTutorialPage(undefined, 0); + report(); + pageTrigger = 0; + return true; + } + case 'back': + { + if (pageTrigger > 0) { + setTutorialPage(undefined, pageTrigger - 1); + pageTrigger--; + report(); + return true; + } + break; + } + case 'lessons': + { + var lessons = $('
    '); + for (var i = 0; i < pages.length; i++) { + if (pages[i].lesson) { + lessons.append($('
  1. '). + html('lesson' + pages[i].lesson + ' - ' + + pages[i].title)); + } + } + var lessonsList = '

    Lessons

    ' + lessons.html(); + tutorialGuide.animate({ + opacity: 0, + height: 0 + }, + 'fast', + function() { + tutorialGuide.html(lessonsList); + tutorialGuide.css({ + height: 'auto' + }); + tutorialGuide.animate({ + opacity: 1 + }, + 'fast'); + makeGuidSamplesClickable(); + }); + report(); + return true; + } + default: + { + if (line.trim() == 'chat') { + notice('irc', + 'Enter your nick on the right hand side and hit Connect!', + 'prompt'); + report(); + runWebchat(); + return true; + } + + var m = line.trim().match(/^link(.*)/); + if (m) { + var data; + if (m[1]) data = m[1].trim(); + else if (lastLine) data = lastLine; + if (data) { + var addr = '?input=' + encodeHex(data); + report([{ + msg: '', + className: 'latest-link' + }]); + var link = $(''). + text('link for ' + data).click(function() { + window.location.href = $(this).attr('href'); + return false; + }); + $('.latest-link').html(link).removeClass('latest-link'); + return true; + } + } + + var m = line.trim().match(/^step([0-9]+)/); + if (m) { + if ((m[1] * 1) <= pages.length) { + setTutorialPage(undefined, m[1] - 1); + report(); + pageTrigger = m[1] - 1; + return true; + } + } + var m = line.trim().match(/^help ([0-9]+)/); + if (m) { + for (var i = 0; i < pages.length; i++) { + if (pages[i].lesson == m[1] * 1) { + setTutorialPage(undefined, i); + report(); + pageTrigger = i; + return true; + } + } + } + } + }; + }; + + //////////////////////////////////////////////////////////////////////// + // Change the tutorial page + function setTutorialPage(result, n) { + if (pages[n]) { + window.location.href = '#' + (1 * n + 1); + tutorialGuide.find('#helpstone').remove(); + tutorialGuide.animate({ + opacity: 0, + height: 0 + }, + 'fast', + function() { + if (typeof(pages[n].guide) == 'function') + tutorialGuide.html(pages[n].guide(result)); + else + tutorialGuide.html(pages[n].guide); + var back = ''; + if (pageTrigger > 0) + back = 'You\'re at step' + (n + 1) + + '. Type back to go back.'; + else + back = 'You\'re at step' + (n + 1) + '. Type step' + (n + 1) + + ' to return here.'; + if (true) tutorialGuide + .append('
    ' + back + '
    ') + .append('
    Lesson: ' + + searchLessonBack(n) + + '
    '); + tutorialGuide.css({ + height: 'auto' + }); + tutorialGuide.animate({ + opacity: 1 + }, + 'fast'); + makeGuidSamplesClickable(); + }); + } + }; + + function searchLessonBack(page) { + for (var i = page; i >= 0; i--) { + if (pages[i].lesson) return pages[i].lesson; + } + return "1"; + } + + //////////////////////////////////////////////////////////////////////// + // Trigger a page according to a result + function triggerTutorialPage(n, result) { + n++; + if (pages[n] && (typeof(pages[n].trigger) == 'function') + && pages[n].trigger(result)) { + pageTrigger++; + setTutorialPage(result, n); + } + }; + + //////////////////////////////////////////////////////////////////////// + // Trigger various libraries after JSONRPC returned + function handleSuccess(report, result) { + if (result.type.match(/^Graphics\.Raphael\.Raphael[\r\n ]/)) { + runRaphael(result.result); + report(); + } + if (result.type.match(/standard/)) { + var hashrocket = ''; + var msg_value = ' => '; + if (result.result == null) { + hashrocket = ' => ' + msg_value = ''; + } + report( + [{ + msg: msg_value + result.output + hashrocket + result.result, + className: "jquery-console-message-value" + }]); + } + + if (result.type.match(/error/)) { + + report( + [{ + msg: result.error, + className: "jquery-console-message-value" + }]); + } + + if (result.type.match(/illegal/)) { + + report( + [{ + msg: 'You are not allowed to run that command!', + className: "jquery-console-message-value" + }]); + } + + + if (result.type.match(/line_continuation/)) { + report( + [{ + msg: '..', + className: "jquery-console-prompt-label" + }]); + } + + }; + + //////////////////////////////////////////////////////////////////////// + // Raphael support + function runRaphael(expr) { + raphaelPaper.clear(); + $('#raphael').parent().parent().slideDown(function() { + var exprs = expr.split(/\n/g); + for (var i = 0; i < exprs.length; i++) + raphaelRunExpr(exprs[i]); + }); + } + function raphaelRunExpr(expr) { + var expr = expr.split(/ /g); + switch (expr[0]) { + case 'new': + { + switch (expr[2]) { + case 'circle': + { + var x = expr[3], + y = expr[4], + radius = expr[5]; + var circle = raphaelPaper.circle(x * 1, y * 1, radius * 1); + circle.attr("fill", "#7360a4"); + break; + } + } + } + } + } + + function notice(name, msg, style) { + if (opera()) return; + if (!notices[name]) { + notices[name] = name; + return controller.notice(msg, style); + } + } + + function limitsError(str) { + if (str == "Terminated!") { + notice('terminated', + "This error means it took to long to work" + + " out on the server.", + 'fadeout'); + return "Terminated!"; + } else if (str == "Time limit exceeded.") { + notice('exceeded', + "This error means it took to long to work out on the server. " + + "Try again.", + 'fadeout'); + return "Terminated! Try again."; + } + return str; + } + +})(jQuery); diff --git a/TryRuby/public/javascripts/raphael-min.js b/TryRuby/public/javascripts/raphael-min.js new file mode 100644 index 0000000..4a99e3e --- /dev/null +++ b/TryRuby/public/javascripts/raphael-min.js @@ -0,0 +1,113 @@ +/* + * Raphael 1.4.3 - JavaScript Vector Library + * + * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com) + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. + */ +Raphael=function(){function m(){if(m.is(arguments[0],U)){for(var a=arguments[0],b=Aa[K](m,a.splice(0,3+m.is(a[0],O))),c=b.set(),d=0,f=a[o];d

    ";if(ha.childNodes[o]!=2)return m.type=null;ha=null}m.svg=!(m.vml=m.type=="VML");G[p]=m[p];m._id=0;m._oid=0;m.fn={};m.is=function(a,b){b=ca.call(b);return b=="object"&&a===Object(a)||b=="undefined"&&typeof a==b||b=="null"&&a==null||ca.call(ob.call(a).slice(8,-1))==b};m.setWindow=function(a){X=a;C=X.document};function ra(a){if(m.vml){var b=/^\s+|\s+$/g;ra=T(function(d){var f;d=(d+s)[I](b, +s);try{var e=new X.ActiveXObject("htmlfile");e.write("");e.close();f=e.body}catch(g){f=X.createPopup().document.body}e=f.createTextRange();try{f.style.color=d;var h=e.queryCommandValue("ForeColor");h=(h&255)<<16|h&65280|(h&16711680)>>>16;return"#"+("000000"+h[N](16)).slice(-6)}catch(i){return"none"}})}else{var c=C.createElement("i");c.title="Rapha\u00ebl Colour Picker";c.style.display="none";C.body[y](c);ra=T(function(d){c.style.color=d;return C.defaultView.getComputedStyle(c,s).getPropertyValue("color")})}return ra(a)} +function qb(){return"hsb("+[this.h,this.s,this.b]+")"}function rb(){return this.hex}m.hsb2rgb=T(function(a,b,c){if(m.is(a,"object")&&"h"in a&&"s"in a&&"b"in a){c=a.b;b=a.s;a=a.h}var d;if(c==0)return{r:0,g:0,b:0,hex:"#000"};if(a>1||b>1||c>1){a/=255;b/=255;c/=255}d=~~(a*6);a=a*6-d;var f=c*(1-b),e=c*(1-b*a),g=c*(1-b*(1-a));a=[c,e,f,f,g,c,c][d];b=[g,c,c,e,f,f,g][d];d=[f,f,g,c,c,e,f][d];a*=255;b*=255;d*=255;c={r:a,g:b,b:d,toString:rb};a=(~~a)[N](16);b=(~~b)[N](16);d=(~~d)[N](16);a=a[I](ga,"0");b=b[I](ga, +"0");d=d[I](ga,"0");c.hex="#"+a+b+d;return c},m);m.rgb2hsb=T(function(a,b,c){if(m.is(a,"object")&&"r"in a&&"g"in a&&"b"in a){c=a.b;b=a.g;a=a.r}if(m.is(a,ea)){var d=m.getRGB(a);a=d.r;b=d.g;c=d.b}if(a>1||b>1||c>1){a/=255;b/=255;c/=255}var f=Y(a,b,c),e=$(a,b,c);d=f;if(e==f)return{h:0,s:0,b:f};else{var g=f-e;e=g/f;a=a==f?(b-c)/g:b==f?2+(c-a)/g:4+(a-b)/g;a/=6;a<0&&a++;a>1&&a--}return{h:a,s:e,b:d,toString:qb}},m);var sb=/,?([achlmqrstvxz]),?/gi,sa=/\s*,\s*/,tb={hs:1,rg:1};m._path2string=function(){return this.join(",")[I](sb, +"$1")};function T(a,b,c){function d(){var f=Array[p].slice.call(arguments,0),e=f[Q]("\u25ba"),g=d.cache=d.cache||{},h=d.count=d.count||[];if(g[z](e))return c?c(g[e]):g[e];h[o]>=1000&&delete g[h.shift()];h[E](e);g[e]=a[K](b,f);return c?c(g[e]):g[e]}return d}m.getRGB=T(function(a){if(!a||(a+=s).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1};if(a=="none")return{r:-1,g:-1,b:-1,hex:"none"};!(tb[z](a.substring(0,2))||a.charAt()=="#")&&(a=ra(a));var b,c,d,f,e;if(a=a.match(pb)){if(a[2]){d=da(a[2].substring(5), +16);c=da(a[2].substring(3,5),16);b=da(a[2].substring(1,3),16)}if(a[3]){d=da((e=a[3].charAt(3))+e,16);c=da((e=a[3].charAt(2))+e,16);b=da((e=a[3].charAt(1))+e,16)}if(a[4]){a=a[4][H](sa);b=A(a[0]);c=A(a[1]);d=A(a[2]);f=A(a[3])}if(a[5]){a=a[5][H](sa);b=A(a[0])*2.55;c=A(a[1])*2.55;d=A(a[2])*2.55;f=A(a[3])}if(a[6]){a=a[6][H](sa);b=A(a[0]);c=A(a[1]);d=A(a[2]);return m.hsb2rgb(b,c,d)}if(a[7]){a=a[7][H](sa);b=A(a[0])*2.55;c=A(a[1])*2.55;d=A(a[2])*2.55;return m.hsb2rgb(b,c,d)}a={r:b,g:c,b:d};b=(~~b)[N](16); +c=(~~c)[N](16);d=(~~d)[N](16);b=b[I](ga,"0");c=c[I](ga,"0");d=d[I](ga,"0");a.hex="#"+b+c+d;isFinite(A(f))&&(a.o=f);return a}return{r:-1,g:-1,b:-1,hex:"none",error:1}},m);m.getColor=function(a){a=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||0.75};var b=this.hsb2rgb(a.h,a.s,a.b);a.h+=0.075;if(a.h>1){a.h=0;a.s-=0.2;a.s<=0&&(this.getColor.start={h:0,s:1,b:a.b})}return b.hex};m.getColor.reset=function(){delete this.start};var ub=/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig, +vb=/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig;m.parsePathString=T(function(a){if(!a)return null;var b={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},c=[];if(m.is(a,U)&&m.is(a[0],U))c=ta(a);c[o]||(a+s)[I](ub,function(d,f,e){var g=[];d=ca.call(f);e[I](vb,function(h,i){i&&g[E](+i)});if(d=="m"&&g[o]>2){c[E]([f][M](g.splice(0,2)));d="l";f=f=="m"?"l":"L"}for(;g[o]>=b[d];){c[E]([f][M](g.splice(0,b[d])));if(!b[d])break}});c[N]=m._path2string;return c});m.findDotsAtSegment=function(a,b,c,d,f,e,g,h,i){var j=1-i,l= +D(j,3)*a+D(j,2)*3*i*c+j*3*i*i*f+D(i,3)*g;j=D(j,3)*b+D(j,2)*3*i*d+j*3*i*i*e+D(i,3)*h;var n=a+2*i*(c-a)+i*i*(f-2*c+a),r=b+2*i*(d-b)+i*i*(e-2*d+b),q=c+2*i*(f-c)+i*i*(g-2*f+c),k=d+2*i*(e-d)+i*i*(h-2*e+d);a=(1-i)*a+i*c;b=(1-i)*b+i*d;f=(1-i)*f+i*g;e=(1-i)*e+i*h;h=90-w.atan((n-q)/(r-k))*180/w.PI;(n>q||r1){B=w.sqrt(B);c=B*c;d=B*d}B=c*c;var L=d*d;B=(e==g?-1:1)*w.sqrt(w.abs((B*L-B*x*x-L*k*k)/(B*x*x+L*k*k)));e=B*c*x/d+(a+h)/2;var B= +B*-d*k/c+(b+i)/2,x=w.asin(((b-B)/d).toFixed(7));k=w.asin(((i-B)/d).toFixed(7));x=ak)x-=l*2;if(!g&&k>x)k-=l*2}l=k-x;if(w.abs(l)>n){q=k;l=h;L=i;k=x+n*(g&&k>x?1:-1);h=e+c*w.cos(k);i=B+d*w.sin(k);q=Qa(h,i,c,d,f,0,g,l,L,[k,q,e,B])}l=k-x;f=w.cos(x);e=w.sin(x);g=w.cos(k);k=w.sin(k);l=w.tan(l/4);c=4/3*c*l;l=4/3*d*l;d=[a,b];a=[a+c*e,b-l*f];b=[h+c*k,i-l*g];h=[h,i];a[0]=2*d[0]-a[0];a[1]=2*d[1]-a[1];if(j)return[a,b,h][M](q);else{q=[a,b,h][M](q)[Q]()[H](","); +j=[];h=0;for(i=q[o];h1000000000000&&(n=0.5);w.abs(i)>1000000000000&&(i=0.5);if(n>0&&n<1){n=la(a,b,c,d,f,e,g,h,n);q[E](n.x);r[E](n.y)}if(i> +0&&i<1){n=la(a,b,c,d,f,e,g,h,i);q[E](n.x);r[E](n.y)}i=e-2*d+b-(h-2*e+d);j=2*(d-b)-2*(e-d);l=b-d;n=(-j+w.sqrt(j*j-4*i*l))/2/i;i=(-j-w.sqrt(j*j-4*i*l))/2/i;w.abs(n)>1000000000000&&(n=0.5);w.abs(i)>1000000000000&&(i=0.5);if(n>0&&n<1){n=la(a,b,c,d,f,e,g,h,n);q[E](n.x);r[E](n.y)}if(i>0&&i<1){n=la(a,b,c,d,f,e,g,h,i);q[E](n.x);r[E](n.y)}return{min:{x:$[K](0,q),y:$[K](0,r)},max:{x:Y[K](0,q),y:Y[K](0,r)}}}),ua=T(function(a,b){var c=ka(a),d=b&&ka(b);a={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null};b={x:0,y:0, +bx:0,by:0,X:0,Y:0,qx:null,qy:null};function f(q,k){var t;if(!q)return["C",k.x,k.y,k.x,k.y,k.x,k.y];!(q[0]in{T:1,Q:1})&&(k.qx=k.qy=null);switch(q[0]){case "M":k.X=q[1];k.Y=q[2];break;case "A":q=["C"][M](Qa[K](0,[k.x,k.y][M](q.slice(1))));break;case "S":t=k.x+(k.x-(k.bx||k.x));k=k.y+(k.y-(k.by||k.y));q=["C",t,k][M](q.slice(1));break;case "T":k.qx=k.x+(k.x-(k.qx||k.x));k.qy=k.y+(k.y-(k.qy||k.y));q=["C"][M](Pa(k.x,k.y,k.qx,k.qy,q[1],q[2]));break;case "Q":k.qx=q[1];k.qy=q[2];q=["C"][M](Pa(k.x,k.y,q[1], +q[2],q[3],q[4]));break;case "L":q=["C"][M](wa(k.x,k.y,q[1],q[2]));break;case "H":q=["C"][M](wa(k.x,k.y,q[1],k.y));break;case "V":q=["C"][M](wa(k.x,k.y,k.x,q[1]));break;case "Z":q=["C"][M](wa(k.x,k.y,k.X,k.Y));break}return q}function e(q,k){if(q[k][o]>7){q[k].shift();for(var t=q[k];t[o];)q.splice(k++,0,["C"][M](t.splice(0,6)));q.splice(k,1);i=Y(c[o],d&&d[o]||0)}}function g(q,k,t,L,B){if(q&&k&&q[B][0]=="M"&&k[B][0]!="M"){k.splice(B,0,["M",L.x,L.y]);t.bx=0;t.by=0;t.x=q[B][1];t.y=q[B][2];i=Y(c[o],d&& +d[o]||0)}}for(var h=0,i=Y(c[o],d&&d[o]||0);h0.5)*2-1;D(f-0.5,2)+D(e-0.5,2)>0.25&&(e=w.sqrt(0.25-D(f-0.5,2))*l+0.5)&&e!=0.5&&(e=e.toFixed(5)-1.0E-5*l)}return s});b=b[H](/\s*\-\s*/);if(d=="linear"){var h=b.shift();h=-A(h);if(isNaN(h))return null;h=[0,0,w.cos(h*w.PI/180),w.sin(h*w.PI/180)];var i=1/(Y(w.abs(h[2]),w.abs(h[3]))||1);h[2]*=i;h[3]*=i;if(h[2]<0){h[0]=-h[2];h[2]=0}if(h[3]<0){h[1]=-h[3];h[3]=0}}b=Ra(b);if(!b)return null; +i=a.getAttribute(aa);(i=i.match(/^url\(#(.*)\)$/))&&c.defs.removeChild(C.getElementById(i[1]));i=v(d+"Gradient");i.id="r"+(m._id++)[N](36);v(i,d=="radial"?{fx:f,fy:e}:{x1:h[0],y1:h[1],x2:h[2],y2:h[3]});c.defs[y](i);c=0;for(h=b[o];cb.height&&(b.height=e.y+e.height-b.y);e.x+e.width-b.x>b.width&&(b.width=e.x+e.width-b.x)}}a&&this.hide();return b};u[p].attr=function(a,b){if(this.removed)return this;if(a==null){a={};for(var c in this.attrs)if(this.attrs[z](c))a[c]=this.attrs[c];this._.rt.deg&&(a.rotation=this.rotate());(this._.sx!=1||this._.sy!= +1)&&(a.scale=this.scale());a.gradient&&a.fill=="none"&&(a.fill=a.gradient)&&delete a.gradient;return a}if(b==null&&m.is(a,ea)){if(a=="translation")return ya.call(this);if(a=="rotation")return this.rotate();if(a=="scale")return this.scale();if(a==aa&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;return this.attrs[a]}if(b==null&&m.is(a,U)){b={};c=0;for(var d=a.length;c1&&(a=1);f.opacity=a}b.fill&&(f.on=true);if(f.on==null||b.fill=="none")f.on=false;if(f.on&&b.fill)if(a=b.fill.match(Na)){f.src=a[1];f.type="tile"}else{f.color=m.getRGB(b.fill).hex;f.src= +s;f.type="solid";if(m.getRGB(b.fill).error&&(g.type in{circle:1,ellipse:1}||(b.fill+s).charAt()!="r")&&ma(g,b.fill)){d.fill="none";d.gradient=b.fill}}e&&c[y](f);f=c.getElementsByTagName("stroke")&&c.getElementsByTagName("stroke")[0];e=false;!f&&(e=f=R("stroke"));if(b.stroke&&b.stroke!="none"||b["stroke-width"]||b["stroke-opacity"]!=null||b["stroke-dasharray"]||b["stroke-miterlimit"]||b["stroke-linejoin"]||b["stroke-linecap"])f.on=true;(b.stroke=="none"||f.on==null||b.stroke==0||b["stroke-width"]== +0)&&(f.on=false);a=m.getRGB(b.stroke);f.on&&b.stroke&&(f.color=a.hex);a=((+d["stroke-opacity"]+1||2)-1)*((+d.opacity+1||2)-1)*((+a.o+1||2)-1);h=(A(b["stroke-width"])||1)*0.75;a<0&&(a=0);a>1&&(a=1);b["stroke-width"]==null&&(h=d["stroke-width"]);b["stroke-width"]&&(f.weight=h);h&&h<1&&(a*=h)&&(f.weight=1);f.opacity=a;b["stroke-linejoin"]&&(f.joinstyle=b["stroke-linejoin"]||"miter");f.miterlimit=b["stroke-miterlimit"]||8;b["stroke-linecap"]&&(f.endcap=b["stroke-linecap"]=="butt"?"flat":b["stroke-linecap"]== +"square"?"square":"round");if(b["stroke-dasharray"]){a={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};f.dashstyle=a[z](b["stroke-dasharray"])?a[b["stroke-dasharray"]]:s}e&&c[y](f)}if(g.type=="text"){f=g.paper.span.style;d.font&&(f.font=d.font);d["font-family"]&&(f.fontFamily=d["font-family"]);d["font-size"]&&(f.fontSize=d["font-size"]);d["font-weight"]&&(f.fontWeight=d["font-weight"]); +d["font-style"]&&(f.fontStyle=d["font-style"]);g.node.string&&(g.paper.span.innerHTML=(g.node.string+s)[I](/"));g.W=d.w=g.paper.span.offsetWidth;g.H=d.h=g.paper.span.offsetHeight;g.X=d.x;g.Y=d.y+F(g.H/2);switch(d["text-anchor"]){case "start":g.node.style["v-text-align"]="left";g.bbx=F(g.W/2);break;case "end":g.node.style["v-text-align"]="right";g.bbx=-F(g.W/2);break;default:g.node.style["v-text-align"]="center";break}}};ma=function(a,b){a.attrs=a.attrs|| +{};var c="linear",d=".5 .5";a.attrs.gradient=b;b=(b+s)[I](Ya,function(i,j,l){c="radial";if(j&&l){j=A(j);l=A(l);D(j-0.5,2)+D(l-0.5,2)>0.25&&(l=w.sqrt(0.25-D(j-0.5,2))*((l>0.5)*2-1)+0.5);d=j+P+l}return s});b=b[H](/\s*\-\s*/);if(c=="linear"){var f=b.shift();f=-A(f);if(isNaN(f))return null}var e=Ra(b);if(!e)return null;a=a.shape||a.node;b=a.getElementsByTagName(aa)[0]||R(aa);!b.parentNode&&a.appendChild(b);if(e[o]){b.on=true;b.method="none";b.color=e[0].color;b.color2=e[e[o]-1].color;a=[];for(var g=0, +h=e[o];g')}}catch(Kb){R=function(a){return C.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}Aa=function(){var a=Sa[K](0,arguments),b=a.container,c=a.height,d=a.width,f=a.x;a=a.y;if(!b)throw new Error("VML container not found.");var e=new G,g=e.canvas=C.createElement("div"),h=g.style;f=f||0;a=a||0;d=d||512; +c=c||342;d==+d&&(d+="px");c==+c&&(c+="px");e.width=1000;e.height=1000;e.coordsize=ja*1000+P+ja*1000;e.coordorigin="0 0";e.span=C.createElement("span");e.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";g[y](e.span);h.cssText=m.format("width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",d,c);if(b==1){C.body[y](g);h.left=f+"px";h.top=a+"px";h.position="absolute"}else b.firstChild?b.insertBefore(g, +b.firstChild):b[y](g);Fa.call(e,e,m.fn);return e};G[p].clear=function(){this.canvas.innerHTML=s;this.span=C.createElement("span");this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";this.canvas[y](this.span);this.bottom=this.top=null};G[p].remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]=Xa(a);return true}}G[p].safari=/^Apple|^Google/.test(X.navigator.vendor)&&(!(X.navigator.userAgent.indexOf("Version/4.0")+ +1)||X.navigator.platform.slice(0,2)=="iP")?function(){var a=this.rect(-99,-99,this.width+99,this.height+99);X.setTimeout(function(){a.remove()})}:function(){};function Db(){this.returnValue=false}function Eb(){return this.originalEvent.preventDefault()}function Fb(){this.cancelBubble=true}function Gb(){return this.originalEvent.stopPropagation()}var Hb=function(){if(C.addEventListener)return function(a,b,c,d){var f=Ba&&Ca[b]?Ca[b]:b;function e(g){if(Ba&&Ca[z](b))for(var h=0,i=g.targetTouches&&g.targetTouches.length;h< +i;h++)if(g.targetTouches[h].target==a){i=g;g=g.targetTouches[h];g.originalEvent=i;g.preventDefault=Eb;g.stopPropagation=Gb;break}return c.call(d,g)}a.addEventListener(f,e,false);return function(){a.removeEventListener(f,e,false);return true}};else if(C.attachEvent)return function(a,b,c,d){function f(g){g=g||X.event;g.preventDefault=g.preventDefault||Db;g.stopPropagation=g.stopPropagation||Fb;return c.call(d,g)}a.attachEvent("on"+b,f);function e(){a.detachEvent("on"+b,f);return true}return e}}();for(ha= +Ma[o];ha--;)(function(a){m[a]=u[p][a]=function(b){if(m.is(b,"function")){this.events=this.events||[];this.events.push({name:a,f:b,unbind:Hb(this.shape||this.node||C,a,b,this)})}return this};m["un"+a]=u[p]["un"+a]=function(b){for(var c=this.events,d=c[o];d--;)if(c[d].name==a&&c[d].f==b){c[d].unbind();c.splice(d,1);!c.length&&delete this.events;return this}return this}})(Ma[ha]);u[p].hover=function(a,b){return this.mouseover(a).mouseout(b)};u[p].unhover=function(a,b){return this.unmouseover(a).unmouseout(b)}; +u[p].drag=function(a,b,c){this._drag={};var d=this.mousedown(function(g){(g.originalEvent?g.originalEvent:g).preventDefault();this._drag.x=g.clientX;this._drag.y=g.clientY;this._drag.id=g.identifier;b&&b.call(this,g.clientX,g.clientY);Raphael.mousemove(f).mouseup(e)});function f(g){var h=g.clientX,i=g.clientY;if(Ba)for(var j=g.touches.length,l;j--;){l=g.touches[j];if(l.identifier==d._drag.id){h=l.clientX;i=l.clientY;(g.originalEvent?g.originalEvent:g).preventDefault();break}}else g.preventDefault(); +a&&a.call(d,h-d._drag.x,i-d._drag.y,h,i)}function e(){d._drag={};Raphael.unmousemove(f).unmouseup(e);c&&c.call(d)}return this};G[p].circle=function(a,b,c){return ab(this,a||0,b||0,c||0)};G[p].rect=function(a,b,c,d,f){return bb(this,a||0,b||0,c||0,d||0,f||0)};G[p].ellipse=function(a,b,c,d){return cb(this,a||0,b||0,c||0,d||0)};G[p].path=function(a){a&&!m.is(a,ea)&&!m.is(a[0],U)&&(a+=s);return Za(m.format[K](m,arguments),this)};G[p].image=function(a,b,c,d,f){return db(this,a||"about:blank",b||0,c||0, +d||0,f||0)};G[p].text=function(a,b,c){return eb(this,a||0,b||0,c||s)};G[p].set=function(a){arguments[o]>1&&(a=Array[p].splice.call(arguments,0,arguments[o]));return new Z(a)};G[p].setSize=fb;G[p].top=G[p].bottom=null;G[p].raphael=m;function ib(){return this.x+P+this.y}u[p].resetScale=function(){if(this.removed)return this;this._.sx=1;this._.sy=1;this.attrs.scale="1 1"};u[p].scale=function(a,b,c,d){if(this.removed)return this;if(a==null&&b==null)return{x:this._.sx,y:this._.sy,toString:ib};b=b||a;!+b&& +(b=a);var f,e,g=this.attrs;if(a!=0){var h=this.getBBox(),i=h.x+h.width/2,j=h.y+h.height/2;f=a/this._.sx;e=b/this._.sy;c=+c||c==0?c:i;d=+d||d==0?d:j;h=~~(a/w.abs(a));var l=~~(b/w.abs(b)),n=this.node.style,r=c+(i-c)*f;j=d+(j-d)*e;switch(this.type){case "rect":case "image":var q=g.width*h*f,k=g.height*l*e;this.attr({height:k,r:g.r*$(h*f,l*e),width:q,x:r-q/2,y:j-k/2});break;case "circle":case "ellipse":this.attr({rx:g.rx*h*f,ry:g.ry*l*e,r:g.r*$(h*f,l*e),cx:r,cy:j});break;case "text":this.attr({x:r,y:j}); +break;case "path":i=Oa(g.path);for(var t=true,L=0,B=i[o];L=i)return r;l=r}});function Ha(a,b){return function(c,d,f){c=ua(c); +for(var e,g,h,i,j="",l={},n=0,r=0,q=c.length;rd){if(b&&!l.start){e=jb(e,g,h[1],h[2],h[3],h[4],h[5],h[6],d-n);j+=["C",e.start.x,e.start.y,e.m.x,e.m.y,e.x,e.y];if(f)return j;l.start=j;j=["M",e.x,e.y+"C",e.n.x,e.n.y,e.end.x,e.end.y,h[5],h[6]][Q]();n+=i;e=+h[5];g=+h[6];continue}if(!a&&!b){e=jb(e,g,h[1],h[2],h[3],h[4],h[5],h[6],d-n);return{x:e.x,y:e.y,alpha:e.alpha}}}n+=i;e=+h[5];g=+h[6]}j+=h}l.end=j;e=a?n: +b?l:m.findDotsAtSegment(e,g,h[1],h[2],h[3],h[4],h[5],h[6],1);e.alpha&&(e={x:e.x,y:e.y,alpha:e.alpha});return e}}var Ib=T(function(a,b,c,d,f,e,g,h){for(var i={x:0,y:0},j=0,l=0;l<1.01;l+=0.01){var n=la(a,b,c,d,f,e,g,h,l);l&&(j+=D(D(i.x-n.x,2)+D(i.y-n.y,2),0.5));i=n}return j}),kb=Ha(1),za=Ha(),Ia=Ha(0,1);u[p].getTotalLength=function(){if(this.type=="path"){if(this.node.getTotalLength)return this.node.getTotalLength();return kb(this.attrs.path)}};u[p].getPointAtLength=function(a){if(this.type=="path")return za(this.attrs.path, +a)};u[p].getSubpath=function(a,b){if(this.type=="path"){if(w.abs(this.getTotalLength()-b)<1.0E-6)return Ia(this.attrs.path,a).end;b=Ia(this.attrs.path,b,1);return a?Ia(b,a).end:b}};m.easing_formulas={linear:function(a){return a},"<":function(a){return D(a,3)},">":function(a){return D(a-1,3)+1},"<>":function(a){a*=2;if(a<1)return D(a,3)/2;a-=2;return(D(a,3)+2)/2},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a-=1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a== +0||a==1)return a;var b=0.3,c=b/4;return D(2,-10*a)*w.sin((a-c)*2*w.PI/b)+1},bounce:function(a){var b=7.5625,c=2.75;if(a<1/c)a=b*a*a;else if(a<2/c){a-=1.5/c;a=b*a*a+0.75}else if(a<2.5/c){a-=2.25/c;a=b*a*a+0.9375}else{a-=2.625/c;a=b*a*a+0.984375}return a}};var S={length:0};function lb(){var a=+new Date;for(var b in S)if(b!="length"&&S[z](b)){var c=S[b];if(c.stop||c.el.removed){delete S[b];S[o]--}else{var d=a-c.start,f=c.ms,e=c.easing,g=c.from,h=c.diff,i=c.to,j=c.t,l=c.prev||0,n=c.el,r=c.callback,q= +{},k;if(d p:first-child{ + margin-top:0; +} +#facebox .content > p:last-child{ + margin-bottom:0; +} + +#facebox .close{ + position:absolute; + top:5px; + right:5px; + padding:2px; + background:#fff; +} +#facebox .close img{ + opacity:0.3; +} +#facebox .close:hover img{ + opacity:1.0; +} + +#facebox .loading { + text-align: center; +} + +#facebox .image { + text-align: center; +} + +#facebox img { + border: 0; + margin: 0; +} + +#facebox_overlay { + position: fixed; + top: 0px; + left: 0px; + height:100%; + width:100%; +} + +.facebox_hide { + z-index:-100; +} + +.facebox_overlayBG { + background-color: #000; + z-index: 99; +} +.facebox-footnote{ + margin-top:40px; +} \ No newline at end of file diff --git a/TryRuby/public/stylesheets/reset.css b/TryRuby/public/stylesheets/reset.css new file mode 100644 index 0000000..d709810 --- /dev/null +++ b/TryRuby/public/stylesheets/reset.css @@ -0,0 +1,46 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-style: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; +} +/* remember to define focus styles! */ +:focus { + outline: 0; +} +body { + line-height: 1; + color: black; + background: white; +} +ol, ul { + list-style: none; +} +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: separate; + border-spacing: 0; +} +caption, th, td { + text-align: left; + font-weight: normal; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ""; +} +blockquote, q { + quotes: "" ""; +} diff --git a/TryRuby/public/stylesheets/scaffold.css b/TryRuby/public/stylesheets/scaffold.css new file mode 100644 index 0000000..1ae7000 --- /dev/null +++ b/TryRuby/public/stylesheets/scaffold.css @@ -0,0 +1,56 @@ +body { background-color: #fff; color: #333; } + +body, p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { color: #000; } +a:visited { color: #666; } +a:hover { color: #fff; background-color:#000; } + +div.field, div.actions { + margin-bottom: 10px; +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; +} + +#error_explanation h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; +} + +#error_explanation ul li { + font-size: 12px; + list-style: square; +} diff --git a/TryRuby/public/tutorials/es_intro.html b/TryRuby/public/tutorials/es_intro.html new file mode 100644 index 0000000..256de2f --- /dev/null +++ b/TryRuby/public/tutorials/es_intro.html @@ -0,0 +1,666 @@ + +
    +

    Usando El Prompt

    +

    La ventana azul de arriba es el prompt de Ruby. ¡Escribe una línea de código Ruby, aprieta Enter + y velo correr!

    +

    Por ejemplo, trata de escribir algo matemático. Como: 2 + 6

    +
    \d+
    +
    +
    +

    Números & Matemática

    +

    ¡Bien! Hiciste algo de matemática. ¿Ves como la respuesta salta a la vista?

    +

    Ruby reconoce números y símbolos matemáticos. Puedes probar otras cosas como:

    +
    • 4 * 10
    • +
    • 5 - 12
    • +
    • 40 / 4
    +

    Seguro, las computadoras son habilidosas y rápidas con las matemáticas. Sigamos... ¿Quieres ver tu nombre al revés? + Escribe tu primer nombre entre comillas así: "Jimmy"

    +
    "(\w+)"
    +
    +
    +

    Decir Tu Nombre Al Revés

    +

    Perfecto, has formado un string de las letras de tu nombre. Un string + es un juego de caracteres que la computadora puede procesar.

    +

    Imagina que las letras están en una cuerda donde + se cuelga la ropa y las comillas son los broches que sostienen los bordes. Las comillas marcan el comienzo y el final.

    +

    Para invertir tu nombre, escribe: "Jimmy".reverse (¡No olvides el punto!)

    +
    "(\w+)"
    +
    +
    +

    Contando Las Letras

    +

    ¡Has usado el método reverse sobre tu nombre! Poniendo tu nombre entre comillas, + hiciste un string. Luego llamaste al método reverse, que funciona sobre un string cambiando + todas las letras de atrás para adelante.

    +

    Ahora, vamos a ver cuantas letras tiene tu nombre: "Jimmy".length

    +
    \d+
    +
    +
    +

    Repitiendo

    +

    Ahora, estoy seguro que te estarás preguntando para que sirve todo esto. Bueno, estoy seguro que habrás estado en alguna + pagina web donde te gritaron, ¡Hey, tu password es muy corto! Ves, algunos programas + usan este código tan simple.

    +

    Mira esto. Vamos a multiplicar tu nombre por 5. "Jimmy" * 5

    +
    "(\w+)"
    +
    +
    +

    Hey, Sumario #1 Listo

    +

    Vamos a ver que es lo que has aprendido en el primer minuto.

    +
      +
    • El prompt. Escribiendo código en el prompt verde obtienes + una respuesta del prompt rojo. Todo código te da una respuesta.
    • +
    • Números y strings son objetos matemáticos y de texto de Ruby.
    • +
    • Métodos Has usado métodos en Ingles como reverse + y métodos simbólicos como * (el método de multiplicación.) ¡Los métodos son acciones!
    • +
    +

    Esta es la esencia del aprendizaje. Tomar cosas simples, jugar con ellas + y trasformarlas en cosas nuevas. ¿Te sientes cómodo con todo? Te aseguro que lo estas.

    +

    Bien, vamos a hacer algo incomodo. Trata de invertir un número: 40.reverse

    +
    NoMethodError: undefined method `reverse' for (\d+):Fixnum
    +
    +
    +

    ¡Basta, Te Volviste Loco!

    +

    No puedes invertir el numero cuarenta. Supongo que puedes poner tu monitor en + frente de un espejo, pero invertir un numero no tiene sentido. Ruby lanza un + mensaje de error. Ruby te dice que no hay un método reverse para los números.

    +

    Tal vez si lo conviertes en un string: 40.to_s.reverse.

    +
    \"(\d+)\"
    +
    +
    +

    Los Chicos Son Diferentes De Las Chicas

    +

    Y los números son diferentes de los strings. Aunque puedes usar métodos en cualquier objeto + en Ruby, algunos métodos solo funcionan en cierto tipo de cosas. Pero siempre puedes + convertir entre diferentes tipos usando el método "to" de Ruby.

    +
    • to_s convierte cosas a strings.
    • +
    • to_i convierte cosas a integers (números.)
    • +
    • to_a convierte cosas a arrays.
    • +
    +

    ¿Que son los arrays?! Son listas. Escribe entre un par de corchetes: [].

    +
    \[\]
    +
    +
    +

    Mantenerse en Cola

    +

    Genial, eso es una lista vacía. Las listas guardan cosas en orden. + Como esperando en la cola para palomitas de maíz. Estas atrás de alguien y jamás + pensarías en empujarlo a un costado, ¿no es así? Y con respecto al tipo detrás de ti, + mantienes un ojo sobre el, ¿correcto?

    +

    Acá hay una lista para ti. Números de la lotería: [12, 47, 35].

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Uno Levanta La Mano

    +

    Una lista de números de la lotería. ¿Cual es el mayor?

    +

    Prueba: [12, 47, 35].max.

    +
    (\d+)
    +
    +
    +

    Manteniendo la Lista

    +

    Bien, bien. Pero es un fastidio el tener que reescribir esa lista, ¿no es así?

    +

    Guardemos nuestros números en un ticket de esta manera: ticket = [12, 47, 35]

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Ahora Escribe Ticket

    +

    Ahora, escribe: ticket

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Guardado, Escondido

    +

    ¡Fantástico! Te has aferrado a tus números de la lotería, escondiéndolos dentro de una + variable llamada ticket.

    +

    Vamos a poner tus números en orden, que te parece? Usa: ticket.sort!

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Sumario #2 Está Sobre Nosotros

    +

    Tenías una lista. Ordenaste la lista. La variable ticket ahora esta cambiada.

    +

    ¿Te diste cuenta que el método sort! tiene un claro y llamativo signo de exclamación al final? + Muchas veces los métodos de Ruby gritan así si es que alteran la variable para bien. No es nada + especial, solo una marca.

    +

    Ahora, mira como te fue en tu segundo minuto:

    +
      +
    • Errors. Si tratas de invertir un número o hacer algo sospechoso, + Ruby salteara el prompt para avisarte.
    • +
    • Arrays son listas para ordenar cosas en orden.
    • +
    • Variables guardan cosas y le dan un nombre. Usaste el + signo igual para hacerlo.
      Like: ticket = [14, 37, 18].
    • +
    +

    En total hay ocho lecciones. Estás a dos octavos de camino! + Esto es cosa simple, no te parece? Las cosas buenas están mas adelante.

    +

    Cambiemos de dirección por un momento. Rellené con un poco de poesía cierta + variable para ti. Hecha un vistazo. Escribe print poem

    +
    poem = "My toast has flown from my hand\nAnd my toast has gone to the +moon.\nBut when I saw it on television,\nPlanting our flag on Halley's +comet,\nMore still did I want to eat it.\n"
    +
    My toast (.+)
    +
    +
    +

    Desgraciadamente, Tú Odias la poesía de Tostadas

    +

    Mira, esta bien. No tiene que gustarte. Hackéalo, yo invito.

    +

    En vez de tostada, ve por un melón o algo. Prueba esto: poem['toast'] = 'honeydew'

    +

    Y luego escribe print poem para ver el nuevo poema.

    +
    My honey(.+)
    +
    +
    +

    Listo, Apuntado

    +

    Los corchetes que acabas de usar son muy comunes en Ruby. Recuerda, escribiste: poem['toast'] = 'honeydew'. Esa casilla con la palabra toast tiene corchetes a ambos lados, ¿ves?

    +

    Los +corchetes son como una mira para alinear un objetivo. Exacto. Estos +corchetes significan, "Estoy buscando ____." Listo, apuntado. Aquí estas buscando +una costada e intercambiándola por una fruta.

    +

    Aquí hay una pregunta: ¿Que pasa si volteamos el poema entero? poem.reverse +

    "\\n.ti tae ot (.+)"
    +
    +
    +

    Demasiado Invertido

    +

    Está bien, seguro. Entonces todo el poema fue puesto al revés. Letra por letra. Sin embargo, yo realmente solo quería + invertir las líneas. Mover la última línea a la primera y la primera hacia abajo a la ultima. Al revés, pero no + ése revés.

    +

    Aquí esta como lograrlo: poem.lines.to_a.reverse

    +
    \["More still did I(.+)"\]
    +
    +
    +

    Rizos de Metodos Concatenados

    +

    Dime, que es lo que ves? Que paso acá? Escribiste poem.lines.to_a.reverse y ¿que pasó?

    +

    Dos cosas pasaron. Convertiste poem en una lista usando +lines.to_a. lines decide la forma en que +el string se divide, luego to_a lo +convierte en un Array. (To array.) Diferentes métodos, como +bytes y chars pueden ser usadas en lugar de +lines. Usando lines, Ruby retornará cada línea de poem.

    +

    Luego, tu revertiste, reversed, esa lista. Tenías cada línea. Las revertiste. Eso es todo.

    +

    Vamos a hilar un método más al final de todo esto: print poem.lines.to_a.reverse.join +

    More still did I(.+)
    +
    +
    +

    De Todos los Sumarios, el #3 esta aquí

    +

    Buen espectáculo, mi amigo! El método join toma la lista de líneas revertidas y las pone juntas en un string. + (Seguro, también podrías haber usado to_s.)

    +

    Tiempo de Revisión.

    +
      +
    • Exclamaciones. Métodos pueden tener signos de exclamación (y también de interrogación) en + sus nombres. No es la gran cosa. Prueba: poem.include? "my hand"
    • +
    • Corchetes. Establece objetivos y busca cosas. Busca y reemplaza.
    • +
    • Concatenar métodos te permite hacer mas cosas juntas. Partir poem, revertirlo, + reensamblarlo: poem.lines.to_a.reverse.join
    • +
    +

    En este punto, querras manosear un poco más el poema. Una lista completa de métodos + String estan + + acá. + No temas y prueba algunos (como poem.downcase o poem.delete.)

    +

    Cuando estés listo para seguir adelante, escribe: books = {}

    +
    \{\}
    +
    +
    +

    Un Pequeñín Libro en Blanco

    +

    Has hecho un hash vacío. (también conocido como: un diccionario vacío.)

    +

    Vamos a rellenar con un libro de críticas en miniatura. Acá esta nuestro sistema de calificación:

    +
      +
    • :splendid → una obra maestra.
    • +
    • :quite_good → disfrutable, por supuesto que sí.
    • +
    • :mediocre → partes iguales de bueno y malo.
    • +
    • :quite_not_good → notablemente malo.
    • +
    • :abyssmal → una perdida de tiempo.
    • +
    +

    Para calificar un libro, pon el título entre corchetes y la calificación luego del signo igual.

    +

    Por ejemplo: books["Gravity's Rainbow"] = :splendid

    +
    :\w+
    +
    +
    +

    Mas Críticas Tamaño Bocadillo

    +

    Tú sigue, agrega mas críticas. Y, si quieres ver toda la lista, + implemente escribe: books

    +

    De nuevo, las calificaciones son: :splendid, :quite_good, :mediocre, + :quite_not_good, and :abyssmal.

    +

    Estas calificaciones no son strings. Cuando colocas dos puntos frente a una palabra simple, obtienes un + symbol. Los símbolos son más baratos que los strings (en términos de memoria de la computadora.) Si usas + una palabra una y otra vez en un programa, usa un símbolo. En vez de tener miles de + copias de una palabra en memoria, la computadora guardara el símbolo solamente una vez.

    +

    Una vez que tengas tres o cuatro libros allí + dentro, escribe: books.length.

    +
    [3-9]
    +
    +
    +

    Espera, ¿Me gustó Gravity's Rainbow?

    +

    Ves, el método length funciona sobre strings, list y hashes. Una gran cosa acerca de + Ruby es que los nombres usualmente se reutilizan, lo que significa menos nombres para recordar.

    +

    Si quisieras ver una de tus críticas hechas, vuelve a poner el título entre corchetes. Pero deja de lado el + signo igual.

    +

    Al igual que aquí: books["Gravity's Rainbow"]

    +
    :\w+
    +
    +
    +

    Hashes como Pares

    +

    Ten en mente que los hashes no mantienen las cosas en orden. Ese no es su trabajo. Solo emparejará dos + cosas: una key (llave) y un valor. En tus críticas, la key es el + titulo del libro y el valor es la calificación.

    +

    Si simplemente quieres ver los títulos de los libros que calificaste: books.keys

    +
    \[".*"\]
    +
    +
    +

    ¿Eres Duro?

    +

    ¿Estás dando duras injustas críticas? Sigamos puntuando con rigurosidad:
    ratings = Hash.new {0}

    +

    Entonces, bien, ahora vamos a contar tus críticas. Trata de seguirme. Escribe:
    + books.values.each { |rate| ratings[rate] += 1 }

    +

    (La línea vertical es el signo de tubería, probablemente lo logres con AltGr+1 con tu teclado.)

    +
    \[:.+\]
    +
    +
    +

    Un Recuento

    +

    Genial, wow! Has hecho un recuento de tus calificaciones. Escribe ratings para ver las cuenta. Este nuevo + hash muestra las calificaciones y luego el numero de veces que has dado esa calificación.

    +

    Una de las asombrosas cosas nuevas que acabamos de usar es un bloque, block. Vamos a explorar + explore these more in the next summary. más esto en el próximo sumario. Pero, básicamente, un bloque es un pedazo de código Ruby + rodeado por llaves.

    +

    Probemos otro bloque: 5.times { print "Odelay!" }

    +
    Odelay!Od.*
    +
    +
    +

    Ahora Arribamos al Sumario #4

    +

    Los bloques están siempre apegados a métodos. Como el método times, que toma el bloque y lo corre + repetidas veces. (En este caso: cinco veces.)

    +

    Esta última lección fue algo mas larga. Probablemente usaste unos tres minutos aprendiendo sobre:

    +
      +
    • Hashes. El pequeño diccionario con páginas arrugadas: {}.
    • +
    • Symbols. Pequeñas, eficientes palabras con dos puntos: :splendid.
    • +
    • Blocks. Pedazos de código que pueden ser clavados a muchos métodos de Ruby. Aquí + esta el código que usaste para crear el recuento:
      books.values.each { |rate| ratings[rate] += 1 }.
    • +
    +

    En tu computadora, probablemente tienes muchos archivos diferentes. Archivos con fotos en ellos, + archivos con programas dentro. Y los archivos usualmente se organizan en carpetas, también llamadas: + directorios.

    +

    He preparado algunos directorios para ti. Echa un vistazo: + Dir.entries "/"

    +
    \["\.", .+\]
    +
    +
    +

    La Privada Colección de Dr. Dir

    +

    Acabas de listar todo lo existente en el directorio superior. El directorio raíz, indicado + por la barra en diagonal. Conteniendo algunos programas y otros tutoriales y semejantes.

    +

    Entonces, ¿que es el método Dir.entries? Bueno, es solo un método ¿si?, + entries es el método llamado sobre la variable Dir. + Y Dir tiene una colección de métodos para chequear los archivos de los directorios.

    +

    Otra pequeña cosa de la que no hemos hablado abiertamente. Argumentos de los métodos, resaltados en verde.

    +
      +
    • Dir.entries "/": Cualquier cosa listada luego de un método + es considerado acoplamiento.
    • +
    • print poem: Ves, print es un método ordinario. + Y el poema esta acoplado. Para ser impreso.
    • +
    • print "pre", "event", "ual", "ism" posee varios argumentos, + con comas entre ellos.
    • +
    +

    Para listar solamente archivos de texto en el directorio: Dir["/*.txt"]

    +
    \["\/comics\.txt"\]
    +
    +
    +

    Ven, Lee Historietas Conmigo

    +

    El método Dir[] hace como entries pero tu buscas por archivos + archivos con carácteres de comodín. ¡Aquí, vemos esos corchetes otra vez! Te das + cuenta como todavía significan, "Estoy buscando _____.".

    +

    Mas específicamente: "Estoy buscando archivos que terminen con .txt."

    +

    Abramos este archivo con historietas de una vez. Aquí esta la manera:
    + print File.read("/comics.txt")

    +
    Achewood.+
    +
    +
    +

    Mi Comicas, Tu Comicas

    +

    ¡De acuerdo! Podemos comenzar a usar archivos para guardar cosas. Esto es excelente + porque normalmente cuando salimos de Ruby, todas nuestras variables desaparecerán. + Ruby, por si mismo, olvida estas cosas. Pero si salvamos cosas en archivos, + podemos leer esos archivos en futuras escapadas a Ruby.

    +

    Hey, y ¿adivina que? ¡El directorio /Home es tuyo! ¡Te lo entrego a ti! ¡Soy generoso! Hagamos una copia del archivo de la historieta.

    +

    Querrás hacer lo siguiente: FileUtils.copy('/comics.txt', '/Home/comics.txt') +

    Si ya has creado el archivo, usa File.delete('/Home/comics.txt') para arrojarlo a la basura.

    +
    nil
    +
    +
    +

    Tu Propio Territorio

    +

    Ok, tienes una copia. Chequéala: Dir["/Home/*.txt"]

    +

    Para agregar tu propia historieta a la lista, abramos el archivo en modo append.

    +

    Empieza asi: File.open("/Home/comics.txt", "a") do |f|.

    +
    ..
    +
    +
    +

    Y Ahora, para la Sorprendente Conclusión

    +

    Asi que tu prompt ha cambiado. ¿Lo notas? Tu prompt es doble punto ahora.

    +

    En este tutorial, este prompt significa que Ruby espera que escribas más. A + medida que vayas completando con líneas de código, los doble puntos se mantendrán + hasta que hallas finalizado.

    +

    Hot tip: si quieres parar de trabajar en el código y salirte de los doble puntos, usa el comando reset. + Si quieres volver a la pagina previa del tutorial, usa el comando back.

    +

    Aquí esta tu código. Ya has escrito la primera línea, asi que simplemente ingresa la segunda. (El \n + es el carácter de Enter.

    +
    • File.open("/Home/comics.txt", "a") do |f|
    • +
    •   f << "Cat and Girl: http://catandgirl.com/\n"
    • +
    • end
    • +
    +

    Y, como te has vuelto avanzado y capaz aquí, otro tip: puedes usar las flechas hacia + arriba y hacia abajo para editar tus viejos comandos o correrlos otra vez.

    +
    ..
    +
    +
    +

    Ruby se Sienta y Espera

    +

    Esa ultima línea agrega la historieta Cat and Girl a la lista, pero Ruby seguirá esperando + hasta que hallas terminado por completo para tomar acción.

    +

    Ahora, para finalizar el código que has empezado. Empezaste un nuevo bloque cuando escribiste do. + Hasta ahora los bloques que hemos visto usaban llaves. Esta vez usaremos do y end en lugar + de las llaves. Muchos Rubyistas usan do...end cuando el bloque ocupa varias líneas.

    +

    Terminemos ese bloque ahora mismo, con: end

    +
    • File.open("/Home/comics.txt", "a") do |f|
    • +
    •   f << "Cat and Girl: http://catandgirl.com/\n"
    • +
    • end
    • +
    +
    #.File:/Home/comics\.txt \(closed\).
    +
    +
    +

    El Reloj Clavado en el Archivo

    +

    ¡Bien, bien! Has añadido esa historieta al archivo. Puedes verlo por ti mismo: print File.read("/Home/comics.txt")

    +

    ¿Qué hora era cuando cambiaste el archivo? Veamos. Escribe: File.mtime("/Home/comics.txt")

    +
    \w+ \w+ \d+ \d{2}:\d{2}:\d{2} [+-]\d{4} \d{4}
    +
    +
    +

    Sólo la Manecilla de la Hora

    +

    Estupendo, allí esta la hora. La hora exacta en la que agregaste la historieta al archivo. El mtime te devuelve un objeto Time de Ruby.

    +

    Si sólo quieres ver la hora que era, aprieta la flecha para arriba y cambia la línea a: File.mtime("/Home/comics.txt").hour

    +
    \d+
    +
    +
    +

    Hola, ¿Quién Anda Ahí? Y el Sumario #5 Agita su Sombrero!

    +

    Bien hecho, bien hecho, bien hecho, ¡bien hecho! Realmente, realmente, realmente, realmente, ¡reaaaaaaaaalllmente!

    +

    Aquí esta tu último minuto de tu vida en retrospectiva:

    +
      +
    • Archivos. ¿Que más se puede decir? Muchos métodos para editar archivos y revisar directorios.
    • +
    • Argumentos. Los argumentos son listas de cosas mandadas en un método. Separadas con comas.
    • +
    • También hablamos sobre do y end que es otra manera de hacer un bloque.
    • +
    +

    Ahora ya sabes como usar Ruby por completo. Me refiero a que tienes lo esencial. Solo necesitas seguir + aprendiendo métodos y probar bloques más complejos.

    +

    Pero existe un lado de Ruby que no hemos hablado. Hacer tus propios métodos y clases.

    +

    Ahem! Acabemos con ello de una vez.

    +

    Empieza con: def load_comics( path )

    +
    ..
    +
    +
    +

    En Ruby, Def Leppard Significa ¡Define Leppard (un Método)!

    +

    Hey, bueno, lo hiciste. Estas haciendo tu propio método. Comenzaste con def, seguido por el nombre del método. + Y una lista de argumentos que va a necesitar el método. ¡Esto no da tanto miedo ni es peligroso!

    +

    Todo lo que debemos hacer es rellenar con Ruby y terminarlo con end.

    +

    Aquí esta el código:

    +
    • def load_comics( path )
    • +
    •   comics = {}
    • +
    •   File.foreach(path) do |line|
    • +
    •     url, name = line.split(': ')
    • +
    •     comics[url] = name.strip
    • +
    •   end
    • +
    •   comics
    • +
    • end
    • +
    +

    No necesitas indentar, si no quieres. Lo hice solo para que sea más legible.

    +
    nil
    +
    +
    +

    La Madura Fruta de tu Propia Creación

    +

    Un nuevo método ha nacido. Vamos a usarlo: comics = load_comics('/comics.txt')

    +

    Si tienes un problema, puedes haberlo escrito mal. Usa el comando back y prueba otra vez.

    +
    \{.*"Achewood"=."http://achewood.com/".*\}
    +
    +
    +

    Hey, Cool, Una Cosa de Historietas

    +

    En tu ventana de Ruby arriba, mira el código que has escrito para el método load_comics. ¿Qué esta pasando? Tu estas + pasando en la variable path y estas recibiendo la variable comics. Ruby permite filtrar el hash comics + que es devuelto al final del método.

    +

    Una cantidad de métodos se usaron para realizar el trabajo. Fíjate si puedes hallarlos.

    +
    • File.foreach es el método que abre un archivo y manda cada línea al bloque. La variable line + dentro del bloque do...end va turnando con cada línea del archivo.
    • +
    • split es un método para strings, que rompe los string en colocándolo en un array. Un hacha es arrojada sobre las comas + y las líneas se cortan en dos, dándonos la url y el nombre, name, de las historietas.
    • +
    • strip remueve los espacios extra alrededor de name. Por si acaso.
    • +
    +

    Justo allí. Bravo. Tienes las historietas en un hash de Ruby. ¿Pero ahora qué? ¿Qué tan bueno es en verdad?

    +

    Hagamos una página de links. ¿Qué te parece? Vamos a necesitar una pequeña librería que hice para ti.

    +

    Escribe: require 'popup'

    +
    true
    +
    +
    +

    El Navegador de Títere

    +

    Excelente, has cargado la librería popup. Está guardada en un archivo en el directorio Libraries. Mira: Dir["/Libraries/*"]

    +

    La librería popup contiene un puñado de métodos que he escrito y te dejaran controlar ventanas emergentes aquí en Try Ruby.

    +

    Mira, prueba esto: Popup.goto "http://google.com/"

    +
    \033\[1;JSm.*popup_goto\(.*\)\033\[m.*
    +
    +
    +

    Haciendo Links e Hilando Redes

    +

    Nuestro propio adorable, pequeño popup para manipular. también puedes rellenarlo con tus cositas. Empecemos por algo pequeño:

    +
    • Popup.make {
    • +
    •   h1 "My Links"
    • +
    •   link "Go to Google", "http://google.com/"
    • +
    • }
    • +
    +

    El termino h1 (h-uno) significa encabezado de nivel uno. En HTML, es el encabezado más grande.

    +
    \033\[1;JSm.*popup_make\(.*h1.*a href.*\)\033\[m.*
    +
    +
    +

    Los Popups son tan fáciles, es una Locura

    +

    Se ve bien, lo hiciste perfecto, tal como se te pidió. Hagamos una lista entonces.

    +

    Aquí esta como haces una lista con la librería de popup:

    +
    • Popup.make do
    • +
    •   h1 "Things To Do"
    • +
    •   list do
    • +
    •     p "Try out Ruby"
    • +
    •     p "Ride a tiger"
    • +
    •     p "(down River Euphrates)"
    • +
    •   end
    • +
    • end
    • +
    +

    El método p es la manera corta para "párrafo".

    +
    \033\[1;JSm.*popup_make\(.*h1.*ul.*li.*li.*\)\033\[m.*
    +
    +
    +

    Expandiendo las Historietas en la Tabla

    +

    Bien, esto esta yendo maravilloso. Esto es algo simple, pero mantén en mente que no sabias nada sobre Ruby hace quince minutos atrás!<

    +

    Ultimo paso. Vamos a juntar todo, ¿sabes? ¡Juntémoslo como esos juegos de + campanillas hermosas que tintinean en los pórticos bajo la hermosa luz del sol + en la playa!

    +

    Asegúrate de que las historietas están cargadas: comics = load_comics( '/comics.txt' )

    +

    Ahora, hagamos una lista de links para cada historieta:

    +
    • Popup.make do
    • +
    •   h1 "Comics on the Web"
    • +
    •   list do
    • +
    •     comics.each do |name, url|
    • +
    •       link name, url
    • +
    •     end
    • +
    •   end
    • +
    • end
    • +
    +

    Puedes clickear en los links y leer las historietas ¡inclusive en la ventana principal! ¡Bárbaro!

    +
    \033\[1;JSm.*popup_make\(.*h1.*ul.*li.*a href.*li.*a href.*\)\033\[m.*
    +
    +
    +

    Sumario #6 lo que Significa que has Llegado Muy Lejos

    +

    Eres un clérigo nivel 6 de Ruby. Quiero decir que buen trabajo has hecho. Vamos a revisar:

    +
      +
    • Agregaste tu propio método con def y usaste ese método load_comics varias veces.
    • +
    • Librerias. Tú usaste el método require para cargar la librería popup.
      Escribiendo: require 'popup'
    • +
    • Y como si no fuera suficiente, hiciste tu propia página web para listar los archivos de historietas. ¡Hiciste un programa real!
    • +
    +

    Entonces ¿Qué podrá venir luego? ¿Qué deberías aprender posiblemente ahora? + Ja, esta es la mejor parte. Has recorrido un largo camino y ahora descubrirás + las clases. En dos lecciones mas y ya estarás hecho.

    +

    Tempranamente, creamos un hash como este: Hash.new pruébalo.

    +
    \{\}
    +
    +
    +

    No una Clase de Escuela, Una Clase Trabajadora

    +

    Ves, las llaves vacías {} son abreviaciones para Hash.new. El método new + es usado para hacer objetos de cierta clase. (Piensa "clase" como en "clase + trabajadora" — un grupo especifico de objetos similares, tienen el + mismo trabajo, la misma camisa.)

    +

    Pregúntate esto: ¿Cómo haría mi blog en Ruby? ¿Dónde deberías + comenzar? Bien, deberías guardar tus entradas del blog en un archivo, ¿cierto? + Pero ¿cómo seguirías los títulos de las entradas y el momento en que fue creado? + Y cuando cargas el archivo, ¿cómo se vería en Ruby? ¿Sería un Hash? ¿O un Array? ¿O + un Array de Arrys? ¿O alguna otra cosa?

    Yo realmente creo que querrás usar una clase. Ya estas familiarizado con varias clases: Hash, Array, String.

    +

    Hagamos una clase nueva: class BlogEntry.

    +
    ..
    +
    +
    +

    El Relleno del Blog esta Hecho de

    +

    Has abierto una nueva clase BlogEntry. ¿De que están hechas las entradas de tu blog? Un titulo, seguro. también, + la fecha en la que fue creada. El texto entero de la entrada.

    +

    Vamos a poner el estado de ánimo, también, tal como LiveJournal. La internet ha traído de vuelta las personas de palitos y emoticones + fuera de la bancarrota.¡Que emoción!

    +

    Bueno, ya tienes la primera línea de la clase, aquí esta el resto:/p> +

    • class BlogEntry
    • +
    •   attr_accessor :title, :time, :fulltext, :mood
    • +
    • end
    • +
    +
    nil
    +
    +
    +

    Accessors Son las Extremidades Colgantes

    +

    Hey, buena clase, colega. Tienes una nueva clase BlogEntry. Para comenzar una entrada:
    entry = BlogEntry.new.

    +

    En la definición de la clase, usaste un método llamado attr_accessor. Existen varios métodos attribute + atributo, como este que agregan pequeñas configuraciones a la clase. Estos atributos son simplemente variables adosadas a la clase.

    +

    Piénsalo de este modo. Una clase es como una persona. Esa forma de estrella + del humano. Y los atributos son las extremidades, las diferentes partes que + hacen un cuerpo.

    +

    Para crear el titulo de tu entrada: entry.title = "Today Mt. Hood Was Stolen!"

    +
    ".+"
    +
    +
    +

    Un Objeto, Ese Estupendo Paquete Pequeño

    +

    Sigue adelante y pon la hora: entry.time = Time.now

    +

    Y el estado de animo: entry.mood = :sick

    +

    Y el anuncio en si: entry.fulltext = "I can't believe Mt. Hood was stolen! I am speechless! It was stolen by a giraffe who drove away + in his Cadillac Seville very nonchalant!!"

    +

    Para ver toda la configuración, simplemente escribe en el prompt: entry.

    +
    #.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
    +
    +
    +

    Agilizando

    +

    Genial, tu blog es impresionante. Hey, hagamos las cosas algo mas fácil. No querrás + poner la hora asi todas las veces que postees. Solo quieres escribir el titulo, + el contenido y el emoticon rápido, ¿verdad?

    +

    Vamos a agregar un método initialize.

    +
    • class BlogEntry
    • +
    •   def initialize( title, mood, fulltext )
    • +
    •     @time = Time.now
    • +
    •     @title, @mood, @fulltext = title, mood, fulltext
    • +
    •   end
    • +
    • end
    • +
    +

    Una vez que lo hallas escrito, prueba hacer una nueva entrada: BlogEntry.new

    +
    ArgumentError: wrong number of arguments \(0 for 3\).*
    +
    +
    +

    Tu le Has Enseñado al Blog a Rechazar lo Malo

    +

    ¿Viste como usamos dentro de la clase el símbolo arroba? De este modo: @time = Time.now

    +

    Fuera de la clase, usamos accesos (accessors): entry.time = Time.now Pero dentro variables de instancia: @time = Time.now + Son exactamente lo mismo, pero expresado en dos partes diferentes de tu programa.

    +

    tu blog ahora necesita un titulo, estado de ánimo y el post para funcionar. Cuando un nuevo BlogEntry es creado, el método initialize + es usado para chequear cualquier argumento para new. ¡Uh, necesitamos tres argumentos!

    +

    Prueba de nuevo con los tres.

    +

    entry2 += BlogEntry.new( "I Left my Hoodie on the Mountain!", :confused, "I am +never going back to that mountain and I hope a giraffe steals it." )

    +
    #.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
    +
    +
    +

    Una Jirafa No Ha Robado el Sumario #7

    +

    Aha, estas aquí. Y todo en una pieza. Todavía vamos a hacer tu blog realidad, pero hasta entonces, vamos a revisar, ¿bien?

    +
      +
    • Clases. Todo en Ruby es algún tipo de objeto. Las clases explican los objetos. Como ciertos objetos + trabajan. Por ejemplo, haces algunas entradas de blog y estos objetos están explicados en la clase BlogEntry. + En otras palabras: los llamas objetos del tipo BlogEntry.
    • +
    • Accessors son variables adosadas a un objeto que pueden ser usadas fuera del objeto. (entry.time = Time.now)
    • +
    • Variables de instancia son las mismas variables para accesos dentro del objeto. + Como en la definición de un método. (@time = Time.now)
    • +
    +

    Bueno, vamos a envolver las cosas, niño. Aquí esta el ultimo capitulo de la FASCINANTE épica + historia de Try Ruby! Ahora que ya has probado como todo funciona, ¿cómo vas a + usar eso alrededor de la casa y en tu tienda de comestibles? Eres una + gran persona (una de mis favoritas), pero necesitas dirección.

    +

    Vamos a terminar tu blog. Tienes entradas de blog, pero no un blog.

    +

    Pon las entradas en un array: blog = [entry, entry2]

    +
    \[#.BlogEntry:0x[0-9a-f]+.*, #.BlogEntry:0x[0-9a-f]+.*\]
    +
    +
    +

    Todo Se Trata de Combinar Cosas

    +

    Cosas hermosas pueden hacerse de partes simples de Ruby, especialmente + cuando las combinas entre ellas para formar algo nuevo. Aquí tenemos un blog + hecho de un array de clases. Y, en realidad, Ruby realmente hace buenas cosas + con este tipo de criaturas.

    +

    Aquí hay un puñado de cosas que puedes hacer con tu blog array:

    +
    • Querrás ordenar tus entradas de mas reciente a viejas. Puedes hacerlo con:
      + blog.sort_by { |entry| entry.time }.reverse
      Ve sort_by para más explicación.
    • +
    • Si quieres buscar en el blog por cualquier cosa relacionada con "cadillac":
      + blog.find_all { |entry| entry.fulltext.match(/cadillac/i) }
      + Lee mas en find_all + y match + para descubrir como funciona. También: /giraffe/i es un objeto Regexp, usado para concordar palabras.
    • +
    • Y agregar nuevas entradas con blog << new_entry
      + Y aquí la documentación del método <<.
    • +
    +

    Puedes buscar entre la lista de los métodos con los que viene Ruby en ruby-doc.org's core. + Otra buena lista hay en online pickaxe.

    +

    Un método realmente útil (probablemente yo uso esto mas que otra cosa) es map. Escribe: blog.map { |entry| entry.mood }

    +
    \[(:\w+, )+:\w+\]
    +
    +
    +

    Mira Su Cara — La Transformación Ha Comenzado

    +

    El método map recorre un array y reemplaza cada ítem con algo nuevo. ¿Dices que quieres reemplazar cada entrada de tu blog + con el nombre de Bruce Willis?. Hazlo entonces: blog.map { "Bruce Willis" }

    +

    Como el bloque siempre devuelve el string "Bruce Willis", eso es lo que obtienes. En el código que acabas de usar, la entrada entry wfue reemplazada + por solo el entry.mood.

    +

    Ahora, quiero que hagas un popup con las entradas de tu blog. Yo no te voy a + dar todo el código. Solo te voy a dar una parte.

    +
    • blog.each do |entry|
    • +
    •   h2 entry.title
    • +
    •   p entry.fulltext
    • +
    • end
    • +
    +

    Ahora, yo espero que pongas el código del popup alrededor y agregues un titulo con el nombre de tu blog usando h1. Como extra, tienes la hora de cada entrada para mostrar.

    +
    \033\[1;JSm.*popup_make\(.*h1.*h2.*li.*h2.*li.*\)\033\[m.*
    +
    +
    +

    Eres Una Especie de Gurú Web, Tengo Estrellas en Mis Ojos

    +

    Bien, ¡eso es! Este es exactamente el código que puedes usar para escribir tu + propio blog real en Ruby. Si te sientes aventurero, yo chequearía el video de + Rails videos donde muestran a un joven compañero creando un blog en 15 minutos. Solo sientete cómodo y mira.

    +

    Debo mencionar a Rails. Tú has estado aprendiendo el lenguaje Ruby, como + hablarlo. Pero Rails es grupo de librerías (algo asi como la librería de popup + que hemos estado usando.) Es un poderoso conjunto de herramientas para crear + sitios web. Si estas interesado en aprender sobre Rails, yo miraría head + por aquí directamente. ¡Empieza a usar tus habilidades en Ruby apropiadamente!

    +

    Algo que tiene Rails son métodos para manejar fechas fácilmente. Como, prueba: Time.now - 2.weeks

    +
    class Integer; def weeks; self * 7*24*60*60; end; end
    +
    \w+ \w+ \d+ \d{2}:\d{2}:\d{2} .*
    +
    +
    +

    Si Quieres Empezar Poco a Poco

    +

    Si quieres comenzar escribiendo pequeños programas en Ruby para practicar, tengo un proyecto llamado MouseHole + que es una pequeña caja de herramientas en la web para escribir programas cortos en Ruby. Puedes ver aquí algunos + scripts para ver que quiero decir.

    +

    MouseHole no es para escribir sitios web en realidad. Es para escribir + pequeños programas y correrlos dentro del navegador. Como hay un programa block + de notas para MouseHole y un programa que agrega una imagen de un ratón a los + links de la web que linkean a programas de MouseHole.

    +

    Tengo un script de MouseHole dentro de un archivo aquí mismo:
    + print File.read("/MouseHole/flickrpedia.user.rb")

    +
    .*Inserts Wikipedia links for Flickr tags.*
    +
    +
    +

    Sumario #8, El Sumario Hey-Relájate-Lo-Hiciste-Bien

    +

    Esta ultima sección se tomó un momento para relajarse, para darte algunos consejos de como + puedes usar Ruby. Si lo has disfrutado, descarga Ruby e instálalo.

    + +

    Una vez que tengas Ruby instalado, puedes usar Ruby Interactivo ejecutando el comando irb en el prompt de tu sistema. Para mas sobre Irb, esta + The Tiger's Vest para ayudarte.

    +

    Tú realmente mereces una torta doble-capa con doble-doble azúcar glaseado y + un tipo tocando una de esas guitarras que son doble-guitarra. Quiero decir + terminaste, ¡lo hiciste! No hay dudas de eso, ¡eres un gran ser certificado!

    +
    +
    + \ No newline at end of file diff --git a/TryRuby/public/tutorials/intro.html b/TryRuby/public/tutorials/intro.html new file mode 100644 index 0000000..56c1d1c --- /dev/null +++ b/TryRuby/public/tutorials/intro.html @@ -0,0 +1,680 @@ + +
    +

    Using the Prompt

    +

    The blue window above is a Ruby prompt. Type a line of Ruby code, hit Enter + and watch it run!

    +

    For example, try typing some math. Like: 2 + 6

    +
    \d+
    +
    +
    +

    Numbers & Math

    +

    Good! You did a bit of math. See how the answer popped out?

    +

    Ruby recognizes numbers and mathematic symbols. You could try some other math like:

    +
    • 4 * 10
    • +
    • 5 - 12
    • +
    • 40 / 4
    +

    Sure, computers are handy and fast for math. Let's move on. Want to see your name reversed? + Type your first name in quotes like this: "Jimmy"

    +
    "(\w+)"
    +
    +
    +

    Say Your Name Backwards

    +

    Perfect, you've formed a string from the letters of your name. A string + is a set of characters the computer can process.

    +

    Imagine the letters are on a string of + laundry line and the quotes are clothespins holding the ends. The quotes mark the beginning and end.

    +

    To reverse your name, type: "Jimmy".reverse (Don't forget the dot!)

    +
    "(\w+)"
    +
    +
    +

    Counting the Letters

    +

    You have used the reverse method on your name! By enclosing your name in + quotes, you made a string. Then you called the reverse method, which works on strings to flip + all the letters backwards.

    +

    Now, let's see how many letters are in your name: "Jimmy".length

    +
    \d+
    +
    +
    +

    On Repeat

    +

    Now, I'm sure by now you're wondering what any of this is good for. Well, I'm sure you've been to + a website that screamed, Hey, your password is too short! See, some programs + use this simple code.

    +

    Watch this. Let's multiply your name by 5. "Jimmy" * 5

    +
    "(\w+)"
    +
    +
    +

    Hey, Summary #1 Already

    +

    Let's look at what you've learned in the first minute.

    +
      +
    • The prompt. Typing code into the green prompt gives you + an answer from a red prompt. All code gives an answer.
    • +
    • Numbers and strings are Ruby's math and text objects.
    • +
    • Methods. You've used English-language methods like reverse + and symbolic methods like * (the multiplication method.) Methods are action!
    • +
    +

    This is the essence of your learning. Taking simple things, toying with + them and turning them into new things. Feeling comfortable yet? I promise you are.

    +

    Okay, let's do something uncomfortable. Try reversing a number: 40.reverse

    +
    NoMethodError: undefined method `reverse' for (\d+):Fixnum
    +
    +
    +

    Stop, You're Barking Mad!

    +

    You can't reverse the number forty. I guess you can hold your monitor up to the + mirror, but reversing a number just doesn't make sense. Ruby has tossed an error + message. Ruby is telling you there is no method reverse for numbers.

    +

    Maybe if you turn it into a string: 40.to_s.reverse.

    +
    \"(\d+)\"
    +
    +
    +

    Boys are Different From Girls

    +

    And numbers are different from strings. While you can use methods on any object + in Ruby, some methods only work on certain types of things. But you can always + convert between different types using Ruby's "to" methods.

    +
    • to_s converts things to strings.
    • +
    • to_i converts things to integers (numbers.)
    • +
    • to_a converts things to arrays.
    • +
    +

    What are arrays?! They are lists. Type in a pair of brackets: [].

    +
    \[\]
    +
    +
    +

    Standing in Line

    +

    Great, that's an empty list. Lists store things in order. + Like standing in line for popcorn. You are behind someone and you wouldn't + dream of pushing them aside, right? And the guy behind you, you've got a + close eye on him, right?

    +

    Here's a list for you. Lottery numbers: [12, 47, 35].

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    One Raises Its Hand

    +

    A list of lottery numbers. Which one is the highest?

    +

    Try: [12, 47, 35].max.

    +
    (\d+)
    +
    +
    +

    Tucking a List Away

    +

    Good, good. But it's annoying to have to retype that list, isn't it?

    +

    Let's save our numbers inside a ticket like so: ticket = [12, 47, 35]

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Now Type Ticket

    +

    Now, type: ticket

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Saved, Tucked Away

    +

    Fantastic! You've hung on to your lotto numbers, tucking them away inside a + variable called ticket.

    +

    Let's put your lotto numbers in order, how about? Use: ticket.sort!

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Summary #2 is Upon Us

    +

    You had a list. You sorted the list. The ticket variable is now changed.

    +

    Did you notice that the sort! method has a big, bright exclamation at the end? + A lot of times Ruby methods shout like that if they alter the variable for good. It's nothin + special, just a mark.

    +

    Now, look how your second minute went:

    +
      +
    • Errors. If you try to reverse a number or do anything fishy, + Ruby will skip the prompt and tell you so.
    • +
    • Arrays are lists for storing things in order.
    • +
    • Variables save a thing and give it a name. You used the + equals sign to do this.
      Like: ticket = [14, 37, 18].
    • +
    +

    In all there are eight lessons. You are two-eighths of the way there! + This is simple stuff, don't you think? Good stuff up ahead.

    +

    Let's change directions for a moment. I've stuffed a bit of poetry for you in + a certain variable. Take a look. Type print poem

    +
    poem = "My toast has flown from my hand\nAnd my toast has gone to the +moon.\nBut when I saw it on television,\nPlanting our flag on Halley's +comet,\nMore still did I want to eat it.\n"
    +
    My toast (.+)
    +
    +
    +

    Sadly, You Hate Toast Poetry

    +

    Look, it's okay. You don't have to like it. Hack it up, be my guest.

    +

    Instead of toast, go for a melon or something. Try this: poem['toast'] = 'honeydew'

    +

    And then type print poem by itself to see the new poem.

    +
    My honey(.+)
    +
    +
    +

    Ready, Aim

    +

    The square brackets you just used are very common in Ruby. Remember, you typed: poem['toast'] = 'honeydew'. That box with the word toast has a square bracket on each side, see?

    +

    The +two brackets are like sights used to line up a target. Exactly. These +brackets mean, "I am looking for ____." Ready, aim. Here you're looking +for toast and swapping it out with fruit.

    +

    Here's a question: what happens when we reverse this whole poem? poem.reverse +

    "\\n.ti tae ot (.+)"
    +
    +
    +

    Too Much Reversal

    +

    Okay, sure. So the whole poem's been turned backwards, letter-by-letter. I really want to just + reverse the lines, though. Move the last line up to first and the first line down to last. Backwards, but not + that backwards.

    +

    Here's how: poem.lines.to_a.reverse

    +
    \["More still did I(.+)"\]
    +
    +
    +

    Ringlets of Chained Methods

    +

    So what do you see? What happened there? You typed poem.lines.to_a.reverse and what happened?

    +

    Two things happened. You turned the poem into a +list using lines.to_a. lines decides the way +the string is split up, then to_a converted it into an +Array. (To array.) Different methods, such +as bytes and chars can be used in place +of lines. By using lines, ruby will return each line of the poem.

    +

    Then, you reversed that list. You had each line. You reversed them. That's it.

    +

    Let's tack one more method on the end there: print poem.lines.to_a.reverse.join +

    More still did I(.+)
    +
    +
    +

    Of All the Summaries, #3 is Here Now

    +

    Good show, my friend! The join method took that list of reversed lines and put them + together into a string. (Sure, you could have also just used to_s.)

    +

    Review time.

    +
      +
    • Exclamations. Methods may have exclamations (and also question marks) + in their name. No big deal. Try: poem.include? "my hand"
    • +
    • Square brackets. Target and find things. Search and replace.
    • +
    • Chaining methods lets you get a lot more done. Break up a poem, + reverse it, reassemble it: poem.lines.to_a.reverse.join
    • +
    +

    At this point, you may want to tinker with the poem a bit more. A complete list of all + the String methods is + + here. + Go ahead and try a few (such as poem.downcase or poem.delete.)

    +

    When you're ready to move on, type: books = {}

    +
    \{\}
    +
    +
    +

    A Wee Blank Book

    +

    You've made an empty hash. (Also known as: an empty dictionary.)

    +

    We're going to stuff some miniature book reviews in this hash. Here's our rating system:

    +
      +
    • :splendid → a masterpiece.
    • +
    • :quite_good → enjoyed, sure, yes.
    • +
    • :mediocre → equal parts great and terrible.
    • +
    • :quite_not_good → notably bad.
    • +
    • :abyssmal → steaming wreck.
    • +
    +

    To rate a book, put the title in square brackets and put the rating after the equals.

    +

    For example: books["Gravity's Rainbow"] = :splendid

    +
    :\w+
    +
    +
    +

    More Bite-Size Reviews

    +

    Keep going, fill it up with reviews. And, if you want to see the whole list, + just type: books

    +

    Again, the ratings are: :splendid, :quite_good, :mediocre, + :quite_not_good, and :abyssmal.

    +

    These ratings are not strings. When you place a colon in front of a simple word, you get a + symbol. Symbols are cheaper than strings (in terms of computer memory.) If + you use a word over and over in your program, use a symbol. Rather than having thousands of + copies of that word in memory, the computer will store the symbol only once.

    +

    Once you've got three or four books in + there, type: books.length.

    +
    [3-9]
    +
    +
    +

    Wait, Did I Like Gravity's Rainbow?

    +

    See, the length method works on strings, list and hashes. One great thing about + Ruby is that names are often reused, which means fewer names you need to remember.

    +

    If you'd like to look up one of your old reviews, again put the title in the square. But leave off + the equals.

    +

    Just like this: books["Gravity's Rainbow"]

    +
    :\w+
    +
    +
    +

    Hashes as Pairs

    +

    Keep in mind that hashes won't keep things in order. That's not their job. It'll just pair up two + things: a key and a value. In your reviews, the key is the book's + title and the value is the rating.

    +

    If you want to just see the titles of the books you've reviewed: books.keys

    +
    \[".*"\]
    +
    +
    +

    Are You Harsh?

    +

    So are you giving out harsh, unfair reviews? Let's keep score with this hash:
    ratings = Hash.new {0}

    +

    Then, okay, now let's count up your reviews. Just stay with me. Type:
    + books.values.each { |rate| ratings[rate] += 1 }

    +

    (The straight line in the code is the pipe character, probably located right above the + Enter key on your keyboard.)

    +
    \[:.+\]
    +
    +
    +

    A Tally

    +

    Great, wow! You've made a scorecard of your ratings. Type ratings to see the count. + This new hash shows a rating and then the number of times you've given that rating.

    +

    One of the amazing new things we've just used is a block. We're going to + explore these more in the next summary. But, basically, a block is a bit of Ruby code surrounded + by curly braces.

    +

    Let's try another block: 5.times { print "Odelay!" }

    +
    Odelay!Od.*
    +
    +
    +

    Now Arriving at Summary #4

    +

    Blocks are always attached to methods. Like the times method, which takes the + block and runs the code over and over. (In this case: five times.)

    +

    This last lesson was a bit longer. You've probably used up three minutes learning about:

    +
      +
    • Hashes. The little dictionary with the curly pages: {}.
    • +
    • Symbols. Tiny, efficient code words with a colon: :splendid.
    • +
    • Blocks. Chunks of code which can be tacked on to many of Ruby's methods. Here's the + code you used to build a scorecard:
      books.values.each { |rate| ratings[rate] += 1 }.
    • +
    +

    On your computer, you probably have a lot of different files. Files with pictures in them, + files with programs in them. And files are often organized into folders, also called: + directories.

    +

    I've prepared a few directories for you. Take a look: + Dir.entries "/"

    +
    \["\.", .+\]
    +
    +
    +

    The Private Collection of Dr. Dir

    +

    You've just listed out everything in the top directory. The root directory, indicated + by a single slash. Containing some programs and other tutorials and such.

    +

    So, what is the Dir.entries method? Well, it's just a method, right? + entries is a method called on the Dir variable. + And Dir has a collection of methods for checking out file directories.

    +

    One other little thing we haven't really talked about openly. Method arguments, highlighted in green.

    +
      +
    • Dir.entries "/": Anything listed after a method + is considered an attachment.
    • +
    • print poem: See, print is an ordinary method. And the + poem is attached. To be printed.
    • +
    • print "pre", "event", "ual", "ism" has several arguments, with commas + between them.
    • +
    +

    To list just the text files in that directory: Dir["/*.txt"]

    +
    \["\/comics\.txt"\]
    +
    +
    +

    Come, Read Comics With Me

    +

    The Dir[] method is like entries but you search for files + with wildcard characters. Here, we see those square brackets again! Notice how + they still mean, "I am looking for _____?"

    +

    More specifically: "I am looking for files which end with .txt."

    +

    Let's crack open this comics file, then. Here's the way:
    + print File.read("/comics.txt")

    +
    Achewood.+
    +
    +
    +

    Mi Comicas, Tu Comicas

    +

    All right! We can start to use files to store things. This is great because normally when + we exit Ruby, all our variables will be gone. Ruby, by itself, forgets these things. + But if we save things in files, we can read those files in future Ruby escapades.

    +

    Hey, and guess what? The /Home directory is yours! I gave it to you! I am generous! Let's make a copy of the comics file.

    +

    You'll want to: FileUtils.copy('/comics.txt', '/Home/comics.txt') +

    If you've already created the file, use File.delete('/Home/comics.txt') to trash it.

    +
    nil
    +
    +
    +

    Your Own Turf

    +

    Okay, you've got a copy. Check it: Dir["/Home/*.txt"]

    +

    To add your own comic to the list, let's open the file in append mode.

    +

    Start like this: File.open("/Home/comics.txt", "a") do |f|.

    +
    ..
    +
    +
    +

    And Now For the Startling Conclusion

    +

    So your prompt has changed. See that? Your prompt is a double dot now.

    +

    In this tutorial, this prompt means that Ruby is expecting you to type more. + As you type in the lines of Ruby code, the double dots will continue until you + are completely finished.

    +

    Hot tip: If you want to stop working on the code and break out of the double dots, use the reset + command. If you want to go the previous page of the tutorial, use the back command.

    +

    Here's your code. You've already typed the first line, so just enter the second line. (The \n + is an Enter character.

    +
    • File.open("/Home/comics.txt", "a") do |f|
    • +
    •   f << "Cat and Girl: http://catandgirl.com/\n"
    • +
    • end
    • +
    +

    And, since you're getting so advanced and capable here, one other tip: you can use the up and down arrow keys to + edit your old commands or run them again.

    +
    ..
    +
    +
    +

    Ruby Sits Still

    +

    That last line adds the Cat and Girl comic to the list, but Ruby's going to wait until you're totally finished to + take action.

    +

    Now, to finish the code you've started. You opened a new block when you typed do. + So far the blocks we've seen have used curly braces. This time we'll be using do and end instead + of curly braces. A lot of Rubyists will use do...end when the block goes on for many lines.

    +

    Let's get that block finished now, with: end

    +
    • File.open("/Home/comics.txt", "a") do |f|
    • +
    •   f << "Cat and Girl: http://catandgirl.com/\n"
    • +
    • end
    • +
    +
    #.File:/Home/comics\.txt \(closed\).
    +
    +
    +

    The Clock Nailed To the File

    +

    Good, good! You've added that new comic to the file. You can see for yourself: print File.read("/Home/comics.txt")

    +

    What time was it when you changed the file? Let's check. Type: File.mtime("/Home/comics.txt")

    +
    \d{4}-\d+-\d+ \d{2}:\d{2}:\d{2} [+-]\d{4}
    +
    +
    +

    Just the Hour Hand

    +

    Great, there's the time. The precise time exactly when you added to the file. The mtime gives you a Ruby Time object.

    +

    If you want to check just what hour it was, hit the up arrow key and change the line to: File.mtime("/Home/comics.txt").hour

    +
    \d+
    +
    +
    +

    Hallo, Who's There? And Summary #5 Waves Its Hat!

    +

    Well done, well done, well done, well done! Truly, truly, truly, truly, truuuuuuuuly!

    +

    Here's the last few minutes of your life in review:

    +
      +
    • Files. What more can be said? Lots of methods for editing files and lookin around in directories.
    • +
    • Arguments. Arguments are a list of things sent into a method. With commas between.
    • +
    • We also spoke about do and end which are another way to make a block.
    • +
    +

    You totally know how to use Ruby now. I mean you've got down the essentials. You just need to keep learning more methods and + try out more complex blocks.

    +

    But there's one side of Ruby we haven't settled. Making your own methods and classes.

    +

    Ahem! Let's get it over with then.

    +

    Start with: def load_comics( path )

    +
    ..
    +
    +
    +

    In Ruby, Def Leppard Means Define Leppard (a Method)!

    +

    Hey, okay, you done it. You're making your own method. You started with def, followed by the name of the method. + And a list of arguments which the method will need. This isn't too scary and dangerous!

    +

    All we have to do is fill it up with Ruby and finish it up with end.

    +

    Here's the code:

    +
    • def load_comics( path )
    • +
    •   comics = {}
    • +
    •   File.foreach(path) do |line|
    • +
    •     name, url = line.split(': ')
    • +
    •     comics[name] = url.strip
    • +
    •   end
    • +
    •   comics
    • +
    • end
    • +
    +

    No need to indent, if you don't want. I just do that to make it read easier.

    +
    nil
    +
    +
    +

    The Ripened Fruit of Your Own Creation

    +

    A new method is born. Let us use it: comics = load_comics('/comics.txt')

    +

    If you have a problem, you might have mistyped. Use the back command and try again.

    +
    \{.*"Achewood"=."http://achewood.com/".*\}
    +
    +
    +

    Hey, Cool, a Comics Thing

    +

    In your Ruby window above, look at the code you've typed for the load_comics method. What is happening? You're + passing in the path variable and you're getting back the comics variable. Ruby lets the comics + hash trickle out the end of the method.

    +

    A number of methods were used to get the job done. See if you can spot them.

    +
    • File.foreach is a method which opens a file and hands each line to the block. The line + variable inside the do...end block took turns with each line in the file.
    • +
    • split is a method for strings, which breaks the string up into an array. An axe is laid on the colon + and the line is chopped in half, giving us the url and name for each comic.
    • +
    • strip removes extra spaces around the name. Just in case.
    • +
    +

    Right on. Bravo. You've got the comics in a Ruby hash. But what now? What good is this really?

    +

    Let's make a page of links. How about that? I went ahead and loaded a little library I've made for you.

    +

    Type: next. This is temporary as I updates new lessons.

    +
    true
    +
    +
    +

    Browser Puppetry

    +

    Excellent, you've loaded the popup library. It's saved in a file in the Libraries folder. See: Dir["/Libraries/*"]

    +

    The popup library contains a bunch of methods I've written which let you control a popup here on the Try Ruby site.

    +

    Here, try this: Popup.goto "http://google.com/"

    +
    \033\[1;JSm.*popup_goto\(.*\)\033\[m.*
    +
    +
    +

    Making Links and Spinning Webs

    +

    Our own lovely, little popup to manipulate. You can also fill it with your own goodies. We'll start small:

    +
    • Popup.make {
    • +
    •   h1 "My Links"
    • +
    •   link "Go to Google", "http://google.com/"
    • +
    • }
    • +
    +

    The term h1 (h-one) means a level-one header. In HTML, this is the largest size of header.

    +
    \033\[1;JSm.*popup_make\(.*h1.*a href.*\)\033\[m.*
    +
    +
    +

    Popups Are So Easy, It's Crazy

    +

    Looks good, you did it perfectly, just as you were asked. Let's make a list then.

    +

    Here's how you make a list with the popup library:

    +
    • Popup.make do
    • +
    •   h1 "Things To Do"
    • +
    •   list do
    • +
    •     p "Try out Ruby"
    • +
    •     p "Ride a tiger"
    • +
    •     p "(down River Euphrates)"
    • +
    •   end
    • +
    • end
    • +
    +

    The p method is short for "paragraph".

    +
    \033\[1;JSm.*popup_make\(.*h1.*ul.*li.*li.*\)\033\[m.*
    +
    +
    +

    Spread the Comics on the Table

    +

    Okay, this is coming along wonderfully. This is simple stuff, but keep in mind that you didn't know any Ruby whatsoever just fifteen minutes ago!

    +

    Last +step. Let's tie it all together, you know? Let's make it chime together +like a very nice set of glistening chimes on the beach in the +maginificent sunlight!

    +

    Make sure the comics are loaded: comics = load_comics( '/comics.txt' )

    +

    Now, let's make a list of the links to each comic:

    +
    • Popup.make do
    • +
    •   h1 "Comics on the Web"
    • +
    •   list do
    • +
    •     comics.each do |name, url|
    • +
    •       link name, url
    • +
    •     end
    • +
    •   end
    • +
    • end
    • +
    +

    You can click on the links and read the comics in the little window even! Smashing!

    +
    \033\[1;JSm.*popup_make\(.*h1.*ul.*li.*a href.*li.*a href.*\)\033\[m.*
    +
    +
    +

    Summary #6 Which Means You've Come So Far

    +

    You're a level six Ruby cleric. I mean what a great job you've done. Let's review:

    +
      +
    • You added your own method with def and you used that load_comics method several times.
    • +
    • Libraries. You used the require method to load the popup library.
      By typing: require 'popup'
    • +
    • And if that wasn't enough, you made your own web page from a list of comics in a file. You made a real program!
    • +
    +

    So +what could possibly be next? What could you possibly have to learn now? +Ha, this is the best part. You've come such a long way that we're going +to uncover classes. For two more short lessons and you're done.

    +

    Earlier, we created a hash like this: Hash.new Try it.

    +
    \{\}
    +
    +
    +

    Not a School Class, a Working Class

    +

    You see, the empty curly braces {} is a shortcut for Hash.new. The new +method is used to make objects of a certain class. (Think "class" as in +"working class" — a specific group of objects which are similar, have +the same jobs, the same shirts.)

    +

    Ask yourself this: How would I make a blog in Ruby? +Where would you start? Well, you might store your blog entries in a +file, right? But how would you keep track of the title of the entry and +the time it was posted? And when you loaded the file, how would it look +in Ruby? Would it be a Hash? Or an Array? Or an Array of Arrays? Or +something else?

    I really think you'll want to use a class. You are already familiar with many classes: Hash, Array, String.

    +

    Let's make a new class: class BlogEntry.

    +
    ..
    +
    +
    +

    The Stuff Blogs are Made of

    +

    You've opened up a new BlogEntry class. What is your blog entry made of? A title, sure. Also, a time when the entry was posted. The + full text of the entry.

    +

    We'll do a mood setting, too, just like LiveJournal. The Internet has really brought back stick people and smileys + out of bankruptcy. Emote!

    +

    Okay, so you've got the first line of the class, here's the rest:

    +
    • class BlogEntry
    • +
    •   attr_accessor :title, :time, :fulltext, :mood
    • +
    • end
    • +
    +
    nil
    +
    +
    +

    Accessors Are the Dangling Limbs

    +

    Hey, good class, man. You've got a new BlogEntry class. To start an entry:
    entry = BlogEntry.new.

    +

    In the class definition, you used a method called attr_accessor. There are many attribute methods like + this which add little settings to classes. These attributes are just variables attached to a class.

    +

    Think +of it this way. A class is like a person. That star-shaped human thing +out there. And the attributes are the dangling limbs, the different +parts that make up a body.

    +

    To set the title of your entry: entry.title = "Today Mt. Hood Was Stolen!"

    +
    ".+"
    +
    +
    +

    An Object, That Neat Little Package

    +

    Go ahead and set the post time: entry.time = Time.now

    +

    And the mood: entry.mood = :sick

    +

    And the post itself: entry.fulltext = "I can't believe Mt. Hood was stolen! I am speechless! It was stolen by a giraffe who drove away + in his Cadillac Seville very nonchalant!!"

    +

    To see all your settings, just type at the prompt: entry.

    +
    #.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
    +
    +
    +

    Quickening it Up

    +

    Cool, +you're blog is awesome. Hey, let's make things a bit easier on you. +You're not going to want to set the time like that every time you post. +You just want to type in the title and the entry and the mood quickly, +right?

    +

    Let's add an initialize method.

    +
    • class BlogEntry
    • +
    •   def initialize( title, mood, fulltext )
    • +
    •     @time = Time.now
    • +
    •     @title, @mood, @fulltext = title, mood, fulltext
    • +
    •   end
    • +
    • end
    • +
    +

    Once you've got that typed in, try making a new entry: BlogEntry.new

    +
    ArgumentError: wrong number of arguments \(0 for 3\).*
    +
    +
    +

    You've Taught Your Blog to Reject Worthless Things

    +

    Did you see how inside the class we used the at-symbols? Like this: @time = Time.now

    +

    Outside the class, we use accessors: entry.time = Time.now But inside we use instance variables: @time = Time.now + They're the exact same thing, but expressed in two different places of your program.

    +

    Your blog now needs a title, a mood and a post in order to work. When a new BlogEntry is created, the initialize method + is used to check for any arguments to new. Uh, we need three arguments!

    +

    Try it again with all three.

    +

    entry2 += BlogEntry.new( "I Left my Hoodie on the Mountain!", :confused, "I am +never going back to that mountain and I hope a giraffe steals it." )

    +
    #.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
    +
    +
    +

    A Giraffe Has Not Stolen Summary #7

    +

    Aha, you're here. And all in one piece. We're still going to make your blog real, but until then, let's review, okay?

    +
      +
    • Classes. Everything in Ruby is some kind of object. Classes explain objects. How a certain object works. + For example, you made a few blog entry objects and these objects are explained in the BlogEntry class. + In other words: you call them BlogEntry objects.
    • +
    • Accessors are variables attached to an object which can be used outside the object. (entry.time = Time.now)
    • +
    • Instance variables are the same variables you're using for accessors when inside the object. + Like in a method definition. (@time = Time.now)
    • +
    +

    Okay, +let's wrap things up, kid. Here's the last chapter of the GRIPPING epic +story of Try Ruby! Now that you've got a taste of how it all works, how +are you going to use it around the house and in your grocer's freezer? +You're a great person (one of my favorites), but you need guidance.

    +

    Let's finish your blog. You have blog entries, but no actual blog.

    +

    Put the entries into an array: blog = [entry, entry2]

    +
    \[#.BlogEntry:0x[0-9a-f]+.*, #.BlogEntry:0x[0-9a-f]+.*\]
    +
    +
    +

    It's All About Combining Things

    +

    Some +beautiful things can be done with the simple parts of Ruby, especially +when you combine them together into new things. Here we've got a blog +made of an array of classes. And, actually, Ruby really does good with +this kind of creature.

    +

    Here's a few things you can do with your array blog:

    +
    • You'll want to sort your entries from newest to oldest. You can do this with:
      + blog.sort_by { |entry| entry.time }.reverse
      See the sort_by explanation for more.
    • +
    • If you want to search your blog for anything related to "cadillac":
      + blog.find_all { |entry| entry.fulltext.match(/cadillac/i) }
      + Read all about find_all + and match + to figure out how that works. Also: the slashy /giraffe/i is a Regexp object, used for matching words.
    • +
    • Add new entries with blog << new_entry
      + And check out the << method documentation.
    • +
    +

    You can browse a list of all Ruby's built-in methods at ruby-doc.org's core list. + Another good list is at the online pickaxe.

    +

    One really useful method (I probably use this more than anything else) is map. Type: blog.map { |entry| entry.mood }

    +
    \[(:\w+, )+:\w+\]
    +
    +
    +

    Look at His Face — The Transformation Has Begun

    +

    The map method cycles through an array and replaces each item with something new. Say you wanted to replace each of your blog entries + with the name Bruce Willis. Do it so: blog.map { "Bruce Willis" }

    +

    Since the block always returns the string "Bruce Willis", that's what you get. In the code you just used, the entry was swapped out + for only the entry.mood.

    +

    Now, +I want you to make a popup with your blog entries. I'm not going to +give you all of the code. I'm just going to give you part of it.

    +
    • blog.each do |entry|
    • +
    •   h2 entry.title
    • +
    •   p entry.fulltext
    • +
    • end
    • +
    +

    Now, I expect you to put the popup code around it and add an h1 title with the name of your blog. For extra haroompf, have the time of each entry display.

    +
    \033\[1;JSm.*popup_make\(.*h1.*h2.*li.*h2.*li.*\)\033\[m.*
    +
    +
    +

    You are Some Kind of Web Guru, I Have Stars in My Eyes

    +

    Good, +that's it! This is exactly the code you can use to write your own real +Ruby blog. If you're feeling adventurous, I'd check out the Rails videos which show a swift young fellow creating a blog in 15 minutes. You just sit back and watch.

    +

    I +should mention Rails. You have been learning the Ruby language, how to +speak it. But Rails is a bunch of libraries (sort of like the popup +library we've been using.) It's a very powerful toolkit for building +websites. If you're interested in learning about Rails, I would head + over there right away. Start using your Ruby skills proper!

    +

    One thing Rails has is easy methods for dates. Like, try: Time.now - 2.weeks

    +
    class Integer; def weeks; self * 7*24*60*60; end; end
    +
    \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+]\d{4}
    +
    +
    +

    If You Want to Start Small

    +

    If you'd like to start writing little Ruby programs just to practice, I have a project called MouseHole + which is a little web toolkit for writing short Ruby programs. You can look over a few + scripts to see what I mean.

    +

    MouseHole +isn't for writing web sites really. It's just for writing little +programs you run inside your browser. Like there's a notepad program +for MouseHole and a program which adds a mouse picture next to links on +the web which link to MouseHole programs.

    +

    I've got a MouseHole script inside a file here:
    + print File.read("/MouseHole/flickrpedia.user.rb")

    +
    .*Inserts Wikipedia links for Flickr tags.*
    +
    +
    +

    Summary #8, The Hey-Relax-You-Did-Good Summary

    +

    This last section took a moment to wind down, to give you some pointers as to how you can use Ruby. If you enjoyed yourself, + download Ruby and install it.

    + +

    Once you have Ruby installed, you can use Interactive Ruby by running irb on your system's prompt. For more on Irb, + there's The Tiger's Vest to help you.

    +

    You +really deserve a double-layer cake with double-double frosting and a +guy playing one of those guitars that's a double guitar. I mean you +finished, you really did! No doubt about it, you're a certified +red-blooded smartiac!

    +
    +
    + + diff --git a/TryRuby/public/tutorials/intro.html.broken b/TryRuby/public/tutorials/intro.html.broken new file mode 100755 index 0000000..5a6c27c --- /dev/null +++ b/TryRuby/public/tutorials/intro.html.broken @@ -0,0 +1,638 @@ + +
    +

    Using the Prompt

    +

    The blue window above is a Ruby prompt. Type a line of Ruby code, hit Enter + and watch it run!

    +

    For example, try typing some math. Like: 2 + 6

    +
    \d+
    +
    +
    +

    Numbers & Math

    +

    Good! You did a bit of math. See how the answer popped out?

    +

    Ruby recognizes numbers and mathematic symbols. You could try some other math like:

    +
    • 4 * 10
    • +
    • 5 - 12
    • +
    • 40 / 4
    +

    Sure, computers are handy and fast for math. Let's move on. Want to see your name reversed? + Type your first name in quotes like this: "Jimmy"

    +
    "(\w+)"
    +
    +
    +

    Say Your Name Backwards

    +

    Perfect, you've formed a string from the letters of your name. A string + is a set of characters the computer can process.

    +

    Imagine the letters are on a string of + laundry line and the quotes are clothespins holding the ends. The quotes mark the beginning and end.

    +

    To reverse your name, type: "Jimmy".reverse (Don't forget the dot!)

    +
    "(\w+)"
    +
    +
    +

    Counting the Letters

    +

    You have used the reverse method on your name! By enclosing your name in + quotes, you made a string. Then you called the reverse method, which works on strings to flip + all the letters backwards.

    +

    Now, let's see how many letters are in your name: "Jimmy".length

    +
    \d+
    +
    +
    +

    On Repeat

    +

    Now, I'm sure by now you're wondering what any of this is good for. Well, I'm sure you've been to + a website that screamed, Hey, your password is too short! See, some programs + use this simple code.

    +

    Watch this. Let's multiply your name by 5. "Jimmy" * 5

    +
    "(\w+)"
    +
    +
    +

    Hey, Summary #1 Already

    +

    Let's look at what you've learned in the first minute.

    +
      +
    • The prompt. Typing code into the green prompt gives you + an answer from a red prompt. All code gives an answer.
    • +
    • Numbers and strings are Ruby's math and text objects.
    • +
    • Methods. You've used English-language methods like reverse + and symbolic methods like * (the multiplication method.) Methods are action!
    • +
    +

    This is the essence of your learning. Taking simple things, toying with + them and turning them into new things. Feeling comfortable yet? I promise you are.

    +

    Okay, let's do something uncomfortable. Try reversing a number: 40.reverse

    +
    NoMethodError: undefined method `reverse' for (\d+):Fixnum
    +
    +
    +

    Stop, You're Barking Mad!

    +

    You can't reverse the number forty. I guess you can hold your monitor up to the + mirror, but reversing a number just doesn't make sense. Ruby has tossed an error + message. Ruby is telling you there is no method reverse for numbers.

    +

    Maybe if you turn it into a string: 40.to_s.reverse.

    +
    \"(\d+)\"
    +
    +
    +

    Boys are Different From Girls

    +

    And numbers are different from strings. While you can use methods on any object + in Ruby, some methods only work on certain types of things. But you can always + convert between different types using Ruby's "to" methods.

    +
    • to_s converts things to strings.
    • +
    • to_i converts things to integers (numbers.)
    • +
    • to_a converts things to arrays.
    • +
    +

    What are arrays?! They are lists. Type in a pair of brackets: [].

    +
    \[\]
    +
    +
    +

    Standing in Line

    +

    Great, that's an empty list. Lists store things in order. + Like standing in line for popcorn. You are behind someone and you wouldn't + dream of pushing them aside, right? And the guy behind you, you've got a + close eye on him, right?

    +

    Here's a list for you. Lottery numbers: [12, 47, 35].

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    One Raises Its Hand

    +

    A list of lottery numbers. Which one is the highest?

    +

    Try: [12, 47, 35].max.

    +
    (\d+)
    +
    +
    +

    Tucking a List Away

    +

    Good, good. But it's annoying to have to retype that list, isn't it?

    +

    Let's save our numbers inside a ticket like so: ticket = [12, 47, 35]

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Now Type Ticket

    +

    Now, type: ticket

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Saved, Tucked Away

    +

    Fantastic! You've hung on to your lotto numbers, tucking them away inside a + variable called ticket.

    +

    Let's put your lotto numbers in order, how about? Use: ticket.sort!

    +
    \[(\d+(, )?){2,}\]
    +
    +
    +

    Summary #2 is Upon Us

    +

    You had a list. You sorted the list. The ticket variable is now changed.

    +

    Did you notice that the sort! method has a big, bright exclamation at the end? + A lot of times Ruby methods shout like that if they alter the variable for good. It's nothin + special, just a mark.

    +

    Now, look how your second minute went:

    +
      +
    • Errors. If you try to reverse a number or do anything fishy, + Ruby will skip the prompt and tell you so.
    • +
    • Arrays are lists for storing things in order.
    • +
    • Variables save a thing and give it a name. You used the + equals sign to do this.
      Like: ticket = [14, 37, 18].
    • +
    +

    In all there are eight lessons. You are two-eighths of the way there! + This is simple stuff, don't you think? Good stuff up ahead.

    +

    Let's change directions for a moment. I've stuffed a bit of poetry for you in + a certain variable. Take a look. Type print poem

    +
    poem = "My toast has flown from my hand\nAnd my toast has gone to the +moon.\nBut when I saw it on television,\nPlanting our flag on Halley's +comet,\nMore still did I want to eat it.\n"
    +
    My toast (.+)
    +
    +
    +

    Sadly, You Hate Toast Poetry

    +

    Look, it's okay. You don't have to like it. Hack it up, be my guest.

    +

    Instead of toast, go for a melon or something. Try this: poem['toast'] = 'honeydew'

    +

    And then type print poem by itself to see the new poem.

    +
    My honey(.+)
    +
    +
    +

    Ready, Aim

    +

    The square brackets you just used are very common in Ruby. Remember, you typed: poem['toast'] = 'honeydew'. That box with the word toast has a square bracket on each side, see?

    +

    The +two brackets are like sights used to line up a target. Exactly. These +brackets mean, "I am looking for ____." Ready, aim. Here you're looking +for toast and swapping it out with fruit.

    +

    Here's a question: what happens when we reverse this whole poem? poem.reverse +

    "\\n.ti tae ot (.+)"
    +
    +
    +

    Too Much Reversal

    +

    Okay, sure. So the whole poem's been turned backwards, letter-by-letter. I really want to just + reverse the lines, though. Move the last line up to first and the first line down to last. Backwards, but not + that backwards.

    +

    Here's how: poem.lines.to_a.reverse

    +
    \["More still did I(.+)"\]
    +
    +
    +

    Ringlets of Chained Methods

    +

    So what do you see? What happened there? You typed poem.lines.to_a.reverse and what happened?

    +

    Two things happened. You turned the poem into a +list using lines.to_a. lines decides the way +the string is split up, then to_a converted it into an +Array. (To array.) Different methods, such +as bytes and chars can be used in place +of lines. By using lines, ruby will return each line of the poem.

    +

    Then, you reversed that list. You had each line. You reversed them. That's it.

    +

    Let's tack one more method on the end there: print poem.lines.to_a.reverse.join +

    More still did I(.+)
    +
    +
    +

    Of All the Summaries, #3 is Here Now

    +

    Good show, my friend! The join method took that list of reversed lines and put them + together into a string. (Sure, you could have also just used to_s.)

    +

    Review time.

    +
      +
    • Exclamations. Methods may have exclamations (and also question marks) + in their name. No big deal. Try: poem.include? "my hand"
    • +
    • Square brackets. Target and find things. Search and replace.
    • +
    • Chaining methods lets you get a lot more done. Break up a poem, + reverse it, reassemble it: poem.lines.to_a.reverse.join
    • +
    +

    At this point, you may want to tinker with the poem a bit more. A complete list of all + the String methods is + + here. + Go ahead and try a few (such as poem.downcase or poem.delete.)

    +

    When you're ready to move on, type: books = {}

    +
    \{\}
    +
    +
    +

    A Wee Blank Book

    +

    You've made an empty hash. (Also known as: an empty dictionary.)

    +

    We're going to stuff some miniature book reviews in this hash. Here's our rating system:

    +
      +
    • :splendid → a masterpiece.
    • +
    • :quite_good → enjoyed, sure, yes.
    • +
    • :mediocre → equal parts great and terrible.
    • +
    • :quite_not_good → notably bad.
    • +
    • :abyssmal → steaming wreck.
    • +
    +

    To rate a book, put the title in square brackets and put the rating after the equals.

    +

    For example: books["Gravity's Rainbow"] = :splendid

    +
    :\w+
    +
    +
    +

    More Bite-Size Reviews

    +

    Keep going, fill it up with reviews. And, if you want to see the whole list, + just type: books

    +

    Again, the ratings are: :splendid, :quite_good, :mediocre, + :quite_not_good, and :abyssmal.

    +

    These ratings are not strings. When you place a colon in front of a simple word, you get a + symbol. Symbols are cheaper than strings (in terms of computer memory.) If + you use a word over and over in your program, use a symbol. Rather than having thousands of + copies of that word in memory, the computer will store the symbol only once.

    +

    Once you've got three or four books in + there, type: books.length.

    +
    [3-9]
    +
    +
    +

    Wait, Did I Like Gravity's Rainbow?

    +

    See, the length method works on strings, list and hashes. One great thing about + Ruby is that names are often reused, which means fewer names you need to remember.

    +

    If you'd like to look up one of your old reviews, again put the title in the square. But leave off + the equals.

    +

    Just like this: books["Gravity's Rainbow"]

    +
    :\w+
    +
    +
    +

    Hashes as Pairs

    +

    Keep in mind that hashes won't keep things in order. That's not their job. It'll just pair up two + things: a key and a value. In your reviews, the key is the book's + title and the value is the rating.

    +

    If you want to just see the titles of the books you've reviewed: books.keys

    +
    \[".*"\]
    +
    +
    +

    Are You Harsh?

    +

    So are you giving out harsh, unfair reviews? Let's keep score with this hash:
    ratings = Hash.new {0}

    +

    Then, okay, now let's count up your reviews. Just stay with me. Type:
    + books.values.each { |rate| ratings[rate] += 1 }

    +

    (The straight line in the code is the pipe character, probably located right above the + Enter key on your keyboard.)

    +
    \[:.+\]
    +
    +
    +

    A Tally

    +

    Great, wow! You've made a scorecard of your ratings. Type ratings to see the count. + This new hash shows a rating and then the number of times you've given that rating.

    +

    One of the amazing new things we've just used is a block. We're going to + explore these more in the next summary. But, basically, a block is a bit of Ruby code surrounded + by curly braces.

    +

    Let's try another block: 5.times { print "Odelay!" }

    +
    Odelay!Od.*
    +
    +
    +

    Now Arriving at Summary #4

    +

    Blocks are always attached to methods. Like the times method, which takes the + block and runs the code over and over. (In this case: five times.)

    +

    This last lesson was a bit longer. You've probably used up three minutes learning about:

    +
      +
    • Hashes. The little dictionary with the curly pages: {}.
    • +
    • Symbols. Tiny, efficient code words with a colon: :splendid.
    • +
    • Blocks. Chunks of code which can be tacked on to many of Ruby's methods. Here's the + code you used to build a scorecard:
      books.values.each { |rate| ratings[rate] += 1 }.
    • +
    +<<<<<<< HEAD + + +======= +

    On your computer, you probably have a lot of different files. Files with pictures in them, + files with programs in them. And files are often organized into folders, also called: + directories.

    +

    I've prepared a few directories for you. Take a look: + Dir.entries "/"

    +
    \["\.", .+\]
    +
    +
    +

    The Private Collection of Dr. Dir

    +

    You've just listed out everything in the top directory. The root directory, indicated + by a single slash. Containing some programs and other tutorials and such.

    +

    So, what is the Dir.entries method? Well, it's just a method, right? + entries is a method called on the Dir variable. + And Dir has a collection of methods for checking out file directories.

    +

    One other little thing we haven't really talked about openly. Method arguments, highlighted in green.

    +
      +
    • Dir.entries "/": Anything listed after a method + is considered an attachment.
    • +
    • print poem: See, print is an ordinary method. And the + poem is attached. To be printed.
    • +
    • print "pre", "event", "ual", "ism" has several arguments, with commas + between them.
    • +
    +

    To list just the text files in that directory: Dir["/*.txt"]

    +
    \["\/comics\.txt"\]
    +
    +
    +

    Come, Read Comics With Me

    +

    The Dir[] method is like entries but you search for files + with wildcard characters. Here, we see those square brackets again! Notice how + they still mean, "I am looking for _____?"

    +

    More specifically: "I am looking for files which end with .txt."

    +

    Let's crack open this comics file, then. Here's the way:
    + print File.read("/comics.txt")

    +
    Achewood.+
    +
    +
    +

    Mi Comicas, Tu Comicas

    +

    All right! We can start to use files to store things. This is great because normally when + we exit Ruby, all our variables will be gone. Ruby, by itself, forgets these things. + But if we save things in files, we can read those files in future Ruby escapades.

    +

    Hey, and guess what? The /Home directory is yours! I gave it to you! I am generous! Let's make a copy of the comics file.

    +

    You'll want to: FileUtils.copy('/comics.txt', '/Home/comics.txt') +

    If you've already created the file, use File.delete('/Home/comics.txt') to trash it.

    +
    nil
    +
    +
    +

    Your Own Turf

    +

    Okay, you've got a copy. Check it: Dir["/Home/*.txt"]

    +

    To add your own comic to the list, let's open the file in append mode.

    +

    Start like this: File.open("/Home/comics.txt", "a") do |f|.

    +
    ..
    +
    +
    +

    And Now For the Startling Conclusion

    +

    So your prompt has changed. See that? Your prompt is a double dot now.

    +

    In this tutorial, this prompt means that Ruby is expecting you to type more. + As you type in the lines of Ruby code, the double dots will continue until you + are completely finished.

    +

    Hot tip: If you want to stop working on the code and break out of the double dots, use the reset + command. If you want to go the previous page of the tutorial, use the back command.

    +

    Here's your code. You've already typed the first line, so just enter the second line. (The \n + is an Enter character.

    +
    • File.open("/Home/comics.txt", "a") do |f|
    • +
    •   f << "Cat and Girl: http://catandgirl.com/\n"
    • +
    • end
    • +
    +

    And, since you're getting so advanced and capable here, one other tip: you can use the up and down arrow keys to + edit your old commands or run them again.

    +
    ..
    +
    +
    +

    Ruby Sits Still

    +

    That last line adds the Cat and Girl comic to the list, but Ruby's going to wait until you're totally finished to + take action.

    +

    Now, to finish the code you've started. You opened a new block when you typed do. + So far the blocks we've seen have used curly braces. This time we'll be using do and end instead + of curly braces. A lot of Rubyists will use do...end when the block goes on for many lines.

    +

    Let's get that block finished now, with: end

    +
    • File.open("/Home/comics.txt", "a") do |f|
    • +
    •   f << "Cat and Girl: http://catandgirl.com/\n"
    • +
    • end
    • +
    +
    #.File:/Home/comics\.txt \(closed\).
    +
    +
    +

    The Clock Nailed To the File

    +

    Good, good! You've added that new comic to the file. You can see for yourself: print File.read("/Home/comics.txt")

    +

    What time was it when you changed the file? Let's check. Type: File.mtime("/Home/comics.txt")

    +
    \d{4}-\d+-\d+ \d{2}:\d{2}:\d{2} [+-]\d{4}
    +
    +
    +

    Just the Hour Hand

    +

    Great, there's the time. The precise time exactly when you added to the file. The mtime gives you a Ruby Time object.

    +

    If you want to check just what hour it was, hit the up arrow key and change the line to: File.mtime("/Home/comics.txt").hour

    +
    \d+
    +>>>>>>> nanothief/master +
    + +
    +

    Hallo, Who's There? And Summary #5 Waves Its Hat!

    +

    Well done, well done, well done, well done! Truly, truly, truly, truly, truuuuuuuuly!

    +

    Here's the last few minutes of your life in review:

    +
      +
    • Arguments. Arguments are a list of things sent into a method. With commas between.
    • +
    • We also spoke about do and end which are another way to make a block.
    • +
    +

    You totally know how to use Ruby now. I mean you've got down the essentials. You just need to keep learning more methods and + try out more complex blocks.

    +

    But there's one side of Ruby we haven't settled. Making your own methods and classes.

    +

    Ahem! Let's get it over with then.

    +
    +
    +

    In Ruby, Def Leppard Means Define Leppard (a Method)!

    +

    Hey, okay, you done it. You're making your own method. You started with def, followed by the name of the method. + And a list of arguments which the method will need. This isn't too scary and dangerous!

    +

    All we have to do is fill it up with Ruby and finish it up with end.

    +
    +
    +

    Hey, Cool, a Popup

    +

    Let's make a page of links. How about that? We'll need to load a little library I've made for you.

    +

    Type: require 'popup'

    +
    true
    +
    +
    +

    Browser Puppetry

    +

    Excellent, you've loaded the popup library.

    +

    The popup library contains a bunch of methods I've written which let you control a popup here on the Try Ruby site.

    +

    Here, try this: Popup.goto "http://google.com/"

    +
    \033\[1;JSm.*popup_goto\(.*\)\033\[m.*
    +
    +
    +

    Making Links and Spinning Webs

    +

    Our own lovely, little popup to manipulate. You can also fill it with your own goodies. We'll start small:

    +
    • Popup.make {
    • +
    •   h1 "My Links"
    • +
    •   link "Go to Google", "http://google.com/"
    • +
    • }
    • +
    +

    The term h1 (h-one) means a level-one header. In HTML, this is the largest size of header.

    +
    \033\[1;JSm.*popup_make\(.*h1.*a href.*\)\033\[m.*
    +
    +
    +

    Popups Are So Easy, It's Crazy

    +

    Looks good, you did it perfectly, just as you were asked. Let's make a list then.

    +

    Here's how you make a list with the popup library:

    +
    • Popup.make do
    • +
    •   h1 "Things To Do"
    • +
    •   list do
    • +
    •     p "Try out Ruby"
    • +
    •     p "Ride a tiger"
    • +
    •     p "(down River Euphrates)"
    • +
    •   end
    • +
    • end
    • +
    +

    The p method is short for "paragraph".

    +
    \033\[1;JSm.*popup_make\(.*h1.*ul.*li.*li.*\)\033\[m.*
    +
    +
    +

    Spread the Comics on the Table

    +

    Okay, this is coming along wonderfully. This is simple stuff, but keep in mind that you didn't know any Ruby whatsoever just fifteen minutes ago!

    +

    Last +step. Let's tie it all together, you know? Let's make it chime together +like a very nice set of glistening chimes on the beach in the +maginificent sunlight!

    +

    Now, let's make a list of the links to each comic:

    +
    • Popup.make do
    • +
    •   h1 "Comics on the Web"
    • +
    •   list do
    • +
    •     comics.each do |name, url|
    • +
    •       link name, url
    • +
    •     end
    • +
    •   end
    • +
    • end
    • +
    +

    You can click on the links and read the comics in the little window even! Smashing!

    +
    \033\[1;JSm.*popup_make\(.*h1.*ul.*li.*a href.*li.*a href.*\)\033\[m.*
    +
    +
    +

    Summary #6 Which Means You've Come So Far

    +

    You're a level six Ruby cleric. I mean what a great job you've done. Let's review:

    +
      +
    • You added your own method with def and you used that [coming soon]
    • +
    • You used the require method to load the popup library.
      By typing: require 'popup'
    • +
    • And if that wasn't enough, you made your own web page from a list of comics in a file no you havent. You made a real program!
    • +
    +

    So +what could possibly be next? What could you possibly have to learn now? +Ha, this is the best part. You've come such a long way that we're going +to uncover classes. For two more short lessons and you're done.

    +

    Earlier, we created a hash like this: Hash.new Try it.

    +
    \{\}
    +
    +
    +

    Not a School Class, a Working Class

    +

    You see, the empty curly braces {} is a shortcut for Hash.new. The new +method is used to make objects of a certain class. (Think "class" as in +"working class" — a specific group of objects which are similar, have +the same jobs, the same shirts.)

    +

    Ask yourself this: How would I make a blog in Ruby? +Where would you start? Well, you might store your blog entries in a +file, right? But how would you keep track of the title of the entry and +the time it was posted? And when you loaded the file, how would it look +in Ruby? Would it be a Hash? Or an Array? Or an Array of Arrays? Or +something else?

    I really think you'll want to use a class. You are already familiar with many classes: Hash, Array, String.

    +

    Let's make a new class: class BlogEntry.

    +
    ..
    +
    +
    +

    The Stuff Blogs are Made of

    +

    You've opened up a new BlogEntry class. What is your blog entry made of? A title, sure. Also, a time when the entry was posted. The + full text of the entry.

    +

    We'll do a mood setting, too, just like LiveJournal. The Internet has really brought back stick people and smileys + out of bankruptcy. Emote!

    +

    Okay, so you've got the first line of the class, here's the rest:

    +
    • class BlogEntry
    • +
    •   attr_accessor :title, :time, :fulltext, :mood
    • +
    • end
    • +
    +
    nil
    +
    +
    +

    Accessors Are the Dangling Limbs

    +

    Hey, good class, man. You've got a new BlogEntry class. To start an entry:
    entry = BlogEntry.new.

    +

    In the class definition, you used a method called attr_accessor. There are many attribute methods like + this which add little settings to classes. These attributes are just variables attached to a class.

    +

    Think +of it this way. A class is like a person. That star-shaped human thing +out there. And the attributes are the dangling limbs, the different +parts that make up a body.

    +

    To set the title of your entry: entry.title = "Today Mt. Hood Was Stolen!"

    +
    ".+"
    +
    +
    +

    An Object, That Neat Little Package

    +

    Go ahead and set the post time: entry.time = Time.now

    +

    And the mood: entry.mood = :sick

    +

    And the post itself: entry.fulltext = "I can't believe Mt. Hood was stolen! I am speechless! It was stolen by a giraffe who drove away + in his Cadillac Seville very nonchalant!!"

    +

    To see all your settings, just type at the prompt: entry.

    +
    #.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
    +
    +
    +

    Quickening it Up

    +

    Cool, +you're blog is awesome. Hey, let's make things a bit easier on you. +You're not going to want to set the time like that every time you post. +You just want to type in the title and the entry and the mood quickly, +right?

    +

    Let's add an initialize method.

    +
    • class BlogEntry
    • +
    •   def initialize( title, mood, fulltext )
    • +
    •     @time = Time.now
    • +
    •     @title, @mood, @fulltext = title, mood, fulltext
    • +
    •   end
    • +
    • end
    • +
    +

    Once you've got that typed in, try making a new entry: BlogEntry.new

    +
    ArgumentError: wrong number of arguments \(0 for 3\).*
    +
    +
    +

    You've Taught Your Blog to Reject Worthless Things

    +

    Did you see how inside the class we used the at-symbols? Like this: @time = Time.now

    +

    Outside the class, we use accessors: entry.time = Time.now But inside we use instance variables: @time = Time.now + They're the exact same thing, but expressed in two different places of your program.

    +

    Your blog now needs a title, a mood and a post in order to work. When a new BlogEntry is created, the initialize method + is used to check for any arguments to new. Uh, we need three arguments!

    +

    Try it again with all three.

    +

    entry2 += BlogEntry.new( "I Left my Hoodie on the Mountain!", :confused, "I am +never going back to that mountain and I hope a giraffe steals it." )

    +
    #.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
    +
    +
    +

    A Giraffe Has Not Stolen Summary #7

    +

    Aha, you're here. And all in one piece. We're still going to make your blog real, but until then, let's review, okay?

    +
      +
    • Classes. Everything in Ruby is some kind of object. Classes explain objects. How a certain object works. + For example, you made a few blog entry objects and these objects are explained in the BlogEntry class. + In other words: you call them BlogEntry objects.
    • +
    • Accessors are variables attached to an object which can be used outside the object. (entry.time = Time.now)
    • +
    • Instance variables are the same variables you're using for accessors when inside the object. + Like in a method definition. (@time = Time.now)
    • +
    +

    Okay, +let's wrap things up, kid. Here's the last chapter of the GRIPPING epic +story of Try Ruby! Now that you've got a taste of how it all works, how +are you going to use it around the house and in your grocer's freezer? +You're a great person (one of my favorites), but you need guidance.

    +

    Let's finish your blog. You have blog entries, but no actual blog.

    +

    Put the entries into an array: blog = [entry, entry2]

    +
    \[#.BlogEntry:0x[0-9a-f]+.*, #.BlogEntry:0x[0-9a-f]+.*\]
    +
    +
    +

    It's All About Combining Things

    +

    Some +beautiful things can be done with the simple parts of Ruby, especially +when you combine them together into new things. Here we've got a blog +made of an array of classes. And, actually, Ruby really does good with +this kind of creature.

    +

    Here's a few things you can do with your array blog:

    +
    • You'll want to sort your entries from newest to oldest. You can do this with:
      + blog.sort_by { |entry| entry.time }.reverse
      See the sort_by explanation for more.
    • +
    • If you want to search your blog for anything related to "cadillac":
      + blog.find_all { |entry| entry.fulltext.match(/cadillac/i) }
      + Read all about find_all + and match + to figure out how that works. Also: the slashy /giraffe/i is a Regexp object, used for matching words.
    • +
    • Add new entries with blog << new_entry
      + And check out the << method documentation.
    • +
    +

    You can browse a list of all Ruby's built-in methods at ruby-doc.org's core list. + Another good list is at the online pickaxe.

    +

    One really useful method (I probably use this more than anything else) is map. Type: blog.map { |entry| entry.mood }

    +
    \[(:\w+, )+:\w+\]
    +
    +
    +

    Look at His Face — The Transformation Has Begun

    +

    The map method cycles through an array and replaces each item with something new. Say you wanted to replace each of your blog entries + with the name Bruce Willis. Do it so: blog.map { "Bruce Willis" }

    +

    Since the block always returns the string "Bruce Willis", that's what you get. In the code you just used, the entry was swapped out + for only the entry.mood.

    +

    Now, +I want you to make a popup with your blog entries. I'm not going to +give you all of the code. I'm just going to give you part of it.

    +
    • blog.each do |entry|
    • +
    •   h2 entry.title
    • +
    •   p entry.fulltext
    • +
    • end
    • +
    +

    Now, I expect you to put the popup code around it and add an h1 title with the name of your blog. For extra haroompf, have the time of each entry display.

    +
    \033\[1;JSm.*popup_make\(.*h1.*h2.*li.*h2.*li.*\)\033\[m.*
    +
    +
    +

    You are Some Kind of Web Guru, I Have Stars in My Eyes

    +

    Good, +that's it! This is exactly the code you can use to write your own real +Ruby blog. If you're feeling adventurous, I'd check out the Rails videos which show a swift young fellow creating a blog in 15 minutes. You just sit back and watch.

    +

    I +should mention Rails. You have been learning the Ruby language, how to +speak it. But Rails is a bunch of libraries (sort of like the popup +library we've been using.) It's a very powerful toolkit for building +websites. If you're interested in learning about Rails, I would head + over there right away. Start using your Ruby skills proper!

    +

    One thing Rails has is easy methods for dates. Like, try: Time.now - 2.weeks

    +
    class Integer; def weeks; self * 7*24*60*60; end; end
    +
    \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+]\d{4}
    +
    +
    +

    Summary #8, The Hey-Relax-You-Did-Good Summary

    +

    This last section took a moment to wind down, to give you some pointers as to how you can use Ruby. If you enjoyed yourself, + download Ruby and install it.

    + +

    Once you have Ruby installed, you can use Interactive Ruby by running irb on your system's prompt. For more on Irb, + there's The Tiger's Vest to help you.

    +

    You +really deserve a double-layer cake with double-double frosting and a +guy playing one of those guitars that's a double guitar. I mean you +finished, you really did! No doubt about it, you're a certified +red-blooded smartiac!

    +
    +
    + diff --git a/TryRuby/public/tutorials/intro_files/sick.gif b/TryRuby/public/tutorials/intro_files/sick.gif new file mode 100755 index 0000000000000000000000000000000000000000..217a67cbae60740fdc90b92e06b977cf7fc56699 GIT binary patch literal 94 zcmZ?wbhEHb|6B "37" + assigns(:irb).should be(mock_irb) + end + end + + describe "GET new" do + it "assigns a new irb as @irb" do + Irb.stub(:new) { mock_irb } + get :new + assigns(:irb).should be(mock_irb) + end + end + + describe "GET edit" do + it "assigns the requested irb as @irb" do + Irb.stub(:find).with("37") { mock_irb } + get :edit, :id => "37" + assigns(:irb).should be(mock_irb) + end + end + + describe "POST create" do + describe "with valid params" do + it "assigns a newly created irb as @irb" do + Irb.stub(:new).with({'these' => 'params'}) { mock_irb(:save => true) } + post :create, :irb => {'these' => 'params'} + assigns(:irb).should be(mock_irb) + end + + it "redirects to the created irb" do + Irb.stub(:new) { mock_irb(:save => true) } + post :create, :irb => {} + response.should redirect_to(irb_url(mock_irb)) + end + end + + describe "with invalid params" do + it "assigns a newly created but unsaved irb as @irb" do + Irb.stub(:new).with({'these' => 'params'}) { mock_irb(:save => false) } + post :create, :irb => {'these' => 'params'} + assigns(:irb).should be(mock_irb) + end + + it "re-renders the 'new' template" do + Irb.stub(:new) { mock_irb(:save => false) } + post :create, :irb => {} + response.should render_template("new") + end + end + end + + describe "PUT update" do + describe "with valid params" do + it "updates the requested irb" do + Irb.stub(:find).with("37") { mock_irb } + mock_irb.should_receive(:update_attributes).with({'these' => 'params'}) + put :update, :id => "37", :irb => {'these' => 'params'} + end + + it "assigns the requested irb as @irb" do + Irb.stub(:find) { mock_irb(:update_attributes => true) } + put :update, :id => "1" + assigns(:irb).should be(mock_irb) + end + + it "redirects to the irb" do + Irb.stub(:find) { mock_irb(:update_attributes => true) } + put :update, :id => "1" + response.should redirect_to(irb_url(mock_irb)) + end + end + + describe "with invalid params" do + it "assigns the irb as @irb" do + Irb.stub(:find) { mock_irb(:update_attributes => false) } + put :update, :id => "1" + assigns(:irb).should be(mock_irb) + end + + it "re-renders the 'edit' template" do + Irb.stub(:find) { mock_irb(:update_attributes => false) } + put :update, :id => "1" + response.should render_template("edit") + end + end + end + + describe "DELETE destroy" do + it "destroys the requested irb" do + Irb.stub(:find).with("37") { mock_irb } + mock_irb.should_receive(:destroy) + delete :destroy, :id => "37" + end + + it "redirects to the irb list" do + Irb.stub(:find) { mock_irb } + delete :destroy, :id => "1" + response.should redirect_to(irb_url) + end + end + +end diff --git a/TryRuby/spec/controllers/public_controller_spec.rb b/TryRuby/spec/controllers/public_controller_spec.rb new file mode 100644 index 0000000..61a96a3 --- /dev/null +++ b/TryRuby/spec/controllers/public_controller_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe PublicController do + +end diff --git a/TryRuby/spec/helpers/classic_helper_spec.rb b/TryRuby/spec/helpers/classic_helper_spec.rb new file mode 100644 index 0000000..2614b0f --- /dev/null +++ b/TryRuby/spec/helpers/classic_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the ClassicHelper. For example: +# +# describe ClassicHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# helper.concat_strings("this","that").should == "this that" +# end +# end +# end +describe ClassicHelper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/TryRuby/spec/helpers/irb_helper_spec.rb b/TryRuby/spec/helpers/irb_helper_spec.rb new file mode 100644 index 0000000..325a0b0 --- /dev/null +++ b/TryRuby/spec/helpers/irb_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the IrbHelper. For example: +# +# describe IrbHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# helper.concat_strings("this","that").should == "this that" +# end +# end +# end +describe IrbHelper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/TryRuby/spec/helpers/public_helper_spec.rb b/TryRuby/spec/helpers/public_helper_spec.rb new file mode 100644 index 0000000..434276a --- /dev/null +++ b/TryRuby/spec/helpers/public_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the PublicHelper. For example: +# +# describe PublicHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# helper.concat_strings("this","that").should == "this that" +# end +# end +# end +describe PublicHelper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/TryRuby/spec/models/irb_spec.rb b/TryRuby/spec/models/irb_spec.rb new file mode 100644 index 0000000..26e0fa0 --- /dev/null +++ b/TryRuby/spec/models/irb_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Irb do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/TryRuby/spec/requests/irb_spec.rb b/TryRuby/spec/requests/irb_spec.rb new file mode 100644 index 0000000..d0c7946 --- /dev/null +++ b/TryRuby/spec/requests/irb_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe "Irb" do + describe "GET /irb" do + it "works! (now write some real specs)" do + # Run the generator again with the --webrat flag if you want to use webrat methods/matchers + get irb_path + response.status.should be(200) + end + end +end diff --git a/TryRuby/spec/routing/irb_routing_spec.rb b/TryRuby/spec/routing/irb_routing_spec.rb new file mode 100644 index 0000000..25ea295 --- /dev/null +++ b/TryRuby/spec/routing/irb_routing_spec.rb @@ -0,0 +1,35 @@ +require "spec_helper" + +describe IrbController do + describe "routing" do + + it "recognizes and generates #index" do + { :get => "/irb" }.should route_to(:controller => "irb", :action => "index") + end + + it "recognizes and generates #new" do + { :get => "/irb/new" }.should route_to(:controller => "irb", :action => "new") + end + + it "recognizes and generates #show" do + { :get => "/irb/1" }.should route_to(:controller => "irb", :action => "show", :id => "1") + end + + it "recognizes and generates #edit" do + { :get => "/irb/1/edit" }.should route_to(:controller => "irb", :action => "edit", :id => "1") + end + + it "recognizes and generates #create" do + { :post => "/irb" }.should route_to(:controller => "irb", :action => "create") + end + + it "recognizes and generates #update" do + { :put => "/irb/1" }.should route_to(:controller => "irb", :action => "update", :id => "1") + end + + it "recognizes and generates #destroy" do + { :delete => "/irb/1" }.should route_to(:controller => "irb", :action => "destroy", :id => "1") + end + + end +end diff --git a/TryRuby/spec/spec_helper.rb b/TryRuby/spec/spec_helper.rb new file mode 100644 index 0000000..9b8b02c --- /dev/null +++ b/TryRuby/spec/spec_helper.rb @@ -0,0 +1,27 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV["RAILS_ENV"] ||= 'test' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' + +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} + +RSpec.configure do |config| + # == Mock Framework + # + # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: + # + # config.mock_with :mocha + # config.mock_with :flexmock + # config.mock_with :rr + config.mock_with :rspec + + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true +end diff --git a/TryRuby/spec/views/irb/edit.html.erb_spec.rb b/TryRuby/spec/views/irb/edit.html.erb_spec.rb new file mode 100644 index 0000000..5b63112 --- /dev/null +++ b/TryRuby/spec/views/irb/edit.html.erb_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe "irb/edit.html.erb" do + before(:each) do + @irb = assign(:irb, stub_model(Irb)) + end + + it "renders the edit irb form" do + render + + # Run the generator again with the --webrat flag if you want to use webrat matchers + assert_select "form", :action => irb_path(@irb), :method => "post" do + end + end +end diff --git a/TryRuby/spec/views/irb/index.html.erb_spec.rb b/TryRuby/spec/views/irb/index.html.erb_spec.rb new file mode 100644 index 0000000..193368a --- /dev/null +++ b/TryRuby/spec/views/irb/index.html.erb_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe "irb/index.html.erb" do + before(:each) do + assign(:irb, [ + stub_model(Irb), + stub_model(Irb) + ]) + end + + it "renders a list of irb" do + render + end +end diff --git a/TryRuby/spec/views/irb/new.html.erb_spec.rb b/TryRuby/spec/views/irb/new.html.erb_spec.rb new file mode 100644 index 0000000..ca7c0ad --- /dev/null +++ b/TryRuby/spec/views/irb/new.html.erb_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe "irb/new.html.erb" do + before(:each) do + assign(:irb, stub_model(Irb).as_new_record) + end + + it "renders new irb form" do + render + + # Run the generator again with the --webrat flag if you want to use webrat matchers + assert_select "form", :action => irb_path, :method => "post" do + end + end +end diff --git a/TryRuby/spec/views/irb/show.html.erb_spec.rb b/TryRuby/spec/views/irb/show.html.erb_spec.rb new file mode 100644 index 0000000..9a12d28 --- /dev/null +++ b/TryRuby/spec/views/irb/show.html.erb_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe "irb/show.html.erb" do + before(:each) do + @irb = assign(:irb, stub_model(Irb)) + end + + it "renders attributes in

    " do + render + end +end diff --git a/TryRuby/vendor/plugins/.gitkeep b/TryRuby/vendor/plugins/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/images/header.png b/public/images/header.png index 1a6a3f243063ba8e2a93933357efbd3174f581ae..d6b9aa0f3ce2e738d3d12c68b52c78175322741c 100644 GIT binary patch literal 51873 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 diff --git a/public/index.html b/public/index.html index 333c950..abc5b18 100644 --- a/public/index.html +++ b/public/index.html @@ -2,15 +2,43 @@ - try ruby! (in your browser) - - - + + try ruby! (in your browser) + + + + - - + + + + + + + + + + +

    @@ -91,5 +119,7 @@

    Got 15 minutes? Give Ruby a shot right now!

    } catch(err) {} +
    Please Support
    Try Ruby!
    + diff --git a/public/javascripts/application.js b/public/javascripts/application.js new file mode 100644 index 0000000..fe45776 --- /dev/null +++ b/public/javascripts/application.js @@ -0,0 +1,2 @@ +// Place your application-specific JavaScript functions and classes here +// This file is automatically included by javascript_include_tag :defaults diff --git a/public/javascripts/console.js b/public/javascripts/console.js new file mode 100644 index 0000000..e7e8b79 --- /dev/null +++ b/public/javascripts/console.js @@ -0,0 +1,288 @@ +jQuery.fn.debug = function() { + var msg = jqArray.args(arguments); + $("
    ").addClass("error").text(msg.join(", ")).prependTo(this); +} + +jQConsole = function(input, output) { + + var args = jqArray.args + + var input = input; + + // History + var command_history = []; + var command_selected = 0; + + var local_scope = safe_scope(); + + function hide_fn(fn) { return function() { return fn.apply(this, arguments); } } + hide_fn.desc = "A function that creates a wrapper that hides the implementation of another function"; + function queue_fn(fn, time) { if(!time) time = 0; return function() { setTimeout(fn, time); } } + queue_fn.desc = "Turns a function into a function that's called later"; + + var keys = function (o) { + var r = []; + if (typeof o != "object") return r; + for (var k in o) r.push(k); + return r; + } + var refocus = queue_fn(function() { input.blur().focus(); }); + var reset_input = queue_fn(function() { input.val("").blur().focus(); }); + function no_recurse(fn, max_depth) { + var count = 0; + if(!max_depth) max_depth = 1; + return function() { + count++; + if(count > max_depth) { + count--; return; + } else { + fn.apply(this, arguments); + } + } + } + + function hook_fn(fn, listener) { + fn.listener = function() { return listener; } + fn.apply = function(thisArg, argArray) { + if(fn == caller) return; + listener(); + return fn.apply(thisArg, argArray); + }; + } + + hook_fn(history, function() { print("Yo"); }); + + $(document).ready(page_onload); + + function page_onload() { + input = $(input); + output = $(output); + input.keypress(map_keyboard()); + $(document).click(refocus); + refocus(); + } + page_onload = hide_fn(page_onload); + + function clear() { output.html(""); } + + function history() { + return command_history.join("\n"); + } + + var keyLogging = false; + + function map_keyboard() { + var cmdKeys = keymap(); + with(cmdKeys) { + mapKeyCode(toggleKeyLogging, 120); + map(executeCommand, {keyCode:13, ctrlKey:true}); + //mapKeyCode(executeCommand, 13); + mapKeyCode(refocus, 9); + map(historyLast, {keyCode:38, ctrlKey:true}); + map(historyNext, {keyCode:40, ctrlKey:true}); + map(function() { return false; }, {keyCode:123}); + } + return function(e) { + if(keyLogging) + log("keyCode: " + e.keyCode, " shiftKey: " + e.shiftKey, " ctrlKey: " + e.ctrlKey); + resize_input(); + return cmdKeys.dispatch(e); + } + } + + function toggleKeyLogging() { keyLogging = !keyLogging; } + + function historyLast() { + command_selected = Math.max(0, command_selected - 1); + edit_command(command_history[command_selected]); + } + + function historyNext() { + command_selected = Math.min(command_history.length, command_selected + 1); + var cmd = (command_selected == command_history.length) ? "" : command_history[command_selected]; + edit_command(cmd); + } + + function tryComplete() { + + refocus(); + } + + function executeCommand(cmd) { + cmd = (!cmd) ? input.val() : cmd; + reset_input(); + var result = evalInScope(cmd, default_scope); + command_selected = command_history.length; + logCommand(cmd, result); + setTimeout(function() { input.attr("rows", 1); }, 2); + return false; + } + + function evalInScope(cmd, scope) { + try { + + //if(!scope) return eval.apply(our_scope, [cmd]); + with(scope) { + with(jQConsole.our_scope) { + return eval(cmd); + } + } + + //move_modified_scope(local_scope, global_scope); + } + catch(e) { + return e.message; + } + } + + function move_modified_scope(l, g) { + + for(var k in g) { + if(g[k] && typeof l[k] == 'undefined') { + l[k] = g[k]; + g[k] = null; + } + } + } + + function safe_scope() { + var s = {}, g = jQConsole.global_scope; + for(var k in g) { s[k] = null; } + s.global_scope = jQConsole.global_scope; + return s; + } + + var encoders = { + "object": function(o, l) { + if(o.constructor == Array) + return encode_array(o); + //return "[array]"; + return "{ " + encode_obj(o, l) + " }"; + }, + "function": function(v) { return v.toString(); }, + "string": function(v) { return "\"" + v + "\""; }, + "undefined": function() { return "undefined"; }, + _default: function(v) { return v.toString(); } + } + + function encode_array(a) { + var r = a.map(enc); + return "[" + r.join(",") + "]"; + } + + enc = function(v, l, root) { + root = root || true; + if(v == null) return (root) ? "" : "null" + l; + if(encoders[typeof v]) return encoders[typeof v](v); + //log("enc", v, l); + return encoders._default(v, l); + } + + function encode_obj(val, expand) { + if(expand <= 0) { return val.toString(); } + var r = []; + for(var i in val) { + r.push(i + ": " + enc(val[i], expand - 1, false)); + } + return r.join(",\n"); + } + + function encode_reg(s) { + return s.replace(/([\\/\t\n])/g, "\\$1"); + } + + function reg_lookup_fn(lookup) { + var re = new RegExp(encode_reg(keys(lookup).join("")), "ig"); + return re; + } + + function print(msg) { + var className = (typeof msg == "function") ? "cmd" : "print"; + msg = enc(msg, 3); + if(!msg) return; + var out = $($.PRE({"className":className}, msg)); + if(className == "cmd") { out.click(select_command); } + output.prepend(out); + } + + function logCommand(cmd, result) { + command_history.push(cmd); + if(result != undefined) { + output.prepend(jQuery.dump(result)); + } + $($.PRE({className:'cmd'}, cmd)).click(select_command).prependTo(output); + //print(result); + return cmd; + } + + function select_command() { + edit_command($(this).text()); + } + + function edit_command(cmd) { + input.val(cmd); + resize_input(); + input.get(0).select(); + } + + function log() { + var msg = args(arguments); + $("
    ").text(msg.join(", ")).prependTo(output); + } + + function resize_input() + { + setTimeout(do_resize, 0); + + function do_resize() { + var rows = input.val().split(/\n/).length + // + 1 // prevent scrollbar flickering in Mozilla + + (window.opera ? 1 : 0); // leave room for scrollbar in Opera + + // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0. + if (input.attr("rows") != rows) + input.attr("rows", rows); + } + } + + var default_scope = { + "log": log, + "history": history, + alert: function(msg) { alert(msg); } + } + disable_functions(default_scope, "window,document,t1"); + + function disable_functions(obj, list) { + var list = list.split(","); + for(var i in list) { + obj[list[i]] = {}; + } + } + + return this; +}; + +jQuery.extend(jQuery.fn, { + "autoresize": function() + { + var thisp = this; + setTimeout(do_resize, 0); + + function do_resize() { + var s = thisp.val() || ""; + var rows = s.split(/\n/).length; + // + 1 // prevent scrollbar flickering in Mozilla + + (window.opera ? 1 : 0); // leave room for scrollbar in Opera + + // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0. + if (thisp.attr("rows") != rows) + thisp.attr("rows", rows); + } + return this; + } +}) + + + +jQConsole.global_scope = this; +jQConsole.our_scope = {}; \ No newline at end of file diff --git a/public/javascripts/controls.js b/public/javascripts/controls.js new file mode 100644 index 0000000..ca29aef --- /dev/null +++ b/public/javascripts/controls.js @@ -0,0 +1,963 @@ +// 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("
  2. " + elem.substr(0, entry.length) + "" + + elem.substr(entry.length) + "
  3. "); + 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("
  4. " + elem.substr(0, foundPos) + "" + + elem.substr(foundPos, entry.length) + "" + elem.substr( + foundPos + entry.length) + "
  5. "); + 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/public/javascripts/dragdrop.js b/public/javascripts/dragdrop.js new file mode 100644 index 0000000..07229f9 --- /dev/null +++ b/public/javascripts/dragdrop.js @@ -0,0 +1,973 @@ +// 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/public/javascripts/effects.js b/public/javascripts/effects.js new file mode 100644 index 0000000..5a639d2 --- /dev/null +++ b/public/javascripts/effects.js @@ -0,0 +1,1128 @@ +// 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/public/javascripts/facebox.js b/public/javascripts/facebox.js new file mode 100755 index 0000000..5ad90ed --- /dev/null +++ b/public/javascripts/facebox.js @@ -0,0 +1,309 @@ +/* + * Facebox (for jQuery) + * version: 1.2 (05/05/2008) + * @requires jQuery v1.2 or later + * + * Examples at http://famspam.com/facebox/ + * + * Licensed under the MIT: + * http://www.opensource.org/licenses/mit-license.php + * + * Copyright 2007, 2008 Chris Wanstrath [ chris@ozmm.org ] + * + * Usage: + * + * jQuery(document).ready(function() { + * jQuery('a[rel*=facebox]').facebox() + * }) + * + * Terms + * Loads the #terms div in the box + * + * Terms + * Loads the terms.html page in the box + * + * Terms + * Loads the terms.png image in the box + * + * + * You can also use it programmatically: + * + * jQuery.facebox('some html') + * jQuery.facebox('some html', 'my-groovy-style') + * + * The above will open a facebox with "some html" as the content. + * + * jQuery.facebox(function($) { + * $.get('blah.html', function(data) { $.facebox(data) }) + * }) + * + * The above will show a loading screen before the passed function is called, + * allowing for a better ajaxy experience. + * + * The facebox function can also display an ajax page, an image, or the contents of a div: + * + * jQuery.facebox({ ajax: 'remote.html' }) + * jQuery.facebox({ ajax: 'remote.html' }, 'my-groovy-style') + * jQuery.facebox({ image: 'stairs.jpg' }) + * jQuery.facebox({ image: 'stairs.jpg' }, 'my-groovy-style') + * jQuery.facebox({ div: '#box' }) + * jQuery.facebox({ div: '#box' }, 'my-groovy-style') + * + * Want to close the facebox? Trigger the 'close.facebox' document event: + * + * jQuery(document).trigger('close.facebox') + * + * Facebox also has a bunch of other hooks: + * + * loading.facebox + * beforeReveal.facebox + * reveal.facebox (aliased as 'afterReveal.facebox') + * init.facebox + * afterClose.facebox + * + * Simply bind a function to any of these hooks: + * + * $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... }) + * + */ +(function($) { + $.facebox = function(data, klass) { + $.facebox.loading() + + if (data.ajax) fillFaceboxFromAjax(data.ajax, klass) + else if (data.image) fillFaceboxFromImage(data.image, klass) + else if (data.div) fillFaceboxFromHref(data.div, klass) + else if ($.isFunction(data)) data.call($) + else $.facebox.reveal(data, klass) + } + + /* + * Public, $.facebox methods + */ + + $.extend($.facebox, { + settings: { + opacity : 0.2, + overlay : true, + loadingImage : '/facebox/loading.gif', + closeImage : '/facebox/closelabel.png', + imageTypes : [ 'png', 'jpg', 'jpeg', 'gif' ], + faceboxHtml : '\ + ' + }, + + loading: function() { + init() + if ($('#facebox .loading').length == 1) return true + showOverlay() + + $('#facebox .content').empty() + $('#facebox .body').children().hide().end(). + append('
    ') + + $('#facebox').css({ + top: getPageScroll()[1] + (getPageHeight() / 10), + left: $(window).width() / 2 - 205 + }).show() + + $(document).bind('keydown.facebox', function(e) { + if (e.keyCode == 27) $.facebox.close() + return true + }) + $(document).trigger('loading.facebox') + }, + + reveal: function(data, klass) { + $(document).trigger('beforeReveal.facebox') + if (klass) $('#facebox .content').addClass(klass) + $('#facebox .content').append(data) + $('#facebox .loading').remove() + $('#facebox .body').children().fadeIn('normal') + $('#facebox').css('left', $(window).width() / 2 - ($('#facebox .popup').width() / 2)) + $(document).trigger('reveal.facebox').trigger('afterReveal.facebox') + }, + + close: function() { + $(document).trigger('close.facebox') + return false + } + }) + + /* + * Public, $.fn methods + */ + + $.fn.facebox = function(settings) { + if ($(this).length == 0) return + + init(settings) + + function clickHandler() { + $.facebox.loading(true) + + // support for rel="facebox.inline_popup" syntax, to add a class + // also supports deprecated "facebox[.inline_popup]" syntax + var klass = this.rel.match(/facebox\[?\.(\w+)\]?/) + if (klass) klass = klass[1] + + fillFaceboxFromHref(this.href, klass) + return false + } + + return this.bind('click.facebox', clickHandler) + } + + /* + * Private methods + */ + + // called one time to setup facebox on this page + function init(settings) { + if ($.facebox.settings.inited) return true + else $.facebox.settings.inited = true + + $(document).trigger('init.facebox') + makeCompatible() + + var imageTypes = $.facebox.settings.imageTypes.join('|') + $.facebox.settings.imageTypesRegexp = new RegExp('\.(' + imageTypes + ')$', 'i') + + if (settings) $.extend($.facebox.settings, settings) + $('body').append($.facebox.settings.faceboxHtml) + + var preload = [ new Image(), new Image() ] + preload[0].src = $.facebox.settings.closeImage + preload[1].src = $.facebox.settings.loadingImage + + $('#facebox').find('.b:first, .bl').each(function() { + preload.push(new Image()) + preload.slice(-1).src = $(this).css('background-image').replace(/url\((.+)\)/, '$1') + }) + + $('#facebox .close').click($.facebox.close) + $('#facebox .close_image').attr('src', $.facebox.settings.closeImage) + } + + // getPageScroll() by quirksmode.com + function getPageScroll() { + var xScroll, yScroll; + if (self.pageYOffset) { + yScroll = self.pageYOffset; + xScroll = self.pageXOffset; + } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict + yScroll = document.documentElement.scrollTop; + xScroll = document.documentElement.scrollLeft; + } else if (document.body) {// all other Explorers + yScroll = document.body.scrollTop; + xScroll = document.body.scrollLeft; + } + return new Array(xScroll,yScroll) + } + + // Adapted from getPageSize() by quirksmode.com + function getPageHeight() { + var windowHeight + if (self.innerHeight) { // all except Explorer + windowHeight = self.innerHeight; + } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode + windowHeight = document.documentElement.clientHeight; + } else if (document.body) { // other Explorers + windowHeight = document.body.clientHeight; + } + return windowHeight + } + + // Backwards compatibility + function makeCompatible() { + var $s = $.facebox.settings + + $s.loadingImage = $s.loading_image || $s.loadingImage + $s.closeImage = $s.close_image || $s.closeImage + $s.imageTypes = $s.image_types || $s.imageTypes + $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml + } + + // Figures out what you want to display and displays it + // formats are: + // div: #id + // image: blah.extension + // ajax: anything else + function fillFaceboxFromHref(href, klass) { + // div + if (href.match(/#/)) { + var url = window.location.href.split('#')[0] + var target = href.replace(url,'') + if (target == '#') return + $.facebox.reveal($(target).html(), klass) + + // image + } else if (href.match($.facebox.settings.imageTypesRegexp)) { + fillFaceboxFromImage(href, klass) + // ajax + } else { + fillFaceboxFromAjax(href, klass) + } + } + + function fillFaceboxFromImage(href, klass) { + var image = new Image() + image.onload = function() { + $.facebox.reveal('
    ', klass) + } + image.src = href + } + + function fillFaceboxFromAjax(href, klass) { + $.get(href, function(data) { $.facebox.reveal(data, klass) }) + } + + function skipOverlay() { + return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null + } + + function showOverlay() { + if (skipOverlay()) return + + if ($('#facebox_overlay').length == 0) + $("body").append('
    ') + + $('#facebox_overlay').hide().addClass("facebox_overlayBG") + .css('opacity', $.facebox.settings.opacity) + .click(function() { $(document).trigger('close.facebox') }) + .fadeIn(200) + return false + } + + function hideOverlay() { + if (skipOverlay()) return + + $('#facebox_overlay').fadeOut(200, function(){ + $("#facebox_overlay").removeClass("facebox_overlayBG") + $("#facebox_overlay").addClass("facebox_hide") + $("#facebox_overlay").remove() + }) + + return false + } + + /* + * Bindings + */ + + $(document).bind('close.facebox', function() { + $(document).unbind('keydown.facebox') + $('#facebox').fadeOut(function() { + $('#facebox .content').removeClass().addClass('content') + $('#facebox .loading').remove() + $(document).trigger('afterClose.facebox') + }) + hideOverlay() + }) + +})(jQuery); diff --git a/public/javascripts/index.html b/public/javascripts/index.html new file mode 100755 index 0000000..e933af9 --- /dev/null +++ b/public/javascripts/index.html @@ -0,0 +1,8 @@ + + + + + +

    you shouldn't be here.. kicking you out!

    + + diff --git a/public/javascripts/irb.js b/public/javascripts/irb.js new file mode 100755 index 0000000..b4cb354 --- /dev/null +++ b/public/javascripts/irb.js @@ -0,0 +1,110 @@ +// +// Copyright (c) 2008 why the lucky stiff +// Copyright (c) 2010 Andrew McElroy +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +var allStretch; +var helpPages; +var chapPages; +var defaultPage; +var toot = window.location.search.substr(1) + +//the main function, call to the effect object +function dumpAlert(obj) { + props = []; + for ( var i in obj ) { + props.push( "" + i + ": " + obj[i] ); + } + alert( props ); +} +window.onload = function() { + defaultPage = $('#helpstone .stretcher').html(); + + window.irb = new MouseApp.Irb('#irb', { + rows: 13, + name: 'IRB', + greeting: "%+r Interactive ruby ready. %-r", + ps: '\033[1;31m>>\033[m', + user: 'guest', + host: 'tryruby', + // original: irbUrl: '/irb', + irbUrl: '/tryruby/run', + init: function () { + helpPages = $(".stretcher"); + chapPages = new Array(); + for (var i = 0; i < helpPages.length; i++ ) { + var cls = helpPages[i].className.split(' '); + for (var j = 0; j < cls.length; j++) { + if (cls[j] == 'chapmark') { + chapPages.push([i, helpPages[i]]); + break; + } + } + } + }, + loadTutorial: function (id, instruct) { + $.ajax({ + url: '/tutorials/' + id , + type: 'GET', + complete: function (r) { + $('#helpstone').html("
    " + defaultPage + "
    " + r.responseText); + window.irb.init(); + window.irb.showHelp(0); + } + }); + }, + showChapter: function (n) { + if (n >= chapPages.length) return; + this.setHelpPage(chapPages[n][0], chapPages[n][1]); + }, + showHelp: function (n) { + if (n >= helpPages.length) return; + this.setHelpPage(n, helpPages[n]); + }, + popup_goto: function (u) { + $('#lilBrowser').show().css({left: '40px', top: '40px'}); + $('#lbIframe').attr('src', u); + }, + popup_make: function (s) { + $('#lilBrowser').show().css({left: '40px', top: '40px'}); + $('#lbIframe').get(0).onIframeLoad = function () { + alert($(this).html()); + alert("$(this).html()"); + return s; + }; + //$('#lbIframe').attr({src: '/blank.html'}); + src = s.replace(/\\/g, "\\\\").replace(/\"/g, "\\\""); + $('#lbIframe').attr({src: "javascript:\"" + src + "\""}); + // $('# + }, + popup_close: function () { + $('#lilBrowser').hide(); + } + }); + + if ( !toot ) { + toot = 'intro'; + } + try { + window.irb.options.loadTutorial( toot, true ); + } catch (e) {} +} diff --git a/public/javascripts/jQuery.irb.js b/public/javascripts/jQuery.irb.js new file mode 100644 index 0000000..765858d --- /dev/null +++ b/public/javascripts/jQuery.irb.js @@ -0,0 +1,1641 @@ +//Try Ruby 1.5 (defacto version Number) +// March 14 2011 + +// large parts of this file are lifted from Try Haskell ( which lifted parts from try ruby without copyright notice >:-/ ) + +// Thus it is fair to include the following banner if/until there is no try haskell code left/ only code that originally existed in try ruby in the +//first place. + + // Try Haskell 1.0.1 + // Tue Feb 23 18:34:48 GMT 2010 + // + // Copyright 2010 Chris Done. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions + // are met: + // + // 1. Redistributions of source code must retain the above + // copyright notice, this list of conditions and the following + // disclaimer. + // 2. Redistributions in binary form must reproduce the above + // copyright notice, this list of conditions and the following + // disclaimer in the documentation and/or other materials + // provided with the distribution. + // + // THIS SOFTWARE IS PROVIDED BY CHRIS DONE ``AS IS'' AND ANY EXPRESS + // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + // ARE DISCLAIMED. IN NO EVENT SHALL CHRIS DONE OR CONTRIBUTORS BE + // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + // BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + // DAMAGE. + // The views and conclusions contained in the software and + // documentation are those of the authors and should not be + // interpreted as representing official policies, either expressed or + // implied, of Chris Done. + // + // TESTED ON + // Internet Explorer 6 + // Opera 10.01 + // Chromium 4.0.237.0 (Ubuntu build 31094) + // Firefox 3.5.8 + + //IOS 4.3 (all devices) + +// Temporary fix +function opera() { + return navigator.userAgent.indexOf("Opera") == 0; +} + +function encodeHex(str) { + var result = ""; + for (var i = 0; i < str.length; i++) { + result += "%" + pad(toHex(str.charCodeAt(i) & 0xff), 2, '0'); + } + return result; +} + +//var handleJSON = function(a){ alert('Unassigned JSONP: ' + a); } +var hexv = { + "00": 0, + "01": 1, + "02": 2, + "03": 3, + "04": 4, + "05": 5, + "06": 6, + "07": 7, + "08": 8, + "09": 9, + "0A": 10, + "0B": 11, + "0C": 12, + "0D": 13, + "0E": 14, + "0F": 15, + "10": 16, + "11": 17, + "12": 18, + "13": 19, + "14": 20, + "15": 21, + "16": 22, + "17": 23, + "18": 24, + "19": 25, + "1A": 26, + "1B": 27, + "1C": 28, + "1D": 29, + "1E": 30, + "1F": 31, + "20": 32, + "21": 33, + "22": 34, + "23": 35, + "24": 36, + "25": 37, + "26": 38, + "27": 39, + "28": 40, + "29": 41, + "2A": 42, + "2B": 43, + "2C": 44, + "2D": 45, + "2E": 46, + "2F": 47, + "30": 48, + "31": 49, + "32": 50, + "33": 51, + "34": 52, + "35": 53, + "36": 54, + "37": 55, + "38": 56, + "39": 57, + "3A": 58, + "3B": 59, + "3C": 60, + "3D": 61, + "3E": 62, + "3F": 63, + "40": 64, + "41": 65, + "42": 66, + "43": 67, + "44": 68, + "45": 69, + "46": 70, + "47": 71, + "48": 72, + "49": 73, + "4A": 74, + "4B": 75, + "4C": 76, + "4D": 77, + "4E": 78, + "4F": 79, + "50": 80, + "51": 81, + "52": 82, + "53": 83, + "54": 84, + "55": 85, + "56": 86, + "57": 87, + "58": 88, + "59": 89, + "5A": 90, + "5B": 91, + "5C": 92, + "5D": 93, + "5E": 94, + "5F": 95, + "60": 96, + "61": 97, + "62": 98, + "63": 99, + "64": 100, + "65": 101, + "66": 102, + "67": 103, + "68": 104, + "69": 105, + "6A": 106, + "6B": 107, + "6C": 108, + "6D": 109, + "6E": 110, + "6F": 111, + "70": 112, + "71": 113, + "72": 114, + "73": 115, + "74": 116, + "75": 117, + "76": 118, + "77": 119, + "78": 120, + "79": 121, + "7A": 122, + "7B": 123, + "7C": 124, + "7D": 125, + "7E": 126, + "7F": 127, + "80": 128, + "81": 129, + "82": 130, + "83": 131, + "84": 132, + "85": 133, + "86": 134, + "87": 135, + "88": 136, + "89": 137, + "8A": 138, + "8B": 139, + "8C": 140, + "8D": 141, + "8E": 142, + "8F": 143, + "90": 144, + "91": 145, + "92": 146, + "93": 147, + "94": 148, + "95": 149, + "96": 150, + "97": 151, + "98": 152, + "99": 153, + "9A": 154, + "9B": 155, + "9C": 156, + "9D": 157, + "9E": 158, + "9F": 159, + "A0": 160, + "A1": 161, + "A2": 162, + "A3": 163, + "A4": 164, + "A5": 165, + "A6": 166, + "A7": 167, + "A8": 168, + "A9": 169, + "AA": 170, + "AB": 171, + "AC": 172, + "AD": 173, + "AE": 174, + "AF": 175, + "B0": 176, + "B1": 177, + "B2": 178, + "B3": 179, + "B4": 180, + "B5": 181, + "B6": 182, + "B7": 183, + "B8": 184, + "B9": 185, + "BA": 186, + "BB": 187, + "BC": 188, + "BD": 189, + "BE": 190, + "BF": 191, + "C0": 192, + "C1": 193, + "C2": 194, + "C3": 195, + "C4": 196, + "C5": 197, + "C6": 198, + "C7": 199, + "C8": 200, + "C9": 201, + "CA": 202, + "CB": 203, + "CC": 204, + "CD": 205, + "CE": 206, + "CF": 207, + "D0": 208, + "D1": 209, + "D2": 210, + "D3": 211, + "D4": 212, + "D5": 213, + "D6": 214, + "D7": 215, + "D8": 216, + "D9": 217, + "DA": 218, + "DB": 219, + "DC": 220, + "DD": 221, + "DE": 222, + "DF": 223, + "E0": 224, + "E1": 225, + "E2": 226, + "E3": 227, + "E4": 228, + "E5": 229, + "E6": 230, + "E7": 231, + "E8": 232, + "E9": 233, + "EA": 234, + "EB": 235, + "EC": 236, + "ED": 237, + "EE": 238, + "EF": 239, + "F0": 240, + "F1": 241, + "F2": 242, + "F3": 243, + "F4": 244, + "F5": 245, + "F6": 246, + "F7": 247, + "F8": 248, + "F9": 249, + "FA": 250, + "FB": 251, + "FC": 252, + "FD": 253, + "FE": 254, + "FF": 255 +}; + +function pad(str, len, pad) { + var result = str; + for (var i = str.length; i < len; i++) { + result = pad + result; + } + return result; +} + +var digitArray = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); + +function toHex(n) { + var result = '' + var start = true; + for (var i = 32; i > 0;) { + i -= 4; + var digit = (n >> i) & 0xf; + if (!start || digit != 0) { + start = false; + result += digitArray[digit]; + } + } + return (result == '' ? '0': result); +} + + (function($) { + var raphaelPaper; + var raphaelObjs; + var tutorialGuide; + + // I don't like this at all... I intend to make this pull via ajax from a key value data store. + var pages = + [ + //////////////////////////////////////////////////////////////////////// + // Lesson 1 + // Simple addition + { + lesson: 1, + title: 'Basics; numbers, strings, etc.', + guide: + '

    ' + rmsg(['Learning By Numbers', 'Music is Math', 'Back to Basics']) + + '

    ' + + "

    To kick off let's try some maths out. Up there you can" + + " type in Haskell expressions. Try this out: 5 + 7

    " + }, + { + guide: function(result) { + if (!result) result = { + expr: '5+7', + result: 12 + }; + var complied = result.expr.replace(/ /g, '') == "5+7"; + var who = complied ? 'we': 'you'; + return '

    ' + rmsg(['Your first Haskell expression', + "First Time's a Charm"]) + '

    ' + + '

    Well done, you typed it perfect! You got back the number' + + ' ' + result.result + '. Just what ' + who + ' wanted. ' + + "

    Let's try something completely different." + + " Type in your name like this:" + + ' "chris"

    ' + }, + trigger: function(result) { + return result.type == "(Num t) => t" || + result.type == "Integer" || + result.type == "Int"; + } + }, + // Strings & types + { + guide: function(result) { + if (!result) result = { + expr: '"chris"', + result: "\"chris\"" + }; + var n = unString(result.result); + if (n) n = ", " + n; + n += "!"; + return '

    ' + rmsg(['Types of values', "What's in a name?"]) + + '

    ' + + '

    Hi there' + htmlEncode(n) + + (n != "!" ? " That's a pretty name. Honest.": "") + + " You're getting the hang of this!

    " + + "

    Note: You can chat to Haskell programmers while learning here, enter chat to start it." + + " You will join the official IRC channel of the Haskell community!

    " + + "

    Each time, you're getting back the value of the expression. So " + + "far, just a number and a list of characters.

    " + + "

    You can have lists of other stuff, too. Let's see your " + + " lottery numbers: [42,13,22]

    " + }, + trigger: function(result) { + return result.type == "[Char]" + || result.type == "String"; + } + }, + // Overview of lesson 1 + { + guide: function(result) { + if (!result) result = { + result: "[42,13,22]" + }; + return '

    ' + rmsg(["Lesson 1 done already!"]) + + '

    ' + + "

    Great, you made a list of numbers! If you win we'll split" + + " the winnings, right?

    " + + "

    Let's see what you've learned so far:

    " + + "
      " + + "
    1. How to write maths and lists of things.
    2. " + + "
    " + + "

    You can do stuff with lists. Maybe you want the lottery " + + "numbers sorted in the right order, try this: " + + "sort " + result.result + "

    " + }, + trigger: function(result) { + return result.expr.match(/^[ ]*\[[0-9, ]+\][ ]*$/) && + result.type == "(Num t) => [t]"; + } + }, + /////////////////////////////////////////////////////////////////////// + // Lesson 2 - Functions + // Functions on lists + { + lesson: 2, + title: 'Simple Functions', + guide: function(result) { + if (!result) result = { + result: "[13,23,30]" + }; + return '

    ' + rmsg(["We put the funk in function"]) + + '

    ' + + "

    Congratulations, you just used a function." + + " They're how you get things done in Haskell." + + "

    As you might've guessed, we got back " + + htmlEncode(result.result) + + ".

    Ever wanted an evil twin nemesis? Me too. " + + "Luckily, you can sort lists of characters, or " + + "strings" + + ", in the same way as numbers! sort \"chris\"

    " + }, + trigger: function(result) { + return result.expr.match(/sort/) && + result.type == "(Num t, Ord t) => [t]"; + } + }, + // Tuples + { + guide: function(result) { + if (!result) result = { + result: "\"chirs\"" + }; + nemesis = htmlEncode(unString(result.result)); + return '

    ' + + rmsg(["Tuples, because sometimes one value ain't enough!"]) + + '

    ' + + "

    Watch out for " + nemesis + "! You should keep their credentials for the police.

    " + + "

    My nemesis is 28 years of age: " + + "(28,\"chirs\")

    " + }, + trigger: function(result) { + return result.expr.match(/sort/) && + result.type == "[Char]"; + } + }, + // Functions on tuples + { + guide: function(result) { + if (!result) result = { + result: "(28,\"chirs\")" + }; + var age = result.result.match(/^\(([0-9]+)+/); + var villain = htmlEncode(result.result.replace(/\\"/g, '"')); + return '

    ' + + rmsg(["We'll keep them safe, don't worry about it."]) + + '

    ' + + "

    Is " + (age ? age[1] : "that") + " a normal age for a " + + "super-villain?

    " + + "

    You just wrote a tuple. It's a way to keep a bunch of values together in Haskell. " + + "You can put as many as you like in there:

    " + + "
    • (1,\"hats\",23/35)
    • (\"Shaggy\",\"Daphnie\",\"Velma\")
    " + + "

    Actually, let's say our villain is " + + "" + villain + "" + + ", how do you get their age?

    " + + "fst " + villain + "" + }, + trigger: function(result) { + return result.expr.match(/\([0-9]+,[ ]*"[^"]+"\)/) && + result.type == "(Num t) => (t, [Char])"; + } + }, + // Summary of lesson 2 + { + guide: function(result) { + return '

    ' + + rmsg(["Lesson 2 done! Wow, great job!", + "Lesson 2 completo!"]) + + '

    ' + + + "

    Good job! You got the age back from the tuple! Didn't " + + " even break a sweat, did you? The fst function " + + "just gets the first value. It's called \"fst\" because " + + "it's used a lot in Haskell so it really needs to be short!

    " + + + "

    Time to take a rest and see what you learned:

    " + + "
      " + + "
    1. Functions can be used on lists of any type.
    2. " + + "
    3. We can stuff values into tuples.
    4. " + + "
    5. Getting the values back from tuples is easy.
    6. " + + "
    " + + + "

    Now let's say you want " + + " to use a value more than once, how would you do it? " + + "To make our lives easier, we can say:

    " + + + "let x = 4 in x * x" + }, + trigger: function(result) { + return result.expr.match(/fst/) && + result.type == "(Num t) => t"; + } + }, + { + guide: function(result) { + return "

    Let them eat cake

    " + + + "

    You just bound a variable. " + + "That is, you bound x to the expression 4, " + + " and then you can write x in some code (the body) and " + + " it will mean the same as if you'd written 4.

    " + + + "

    It's like this: let var = expression in body

    " + + + "The in part just separates the expression from the body.

    " + + + "

    For example try: " + + "let x = 8 * 10 in x + x

    " + + + "

    So if we wanted to get the age of our villain, we could do:

    " + + + "let villain = (28,\"chirs\") in fst villain" + + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+x[ ]*=[ ]*[0-9]+[ ]*in[ ]*x[ ]*\*[ ]*x/) && + result.type == "(Num t) => t"; + } + }, + { + guide: function(result) { + return "

    Basics over, let's go!

    " + + "

    Next, let's take a short detour to learn about " + + "syntactic sugar. " + + "Try typing this out:

    " + + "

    'a' : []

    " + + "

    Or skip to lesson4 to learn about functions," + + " the meat of Haskell!"; + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+villain[ ]*=[ ]*\([0-9]+,[ ]*"[^"]+"\)[ ]*in[ ]+fst[ ]+villain[ ]*/) && + result.type == "(Num t) => t"; + } + }, + // Lesson 3: Syntactic sugar + { + lesson: 3, + title: 'Syntactic Sugar', + guide: function(result) { + return '

    ' + + rmsg(["You constructed a list!"]) + + '

    ' + + "

    Well done, that was tricky syntax. You used the (:) " + + "function. It takes two values, some value and a list, and " + + " constructs a new list" + + " out of them. We call it 'cons' for short.

    " + + "

    'a' is " + + "the character 'a', [] is an empty list. So " + + "tacking 'a' at the start of an empty list just " + + "makes a list ['a']!

    " + + "

    But thankfully we don't have to type out " + + "'a' : 'b' : [] every time to we want to make a " + + "list of characters; we can use " + + "syntactic sugar and just write" + + " ['a','b']. Don't believe me, check this!

    " + + "'a' : 'b' : [] == ['a','b']" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*'a'[ ]*:[ ]*\[\][ ]*/) && + result.type == "[Char]"; + } + }, + // Booleans and string syntactic sugar + { + guide: function(result) { + return '

    ' + + rmsg(["You're on fire!"]) + + '

    ' + + "

    You're handling this syntax really well, nice!

    " + + "

    You just got a boolean value back, and it said " + + "True. That means they're equal!

    " + + "

    One final demonstration on syntactic sugar for now:

    " + + "['a','b','c'] == \"abc\"" + }, + trigger: function(result) { + return result.type == "Bool" && + result.expr.replace(/[^':\[\]\=,]/g, '') == "'':'':[]==['','']"; + } + }, + // Summary of syntactic sugar section + { + guide: function(result) { + return '

    ' + + rmsg(["Lesson 3 over! Syntactic sugar is sweet"]) + + '

    ' + + "

    Let's have a gander at what you learned:

    " + + "
      " + + "
    1. In 'a' : [], : is really just " + + " another function, just clever looking.
    2. " + + "
    3. Pretty functions like this are written like (:) when " + + " you talk about them.
    4. " + + "
    5. A list of characters ['a','b'] can just be written " + + "\"ab\". Much easier!
    6. " + + "
    " + + "

    Phew! You're getting pretty deep! Your arch nemesis, " + + nemesis + ", is gonna try to steal your " + rmsg(['mojo', + 'pizza']) + + "! Let's learn a bit more about functions and passing " + + "them around. Try this:

    map (+1) [1..5]

    "; + }, + trigger: function(result) { + return result.expr.replace(/[^\]\[',=\"]?/g, '') == "['','','']==\"\"" && + result.type == "Bool"; + } + }, + { + lesson: 4, + title: 'Functions, reloaded; passing, defining, etc.', + guide: function() { + var title = + rmsg(["Functions [of a Geisha]", + "Functions, functors, functoids, funky", + "Functions: Expanded fo' real"]); + return "

    " + title + "

    " + + + "

    Here's where the magic begins!

    " + + + "

    You just passed the (+1) " + + "function to the map function.

    " + + + "

    You can try other things like (remember: click to insert them):

    " + + + "
      " + + "
    • map (*99) [1..10]
    • " + + "
    • map (/5) [13,24,52,42]
    • " + + "
    • filter (>5) [62,3,25,7,1,9]
    • " + + "
    " + + + "

    Note that a tuple is different to a list because you can do this:

    " + + "(1,\"George\")" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*map[ ]+\(\+1\)[ ]*\[1..5\][ ]*$/) && + result.type == "(Num a, Enum a) => [a]"; + } + }, + { + guide: function(result) { + return "

    Lists and Tuples

    " + + + "

    You can only " + + " have a list of numbers or a list of characters, whereas in a tuple you can throw anything in!

    " + + + "

    We've also seen that you can make a new list with (:) that joins two values together, like:

    " + + "

    1 : [2,3]

    " + + + "

    But we can't do this with tuples! You can only write a tuple and then look at what's inside. You can't make new ones on the fly like a list." + + + "

    Let's write our own functions! It's really easy. How about something simple:

    " + + "let square x = x * x in square " + rmsg([52, 10, 3]) + "" + + }, + trigger: function(result) { + return result.expr.match(/^[ ]*\(1,"[^"]+"\)[ ]*$/) && + result.type == "(Num t) => (t, [Char])"; + } + }, + { + guide: function(result) { + return "

    Let there be functions

    " + + "

    Nice one! I think you're getting used to the let syntax.

    " + + "

    You defined a function. You can read it as, as for a given " + + "parameter called x, square of " + + "x is x * x." + + "

    Some others you can try are:

    " + + "
    • let add1 x = x + 1 in add1 5
    • " + + "
    • let second x = snd x in second (3,4)
    • " + + "
    " + + "

    Let's go crazy and use our square function with map:

    " + + "let square x = x * x in map square [1..10]" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]*square[ ]+x[ ]*=[ ]*x[ ]*\*[ ]*x[ ]*in[ ]*square[ ]+[0-9]+/) && + result.type == "(Num t) => t"; + } + }, + { + guide: function(result) { + if (!result || !result.value) result = { + value: "[1,4,9,16,25,36,49,64,81,100]" + }; + return "

    Let there be functions

    " + + + "

    That's so cool! You described a simple function square and then " + + "you just passed it to another function (map) and got back " + + htmlEncode(result.value) + ", exactly what you expected!

    " + + + "

    Haskell is pretty good at composing things together like this. " + + "Some other things you can try are:

    " + + + "
      " + + "
    • let add1 x = x + 1 in map add1 [1,5,7]
    • " + + "
    • let take5s = filter (==5) in take5s [1,5,2,5,3,5]
    • " + + "
    • let take5s = filter (==5) in map take5s [[1,5],[5],[1,1]]
    • " + + "
    " + + + "

    Did you get back what you expected?

    " + + + "

    One more example for text; how do you upcase a letter?

    " + + + "

    toUpper 'a'

    " + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+square[ ]+x[ ]*=[ ]*x[ ]*\*[ ]*x[ ]*in[ ]+map[ ]+square[ ]*\[1..10\][ ]*$/) && + result.type == "(Num a, Enum a) => [a]"; + } + }, + { + guide: function(result) { + return "

    Exercise time!

    " + + + "

    Easy! Remember: characters are written like 'a' and " + + "strings (lists of characters) are written like \"a\"." + + + "

    I need you to use toUpper capitalise my whole name, " + + "\"Chris\". Give it a try." + + " You can do it, I believe in you!

    " + + + '

    Spoiler: map toUpper "Chris"

    ' + }, + trigger: function(result) { + return result.expr.match(/^toUpper 'a'$/) && + result.type == "Char"; + } + }, + { + guide: function(result) { + return "

    Lesson 4 complete!

    " + + + "

    Brilliant! You're making excellent progress! " + + "You just passed toUpper to map. No problem.

    " + + + "

    Let's go over what you've learned in this lesson:

    " + + + "
      " + + "
    1. Functions like map take other functions as parameters.
    2. " + + "
    3. Functions like (+1), (>5) and " + + "square can be passed to other functions.
    4. " + + "
    5. Defining functions is just a case of writing what " + + "to do with the parameters.
    6. " + "
    " + + + "

    Let's check out pattern matching; a way to " + + "get values from other values using patterns. Try this:

    " + + "

    let (a,b) = (10,12) in a * 2

    " + + + "

    Or you can skip this section and go to straight to lesson6; types!

    " + }, + trigger: function(result) { + return result.type == "[Char]" && + result.expr.match(/^map[ ]+toUpper/); + } + }, + { + lesson: 5, + title: 'Pattern Matching', + guide: function(result) { + var title = + rmsg(["And therefore, patterns emerge in nature.", + "And Then Patterns", + "Pattern matching!"]) + return "

    " + title + "

    " + + + "

    Jolly good show!

    " + + "

    So you had a value (10,12) and matched " + + "it against a pattern (a,b), then you were able" + + " to do stuff with the a and b!" + + + "

    Note: Pattern matching (a,b) against " + + "(1,2) to get the a is the same as" + + " doing fst (1,2), like you did in step7!

    " + + + "

    A pattern always matches the way the " + + "value was originally constructed. Remember that \"abc\" is " + + "syntactic sugar for 'a' : 'b' : 'c' : [].

    " + + + "

    So you can get the characters from a string with patterns:

    " + + + "let (a:b:c:[]) = \"xyz\" in a" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+\(a,b\)[ ]+=[ ]+\(10,12\)[ ]+in[ ]+a[ ]*\*[ ]*2[ ]*$/) && + result.type == "(Num t) => t"; + } + }, + { + guide: function(result) { + return "

    " + rmsg(["Ignorance is bliss", "Ignoring values"]) + "

    " + + + "

    You're getting into tricky syntax, huh? I know you can handle it!

    " + + + "

    If you just want some of the values, you can ignore the others with _ (underscore) like this:

    " + + + "

    let (a:_:_:_) = \"xyz\" in a

    " + + + "

    In fact, (a:b:c:d) is short-hand for " + + "(a:(b:(c:d))), so you can just ignore the rest in one go:

    " + + + "let (a:_) = \"xyz\" in a" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]+\(a:b:c:\[\]\)[ ]*=[ ]*\"xyz\"[ ]*in[ ]+a[ ]*$/) && + result.type == "Char"; + } + }, + { + guide: function(result) { + return "

    " + rmsg(["Exercise!", "Show me the money!"]) + "

    " + + + "

    Try to get the 'a' value from this value using pattern matching:

    " + + "

    (10,\"abc\")

    " + + + "

    Spoiler: let (_,(a:_)) = (10,\"abc\") in a

    " + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]*\(a:_\)[ ]*=[ ]*"xyz"[ ]*in[ ]*a[ ]*$/) && + result.type == "Char"; + } + }, + { + guide: function(result) { + return "

    " + rmsg(["Well done!", "Brilliant!", "Perfetto!"]) + "

    " + + + "

    Wizard! I think you've got pattern-matching down.

    " + + + "

    If you're still a bit unsure, here are some other things you can try:

    " + + + "
      " + + "
    • let _:_:c:_ = \"abcd\" in c
    • " + + "
    • let [a,b,c] = \"cat\" in (a,b,c)
    • " + + "
    " + + + "

    You can also grab a whole value and pattern match on it (have your cake and eat it too):

    " + + + "let abc@(a,b,c) = (10,20,30) in (abc,a,b,c)" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*let[ ]*\(_,\(?a:_\)?\)[ ]*=[ ]*\(10,\"abc\"\)[ ]*in[ ]*a[ ]*$/) && + result.type == "Char"; + } + }, + { + guide: function(result) { + return "

    " + rmsg(["And that's the end of that chapter"]) + "

    " + + + "

    That was easy, right?

    " + + + "

    Let's go over what you've learned in this lesson:

    " + + + "
      " + + "
    1. Values are pattern matched, or deconstructed, by writing however they were constructed.
    2. " + + "
    3. Patterns let you use the values that you match.
    4. " + + "
    5. You can ignore whichever values you want.
    6. " + + "
    7. You can pattern match and keep hold of the original value too.
    8. " + + "
    " + + + "

    Now we get to the Deep Ones. Types!

    " + + + "

    Consider the following value: 'a'

    " + + }, + trigger: function(result) { + return result.type == "(Num t, Num t1, Num t2) => ((t, t1, t2), t, t1, t2)"; + } + }, + { + lesson: 6, + title: 'Types', + guide: function(result) { + showTypes = true; + return "

    " + rmsg(["Types", "What's in a Type?", "Types & Values"]) + "

    " + + "

    What's this? Something new!

    " + + + "

    In Haskell there are types of values. Every value belongs to a type. To demonstrate this fact, I've sneakily enabled types to be " + + "shown of every value in the console from now on.

    " + + + "

    The type of the value 'a' is Char (short for 'character', but you guessed that, right?).

    " + + + "

    You've seen the type of a character, now what about" + + " a list of characters?

    " + + "\"Spartacus\"" + }, + trigger: function(result) { + return result.type == 'Char'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

    " + rmsg(["Lists of stuff, types"]) + "

    " + + + "

    I'm Spartacus!

    " + + + "

    Okay, so a list of characters has type [Char].

    " + + + "

    Notice that when we write a :: X it means the value a has type X. It's just a short-hand called a signature.

    " + + + "

    If you just want the type of a value, without actually evaluating it, you can just type:

    " + + ":t toUpper" + }, + trigger: function(result) { + return result.expr.match(/"[^"]+"/) && + result.type == '[Char]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

    " + rmsg(["Function types"]) + "

    " + + + "

    Woah! Hold your blinkin' 'orses! The type of toUpper reads: Char -> Char

    " + + + "

    It's pretty easy; a -> b means function from a to b. " + + "So

    toUpper :: Char -> Char means: for a" + + " given character (Char value) a, toUpper a has type Char.

    " + + + "

    Some other things you can try are:

    " + + + "
    • :t words
    • " + + "
    • :t unwords
    • " + + "
    • :t True
    • " + + "
    • :t not
    • " + + "
    " + + + "

    The words function is pretty handy. Want to get a list of words from a sentence?

    " + + "words \"There's jam in my pants.\"" + }, + trigger: function(result) { + return result.type == 'Char -> Char'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

    " + rmsg(["Mid-way review"]) + "

    " + + + "

    The type of words was String -> [String]. You got a list of strings back! Just what you expected, right?

    " + + + "

    Let's take a rest in the middle of this lesson and go over what we've learned:

    " + + + "
      " + + "
    1. All values in Haskell have a type. We describe the types of values with signatures, like True :: Bool.
    2. " + + "
    3. Functions are values too, and they have types, notated a -> b.
    4. " + + "
    5. Functions can be defined for any type to any other type.
    6. " + + "
    7. Humble reader has a thing for jammy pants.
    8. " + + "
    " + + + "

    But what if you have a type that can contain values of any type, like a tuple?

    " + + ":t fst" + + }, + trigger: function(result) { + return result.expr.match(/^[ ]*words[ ]*\"[^"]+\"[ ]*$/) && + result.type == '[String]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

    " + rmsg(["Polymorphic functions"]) + "

    " + + + "

    Remember this one? I know you do! fst (1,2) is 1, right?

    " + + "

    We read its type

    " + + "

    fst :: (a, b) -> a

    " + + "

    as: for all types a and b, the fst has type (a,b) to a. So the fst " + + "function works on a pair of values of any types! We call such a function polymorphic." + + "

    " + + "

    Remember the drop function? Maybe you don't. I don't! Let's check out its type:

    " + + "

    :t drop

    " + }, + trigger: function(result) { + return result.type == '(a, b) -> a'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

    " + rmsg(["Multi parameter functions"]) + "

    " + + + "

    So the drop function has type

    Int -> [a] -> [a].

    " + + + "

    This is something new. You've got two arrows! Relax. You can read

    " + + "

    a -> b -> c as a -> (b -> c)

    " + + + "

    In other words, drop is a function from integers (Int values) to functions of lists to lists ([a] -> [a] values). Drop is a function to another function.

    " + + + "

    Check for yourself! :t drop 3

    " + }, + trigger: function(result) { + return result.type == 'Int -> [a] -> [a]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

    " + rmsg(["Partial application"]) + "

    " + + + "

    You've got a function of type [a] -> [a]! The drop function is considered a multi-parameter function. Remember the map function? Its parameters were a function and a list. Just another multi-parameter function.

    " + + + "

    You can add another parameter and, hey presto, you get a list!

    " + + + "drop 3 \"hello!\"" + + }, + trigger: function(result) { + return result.type == '[a] -> [a]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

    " + rmsg(["Higher order functions"]) + "

    " + + + "

    'Lo bob! You've already used the map function loads. I wonder if you can guess its type?

    " + + + "

    map :: (a -> b) -> [a] -> [b] (spoiler)

    " + + + "

    It's okay to peek! Have a go at guessing these: filter, take

    " + + + "

    Tip: You can use parantheses to use more than one function. You want to double all the numbers over five? Psch!

    " + + "map (*2) (filter (>5) [10,2,16,9,4])" + }, + trigger: function(result) { + return result.expr.match(/^[ ]*drop[ ]*[0-9]+[ ]*"[^"]+"[ ]*$/) && + result.type == '[Char]'; + } + }, + { + guide: function(result) { + showTypes = true; + return "

    " + rmsg(["Phew! Rest time!"]) + "

    " + + + "

    Wow! You're doing so great! Have a look at what you know now!

    " + + + "
      " + + "
    1. Function parameters can be polymorphic; any type!
    2. " + + "
    3. Functions can have multiple parameters by returning more functions.
    4. " + + "
    5. You can wrap expressions in parentheses and apply functions to them as a whole value.
    6. " + + "
    " + + + "

    You're really making great progress. Don't hesitate to sit and play in the console between chapters to get a good feel of it!

    " + + + "

    Stay tuned for more chapters on type classes and the meaning of :t 1, :t (*), etc.

    " + + learnMore + }, + trigger: function(result) { + return result.type == '(Num a, Ord a) => [a]'; + } + } + ]; + // + // var webchat; + // + // function runWebchat() { + // if (!webchat) { + // // Create webchat frame + // var webchat = + // $(''); + // webchat.attr('width', 635); + // webchat.attr('height', 500); + // webchat.css('float', 'left'); + // webchat.css('webkit-border-radius', '3px'); + // webchat.css('moz-border-radius', '3px'); + // webchat.css('border-radius', '3px'); + // webchat.css('border', '5px solid #eeeeee'); + // + // // Extend page wrap to fit console and chat + // $('.page-wrap').css({ + // width: '1250px' + // }); + // $('.primary-content').css('margin-left', 0); + // $('.page-wrap').append(webchat.css('margin-left', '5px')); + // } + // } + + var pageTrigger = -1; + var notices = []; + var controller; + // Console controller + var learnMore; + + //////////////////////////////////////////////////////////////////////// + // Unshow a string + function unString(str) { + return str.replace(/^"(.*)"$/, "$1").replace(/\\"/, '"'); + } + + //////////////////////////////////////////////////////////////////////// + // Random message from a list of messages + function rmsg(choices) { + return choices[Math.floor((Math.random() * 100) % choices.length)]; + } + + // Simple HTML encoding + // Simply replace '<', '>' and '&' + // TODO: Use jQuery's .html() trick, or grab a proper, fast + // HTML encoder. + function htmlEncode(text, shy) { + return ( + ('' + text).replace(/&/g, '&') + .replace(/'); + handleJSON = function(r) { + script.remove(); + func(r); + }; + script.attr('src', url); + $('body').append(script); + } + + + var console = $('.console'); + controller = console.console({ + promptLabel: '> ', + continuedPromptLabel: ' .. ', /*not quiet right but good enough*/ + cancelHandle: function() { + controller.commandRef.ignore = true; + controller.finishCommand(); + controller.report(); + }, + + commandHandle: function(line, report) { + + controller.ajaxloader = $('

    Loading...

    '); + var commandRef = {}; + controller.currentLine = line; + controller.commandRef = commandRef; + controller.report = report; + if (tellAboutRet) tellAboutRet.fadeOut(function() { + $(this).remove(); + }); + + if (libTrigger(line, report)) return; + controller.inner.append(controller.ajaxloader); + controller.scrollToBottom(); + + jsonp("http://localhost:3030/tryruby/run/?cmd=" + encodeHex(line), + function(resp) { + if (commandRef.ignore) { + return; + } + controller.finishCommand(); + var result = resp; + if (pageTrigger > -1 && result.expr) { + triggerTutorialPage(pageTrigger, result); + } + if (result.type) { + if (pageTrigger == 24) showTypes = false; + handleSuccess(report, result); + } else if (result.error) { + report( + [{ + msg: result.error, + className: "jquery-console-message-error jquery-console-message-compile-error" + }] + ); + notice('compile-error', + "A compile-time error! " + + "It just means the expression wasn't quite right. " + + "Try again.", + 'prompt'); + } else if (result.exception) { + var err = limitsError(result.exception); + report( + [{ + msg: err, + className: "jquery-console-message-error jquery-console-message-exception" + }] + ); + if (err == result.exception) { + notice('compile-error', + "A run-time error! The expression was right but the" + + " result didn't make sense. Check your expression and try again.", + 'prompt'); + } + } else if (result.internal) { + report( + [{ + msg: limitsError(result.internal), + className: "jquery-console-message-error jquery-console-message-internal" + }] + ); + } else if (result.bind) { + report(); + } else if (result.result) { + if (result.expr.match(/^:modules/)) { + report( + [{ + msg: result.result.replace(/[\["\]]/g, '') + .replace(/,/g, ', '), + className: "jquery-console-message-type" + }]); + } + } + }); + + }, + + charInsertTrigger: function() { + var t = notice('tellaboutreturn', + "Hit Return when you're " + + "finished typing your expression."); + if (t) tellAboutRet = t; + return true; + }, + autofocus: true, + promptHistory: true, + historyPreserveColumn: true, + welcomeMessage: 'Interactive ruby ready' + }); + + controller.finishCommand = function() { + controller.ajaxloader.remove(); + $('.jquery-console-prompt :last').each(function() { + lastLine = controller.currentLine; + if (!$(this).hasClass('prompt-done')) { + $(this).addClass('prompt-done'); + $(this).click(function() { + controller.promptText(controller.currentLine); + }); + } + }); + } + + makeGuidSamplesClickable(); + + var match = window.location.href.match(/#([0-9]+)$/); + if (match) { + pageTrigger = match[1] - 1; + setTutorialPage(undefined, match[1] - 1); + } + + var match = window.location.href.match(/\?input=([^&]+)/); + if (match) { + controller.promptText(urlDecode(match[1])); + controller.inner.click(); + controller.typer.consoleControl(13); + } + }); + + function urlDecode(encodedString) { + var output = encodedString; + var binVal, + thisString; + var myregexp = /(%[^%]{2})/; + while ((match = myregexp.exec(output)) != null + && match.length > 1 + && match[1] != '') { + binVal = parseInt(match[1].substr(1), 16); + thisString = String.fromCharCode(binVal); + output = output.replace(match[1], thisString); + } + return output; + } + + function makeGuidSamplesClickable() { + $('.chapmark code').each(function() { + $(this).css('cursor', 'pointer'); + $(this).attr('title', 'Click me to insert "' + + $(this).text() + '" into the console.'); + $(this).click(function() { + controller.promptText($(this).text()); + controller.inner.click(); + }); + }); + } + + String.prototype.trim = function() { + return this.replace(/^[\t ]*(.*)[\t ]*$/, '$1'); + }; + + //////////////////////////////////////////////////////////////////////// + // Trigger console commands + function libTrigger(line, report) { + switch (line.trim()) { + case 'help': + { + setTutorialPage(undefined, 0); + report(); + pageTrigger = 0; + return true; + } + case 'back': + { + if (pageTrigger > 0) { + setTutorialPage(undefined, pageTrigger - 1); + pageTrigger--; + report(); + return true; + } + break; + } + case 'lessons': + { + var lessons = $('
      '); + for (var i = 0; i < pages.length; i++) { + if (pages[i].lesson) { + lessons.append($('
    1. '). + html('lesson' + pages[i].lesson + ' - ' + + pages[i].title)); + } + } + var lessonsList = '

      Lessons

      ' + lessons.html(); + tutorialGuide.animate({ + opacity: 0, + height: 0 + }, + 'fast', + function() { + tutorialGuide.html(lessonsList); + tutorialGuide.css({ + height: 'auto' + }); + tutorialGuide.animate({ + opacity: 1 + }, + 'fast'); + makeGuidSamplesClickable(); + }); + report(); + return true; + } + default: + { + if (line.trim() == 'chat') { + notice('irc', + 'Enter your nick on the right hand side and hit Connect!', + 'prompt'); + report(); + runWebchat(); + return true; + } + + var m = line.trim().match(/^link(.*)/); + if (m) { + var data; + if (m[1]) data = m[1].trim(); + else if (lastLine) data = lastLine; + if (data) { + var addr = '?input=' + encodeHex(data); + report([{ + msg: '', + className: 'latest-link' + }]); + var link = $(''). + text('link for ' + data).click(function() { + window.location.href = $(this).attr('href'); + return false; + }); + $('.latest-link').html(link).removeClass('latest-link'); + return true; + } + } + + var m = line.trim().match(/^step([0-9]+)/); + if (m) { + if ((m[1] * 1) <= pages.length) { + setTutorialPage(undefined, m[1] - 1); + report(); + pageTrigger = m[1] - 1; + return true; + } + } + var m = line.trim().match(/^help ([0-9]+)/); + if (m) { + for (var i = 0; i < pages.length; i++) { + if (pages[i].lesson == m[1] * 1) { + setTutorialPage(undefined, i); + report(); + pageTrigger = i; + return true; + } + } + } + } + }; + }; + + //////////////////////////////////////////////////////////////////////// + // Change the tutorial page + function setTutorialPage(result, n) { + if (pages[n]) { + window.location.href = '#' + (1 * n + 1); + tutorialGuide.find('#helpstone').remove(); + tutorialGuide.animate({ + opacity: 0, + height: 0 + }, + 'fast', + function() { + if (typeof(pages[n].guide) == 'function') + tutorialGuide.html(pages[n].guide(result)); + else + tutorialGuide.html(pages[n].guide); + var back = ''; + if (pageTrigger > 0) + back = 'You\'re at step' + (n + 1) + + '. Type back to go back.'; + else + back = 'You\'re at step' + (n + 1) + '. Type step' + (n + 1) + + ' to return here.'; + if (true) tutorialGuide + .append('
      ' + back + '
      ') + .append('
      Lesson: ' + + searchLessonBack(n) + + '
      '); + tutorialGuide.css({ + height: 'auto' + }); + tutorialGuide.animate({ + opacity: 1 + }, + 'fast'); + makeGuidSamplesClickable(); + }); + } + }; + + function searchLessonBack(page) { + for (var i = page; i >= 0; i--) { + if (pages[i].lesson) return pages[i].lesson; + } + return "1"; + } + + //////////////////////////////////////////////////////////////////////// + // Trigger a page according to a result + function triggerTutorialPage(n, result) { + n++; + if (pages[n] && (typeof(pages[n].trigger) == 'function') + && pages[n].trigger(result)) { + pageTrigger++; + setTutorialPage(result, n); + } + }; + + //////////////////////////////////////////////////////////////////////// + // Trigger various libraries after JSONRPC returned + function handleSuccess(report, result) { + if (result.type.match(/^Graphics\.Raphael\.Raphael[\r\n ]/)) { + runRaphael(result.result); + report(); + } + if (result.type.match(/standard/)) { + var hashrocket = ''; + var msg_value = ' => '; + if (result.result == null) { + hashrocket = ' => ' + msg_value = ''; + } + report( + [{ + msg: msg_value + result.output + hashrocket + result.result, + className: "jquery-console-message-value" + }]); + } + + if (result.type.match(/error/)) { + + report( + [{ + msg: result.error, + className: "jquery-console-message-value" + }]); + } + + if (result.type.match(/illegal/)) { + + report( + [{ + msg: 'You are not allowed to run that command!', + className: "jquery-console-message-value" + }]); + } + + + if (result.type.match(/line_continuation/)) { + report( + [{ + msg: '..', + className: "jquery-console-prompt-label" + }]); + } + + }; + + //////////////////////////////////////////////////////////////////////// + // Raphael support + function runRaphael(expr) { + raphaelPaper.clear(); + $('#raphael').parent().parent().slideDown(function() { + var exprs = expr.split(/\n/g); + for (var i = 0; i < exprs.length; i++) + raphaelRunExpr(exprs[i]); + }); + } + function raphaelRunExpr(expr) { + var expr = expr.split(/ /g); + switch (expr[0]) { + case 'new': + { + switch (expr[2]) { + case 'circle': + { + var x = expr[3], + y = expr[4], + radius = expr[5]; + var circle = raphaelPaper.circle(x * 1, y * 1, radius * 1); + circle.attr("fill", "#7360a4"); + break; + } + } + } + } + } + + function notice(name, msg, style) { + if (opera()) return; + if (!notices[name]) { + notices[name] = name; + return controller.notice(msg, style); + } + } + + function limitsError(str) { + if (str == "Terminated!") { + notice('terminated', + "This error means it took to long to work" + + " out on the server.", + 'fadeout'); + return "Terminated!"; + } else if (str == "Time limit exceeded.") { + notice('exceeded', + "This error means it took to long to work out on the server. " + + "Try again.", + 'fadeout'); + return "Terminated! Try again."; + } + return str; + } + +})(jQuery); diff --git a/public/javascripts/jquery-1.3.2.min.js b/public/javascripts/jquery-1.3.2.min.js new file mode 100644 index 0000000..b1ae21d --- /dev/null +++ b/public/javascripts/jquery-1.3.2.min.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
      "]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
      ","
      "]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

      ";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
      ";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
      ").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
      ';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/public/javascripts/jquery-1.4.2.min.js b/public/javascripts/jquery-1.4.2.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/public/javascripts/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
      a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

      ";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
      ";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
      ","
      "];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
      ").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
      "; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/public/javascripts/jquery.console.js b/public/javascripts/jquery.console.js new file mode 100644 index 0000000..d45390d --- /dev/null +++ b/public/javascripts/jquery.console.js @@ -0,0 +1,609 @@ +// JQuery Console 1.0 +// Sun Feb 21 20:28:47 GMT 2010 +// +// Copyright 2010 Chris Done, Simon David Pratt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above +// copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// TESTED ON +// Internet Explorer 6 +// Opera 10.01 +// Chromium 4.0.237.0 (Ubuntu build 31094) +// Firefox 3.5.8, 3.6.2 (Mac) +// Safari 4.0.5 (6531.22.7) (Mac) +// Google Chrome 5.0.375.55 (Mac) + +(function($){ + $.fn.console = function(config){ + //////////////////////////////////////////////////////////////////////// + // Constants + // Some are enums, data types, others just for optimisation + var keyCodes = { + // left + 37: moveBackward, + // right + 39: moveForward, + // up + 38: previousHistory, + // down + 40: nextHistory, + // backspace + 8: backDelete, + // delete + 46: forwardDelete, + // end + 35: moveToEnd, + // start + 36: moveToStart, + // return + 13: commandTrigger, + // tab + 18: doNothing + }; + var ctrlCodes = { + // C-a + 65: moveToStart, + // C-e + 69: moveToEnd, + // C-d + 68: forwardDelete, + // C-n + 78: nextHistory, + // C-p + 80: previousHistory, + // C-b + 66: moveBackward, + // C-f + 70: moveForward, + // C-k + 75: deleteUntilEnd + }; + var altCodes = { + // M-f + 70: moveToNextWord, + // M-b + 66: moveToPreviousWord, + // M-d + 68: deleteNextWord + }; + var cursor = ' '; + // Opera only works with this character, not or ­, + // but IE6 displays this character, which is bad, so just use + // it on Opera. + var wbr = $.browser.opera? '​' : ''; + + //////////////////////////////////////////////////////////////////////// + // Globals + var container = $(this); + var inner = $('
      '); + var typer = $(''); + // Prompt + var promptBox; + var prompt; + var promptLabel = config && config.promptLabel? config.promptLabel : "> "; + var column = 0; + var promptText = ''; + var restoreText = ''; + // Prompt history stack + var history = []; + var ringn = 0; + // For reasons unknown to The Sword of Michael himself, Opera + // triggers and sends a key character when you hit various + // keys like PgUp, End, etc. So there is no way of knowing + // when a user has typed '#' or End. My solution is in the + // typer.keydown and typer.keypress functions; I use the + // variable below to ignore the keypress event if the keydown + // event succeeds. + var cancelKeyPress = 0; + // When this value is false, the prompt will not respond to input + var acceptInput = true; + // When this value is true, the command has been canceled + var cancelCommand = false; + + // External exports object + var extern = {}; + + //////////////////////////////////////////////////////////////////////// + // Main entry point + (function(){ + container.append(inner); + inner.append(typer); + typer.css({position:'absolute',top:0,left:'-9999px'}); + if (config.welcomeMessage) + message(config.welcomeMessage,'jquery-console-welcome'); + newPromptBox(); + if (config.autofocus) { + inner.addClass('jquery-console-focus'); + typer.focus(); + setTimeout(function(){ + inner.addClass('jquery-console-focus'); + typer.focus(); + },100); + } + extern.inner = inner; + extern.typer = typer; + extern.scrollToBottom = scrollToBottom; + })(); + + //////////////////////////////////////////////////////////////////////// + // Reset terminal + extern.reset = function(){ + var welcome = true; + inner.parent().fadeOut(function(){ + inner.find('div').each(function(){ + if (!welcome) + $(this).remove(); + welcome = false; + }); + newPromptBox(); + inner.parent().fadeIn(function(){ + inner.addClass('jquery-console-focus'); + typer.focus(); + }); + }); + }; + + //////////////////////////////////////////////////////////////////////// + // Reset terminal + extern.notice = function(msg,style){ + var n = $('
      ').append($('
      ').text(msg)) + .css({visibility:'hidden'}); + container.append(n); + var focused = true; + if (style=='fadeout') + setTimeout(function(){ + n.fadeOut(function(){ + n.remove(); + }); + },4000); + else if (style=='prompt') { + var a = $('
      '); + n.append(a); + focused = false; + a.click(function(){ n.fadeOut(function(){ n.remove();inner.css({opacity:1}) }); }); + } + var h = n.height(); + n.css({height:'0px',visibility:'visible'}) + .animate({height:h+'px'},function(){ + if (!focused) inner.css({opacity:0.5}); + }); + n.css('cursor','default'); + return n; + }; + + //////////////////////////////////////////////////////////////////////// + // Make a new prompt box + function newPromptBox() { + column = 0; + promptText = ''; + ringn = 0; // Reset the position of the history ring + enableInput(); + promptBox = $('
      '); + var label = $(''); + promptBox.append(label.text(promptLabel).show()); + prompt = $(''); + promptBox.append(prompt); + inner.append(promptBox); + updatePromptDisplay(); + }; + + //////////////////////////////////////////////////////////////////////// + // Handle setting focus + container.click(function(){ + inner.addClass('jquery-console-focus'); + inner.removeClass('jquery-console-nofocus'); + typer.focus(); + scrollToBottom(); + return false; + }); + + //////////////////////////////////////////////////////////////////////// + // Handle losing focus + typer.blur(function(){ + inner.removeClass('jquery-console-focus'); + inner.addClass('jquery-console-nofocus'); + }); + + //////////////////////////////////////////////////////////////////////// + // Handle key hit before translation + // For picking up control characters like up/left/down/right + + typer.keydown(function(e){ + cancelKeyPress = 0; + var keyCode = e.keyCode; + // C-c: cancel the execution + if(e.ctrlKey && keyCode == 67) { + cancelKeyPress = keyCode; + cancelExecution(); + return false; + } + if (acceptInput) { + if (keyCode in keyCodes) { + cancelKeyPress = keyCode; + (keyCodes[keyCode])(); + return false; + } else if (e.ctrlKey && keyCode in ctrlCodes) { + cancelKeyPress = keyCode; + (ctrlCodes[keyCode])(); + return false; + } else if (e.altKey && keyCode in altCodes) { + cancelKeyPress = keyCode; + (altCodes[keyCode])(); + return false; + } + } + }); + + //////////////////////////////////////////////////////////////////////// + // Handle key press + typer.keypress(function(e){ + var keyCode = e.keyCode || e.which; + if (isIgnorableKey(e)) { + return false; + } + if (acceptInput && cancelKeyPress != keyCode && keyCode >= 32){ + if (cancelKeyPress) return false; + if (typeof config.charInsertTrigger == 'undefined' || + (typeof config.charInsertTrigger == 'function' && + config.charInsertTrigger(keyCode,promptText))) + typer.consoleInsert(keyCode); + } + if ($.browser.webkit) return false; + }); + + function isIgnorableKey(e) { + // for now just filter alt+tab that we receive on some platforms when + // user switches windows (goes away from the browser) + return ((e.keyCode == keyCodes.tab || e.keyCode == 192) && e.altKey); + }; + + //////////////////////////////////////////////////////////////////////// + // Rotate through the command history + function rotateHistory(n){ + if (history.length == 0) return; + ringn += n; + if (ringn < 0) ringn = history.length; + else if (ringn > history.length) ringn = 0; + var prevText = promptText; + if (ringn == 0) { + promptText = restoreText; + } else { + promptText = history[ringn - 1]; + } + if (config.historyPreserveColumn) { + if (promptText.length < column + 1) { + column = promptText.length; + } else if (column == 0) { + column = promptText.length; + } + } else if (config.historyColumnAtEnd) { + column = promptText.length; + } else { + column = 0; + } + updatePromptDisplay(); + }; + + function previousHistory() { + rotateHistory(-1); + }; + + function nextHistory() { + rotateHistory(1); + }; + + // Add something to the history ring + function addToHistory(line){ + history.push(line); + restoreText = ''; + }; + + // Delete the character at the current position + function deleteCharAtPos(){ + if (column < promptText.length){ + promptText = + promptText.substring(0,column) + + promptText.substring(column+1); + restoreText = promptText; + return true; + } else return false; + }; + + function backDelete() { + if (moveColumn(-1)){ + deleteCharAtPos(); + updatePromptDisplay(); + } + }; + + function forwardDelete() { + if (deleteCharAtPos()) + updatePromptDisplay(); + }; + + function deleteUntilEnd() { + while(deleteCharAtPos()) { + updatePromptDisplay(); + } + }; + + function deleteNextWord() { + // A word is defined within this context as a series of alphanumeric + // characters. + // Delete up to the next alphanumeric character + while(column < promptText.length && + !isCharAlphanumeric(promptText[column])) { + deleteCharAtPos(); + updatePromptDisplay(); + } + // Then, delete until the next non-alphanumeric character + while(column < promptText.length && + isCharAlphanumeric(promptText[column])) { + deleteCharAtPos(); + updatePromptDisplay(); + } + }; + + //////////////////////////////////////////////////////////////////////// + // Validate command and trigger it if valid, or show a validation error + function commandTrigger() { + var line = promptText; + if (typeof config.commandValidate == 'function') { + var ret = config.commandValidate(line); + if (ret == true || ret == false) { + if (ret) { + handleCommand(); + } + } else { + commandResult(ret,"jquery-console-message-error"); + } + } else { + handleCommand(); + } + }; + + // Scroll to the bottom of the view + function scrollToBottom() { + inner.attr({ scrollTop: inner.attr("scrollHeight") });; + }; + + function cancelExecution() { + if(typeof config.cancelHandle == 'function') { + config.cancelHandle(); + } + } + + //////////////////////////////////////////////////////////////////////// + // Handle a command + function handleCommand() { + if (typeof config.commandHandle == 'function') { + disableInput(); + addToHistory(promptText); + var ret = config.commandHandle(promptText,function(msgs){ + commandResult(msgs); + }); + if (typeof ret == 'boolean') { + if (ret) { + // Command succeeded without a result. + commandResult(); + } else { + commandResult('Command failed.', + "jquery-console-message-error"); + } + } else if (typeof ret == "string") { + commandResult(ret,"jquery-console-message-success"); + } else if (typeof ret == 'object' && ret.length) { + commandResult(ret); + } + } + }; + + //////////////////////////////////////////////////////////////////////// + // Disable input + function disableInput() { + acceptInput = false; + }; + + // Enable input + function enableInput() { + acceptInput = true; + } + + //////////////////////////////////////////////////////////////////////// + // Reset the prompt in invalid command + function commandResult(msg,className) { + column = -1; + updatePromptDisplay(); + if (typeof msg == 'string') { + message(msg,className); + } else { + for (var x in msg) { + var ret = msg[x]; + message(ret.msg,ret.className); + } + } + newPromptBox(); + }; + + //////////////////////////////////////////////////////////////////////// + // Display a message + function message(msg,className) { + var mesg = $('
      '); + if (className) mesg.addClass(className); + mesg.filledText(msg).hide(); + inner.append(mesg); + mesg.show(); + }; + + //////////////////////////////////////////////////////////////////////// + // Handle normal character insertion + typer.consoleInsert = function(keyCode){ + // TODO: remove redundant indirection + var char = String.fromCharCode(keyCode); + var before = promptText.substring(0,column); + var after = promptText.substring(column); + promptText = before + char + after; + moveColumn(1); + restoreText = promptText; + updatePromptDisplay(); + }; + + //////////////////////////////////////////////////////////////////////// + // Move to another column relative to this one + // Negative means go back, positive means go forward. + function moveColumn(n){ + if (column + n >= 0 && column + n <= promptText.length){ + column += n; + return true; + } else return false; + }; + + function moveForward() { + if(moveColumn(1)) { + updatePromptDisplay(); + return true; + } + return false; + }; + + function moveBackward() { + if(moveColumn(-1)) { + updatePromptDisplay(); + return true; + } + return false; + }; + + function moveToStart() { + if (moveColumn(-column)) + updatePromptDisplay(); + }; + + function moveToEnd() { + if (moveColumn(promptText.length-column)) + updatePromptDisplay(); + }; + + function moveToNextWord() { + while(column < promptText.length && + !isCharAlphanumeric(promptText[column]) && + moveForward()) { + } + while(column < promptText.length && + isCharAlphanumeric(promptText[column]) && + moveForward()) { + } + }; + + function moveToPreviousWord() { + // Move backward until we find the first alphanumeric + while(column -1 >= 0 && + !isCharAlphanumeric(promptText[column-1]) && + moveBackward()) { + } + // Move until we find the first non-alphanumeric + while(column -1 >= 0 && + isCharAlphanumeric(promptText[column-1]) && + moveBackward()) { + } + }; + + function isCharAlphanumeric(charToTest) { + if(typeof charToTest == 'string') { + var code = charToTest.charCodeAt(); + return (code >= 'A'.charCodeAt() && code <= 'Z'.charCodeAt()) || + (code >= 'a'.charCodeAt() && code <= 'z'.charCodeAt()) || + (code >= '0'.charCodeAt() && code <= '9'.charCodeAt()); + } + return false; + }; + + function doNothing() {}; + + extern.promptText = function(text){ + if (text) { + promptText = text; + if (column > promptText.length) + column = promptText.length; + updatePromptDisplay(); + } + return promptText; + }; + + //////////////////////////////////////////////////////////////////////// + // Update the prompt display + function updatePromptDisplay(){ + var line = promptText; + var html = ''; + if (column > 0 && line == ''){ + // When we have an empty line just display a cursor. + html = cursor; + } else if (column == promptText.length){ + // We're at the end of the line, so we need to display + // the text *and* cursor. + html = htmlEncode(line) + cursor; + } else { + // Grab the current character, if there is one, and + // make it the current cursor. + var before = line.substring(0, column); + var current = line.substring(column,column+1); + if (current){ + current = + '' + + htmlEncode(current) + + ''; + } + var after = line.substring(column+1); + html = htmlEncode(before) + current + htmlEncode(after); + } + prompt.html(html); + scrollToBottom(); + }; + + // Simple HTML encoding + // Simply replace '<', '>' and '&' + // TODO: Use jQuery's .html() trick, or grab a proper, fast + // HTML encoder. + function htmlEncode(text){ + return ( + text.replace(/&/g,'&') + .replace(/&]{10})/g,'$1­' + wbr) + ); + }; + + return extern; + }; + // Simple utility for printing messages + $.fn.filledText = function(txt){ + $(this).text(txt); + $(this).html($(this).html().replace(/\n/g,'
      ')); + return this; + }; +})(jQuery); diff --git a/public/javascripts/jquery.console.min.js b/public/javascripts/jquery.console.min.js new file mode 100644 index 0000000..7ae1e6b --- /dev/null +++ b/public/javascripts/jquery.console.min.js @@ -0,0 +1 @@ +(function($){$.fn.console=function(config){var keyCodes={left:37,right:39,up:38,down:40,back:8,del:46,end:35,start:36,ret:13};var cursor=' ';var wbr=$.browser.opera?'​':'';var container=$(this);var inner=$('
      ');var typer=$('');var promptBox;var prompt;var promptLabel=config&&config.promptLabel?config.promptLabel:"> ";var column=0;var promptText='';var restoreText='';var history=[];var ringn=0;var cancelKeyPress=0;var extern={};(function(){container.append(inner);inner.append(typer);typer.css({position:'absolute',top:0,left:'-999px'});if(config.welcomeMessage)message(config.welcomeMessage,'jquery-console-welcome');newPromptBox();if(config.autofocus){inner.addClass('jquery-console-focus');typer.focus();setTimeout(function(){inner.addClass('jquery-console-focus');typer.focus()},100)}})();extern.reset=function(){var welcome=true;inner.parent().fadeOut(function(){inner.find('div').each(function(){if(!welcome)$(this).remove();welcome=false});newPromptBox();inner.parent().fadeIn(function(){inner.addClass('jquery-console-focus');typer.focus()})})};function newPromptBox(){column=0;promptText='';promptBox=$('
      ');var label=$('');promptBox.append(label.text(promptLabel).show());prompt=$('');promptBox.append(prompt);inner.append(promptBox);updatePromptDisplay()};container.click(function(){inner.addClass('jquery-console-focus');inner.removeClass('jquery-console-nofocus');typer.focus();scrollToBottom();return false});typer.blur(function(){inner.removeClass('jquery-console-focus');inner.addClass('jquery-console-nofocus')});typer.keydown(function(e){cancelKeyPress=0;var keyCode=e.keyCode;if(isControlCharacter(keyCode)){cancelKeyPress=keyCode;if(!typer.consoleControl(keyCode)){return false}}});typer.keypress(function(e){var keyCode=e.keyCode||e.which;if(cancelKeyPress!=keyCode&&keyCode>=32){if(cancelKeyPress)return false;typer.consoleInsert(keyCode)}if($.browser.webkit)return false});function isControlCharacter(keyCode){return((keyCode>=keyCodes.left&&keyCode<=keyCodes.down)||keyCode==keyCodes.back||keyCode==keyCodes.del||keyCode==keyCodes.end||keyCode==keyCodes.start||keyCode==keyCodes.ret)};typer.consoleControl=function(keyCode){switch(keyCode){case keyCodes.left:{moveColumn(-1);updatePromptDisplay();return false;break}case keyCodes.right:{moveColumn(1);updatePromptDisplay();return false;break}case keyCodes.back:{if(moveColumn(-1)){deleteCharAtPos();updatePromptDisplay()}return false;break}case keyCodes.del:{if(deleteCharAtPos())updatePromptDisplay();return false;break}case keyCodes.end:{if(moveColumn(promptText.length-column))updatePromptDisplay();return false;break}case keyCodes.start:{if(moveColumn(-column))updatePromptDisplay();return false;break}case keyCodes.ret:{commandTrigger();return false}case keyCodes.up:{rotateHistory(-1);return false}case keyCodes.down:{rotateHistory(1);return false}default:}};function rotateHistory(n){if(history.length==0)return;ringn+=n;if(ringn<0)ringn=history.length;else if(ringn>history.length)ringn=0;var prevText=promptText;if(ringn==0){promptText=restoreText}else{promptText=history[ringn-1]}if(config.historyPreserveColumn){if(promptText.length
      ');if(className)mesg.addClass(className);mesg.filledText(msg).hide();inner.append(mesg);mesg.show()};typer.consoleInsert=function(keyCode){var char=String.fromCharCode(keyCode);var before=promptText.substring(0,column);var after=promptText.substring(column);promptText=before+char+after;moveColumn(1);restoreText=promptText;updatePromptDisplay()};function moveColumn(n){if(column+n>=0&&column+n<=promptText.length){column+=n;return true}else return false};function updatePromptDisplay(){var line=promptText;var html='';if(column>0&&line==''){html=cursor}else if(column==promptText.length){html=htmlEncode(line)+cursor}else{var before=line.substring(0,column);var current=line.substring(column,column+1);if(current){current=''+htmlEncode(current)+''}var after=line.substring(column+1);html=htmlEncode(before)+current+htmlEncode(after)}prompt.html(html);scrollToBottom()};function htmlEncode(text){return(text.replace(/&/g,'&').replace(/&]{10})/g,'$1­'+wbr))};return extern};$.fn.filledText=function(txt){$(this).text(txt);$(this).html($(this).html().replace(/\n/g,'
      '));return this}})(jQuery); \ No newline at end of file diff --git a/public/javascripts/jquery.js b/public/javascripts/jquery.js new file mode 100755 index 0000000..3747929 --- /dev/null +++ b/public/javascripts/jquery.js @@ -0,0 +1,32 @@ +/* + * jQuery 1.2.3 - New Wave Javascript + * + * Copyright (c) 2008 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2008-02-06 00:21:25 -0500 (Wed, 06 Feb 2008) $ + * $Rev: 4663 $ + */ +(function(){if(window.jQuery)var _jQuery=window.jQuery;var jQuery=window.jQuery=function(selector,context){return new jQuery.prototype.init(selector,context);};if(window.$)var _$=window.$;window.$=jQuery;var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;var isSimple=/^.[^:#\[\.]*$/;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}else if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem)if(elem.id!=match[3])return jQuery().find(selector);else{this[0]=elem;this.length=1;return this;}else +selector=[];}}else +return new jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return new jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(selector.constructor==Array&&selector||(selector.jquery||selector.length&&selector!=window&&!selector.nodeType&&selector[0]!=undefined&&selector[0].nodeType)&&jQuery.makeArray(selector)||[selector]);},jquery:"1.2.3",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;this.each(function(i){if(this==elem)ret=i;});return ret;},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value==undefined)return this.length&&jQuery[type||"attr"](this[0],name)||undefined;else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else +return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else +selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return!selector?this:this.pushStack(jQuery.merge(this.get(),selector.constructor==String?jQuery(selector).get():selector.length!=undefined&&(!selector.nodeName||jQuery.nodeName(selector,"form"))?selector:[selector]));},is:function(selector){return selector?jQuery.multiFilter(selector,this).length>0:false;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=value.constructor==Array?value:[value];jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else +this.value=value;});},html:function(value){return value==undefined?(this.length?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value==null){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data==undefined&&this.length)data=jQuery.data(this[0],key);return data==null&&parts[1]?this.data(parts[0]):data;}else +return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script")){scripts=scripts.add(elem);}else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.prototype.init.prototype=jQuery.prototype;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else +jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==1){target=this;i=0;}for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else +jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret;function color(elem){if(!jQuery.browser.safari)return false;var ret=document.defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(elem.style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=elem.style.outline;elem.style.outline="0 solid black";elem.style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&elem.style&&elem.style[name])ret=elem.style[name];else if(document.defaultView&&document.defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var getComputedStyle=document.defaultView.getComputedStyle(elem,null);if(getComputedStyle&&!color(elem))ret=getComputedStyle.getPropertyValue(name);else{var swap=[],stack=[];for(var a=elem;a&&color(a);a=a.parentNode)stack.unshift(a);for(var i=0;i]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
      "]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||jQuery.browser.msie&&[1,"div
      ","
      "]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf(""&&tags.indexOf("=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else +ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var fix=jQuery.isXMLDoc(elem)?{}:jQuery.props;if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(fix[name]){if(value!=undefined)elem[fix[name]]=value;return elem[fix[name]];}else if(jQuery.browser.msie&&name=="style")return jQuery.attr(elem.style,"cssText",value);else if(value==undefined&&jQuery.browser.msie&&jQuery.nodeName(elem,"form")&&(name=="action"||name=="method"))return elem.getAttributeNode(name).nodeValue;else if(elem.tagName){if(value!=undefined){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem.setAttribute(name,""+value);}if(jQuery.browser.msie&&/href|src/.test(name)&&!jQuery.isXMLDoc(elem))return elem.getAttribute(name,2);return elem.getAttribute(name);}else{if(name=="opacity"&&jQuery.browser.msie){if(value!=undefined){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseFloat(value).toString()=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100).toString():"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(value!=undefined)elem[name]=value;return elem[name];}},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(typeof array!="array")for(var i=0,length=array.length;i*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return im[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false;var re=quickChild;var m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[];var cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&(!elem||n!=elem))r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval!=undefined)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=function(){return fn.apply(this,arguments);};handler.data=data;handler.guid=fn.guid;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){var val;if(typeof jQuery=="undefined"||jQuery.event.triggered)return val;val=jQuery.event.handle.apply(arguments.callee.elem,arguments);return val;});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else +for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data||[]);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event)data.unshift(this.fix({type:type,target:elem}));data[0].type=type;if(exclusive)data[0].exclusive=true;if(jQuery.isFunction(jQuery.data(elem,"handle")))val=jQuery.data(elem,"handle").apply(elem,data);if(!fn&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val;event=jQuery.event.fix(event||window.event||{});var parts=event.type.split(".");event.type=parts[0];var handlers=jQuery.data(this,"events")&&jQuery.data(this,"events")[event.type],args=Array.prototype.slice.call(arguments,1);args.unshift(event);for(var j in handlers){var handler=handlers[j];args[0].handler=handler;args[0].data=handler.data;if(!parts[1]&&!event.exclusive||handler.type==parts[1]){var ret=handler.apply(this,args);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}if(jQuery.browser.msie)event.target=event.preventDefault=event.stopPropagation=event.handler=event.data=null;return val;},fix:function(event){var originalEvent=event;event=jQuery.extend({},originalEvent);event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=originalEvent.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;arguments[0].type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;arguments[0].type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){return this.each(function(){jQuery.event.add(this,type,function(event){jQuery(this).unbind(event);return(fn||data).apply(this,arguments);},fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){if(this[0])return jQuery.event.trigger(type,data,this[0],false,fn);return undefined;},toggle:function(){var args=arguments;return this.click(function(event){this.lastToggle=0==this.lastToggle?1:0;event.preventDefault();return args[this.lastToggle].apply(this,arguments)||false;});},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else +jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.apply(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("
      ").append(res.responseText.replace(//g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=(new Date).getTime();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){var jsonp,jsre=/=\?(&|$)/g,status,data;s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(s.type.toLowerCase()=="get"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&s.type.toLowerCase()=="get"){var ts=(new Date()).getTime();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&s.type.toLowerCase()=="get"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");if((!s.url.indexOf("http")||!s.url.indexOf("//"))&&s.dataType=="script"&&s.type.toLowerCase()=="get"){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xml=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();xml.open(s.type,s.url,s.async,s.username,s.password);try{if(s.data)xml.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xml.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xml.setRequestHeader("X-Requested-With","XMLHttpRequest");xml.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend)s.beforeSend(xml);if(s.global)jQuery.event.trigger("ajaxSend",[xml,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xml&&(xml.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xml)&&"error"||s.ifModified&&jQuery.httpNotModified(xml,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xml,s.dataType);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xml.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else +jQuery.handleError(s,xml,status);complete();if(s.async)xml=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xml){xml.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xml.send(s.data);}catch(e){jQuery.handleError(s,xml,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xml,s]);}function complete(){if(s.complete)s.complete(xml,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xml,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xml;},handleError:function(s,xml,status,e){if(s.error)s.error(xml,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xml,s,e]);},active:0,httpSuccess:function(r){try{return!r.status&&location.protocol=="file:"||(r.status>=200&&r.status<300)||r.status==304||r.status==1223||jQuery.browser.safari&&r.status==undefined;}catch(e){}return false;},httpNotModified:function(xml,url){try{var xmlRes=xml.getResponseHeader("Last-Modified");return xml.status==304||xmlRes==jQuery.lastModified[url]||jQuery.browser.safari&&xml.status==undefined;}catch(e){}return false;},httpData:function(r,type){var ct=r.getResponseHeader("content-type");var xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0;var data=xml?r.responseXML:r.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else +for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else +s.push(encodeURIComponent(j)+"="+encodeURIComponent(a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle(fn,fn2):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall);var hidden=jQuery(this).is(":hidden"),self=this;for(var p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return jQuery.isFunction(opt.complete)&&opt.complete.apply(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else +e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.apply(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(!elem)return undefined;type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",array?jQuery.makeArray(array):[]);return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].apply(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:{slow:600,fast:200}[opt.duration])||400;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.apply(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.apply(this.elem,[this.now,this]);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=(new Date()).getTime();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;ithis.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done&&jQuery.isFunction(this.options.complete))this.options.complete.apply(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.fx.step={scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}};jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),fixed=jQuery.css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&jQuery.css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(jQuery.css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&jQuery.css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||jQuery.css(offsetChild,"position")=="absolute"))||(mozilla&&jQuery.css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l)||0;top+=parseInt(t)||0;}return results;};})(); \ No newline at end of file diff --git a/public/javascripts/json2.js b/public/javascripts/json2.js new file mode 100644 index 0000000..d4f4541 --- /dev/null +++ b/public/javascripts/json2.js @@ -0,0 +1 @@ +if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?'0'+n:n}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key)}if(typeof rep==='function'){value=rep.call(holder,key,value)}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null'}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
      a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

      ";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
      ";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
      ","
      "];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
      ").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
      "; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); +/*json2.js*/ +if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?'0'+n:n}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key)}if(typeof rep==='function'){value=rep.call(holder,key,value)}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null'}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i
      ');var typer=$('');var promptBox;var prompt;var promptLabel=config&&config.promptLabel?config.promptLabel:"> ";var column=0;var promptText='';var restoreText='';var history=[];var ringn=0;var cancelKeyPress=0;var extern={};(function(){container.append(inner);inner.append(typer);typer.css({position:'absolute',top:0,left:'-999px'});if(config.welcomeMessage)message(config.welcomeMessage,'jquery-console-welcome');newPromptBox();if(config.autofocus){inner.addClass('jquery-console-focus');typer.focus();setTimeout(function(){inner.addClass('jquery-console-focus');typer.focus()},100)}})();extern.reset=function(){var welcome=true;inner.parent().fadeOut(function(){inner.find('div').each(function(){if(!welcome)$(this).remove();welcome=false});newPromptBox();inner.parent().fadeIn(function(){inner.addClass('jquery-console-focus');typer.focus()})})};function newPromptBox(){column=0;promptText='';promptBox=$('
      ');var label=$('');promptBox.append(label.text(promptLabel).show());prompt=$('');promptBox.append(prompt);inner.append(promptBox);updatePromptDisplay()};container.click(function(){inner.addClass('jquery-console-focus');inner.removeClass('jquery-console-nofocus');typer.focus();scrollToBottom();return false});typer.blur(function(){inner.removeClass('jquery-console-focus');inner.addClass('jquery-console-nofocus')});typer.keydown(function(e){cancelKeyPress=0;var keyCode=e.keyCode;if(isControlCharacter(keyCode)){cancelKeyPress=keyCode;if(!typer.consoleControl(keyCode)){return false}}});typer.keypress(function(e){var keyCode=e.keyCode||e.which;if(cancelKeyPress!=keyCode&&keyCode>=32){if(cancelKeyPress)return false;typer.consoleInsert(keyCode)}if($.browser.webkit)return false});function isControlCharacter(keyCode){return((keyCode>=keyCodes.left&&keyCode<=keyCodes.down)||keyCode==keyCodes.back||keyCode==keyCodes.del||keyCode==keyCodes.end||keyCode==keyCodes.start||keyCode==keyCodes.ret)};typer.consoleControl=function(keyCode){switch(keyCode){case keyCodes.left:{moveColumn(-1);updatePromptDisplay();return false;break}case keyCodes.right:{moveColumn(1);updatePromptDisplay();return false;break}case keyCodes.back:{if(moveColumn(-1)){deleteCharAtPos();updatePromptDisplay()}return false;break}case keyCodes.del:{if(deleteCharAtPos())updatePromptDisplay();return false;break}case keyCodes.end:{if(moveColumn(promptText.length-column))updatePromptDisplay();return false;break}case keyCodes.start:{if(moveColumn(-column))updatePromptDisplay();return false;break}case keyCodes.ret:{commandTrigger();return false}case keyCodes.up:{rotateHistory(-1);return false}case keyCodes.down:{rotateHistory(1);return false}default:}};function rotateHistory(n){if(history.length==0)return;ringn+=n;if(ringn<0)ringn=history.length;else if(ringn>history.length)ringn=0;var prevText=promptText;if(ringn==0){promptText=restoreText}else{promptText=history[ringn-1]}if(config.historyPreserveColumn){if(promptText.length
      ');if(className)mesg.addClass(className);mesg.filledText(msg).hide();inner.append(mesg);mesg.show()};typer.consoleInsert=function(keyCode){var char=String.fromCharCode(keyCode);var before=promptText.substring(0,column);var after=promptText.substring(column);promptText=before+char+after;moveColumn(1);restoreText=promptText;updatePromptDisplay()};function moveColumn(n){if(column+n>=0&&column+n<=promptText.length){column+=n;return true}else return false};function updatePromptDisplay(){var line=promptText;var html='';if(column>0&&line==''){html=cursor}else if(column==promptText.length){html=htmlEncode(line)+cursor}else{var before=line.substring(0,column);var current=line.substring(column,column+1);if(current){current=''+htmlEncode(current)+''}var after=line.substring(column+1);html=htmlEncode(before)+current+htmlEncode(after)}prompt.html(html);scrollToBottom()};function htmlEncode(text){return(text.replace(/&/g,'&').replace(/&]{10})/g,'$1­'+wbr))};return extern};$.fn.filledText=function(txt){$(this).text(txt);$(this).html($(this).html().replace(/\n/g,'
      '));return this}})(jQuery); diff --git a/public/javascripts/mouseapp_2.js b/public/javascripts/mouseapp_2.js new file mode 100755 index 0000000..de5c777 --- /dev/null +++ b/public/javascripts/mouseapp_2.js @@ -0,0 +1,913 @@ +// +// Copyright (c) 2008 why the lucky stiff +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +var MouseApp = { + Version: '0.12', + CharCodes: { + 0: ' ', 1: ' ', 9: ' ', + 32: ' ', 34: '"', 38: '&', + 60: '<', 62: '>', 127: '◊', + 0x20AC: '€' + }, + KeyCodes: { + Backspace: 8, Tab: 9, Enter: 13, Esc: 27, PageUp: 33, PageDown: 34, + End: 35, Home: 36, Left: 37, Up: 38, Right: 39, Down: 40, Insert: 45, + Delete: 46, F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, + F7: 118, F8: 119, F10: 121 + }, + CodeKeys: {}, + Modes: { 1: 'b', 2: 'u', 4: 'i', 8: 'strike' }, + ModeIds: { r: 1, u: 2, i: 4, s: 8 }, + Colors: ['black', 'blue', 'green', + 'cyan', 'red', 'purple', 'brown', + 'gray', 'dark_gray', 'lt_blue', + 'lt_green', 'lt_cyan', 'lt_red', + 'lt_purple', 'yellow', 'white'] +} +//some of these are patently false, because I need to get on a real keyboard-- not a macbook. +if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1){ + MouseApp.KeyCodes = { + Backspace: 8, Tab: 9, Enter: 13, Esc: 27, PageUp: 63276, PageDown: 63277, + End: 63275, Home: 63273, Left: 37, Up: 38, Right: 39, Down: 40, Insert: 632325, + Delete: 46, F1: 63236, F2: 63237, F3: 63238, F4: 63239, F5: 63240, F6: 63241, + F7: 63242, F8: 63243, F10: 63244 + } + +} + +//even though I am tempted to combine chrome and opera into the same if statment, I will refrain for now +// i dont have proper access to a proper keyboard and there might be a single difference between the two +if (navigator.userAgent.indexOf("Opera") > -1){ + MouseApp.KeyCodes = { + Backspace: 8, Tab: 9, Enter: 13, Esc: 27, PageUp: 63276, PageDown: 63277, + End: 63275, Home: 63273, Left: 37, Up: 38, Right: 39, Down: 40, Insert: 632325, + Delete: 46, F1: 63236, F2: 63237, F3: 63238, F4: 63239, F5: 63240, F6: 63241, + F7: 63242, F8: 63243, F10: 63244 + } + +} + +// i am going to comment this out, since this seems un needed. if it past december 15 2009 and this +// is still commented it, please del me +//if ( navigator.appVersion.indexOf('AppleWebKit') > 0 ) { +// MouseApp.KeyCodes = { +// Backspace: 8, Tab: 9, Enter: 13, Esc: 27, PageUp: 63276, PageDown: 63277, +// End: 63275, Home: 63273, Left: 63234, Up: 63232, Right: 63235, Down: 63233, Insert: 632325, +// Delete: 63272, F1: 63236, F2: 63237, F3: 63238, F4: 63239, F5: 63240, F6: 63241, +// F7: 63242, F8: 63243, F10: 63244 +// };// +//} +for ( var k in MouseApp.KeyCodes ) { + MouseApp.CodeKeys[MouseApp.KeyCodes[k]] = k; +} + +MouseApp.isPrintable = function(ch) { + return (ch >= 32); +}; + +MouseApp.Base = function(){}; +MouseApp.Base.prototype = { + setOptions: function(options) { + this.options = { + columns: 72, rows: 24, indent: 2, + title: 'MouseApp', + blinkRate: 500, + ps: '>', + greeting:'%+r Terminal ready. %-r' + } + $.extend(this.options, options || {}); + } +} + +MouseApp.Manager = new Object(); +$.extend(MouseApp.Manager, { + observeTerm: function(term) { + this.activeTerm = term; + if ( this.observingKeyboard ) return; + var mgr = this; + if ( term.input ) { + term.input.keypress(function(e) { mgr.onKeyPress(e) }); + if (!window.opera) term.input.keydown(function(e) { mgr.onKeyDown(e) }); + else window.setInterval(function(){term.input.focus()},1); + } else { + if (!window.opera) $(document).keydown(function(e) { mgr.onKeyDown(e) }); + $(document).keypress(function(e) { mgr.onKeyPress(e) }); + } + this.observingKeyboard = true; + }, + + onKeyDown: function(e) { + e = (e) ? e : ((event) ? event : null); + if ( e && MouseApp.CodeKeys[e.keyCode] ) { + if ( window.event ) { + this.sendKeyPress(e); + } + this.blockEvent(e); + return false; + } + return true; + }, + + onKeyPress: function(e) { + if ( !window.opera && window.event && e.keyCode != 13 && e.keyCode != 8 ) { + e.charCode = e.keyCode; e.keyCode = null; + } + if ( e.keyCode == 191 ) { /* FF 1.0.x sends this upsy quizy -- ignore */ + return; + } + return this.sendKeyPress(e); + }, + + sendKeyPress: function(e) { + var term = MouseApp.Manager.activeTerm; + term.cursorOff(); + b = term.onKeyPress(e); + term.cursorOn(); + return b; + }, + + blockEvent: function (e) { + e.cancelBubble=true; + if (window.event && !window.opera) e.keyCode=0; + if (e.stopPropagation) e.stopPropagation(); + if (e.preventDefault) e.preventDefault(); + } +}); + +/* Basic text window functionality */ +MouseApp.Window = function(element, options) { + this.element = $(element); + this.setOptions(options); + this.initWindow(); +}; + +$.extend(MouseApp.Window.prototype, (new MouseApp.Base()), { + initWindow: function() { + var html = ''; + for ( var i = 0; i < this.options.rows; i++ ) { + html += "
       
      \n"; + } + this.element.html(html); + this.typingOn(); + if (this.options.input) { + this.input = $(this.options.input); + this.input.focus(); + } + MouseApp.Manager.observeTerm(this); + this.clear(); + this.cursorOn(); + this.painting = true; + this.element.css({visibility: 'visible'}); + }, + + text: function() { + var str = ""; + for (var i = 0; i < this.screen.length; i++ ) { + for (var j = 0; j < this.options.columns; j++ ) { + var ch = this.screen[i][j]; + if ( ch[0] != 0 ) { + str += String.fromCharCode(ch[0]); + } + } + } + return str; + }, + + clear: function() { + this.rpos = 0; + this.cpos = 0; + this.screen = []; + this.element.html(''); + this.screen[0] = this.fillRow(this.options.columns, 0); + this.paint(0); + }, + + typingOn: function() { this.typing = true; }, + typingOff: function() { this.typing = false; }, + + cursorOn: function() { + if ( this.blinker ) { + clearInterval( this.blinker ); + } + this.underblink = this.screen[this.rpos][this.cpos][1]; + MouseApp.Manager.activeTerm.blink(); + this.blinker = setInterval(function(){MouseApp.Manager.activeTerm.blink();}, this.options.blinkRate); + this.cursor = true; + }, + + cursorOff: function() { + if ( this.blinker ) { + clearInterval( this.blinker ); + } + if ( this.cursor ) { + this.screen[this.rpos][this.cpos][1] = this.underblink; + this.paint(this.rpos); + this.cursor = false; + } + }, + + blink: function() { + if ( this == MouseApp.Manager.activeTerm ) { + var mode = this.screen[this.rpos][this.cpos][1]; + this.screen[this.rpos][this.cpos][1] = ( mode & 1 ) ? mode & 4094 : mode | 1; + this.paint(this.rpos); + } + }, + + fillRow: function(len, ch, mode) { + ary = [] + for (var i = 0; i < len; i++) { + ary[i] = [ch, mode]; + } + return ary; + }, + + paint: function(start, end) { + if (!this.painting) return; + + if (!end) end = start; + for (var row = start; row <= end && row < this.screen.length; row++) { + var html = ''; + var mode = 0; + var fcolor = 0; + var bcolor = 0; + var spans = 0; + for (var i = 0; i < this.options.columns; i++ ) { + var c = this.screen[row][i][0]; + var m = this.screen[row][i][1] & 15; // 4 mode bits + var f = (this.screen[row][i][1] & (15 << 4)) >> 4; // 4 foreground bits + var b = (this.screen[row][i][1] & (15 << 8)) >> 8; // 4 background bits + if ( m != mode ) { + if ( MouseApp.Modes[mode] ) html += ""; + if ( MouseApp.Modes[m] ) html += "<" + MouseApp.Modes[m] + ">"; + mode = m; + } + if ( ( f != fcolor && f == 0 ) || ( b != bcolor && b == 0 ) ) { + for ( var s = 0; s < spans; s++ ) html += ""; + fcolor = 0; bcolor = 0; + } + if ( f != fcolor ) { + if ( MouseApp.Colors[f] ) { + html += ""; + spans++; + } + fcolor = f; + } + if ( b != bcolor ) { + if ( MouseApp.Colors[b] ) html += ""; + spans++; bcolor = b; + } + html += MouseApp.CharCodes[c] ? MouseApp.CharCodes[c] : String.fromCharCode(c); + } + if ( MouseApp.Modes[mode] ) html += ""; + for ( var s = 0; s < spans; s++ ) html += ""; + var new_id = this.element.attr('id') + '_' + row; + if (!$('#' + new_id).get(0)) { + this.element.append("
       
      "); + this.scrollAllTheWayDown(); + } + $('#' + new_id).html(html); + } + }, + + onAfterKey: function() { + this.scrollAllTheWayDown(); + }, + + highlightLine: function(i) { + if (i >= 0 && i < this.screen.length) + { + $("#" + this.element.attr('id') + "_" + i); + } + }, + + scrollToLine: function(i) { + var p = this.element[0].parentNode; + if ( p.scrollHeight > p.clientHeight ) { + p.scrollTop = (p.scrollHeight - p.clientHeight); + } + }, + + scrollAllTheWayDown: function() { + var p = this.element[0].parentNode; + if ( p.scrollHeight > p.clientHeight ) { + p.scrollTop = (p.scrollHeight - p.clientHeight); + } + }, + + putc: function(ch, mode) { + if ( ch == 13 ) { + return; + } else if ( ch == 10 ) { + this.screen[this.rpos][this.cpos] = [ch, mode]; + this.advanceLine(); + } else { + this.screen[this.rpos][this.cpos] = [ch, mode]; + this.paint(this.rpos); + this.advance(); + } + }, + + zpad: function(n) { + if (n < 10) n = "0" + n; + return n; + }, + + puts: function(str, mode) { + if ( !str ) return; + var p = this.painting; + var r = this.rpos; + this.painting = false; + for ( var i = 0; i < str.length; i++ ) { + this.insertc(str.charCodeAt(i), mode); + } + this.painting = p; + this.paint(r, this.rpos); + }, + + advance: function() { + this.cpos++; + if ( this.cpos >= this.options.columns ) { + this.advanceLine(); + } + }, + + advanceLine: function() { + this.cpos = 0; + this.rpos++; + this.ensureRow(this.rpos); + this.paint(this.rpos, this.screen.length - 1); + }, + + fwdc: function() { + var r = this.rpos; + var c = this.cpos; + if ( c < this.options.columns - 1 ) { + c++; + } else if ( r < this.screen.length - 1 ) { + r++; + c = 0; + } + var ch = (c == 0 ? this.screen[r-1][this.options.columns-1] : this.screen[r][c-1]); + if ( MouseApp.isPrintable(ch[0]) ) { + this.rpos = r; + this.cpos = c; + } + }, + + fwdLine: function() { + if ( this.rpos >= this.screen.length - 1 ) return; + this.rpos++; + while ( this.cpos > 0 && !MouseApp.isPrintable(this.screen[this.rpos][this.cpos - 1][0]) ) { + this.cpos--; + } + }, + + backc: function() { + var r = this.rpos; + var c = this.cpos; + if ( c > 0 ) { + c--; + } else if ( r > 0 ) { + c = this.options.columns - 1; + r--; + } + if ( MouseApp.isPrintable(this.screen[r][c][0]) ) { + this.rpos = r; + this.cpos = c; + return true; + } + return false; + }, + + getTypingStart: function() { + var c = this.cpos; + if ( !MouseApp.isPrintable(this.screen[this.rpos][c][0]) ) { + c--; + } + var pos = null; + for ( var r = this.rpos; r >= 0; r-- ) { + while ( c >= 0 ) { + if ( !MouseApp.isPrintable(this.screen[r][c][0]) ) { + return pos; + } + pos = [r, c]; + c--; + } + c = this.options.columns - 1; + } + }, + + getTypingEnd: function(mod) { + var c = this.cpos; + if ( !MouseApp.isPrintable(this.screen[this.rpos][c][0]) ) { + c--; + } + var pos = null; + for ( var r = this.rpos; r < this.screen.length; r++ ) { + while ( c < this.options.columns ) { + if ( !this.screen[r] || !this.screen[r][c] || !MouseApp.isPrintable(this.screen[r][c][0]) ) { + if (!mod) return pos; + mod--; + } + pos = [r, c]; + c++; + } + c = 0; + } + }, + + getTypingAt: function(start, end) { + var r = start[0]; + var c = start[1]; + var str = ''; + while ( r < end[0] || c <= end[1] ) { + if ( c < this.options.columns ) { + str += String.fromCharCode(this.screen[r][c][0]); + c++; + } else { + c = 0; + r++; + } + } + return str; + }, + + ensureRow: function(r) { + if (!this.screen[r]) { + this.screen[r] = this.fillRow(this.options.columns, 0); + } + }, + + insertc: function(ch, mode) { + var r = this.rpos; var c = this.cpos; + var end = this.getTypingEnd(+1); + if (end) { + var thisc = null; + var lastc = this.screen[this.rpos][this.cpos]; + while ( r < end[0] || c <= end[1] ) { + if ( c < this.options.columns ) { + thisc = this.screen[r][c]; + this.screen[r][c] = lastc; + lastc = thisc; + c++; + } else { + c = 0; + r++; + this.ensureRow(r); + } + } + this.paint(this.rpos, end[0]); + } + this.putc(ch, mode); + }, + + delc: function() { + /* end of line */ + if ( MouseApp.isPrintable(this.screen[this.rpos][this.cpos][0]) ) { + var end = this.getTypingEnd(); + var thisc = null; + var lastc = [0, 0]; + while ( this.rpos < end[0] || this.cpos <= end[1] ) { + if ( end[1] >= 0 ) { + thisc = this.screen[end[0]][end[1]]; + this.screen[end[0]][end[1]] = lastc; + lastc = thisc; + end[1]--; + } else { + end[1] = this.options.columns - 1; + this.paint(end[0]); + end[0]--; + } + } + } + }, + + backspace: function() { + /* end of line */ + if ( !MouseApp.isPrintable(this.screen[this.rpos][this.cpos][0]) ) { + this.backc(); + this.screen[this.rpos][this.cpos] = [0, 0]; + } else { + if ( this.backc() ) this.delc(); + } + }, + + backLine: function() { + if ( this.rpos < 1 ) return; + this.rpos--; + while ( this.cpos > 0 && !MouseApp.isPrintable(this.screen[this.rpos][this.cpos - 1][0]) ) { + this.cpos--; + } + }, + + onKeyPress: function(e) { + var ch = e.keyCode; + var key_name = MouseApp.CodeKeys[ch]; + if (window.opera && !e.altKey && e.keyCode != 13 && e.keyCode != 8) key_name = null; + ch = (e.which || e.charCode || e.keyCode); + if (e.which) ch = e.which; + if (!key_name) { key_name = String.fromCharCode(ch); } + if (e.ctrlKey) { key_name = 'Ctrl' + key_name; } + + // alert([e.keyCode, e.which, key_name, this['onKey' + key_name]]); + if (this.typing && this.onAnyKey) this.onAnyKey(key_name); + if (key_name && this['onKey' + key_name]) { + if (this.typing) this['onKey' + key_name](); + MouseApp.Manager.blockEvent(e); + if (this.typing && this.onAfterKey) this.onAfterKey(key_name, true); + return false; + } + if (!e.ctrlKey) { + if (MouseApp.isPrintable(ch)) { + if (this.typing) this.insertc(ch, 0); + MouseApp.Manager.blockEvent(e); + if (this.typing && this.onAfterKey) this.onAfterKey(key_name, true); + return false; + } + } + if (this.typing && this.onAfterKey) this.onAfterKey(key_name, false); + return true; + }, + onKeyHome: function() { + var s = this.getTypingStart(); + this.rpos = s[0]; this.cpos = s[1]; + }, + onKeyEnd: function() { + var e = this.getTypingEnd(+1); + this.rpos = e[0]; this.cpos = e[1]; + }, + onKeyInsert: function() { }, + onKeyDelete: function() { this.delc(); }, + onKeyUp: function() { this.backLine(); }, + onKeyLeft: function() { this.backc(); }, + onKeyRight: function() { this.fwdc(); }, + onKeyDown: function() { this.fwdLine(); }, + onKeyBackspace: function() { this.backspace(); }, + onKeyEnter: function() { this.insertc(10, 0); }, + onKeyTab: function() { + this.insertc(32, 0); + while (this.cpos % this.options.indent != 0) this.insertc(32, 0); + } +}); + +/* Terminal running moush */ +MouseApp.Terminal = function(element, options) { + this.element = $(element); + this.setOptions(options); + this.initWindow(); + this.setup(); +}; + +$.extend(MouseApp.Terminal.prototype, MouseApp.Window.prototype, { + setup: function() { + this.history = []; + this.backupNum = this.historyNum = this.commandNum = 0; + if (this.onStart) { + this.onStart(); + } else { + this.write(this.options.greeting + "\n", true); + this.prompt(); + } + }, + + prompt: function(ps, pt) { + if (!ps) { + ps = this.options.ps; pt = true; + } + this.write(ps, pt); + this.putc(1, 0); + this.typingOn(); + }, + + getCommand: function() { + var s = this.getTypingStart(); + var e = this.getTypingEnd(); + if (!s || !e) return; + return this.getTypingAt(s, e); + }, + + clearCommand: function() { + var s = this.getTypingStart(); + var e = this.getTypingEnd(); + if (!s || !e) return; + var r = s[0]; + var c = s[1]; + this.rpos = r; this.cpos = c; + while ( r < e[0] || c <= e[1] ) { + if ( c < this.options.columns ) { + this.screen[r][c] = [0, 0]; + c++; + } else { + c = 0; + this.paint(r); + r++; + } + } + this.paint(r); + }, + + write: function(str, pcodes) { + var p = this.painting; + var r = this.rpos; + this.painting = false; + var mode = 0; + var today = new Date(); + for ( var i = 0; i < str.length; i++ ) { + if ( str.substr(i,1) == "\n" ) { + this.advanceLine(); + continue; + } else if ( str.substr(i,1) == "\033" ) { + if ( str.substr(i+1,2) == "[m" ) { + mode = 0; + i += 2; + continue; + } + if ( str.substr(i+1,5) == "[0;0m" ) { + mode = 0; + i += 5; + continue; + } + var colors = str.substr(i+1,7).match(/^\[(\d);(\d+)m/); + if ( colors ) { + var colCode = parseInt( colors[2] ); + var color = colCode % 10; + if ( colors[1] == '1' ) { + color += 8; + } + if ( colCode / 10 == 4 ) { + color = color << 4; + } + mode = (mode & 15) + color << 4; + i += colors[0].length; + continue; + } + } else if ( str.substr(i,1) == '%' && pcodes ) { + var s2 = str.substr(i,2); + switch ( s2 ) { + case '%h': + this.puts(this.options.host, mode); + i++; + continue; + case '%l': + this.puts(this.options.name, mode); + i++; + continue; + case '%n': + this.advanceLine(); + i++; + continue; + case '%s': + this.puts("moush", mode); + i++; + continue; + case '%t': + this.puts(this.zpad(today.getHours()) + ":" + this.zpad(today.getMinutes()) + ":" + + this.zpad(today.getSeconds()), mode); + i++; + continue; + case '%u': + this.puts(this.options.user, mode); + i++; + continue; + case '%v': + this.puts(MouseApp.Version, mode); + i++; + continue; + case '%!': + this.puts(this.historyNum.toString(), mode); + i++; + continue; + case '%#': + this.puts(this.commandNum.toString(), mode); + i++; + continue; + case '%+': + var kind = str.substr(i+2, 1); + if ( MouseApp.ModeIds[kind] ) { + mode = mode | MouseApp.ModeIds[kind]; + i += 2; + continue; + } + break; + case '%-': + var kind = str.substr(i+2, 1); + if ( MouseApp.ModeIds[kind] ) { + mode = mode & ( 4095 - MouseApp.ModeIds[kind] ); + i += 2; + continue; + } + break; + } + } + this.putc(str.charCodeAt(i), mode); + } + this.painting = p; + this.paint(r, this.rpos); + }, + + onKeyUp: function() { + if ( this.backupNum == 0 ) return; + if ( this.backupNum == this.historyNum ) { + this.history[this.historyNum] = this.getCommand(); + } + this.clearCommand(); + this.backupNum--; + this.puts(this.history[this.backupNum]); + }, + onKeyDown: function() { + if ( this.backupNum >= this.historyNum ) return; + this.clearCommand(); + this.backupNum++; + this.puts(this.history[this.backupNum]); + }, + onKeyEnter: function() { + var cmd = this.getCommand(); + if (cmd) { + this.history[this.historyNum] = cmd; + this.backupNum = ++this.historyNum; + } + this.commandNum++; + this.advanceLine(); + if (cmd) { + var str = this.onCommand(cmd); + if (str) { + if ( str.substr(str.length - 1, 1) != "\n" ) { + str += "\n"; + } + this.write(str); + } + } + this.prompt(); + }, + onCommand: function(line) { + // this.puts("Echoing: " + line + "\n"); + if ( line == "clear" ) { + this.clear(); + } else { + return "\033[1;37m\033[0;44mYou typed:\033[m " + line; + } + } +}); + +/* Notepad sort of editor */ +MouseApp.Notepad = function(element, options) { + this.element = $(element); + this.setOptions(options); + this.initWindow(); + this.history = []; + this.lineno = 0; +}; + +$.extend(MouseApp.Notepad.prototype, MouseApp.Window.prototype, { + csave: function() { + if ( this.cpos_save ) { + this.cpos = this.cpos_save; + } else { + this.cpos_save = this.cpos; + } + }, + onKeyUp: function() { if ( this.rpos < 1 ) { return; } this.csave(); this.backLine(); }, + onKeyDown: function() { if ( this.rpos < this.screen.length - 1 ) { this.csave(); this.fwdLine(); } }, + onAfterKey: function(key, st) { + if ( st && !(key == 'Up' || key == 'Down') ) { + this.cpos_save = null; + } + }, + insertc: function(ch, mode) { + if (ch == 10) { + this.element.append("
       
      "); + this.screen.splice(this.rpos + 1, 0, this.fillRow(this.options.columns, 0)); + var c = this.cpos; var c2 = 0; + while (c < this.options.columns) + { + if (this.screen[this.rpos][c] == 0) break; + this.screen[this.rpos + 1][c2] = this.screen[this.rpos][c]; + this.screen[this.rpos][c] = [0, 0]; + c++; c2++; + } + this.paint(this.rpos); + if (MouseApp.isPrintable(this.screen[this.rpos][c])) + { + var r = this.rpos; var c = this.cpos; + this.rpos += 1; this.cpos = c2; + this.delc(); + this.rpos = r; this.cpos = c; + } + this.putc(ch, mode); + if (this.rpos == this.screen.length - 1) + this.scrollAllTheWayDown(); + } else { + var c = this.cpos + 1; + var lastc = this.screen[this.rpos][this.cpos]; + this.putc(ch, mode); + for ( var r = this.rpos; r < this.screen.length; r++ ) { + while (c < this.options.columns) + { + var tmpc = this.screen[r][c]; + if (lastc[0] == 0) + break; + this.screen[r][c] = lastc; + lastc = tmpc; + c++; + } + if (c < this.options.columns) { + break; + } + c = 0; + } + } + }, + + backc: function() { + var r = this.rpos; + var c = this.cpos - 1; + for ( var r = this.rpos; r >= 0; r-- ) { + while ( c >= 0 ) { + this.rpos = r; + this.cpos = c; + if ( this.screen[r][c][0] != 0 ) { + this.paint(r); + return true; + } + this.screen[r][c] = [0, 0]; + c--; + } + c = this.options.columns - 1; + } + return false; + }, + delc: function() { + var c = this.cpos + 1; + for ( var r = this.rpos; r < this.screen.length; r++ ) { + while ( c < this.options.columns ) { + if ( this.screen[r][c][0] != 0 ) { + break; + } + c++; + } + if ( c < this.options.columns ) break; + c = 0; + } + + if (r >= this.screen.length) return; + + var r2 = this.rpos; + var c2 = this.cpos; + for ( var r2 = this.rpos; r2 < this.screen.length; r2++ ) { + while (c2 < this.options.columns) + { + if (this.screen[r][c][0] == 0) + break; + this.screen[r2][c2] = this.screen[r][c]; + c2++; + c++; + if (c >= this.options.columns) { + r++; + if (r >= this.options.rows) break; + c = 0; + } + } + if (c2 < this.options.columns) { + while (c2 < this.options.columns) { + this.screen[r2][c2] = [0, 0]; + c2++; + } + break; + } + c2 = 0; + } + + if (r != r2 && r < this.screen.length) + { + this.screen.splice(r, 1); + $("#" + this.element.attr('id') + "_" + this.screen.length).remove(); + } + this.paint(this.rpos, this.screen.length); + }, + onKeyBackspace: function() { + if (this.backc()) this.delc(); + } + +}); diff --git a/public/javascripts/mouseirb_2.js b/public/javascripts/mouseirb_2.js new file mode 100755 index 0000000..0fa5f12 --- /dev/null +++ b/public/javascripts/mouseirb_2.js @@ -0,0 +1,179 @@ +// +// Copyright (c) 2008 why the lucky stiff +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/* Irb running moush */ +MouseApp.Irb = function(element, options) { + this.element = $(element); + this.setOptions(options); + this.showHelp = this.options.showHelp; + if ( this.options.showChapter ) { + this.showChapter = this.options.showChapter; + } + if ( this.options.init ) { + this.init = this.options.init; + } + this.initWindow(); + this.setup(); + this.helpPage = null; + this.irbInit = false; +}; + +$.extend(MouseApp.Irb.prototype, MouseApp.Terminal.prototype, { + cmdToQuery: function(cmd) { + return "cmd=" + escape(cmd.replace(/</g, '<').replace(/>/g, '>'). + replace(/&/g, '&').replace(/\r?\n/g, "\n")).replace(/\+/g, "%2B"); + }, + + fireOffCmd: function(cmd, func) { + var irb = this; + if (!this.irbInit) + { + $.ajax({url: this.options.irbUrl + "?" + this.cmdToQuery("!INIT!IRB!"), type: "GET", + complete: (function(r) { irb.irbInit = true; irb.fireOffCmd(cmd, func); })}); + } + else + { + $.ajax({url: this.options.irbUrl + "?" + this.cmdToQuery(cmd), type: "GET", + complete: func}); + } + }, + + reply: function(str) { + var raw = str.replace(/\033\[(\d);(\d+)m/g, ''); + this.checkAnswer(raw); + if (!str.match(/^(\.\.)+$/)) { + if ( str[str.length - 1] != "\n" ) { + str += "\n"; + } + js_payload = /\033\[1;JSm(.*)\033\[m/; + js_in = str.match(js_payload); + if (js_in) { + try { + js_in = eval(js_in[1]); + } catch (e) {} + str = str.replace(js_payload, ''); + } + var pr_re = new RegExp("(^|\\n)=>"); + if ( str.match( pr_re ) ) { + str = str.replace(new RegExp("(^|\\n)=>"), "$1\033[1;34m=>\033[m"); + } else { + str = str.replace(new RegExp("(^|\\n)= (.+?) ="), "$1\033[1;33m$2\033[m"); + } + this.write(str); + this.prompt(); + } else { + this.prompt("\033[1;32m" + ".." + "\033[m", true); + this.puts(str.replace(/\./g, ' '), 0); + } + }, + + setHelpPage: function(n, page) { + if (this.helpPage) + $(this.helpPage.ele).hide('fast'); + this.helpPage = {index: n, ele: page}; + match = this.scanHelpPageFor('load'); + if (match != -1) + { + this.fireOffCmd(match, (function(r) { + $(page).show('fast'); + })); + } + else + { + $(page).show('fast'); + } + }, + + scanHelpPageFor: function(eleClass) { + match = $("div." + eleClass, this.helpPage.ele); + if ( match[0] ) return match[0].innerHTML; + else return -1; + }, + + checkAnswer: function(str) { + if ( this.helpPage ) { + match = this.scanHelpPageFor('answer'); + if ( match != -1 ) { + if ( str.match( new RegExp('^\s*=> ' + match + '\s*$', 'm') ) ) { + this.showHelp(this.helpPage.index + 1); + } + } else { + match = this.scanHelpPageFor('stdout'); + if ( match != -1 ) { + if ( match == '' ) { + if ( str == '' || str == null ) this.showHelp(this.helpPage.index + 1); + } else if ( str.match( new RegExp('^\s*' + match + '$', 'm') ) ) { + this.showHelp(this.helpPage.index + 1); + } + } + } + } + }, + + onKeyCtrld: function() { + this.clearCommand(); + this.puts("reset"); + this.onKeyEnter(); + }, + + onKeyEnter: function() { + this.typingOff(); + var cmd = this.getCommand(); + if (cmd) { + this.history[this.historyNum] = cmd; + this.backupNum = ++this.historyNum; + } + this.commandNum++; + this.advanceLine(); + if (cmd) { + if ( cmd == "clear" ) { + this.clear(); + this.prompt(); + } else if ( cmd.match(/^(back)$/) ) { + if (this.helpPage && this.helpPage.index >= 1) { + this.showHelp(this.helpPage.index - 1); + } + this.prompt(); + } else if ( cmd.match(/^(next)$/) ) { + if (this.helpPage) { + this.showHelp(this.helpPage.index + 1); + } + this.prompt(); + } else if ( cmd.match(/^(help|wtf\?*)$/) ) { + this.showHelp(1); + this.prompt(); + } else if ( regs = cmd.match(/^(help|wtf\?*)\s+#?(\d+)\s*$/) ) { + this.showChapter(parseInt(regs[2])); + this.prompt(); + } else { + var term = this; + this.fireOffCmd(cmd, (function(r) { term.reply(r.responseText ? r.responseText : ''); })); + } + } else { + this.prompt(); + } + } +}); + diff --git a/public/javascripts/raphael-min.js b/public/javascripts/raphael-min.js new file mode 100644 index 0000000..4a99e3e --- /dev/null +++ b/public/javascripts/raphael-min.js @@ -0,0 +1,113 @@ +/* + * Raphael 1.4.3 - JavaScript Vector Library + * + * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com) + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. + */ +Raphael=function(){function m(){if(m.is(arguments[0],U)){for(var a=arguments[0],b=Aa[K](m,a.splice(0,3+m.is(a[0],O))),c=b.set(),d=0,f=a[o];d

      ";if(ha.childNodes[o]!=2)return m.type=null;ha=null}m.svg=!(m.vml=m.type=="VML");G[p]=m[p];m._id=0;m._oid=0;m.fn={};m.is=function(a,b){b=ca.call(b);return b=="object"&&a===Object(a)||b=="undefined"&&typeof a==b||b=="null"&&a==null||ca.call(ob.call(a).slice(8,-1))==b};m.setWindow=function(a){X=a;C=X.document};function ra(a){if(m.vml){var b=/^\s+|\s+$/g;ra=T(function(d){var f;d=(d+s)[I](b, +s);try{var e=new X.ActiveXObject("htmlfile");e.write("");e.close();f=e.body}catch(g){f=X.createPopup().document.body}e=f.createTextRange();try{f.style.color=d;var h=e.queryCommandValue("ForeColor");h=(h&255)<<16|h&65280|(h&16711680)>>>16;return"#"+("000000"+h[N](16)).slice(-6)}catch(i){return"none"}})}else{var c=C.createElement("i");c.title="Rapha\u00ebl Colour Picker";c.style.display="none";C.body[y](c);ra=T(function(d){c.style.color=d;return C.defaultView.getComputedStyle(c,s).getPropertyValue("color")})}return ra(a)} +function qb(){return"hsb("+[this.h,this.s,this.b]+")"}function rb(){return this.hex}m.hsb2rgb=T(function(a,b,c){if(m.is(a,"object")&&"h"in a&&"s"in a&&"b"in a){c=a.b;b=a.s;a=a.h}var d;if(c==0)return{r:0,g:0,b:0,hex:"#000"};if(a>1||b>1||c>1){a/=255;b/=255;c/=255}d=~~(a*6);a=a*6-d;var f=c*(1-b),e=c*(1-b*a),g=c*(1-b*(1-a));a=[c,e,f,f,g,c,c][d];b=[g,c,c,e,f,f,g][d];d=[f,f,g,c,c,e,f][d];a*=255;b*=255;d*=255;c={r:a,g:b,b:d,toString:rb};a=(~~a)[N](16);b=(~~b)[N](16);d=(~~d)[N](16);a=a[I](ga,"0");b=b[I](ga, +"0");d=d[I](ga,"0");c.hex="#"+a+b+d;return c},m);m.rgb2hsb=T(function(a,b,c){if(m.is(a,"object")&&"r"in a&&"g"in a&&"b"in a){c=a.b;b=a.g;a=a.r}if(m.is(a,ea)){var d=m.getRGB(a);a=d.r;b=d.g;c=d.b}if(a>1||b>1||c>1){a/=255;b/=255;c/=255}var f=Y(a,b,c),e=$(a,b,c);d=f;if(e==f)return{h:0,s:0,b:f};else{var g=f-e;e=g/f;a=a==f?(b-c)/g:b==f?2+(c-a)/g:4+(a-b)/g;a/=6;a<0&&a++;a>1&&a--}return{h:a,s:e,b:d,toString:qb}},m);var sb=/,?([achlmqrstvxz]),?/gi,sa=/\s*,\s*/,tb={hs:1,rg:1};m._path2string=function(){return this.join(",")[I](sb, +"$1")};function T(a,b,c){function d(){var f=Array[p].slice.call(arguments,0),e=f[Q]("\u25ba"),g=d.cache=d.cache||{},h=d.count=d.count||[];if(g[z](e))return c?c(g[e]):g[e];h[o]>=1000&&delete g[h.shift()];h[E](e);g[e]=a[K](b,f);return c?c(g[e]):g[e]}return d}m.getRGB=T(function(a){if(!a||(a+=s).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1};if(a=="none")return{r:-1,g:-1,b:-1,hex:"none"};!(tb[z](a.substring(0,2))||a.charAt()=="#")&&(a=ra(a));var b,c,d,f,e;if(a=a.match(pb)){if(a[2]){d=da(a[2].substring(5), +16);c=da(a[2].substring(3,5),16);b=da(a[2].substring(1,3),16)}if(a[3]){d=da((e=a[3].charAt(3))+e,16);c=da((e=a[3].charAt(2))+e,16);b=da((e=a[3].charAt(1))+e,16)}if(a[4]){a=a[4][H](sa);b=A(a[0]);c=A(a[1]);d=A(a[2]);f=A(a[3])}if(a[5]){a=a[5][H](sa);b=A(a[0])*2.55;c=A(a[1])*2.55;d=A(a[2])*2.55;f=A(a[3])}if(a[6]){a=a[6][H](sa);b=A(a[0]);c=A(a[1]);d=A(a[2]);return m.hsb2rgb(b,c,d)}if(a[7]){a=a[7][H](sa);b=A(a[0])*2.55;c=A(a[1])*2.55;d=A(a[2])*2.55;return m.hsb2rgb(b,c,d)}a={r:b,g:c,b:d};b=(~~b)[N](16); +c=(~~c)[N](16);d=(~~d)[N](16);b=b[I](ga,"0");c=c[I](ga,"0");d=d[I](ga,"0");a.hex="#"+b+c+d;isFinite(A(f))&&(a.o=f);return a}return{r:-1,g:-1,b:-1,hex:"none",error:1}},m);m.getColor=function(a){a=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||0.75};var b=this.hsb2rgb(a.h,a.s,a.b);a.h+=0.075;if(a.h>1){a.h=0;a.s-=0.2;a.s<=0&&(this.getColor.start={h:0,s:1,b:a.b})}return b.hex};m.getColor.reset=function(){delete this.start};var ub=/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig, +vb=/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig;m.parsePathString=T(function(a){if(!a)return null;var b={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},c=[];if(m.is(a,U)&&m.is(a[0],U))c=ta(a);c[o]||(a+s)[I](ub,function(d,f,e){var g=[];d=ca.call(f);e[I](vb,function(h,i){i&&g[E](+i)});if(d=="m"&&g[o]>2){c[E]([f][M](g.splice(0,2)));d="l";f=f=="m"?"l":"L"}for(;g[o]>=b[d];){c[E]([f][M](g.splice(0,b[d])));if(!b[d])break}});c[N]=m._path2string;return c});m.findDotsAtSegment=function(a,b,c,d,f,e,g,h,i){var j=1-i,l= +D(j,3)*a+D(j,2)*3*i*c+j*3*i*i*f+D(i,3)*g;j=D(j,3)*b+D(j,2)*3*i*d+j*3*i*i*e+D(i,3)*h;var n=a+2*i*(c-a)+i*i*(f-2*c+a),r=b+2*i*(d-b)+i*i*(e-2*d+b),q=c+2*i*(f-c)+i*i*(g-2*f+c),k=d+2*i*(e-d)+i*i*(h-2*e+d);a=(1-i)*a+i*c;b=(1-i)*b+i*d;f=(1-i)*f+i*g;e=(1-i)*e+i*h;h=90-w.atan((n-q)/(r-k))*180/w.PI;(n>q||r1){B=w.sqrt(B);c=B*c;d=B*d}B=c*c;var L=d*d;B=(e==g?-1:1)*w.sqrt(w.abs((B*L-B*x*x-L*k*k)/(B*x*x+L*k*k)));e=B*c*x/d+(a+h)/2;var B= +B*-d*k/c+(b+i)/2,x=w.asin(((b-B)/d).toFixed(7));k=w.asin(((i-B)/d).toFixed(7));x=ak)x-=l*2;if(!g&&k>x)k-=l*2}l=k-x;if(w.abs(l)>n){q=k;l=h;L=i;k=x+n*(g&&k>x?1:-1);h=e+c*w.cos(k);i=B+d*w.sin(k);q=Qa(h,i,c,d,f,0,g,l,L,[k,q,e,B])}l=k-x;f=w.cos(x);e=w.sin(x);g=w.cos(k);k=w.sin(k);l=w.tan(l/4);c=4/3*c*l;l=4/3*d*l;d=[a,b];a=[a+c*e,b-l*f];b=[h+c*k,i-l*g];h=[h,i];a[0]=2*d[0]-a[0];a[1]=2*d[1]-a[1];if(j)return[a,b,h][M](q);else{q=[a,b,h][M](q)[Q]()[H](","); +j=[];h=0;for(i=q[o];h1000000000000&&(n=0.5);w.abs(i)>1000000000000&&(i=0.5);if(n>0&&n<1){n=la(a,b,c,d,f,e,g,h,n);q[E](n.x);r[E](n.y)}if(i> +0&&i<1){n=la(a,b,c,d,f,e,g,h,i);q[E](n.x);r[E](n.y)}i=e-2*d+b-(h-2*e+d);j=2*(d-b)-2*(e-d);l=b-d;n=(-j+w.sqrt(j*j-4*i*l))/2/i;i=(-j-w.sqrt(j*j-4*i*l))/2/i;w.abs(n)>1000000000000&&(n=0.5);w.abs(i)>1000000000000&&(i=0.5);if(n>0&&n<1){n=la(a,b,c,d,f,e,g,h,n);q[E](n.x);r[E](n.y)}if(i>0&&i<1){n=la(a,b,c,d,f,e,g,h,i);q[E](n.x);r[E](n.y)}return{min:{x:$[K](0,q),y:$[K](0,r)},max:{x:Y[K](0,q),y:Y[K](0,r)}}}),ua=T(function(a,b){var c=ka(a),d=b&&ka(b);a={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null};b={x:0,y:0, +bx:0,by:0,X:0,Y:0,qx:null,qy:null};function f(q,k){var t;if(!q)return["C",k.x,k.y,k.x,k.y,k.x,k.y];!(q[0]in{T:1,Q:1})&&(k.qx=k.qy=null);switch(q[0]){case "M":k.X=q[1];k.Y=q[2];break;case "A":q=["C"][M](Qa[K](0,[k.x,k.y][M](q.slice(1))));break;case "S":t=k.x+(k.x-(k.bx||k.x));k=k.y+(k.y-(k.by||k.y));q=["C",t,k][M](q.slice(1));break;case "T":k.qx=k.x+(k.x-(k.qx||k.x));k.qy=k.y+(k.y-(k.qy||k.y));q=["C"][M](Pa(k.x,k.y,k.qx,k.qy,q[1],q[2]));break;case "Q":k.qx=q[1];k.qy=q[2];q=["C"][M](Pa(k.x,k.y,q[1], +q[2],q[3],q[4]));break;case "L":q=["C"][M](wa(k.x,k.y,q[1],q[2]));break;case "H":q=["C"][M](wa(k.x,k.y,q[1],k.y));break;case "V":q=["C"][M](wa(k.x,k.y,k.x,q[1]));break;case "Z":q=["C"][M](wa(k.x,k.y,k.X,k.Y));break}return q}function e(q,k){if(q[k][o]>7){q[k].shift();for(var t=q[k];t[o];)q.splice(k++,0,["C"][M](t.splice(0,6)));q.splice(k,1);i=Y(c[o],d&&d[o]||0)}}function g(q,k,t,L,B){if(q&&k&&q[B][0]=="M"&&k[B][0]!="M"){k.splice(B,0,["M",L.x,L.y]);t.bx=0;t.by=0;t.x=q[B][1];t.y=q[B][2];i=Y(c[o],d&& +d[o]||0)}}for(var h=0,i=Y(c[o],d&&d[o]||0);h0.5)*2-1;D(f-0.5,2)+D(e-0.5,2)>0.25&&(e=w.sqrt(0.25-D(f-0.5,2))*l+0.5)&&e!=0.5&&(e=e.toFixed(5)-1.0E-5*l)}return s});b=b[H](/\s*\-\s*/);if(d=="linear"){var h=b.shift();h=-A(h);if(isNaN(h))return null;h=[0,0,w.cos(h*w.PI/180),w.sin(h*w.PI/180)];var i=1/(Y(w.abs(h[2]),w.abs(h[3]))||1);h[2]*=i;h[3]*=i;if(h[2]<0){h[0]=-h[2];h[2]=0}if(h[3]<0){h[1]=-h[3];h[3]=0}}b=Ra(b);if(!b)return null; +i=a.getAttribute(aa);(i=i.match(/^url\(#(.*)\)$/))&&c.defs.removeChild(C.getElementById(i[1]));i=v(d+"Gradient");i.id="r"+(m._id++)[N](36);v(i,d=="radial"?{fx:f,fy:e}:{x1:h[0],y1:h[1],x2:h[2],y2:h[3]});c.defs[y](i);c=0;for(h=b[o];cb.height&&(b.height=e.y+e.height-b.y);e.x+e.width-b.x>b.width&&(b.width=e.x+e.width-b.x)}}a&&this.hide();return b};u[p].attr=function(a,b){if(this.removed)return this;if(a==null){a={};for(var c in this.attrs)if(this.attrs[z](c))a[c]=this.attrs[c];this._.rt.deg&&(a.rotation=this.rotate());(this._.sx!=1||this._.sy!= +1)&&(a.scale=this.scale());a.gradient&&a.fill=="none"&&(a.fill=a.gradient)&&delete a.gradient;return a}if(b==null&&m.is(a,ea)){if(a=="translation")return ya.call(this);if(a=="rotation")return this.rotate();if(a=="scale")return this.scale();if(a==aa&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;return this.attrs[a]}if(b==null&&m.is(a,U)){b={};c=0;for(var d=a.length;c1&&(a=1);f.opacity=a}b.fill&&(f.on=true);if(f.on==null||b.fill=="none")f.on=false;if(f.on&&b.fill)if(a=b.fill.match(Na)){f.src=a[1];f.type="tile"}else{f.color=m.getRGB(b.fill).hex;f.src= +s;f.type="solid";if(m.getRGB(b.fill).error&&(g.type in{circle:1,ellipse:1}||(b.fill+s).charAt()!="r")&&ma(g,b.fill)){d.fill="none";d.gradient=b.fill}}e&&c[y](f);f=c.getElementsByTagName("stroke")&&c.getElementsByTagName("stroke")[0];e=false;!f&&(e=f=R("stroke"));if(b.stroke&&b.stroke!="none"||b["stroke-width"]||b["stroke-opacity"]!=null||b["stroke-dasharray"]||b["stroke-miterlimit"]||b["stroke-linejoin"]||b["stroke-linecap"])f.on=true;(b.stroke=="none"||f.on==null||b.stroke==0||b["stroke-width"]== +0)&&(f.on=false);a=m.getRGB(b.stroke);f.on&&b.stroke&&(f.color=a.hex);a=((+d["stroke-opacity"]+1||2)-1)*((+d.opacity+1||2)-1)*((+a.o+1||2)-1);h=(A(b["stroke-width"])||1)*0.75;a<0&&(a=0);a>1&&(a=1);b["stroke-width"]==null&&(h=d["stroke-width"]);b["stroke-width"]&&(f.weight=h);h&&h<1&&(a*=h)&&(f.weight=1);f.opacity=a;b["stroke-linejoin"]&&(f.joinstyle=b["stroke-linejoin"]||"miter");f.miterlimit=b["stroke-miterlimit"]||8;b["stroke-linecap"]&&(f.endcap=b["stroke-linecap"]=="butt"?"flat":b["stroke-linecap"]== +"square"?"square":"round");if(b["stroke-dasharray"]){a={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};f.dashstyle=a[z](b["stroke-dasharray"])?a[b["stroke-dasharray"]]:s}e&&c[y](f)}if(g.type=="text"){f=g.paper.span.style;d.font&&(f.font=d.font);d["font-family"]&&(f.fontFamily=d["font-family"]);d["font-size"]&&(f.fontSize=d["font-size"]);d["font-weight"]&&(f.fontWeight=d["font-weight"]); +d["font-style"]&&(f.fontStyle=d["font-style"]);g.node.string&&(g.paper.span.innerHTML=(g.node.string+s)[I](/"));g.W=d.w=g.paper.span.offsetWidth;g.H=d.h=g.paper.span.offsetHeight;g.X=d.x;g.Y=d.y+F(g.H/2);switch(d["text-anchor"]){case "start":g.node.style["v-text-align"]="left";g.bbx=F(g.W/2);break;case "end":g.node.style["v-text-align"]="right";g.bbx=-F(g.W/2);break;default:g.node.style["v-text-align"]="center";break}}};ma=function(a,b){a.attrs=a.attrs|| +{};var c="linear",d=".5 .5";a.attrs.gradient=b;b=(b+s)[I](Ya,function(i,j,l){c="radial";if(j&&l){j=A(j);l=A(l);D(j-0.5,2)+D(l-0.5,2)>0.25&&(l=w.sqrt(0.25-D(j-0.5,2))*((l>0.5)*2-1)+0.5);d=j+P+l}return s});b=b[H](/\s*\-\s*/);if(c=="linear"){var f=b.shift();f=-A(f);if(isNaN(f))return null}var e=Ra(b);if(!e)return null;a=a.shape||a.node;b=a.getElementsByTagName(aa)[0]||R(aa);!b.parentNode&&a.appendChild(b);if(e[o]){b.on=true;b.method="none";b.color=e[0].color;b.color2=e[e[o]-1].color;a=[];for(var g=0, +h=e[o];g')}}catch(Kb){R=function(a){return C.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}Aa=function(){var a=Sa[K](0,arguments),b=a.container,c=a.height,d=a.width,f=a.x;a=a.y;if(!b)throw new Error("VML container not found.");var e=new G,g=e.canvas=C.createElement("div"),h=g.style;f=f||0;a=a||0;d=d||512; +c=c||342;d==+d&&(d+="px");c==+c&&(c+="px");e.width=1000;e.height=1000;e.coordsize=ja*1000+P+ja*1000;e.coordorigin="0 0";e.span=C.createElement("span");e.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";g[y](e.span);h.cssText=m.format("width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",d,c);if(b==1){C.body[y](g);h.left=f+"px";h.top=a+"px";h.position="absolute"}else b.firstChild?b.insertBefore(g, +b.firstChild):b[y](g);Fa.call(e,e,m.fn);return e};G[p].clear=function(){this.canvas.innerHTML=s;this.span=C.createElement("span");this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";this.canvas[y](this.span);this.bottom=this.top=null};G[p].remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]=Xa(a);return true}}G[p].safari=/^Apple|^Google/.test(X.navigator.vendor)&&(!(X.navigator.userAgent.indexOf("Version/4.0")+ +1)||X.navigator.platform.slice(0,2)=="iP")?function(){var a=this.rect(-99,-99,this.width+99,this.height+99);X.setTimeout(function(){a.remove()})}:function(){};function Db(){this.returnValue=false}function Eb(){return this.originalEvent.preventDefault()}function Fb(){this.cancelBubble=true}function Gb(){return this.originalEvent.stopPropagation()}var Hb=function(){if(C.addEventListener)return function(a,b,c,d){var f=Ba&&Ca[b]?Ca[b]:b;function e(g){if(Ba&&Ca[z](b))for(var h=0,i=g.targetTouches&&g.targetTouches.length;h< +i;h++)if(g.targetTouches[h].target==a){i=g;g=g.targetTouches[h];g.originalEvent=i;g.preventDefault=Eb;g.stopPropagation=Gb;break}return c.call(d,g)}a.addEventListener(f,e,false);return function(){a.removeEventListener(f,e,false);return true}};else if(C.attachEvent)return function(a,b,c,d){function f(g){g=g||X.event;g.preventDefault=g.preventDefault||Db;g.stopPropagation=g.stopPropagation||Fb;return c.call(d,g)}a.attachEvent("on"+b,f);function e(){a.detachEvent("on"+b,f);return true}return e}}();for(ha= +Ma[o];ha--;)(function(a){m[a]=u[p][a]=function(b){if(m.is(b,"function")){this.events=this.events||[];this.events.push({name:a,f:b,unbind:Hb(this.shape||this.node||C,a,b,this)})}return this};m["un"+a]=u[p]["un"+a]=function(b){for(var c=this.events,d=c[o];d--;)if(c[d].name==a&&c[d].f==b){c[d].unbind();c.splice(d,1);!c.length&&delete this.events;return this}return this}})(Ma[ha]);u[p].hover=function(a,b){return this.mouseover(a).mouseout(b)};u[p].unhover=function(a,b){return this.unmouseover(a).unmouseout(b)}; +u[p].drag=function(a,b,c){this._drag={};var d=this.mousedown(function(g){(g.originalEvent?g.originalEvent:g).preventDefault();this._drag.x=g.clientX;this._drag.y=g.clientY;this._drag.id=g.identifier;b&&b.call(this,g.clientX,g.clientY);Raphael.mousemove(f).mouseup(e)});function f(g){var h=g.clientX,i=g.clientY;if(Ba)for(var j=g.touches.length,l;j--;){l=g.touches[j];if(l.identifier==d._drag.id){h=l.clientX;i=l.clientY;(g.originalEvent?g.originalEvent:g).preventDefault();break}}else g.preventDefault(); +a&&a.call(d,h-d._drag.x,i-d._drag.y,h,i)}function e(){d._drag={};Raphael.unmousemove(f).unmouseup(e);c&&c.call(d)}return this};G[p].circle=function(a,b,c){return ab(this,a||0,b||0,c||0)};G[p].rect=function(a,b,c,d,f){return bb(this,a||0,b||0,c||0,d||0,f||0)};G[p].ellipse=function(a,b,c,d){return cb(this,a||0,b||0,c||0,d||0)};G[p].path=function(a){a&&!m.is(a,ea)&&!m.is(a[0],U)&&(a+=s);return Za(m.format[K](m,arguments),this)};G[p].image=function(a,b,c,d,f){return db(this,a||"about:blank",b||0,c||0, +d||0,f||0)};G[p].text=function(a,b,c){return eb(this,a||0,b||0,c||s)};G[p].set=function(a){arguments[o]>1&&(a=Array[p].splice.call(arguments,0,arguments[o]));return new Z(a)};G[p].setSize=fb;G[p].top=G[p].bottom=null;G[p].raphael=m;function ib(){return this.x+P+this.y}u[p].resetScale=function(){if(this.removed)return this;this._.sx=1;this._.sy=1;this.attrs.scale="1 1"};u[p].scale=function(a,b,c,d){if(this.removed)return this;if(a==null&&b==null)return{x:this._.sx,y:this._.sy,toString:ib};b=b||a;!+b&& +(b=a);var f,e,g=this.attrs;if(a!=0){var h=this.getBBox(),i=h.x+h.width/2,j=h.y+h.height/2;f=a/this._.sx;e=b/this._.sy;c=+c||c==0?c:i;d=+d||d==0?d:j;h=~~(a/w.abs(a));var l=~~(b/w.abs(b)),n=this.node.style,r=c+(i-c)*f;j=d+(j-d)*e;switch(this.type){case "rect":case "image":var q=g.width*h*f,k=g.height*l*e;this.attr({height:k,r:g.r*$(h*f,l*e),width:q,x:r-q/2,y:j-k/2});break;case "circle":case "ellipse":this.attr({rx:g.rx*h*f,ry:g.ry*l*e,r:g.r*$(h*f,l*e),cx:r,cy:j});break;case "text":this.attr({x:r,y:j}); +break;case "path":i=Oa(g.path);for(var t=true,L=0,B=i[o];L=i)return r;l=r}});function Ha(a,b){return function(c,d,f){c=ua(c); +for(var e,g,h,i,j="",l={},n=0,r=0,q=c.length;rd){if(b&&!l.start){e=jb(e,g,h[1],h[2],h[3],h[4],h[5],h[6],d-n);j+=["C",e.start.x,e.start.y,e.m.x,e.m.y,e.x,e.y];if(f)return j;l.start=j;j=["M",e.x,e.y+"C",e.n.x,e.n.y,e.end.x,e.end.y,h[5],h[6]][Q]();n+=i;e=+h[5];g=+h[6];continue}if(!a&&!b){e=jb(e,g,h[1],h[2],h[3],h[4],h[5],h[6],d-n);return{x:e.x,y:e.y,alpha:e.alpha}}}n+=i;e=+h[5];g=+h[6]}j+=h}l.end=j;e=a?n: +b?l:m.findDotsAtSegment(e,g,h[1],h[2],h[3],h[4],h[5],h[6],1);e.alpha&&(e={x:e.x,y:e.y,alpha:e.alpha});return e}}var Ib=T(function(a,b,c,d,f,e,g,h){for(var i={x:0,y:0},j=0,l=0;l<1.01;l+=0.01){var n=la(a,b,c,d,f,e,g,h,l);l&&(j+=D(D(i.x-n.x,2)+D(i.y-n.y,2),0.5));i=n}return j}),kb=Ha(1),za=Ha(),Ia=Ha(0,1);u[p].getTotalLength=function(){if(this.type=="path"){if(this.node.getTotalLength)return this.node.getTotalLength();return kb(this.attrs.path)}};u[p].getPointAtLength=function(a){if(this.type=="path")return za(this.attrs.path, +a)};u[p].getSubpath=function(a,b){if(this.type=="path"){if(w.abs(this.getTotalLength()-b)<1.0E-6)return Ia(this.attrs.path,a).end;b=Ia(this.attrs.path,b,1);return a?Ia(b,a).end:b}};m.easing_formulas={linear:function(a){return a},"<":function(a){return D(a,3)},">":function(a){return D(a-1,3)+1},"<>":function(a){a*=2;if(a<1)return D(a,3)/2;a-=2;return(D(a,3)+2)/2},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a-=1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a== +0||a==1)return a;var b=0.3,c=b/4;return D(2,-10*a)*w.sin((a-c)*2*w.PI/b)+1},bounce:function(a){var b=7.5625,c=2.75;if(a<1/c)a=b*a*a;else if(a<2/c){a-=1.5/c;a=b*a*a+0.75}else if(a<2.5/c){a-=2.25/c;a=b*a*a+0.9375}else{a-=2.625/c;a=b*a*a+0.984375}return a}};var S={length:0};function lb(){var a=+new Date;for(var b in S)if(b!="length"&&S[z](b)){var c=S[b];if(c.stop||c.el.removed){delete S[b];S[o]--}else{var d=a-c.start,f=c.ms,e=c.easing,g=c.from,h=c.diff,i=c.to,j=c.t,l=c.prev||0,n=c.el,r=c.callback,q= +{},k;if(d p:first-child{ + margin-top:0; +} +#facebox .content > p:last-child{ + margin-bottom:0; +} + +#facebox .close{ + position:absolute; + top:5px; + right:5px; + padding:2px; + background:#fff; +} +#facebox .close img{ + opacity:0.3; +} +#facebox .close:hover img{ + opacity:1.0; +} + +#facebox .loading { + text-align: center; +} + +#facebox .image { + text-align: center; +} + +#facebox img { + border: 0; + margin: 0; +} + +#facebox_overlay { + position: fixed; + top: 0px; + left: 0px; + height:100%; + width:100%; +} + +.facebox_hide { + z-index:-100; +} + +.facebox_overlayBG { + background-color: #000; + z-index: 99; +} +.facebox-footnote{ + margin-top:40px; +} \ No newline at end of file diff --git a/public/stylesheets/reset.css b/public/stylesheets/reset.css new file mode 100644 index 0000000..d709810 --- /dev/null +++ b/public/stylesheets/reset.css @@ -0,0 +1,46 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-style: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; +} +/* remember to define focus styles! */ +:focus { + outline: 0; +} +body { + line-height: 1; + color: black; + background: white; +} +ol, ul { + list-style: none; +} +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: separate; + border-spacing: 0; +} +caption, th, td { + text-align: left; + font-weight: normal; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ""; +} +blockquote, q { + quotes: "" ""; +} diff --git a/public/stylesheets/scaffold.css b/public/stylesheets/scaffold.css new file mode 100644 index 0000000..1ae7000 --- /dev/null +++ b/public/stylesheets/scaffold.css @@ -0,0 +1,56 @@ +body { background-color: #fff; color: #333; } + +body, p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { color: #000; } +a:visited { color: #666; } +a:hover { color: #fff; background-color:#000; } + +div.field, div.actions { + margin-bottom: 10px; +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; +} + +#error_explanation h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; +} + +#error_explanation ul li { + font-size: 12px; + list-style: square; +} diff --git a/public/stylesheets/site.css b/public/stylesheets/site.css new file mode 100755 index 0000000..62b1526 --- /dev/null +++ b/public/stylesheets/site.css @@ -0,0 +1,378 @@ +body { + font-family: verdana, arial, sans-serif; + font-size: 14px; + text-align: center; +} +h1, h2, h3, h4 { + font-family: georgia, serif; + margin: 10px 45px; padding: 0; +} +h1 { + color: #333; + font-size: 48px; + font-weight: normal; + margin: 10px 0px; +} +h3 { + color: white; + font-size: 21px; + font-weight: normal; +} +a,a:link,a:visited { + text-decoration: none; + color: #57ad11; +} +a:hover { + text-decoration: underline; + color: #57ad11; +} +a:active { + text-decoration: underline; + color: #dddddd; +} +input.keyboard-selector-input { + position: fixed; + top: 0; + _position: absolute; + _top: expression(eval(document.body.scrollTop)); + left: -300px; +} +.container { + width: 678px; + margin: 0 auto; + text-align: left; +} +.content { + width: 712px; + background: url(/images/tile.png) repeat-y; +} +#lilBrowser { + position: absolute; + background-color: white; + top: -540px; + left: -440px; + width: 510px; + height: 430px; + padding: 5px; + border: solid 1px #444; + z-index: 100; +} +h3#lbTitle { + color: #444; + font-family: verdana, arial, sans-serif; + font-size: small; + float:left; + line-height: 90%; + margin: 0; padding: 3px; +} +p#lbClose { + font-size: small; + float: right; + margin: 0; padding: 3px; +} +p#lbClose a { + display: inline; +} +.shellwin { + width: 712px; + padding-left: 12px; + background: url(/images/background.png) no-repeat; +} + +/* tutorial panes */ +.stretcher { + color: #f1f1ff; + display: none; + margin: 0; + padding: 0; + padding-left: 12px; + background: url(/images/tile.png) repeat-y; +} +.stretcher a, .stretcher a:link, .stretcher a:visited, .stretcher a:active { + text-decoration: none; + color: #a7ed91; +} +.stretcher a:hover { + text-decoration: underline; + color: #b7fd91; +} +.stretcher p { + margin: 10px 16px; +} +.stretcher dl, .stretcher ul { + background-color: white; + color: #333; + padding: 4px 8px; + font-size: 12px; + margin-left: 16px; + list-style: none; +} +.stretcher li { + margin: 6px; +} +.stretcher p code { + background-color: #874a20; + color: #fedeec; + padding: 1px 4px; +} +.stretcher p code.cmd { + background-color: #eeeeec; + color: #204a87; +} +.stretcher dt { + font-weight: bold; +} + +.chapmark { + padding: 6px 0; + margin-left: 12px; + margin-right: 22px; + color: #553; + background: #efefe1; +} +.chapmark h3 { + color: #335; +} +.chapmark a, .chapmark a:link, .chapmark a:visited, .chapmark a:active { + text-decoration: none; + color: #372d61; +} +.chapmark a:hover { + text-decoration: underline; + color: #477d51; +} +.note { color: #ddc; text-align: center; font-size: xx-small; } +ul li strong { color: #286; border-bottom: solid 2px #cca; } +ul li code { background-color: #f1f1f1; padding: 1px 3px; border-bottom: solid 2px #ddd; } +ul li code.faded { color: #899; } +code strong { background-color: #dcffb9; padding: 1px 3px; } +ul.commands li strong { display: block; float: left; width: 60px; border: none; } + +/* irb terminal */ +.terminal { + background-color: #ffffff; + border: solid 1px #204a87; + width: 678px; + height: 240px; + overflow: auto; +} +.console { + padding: 4px; margin-left: -50px; + font-family: "Andale Mono", courier, fixed, monospace; + font-size: 14px; + line-height: 16px; + color: #204a87; + text-align: left; + width: 664px; + height:220px; +} + +.console div b { + background-color: #874a20; + color: #fedeac; +} +div.answer, div.stdout, div.no_answer, div.load { + display: none; +} + +/* terminal escape colors */ +span.fore_black { color: #2e3436; } +span.fore_dark_gray { color: #888a85; } +span.fore_gray { color: #babdb6; } +span.fore_white { color: #eeeeec; } +span.fore_blue { color: #204a87; } +span.fore_lt_blue { color: #729fcf; } +span.fore_green { color: #788600; font-weight: bold; } +span.fore_lt_green { color: #cbe134; } +span.fore_cyan { color: #c4a000; } /* using cyan for yellows */ +span.fore_lt_cyan { color: #fc994f; } +span.fore_red { color: #a40000; } +span.fore_lt_red { color: #ef2929; font-weight: bold; } +span.fore_purple { color: #5c3566; } +span.fore_lt_purple { color: #ad7fa8; } +span.fore_brown { color: #8f5972; } +span.fore_lt_brown { color: #b9b9de; } +span.back_black { background-color: #2e3436; } +span.back_dark_gray { background-color: #888a85; } +span.back_gray { background-color: #babdb6; } +span.back_white { background-color: #eeeeec; } +span.back_blue { background-color: #204a87; } +span.back_lt_blue { background-color: #729fcf; } +span.back_green { background-color: #788600; } +span.back_lt_green { background-color: #cbe134; } +span.back_cyan { background-color: #c4a000; } /* using cyan for yellows */ +span.back_lt_cyan { background-color: #fce94f; } +span.back_red { background-color: #a40000; } +span.back_lt_red { background-color: #ef2929; } +span.back_purple { background-color: #5c3566; } +span.back_lt_purple { background-color: #ad7fa8; } +span.back_brown { background-color: #8f5902; } +span.back_lt_brown { background-color: #b9b96e; } + +/** no ways***/ + +div.main-wrapper-bottom { + height:4px;background-position: 0px -80px; background-repeat: no-repeat; + font-size:0 /* IE6, go figure */ +} +div.main-wrapper-borders { + background-position: -1731px 0; padding:15px; + background-repeat:repeat-y; +} +div.console-wrapper { + margin:0px auto ;width:566px; + cursor:text; + font-family:monospace; +} +div.console-wrapper-top { + height:3px;background-position: -50px -48px; background-repeat: no-repeat; + font-size:0 /* IE6, go figure */ +} +div.console-wrapper-bottom { + height:3px;background-position: -50px -51px; background-repeat: no-repeat; + font-size:0 /* IE6, go figure */ +} +div.console-wrapper-borders { + background-position: 0px 0px; padding:1px; + background-repeat:repeat-y; +} +div.guide-wrapper { + color:#fff; width:566px;margin-left:2px +} +div.guide-wrapper-top { + height:4px;background-position: 0 -65px; + font-size:0 /* IE6, go figure */ +} +div.guide-wrapper-bottom { + height:4px;background-position: 0 -70px; + font-size:0 /* IE6, go figure */ +} +div.guide-wrapper-borders { + background-position: -1166px 0px;padding:15px; + background-repeat:repeat-y; +} + +div.footer-wrapper-borders { + background-position: -566px 0px; padding:10px; + background-repeat:repeat-y; + font-size:12px +} +h1.main-header { + text-indent:-9999px; background-position: -49px 0px; + background-repeat: no-repeat; + width:318px; height:48px; margin-bottom:20px; + float:left +} + +div.footer { + line-height: 1.3em; +} + div.console div.jquery-console-inner +{ height:100%; overflow:auto; background:white} +div.console div.jquery-console-prompt-box +{ color:#437375; font-family:monospace; margin-top:0.5em; } +div.console div.jquery-console-prompt-box .prompt-done +{ cursor: pointer } +div.console div.jquery-console-prompt-box .prompt-done:hover +{ background:#453D5B; color: white; } +div.console div.jquery-console-focus span.jquery-console-cursor +{ background:#666; color:#fff; } +div.console div.jquery-console-message-error { + color:#ef0505; font-family:sans-serif; font-weight:bold; + padding-top:0.25em +} +div.console div.jquery-console-message-value +{ color:#000; font-family:monospace;padding-top:0.25em; font-weight: bold; } +div.console div.jquery-console-message-type +{ color:#382567; font-family:monospace;padding-left:0em;padding-top:0.25em; font-size:.9em } +div.console span.jquery-console-prompt-label { font-weight:bold } +div.console div.jquery-console-welcome { font-family:"DejaVu Sans",sans-serif; } + +div.share-wrapper { font-size:12px;padding:10px 0em 0em 10px } +div.share-wrapper strong { font-weight: bold } + +.clearfix:after { content:"."; display:block; height:0; clear:both; visibility:hidden } +div.menu { + float:right; + margin-right:2px; + margin-top:40px; + margin-bottom:5px +} +a.reset-btn { + float:left; + display:block; + width:59px; + height:24px; + background-position: -427px -18px; background-repeat: no-repeat; +} +a.reset-btn span { display:none } + +div.clear { clear:both } + + +div.console-wrapper .notice { + position:absolute; + bottom:0;right:0; + margin:1px; + background:#eee; + color:black; + padding:10px; + font-size:12px; + font-family:sans-serif; + font-weight:bold; +} +p.ajax-loader { background:url(../images/ajax-loader.gif); width:16px; height:16px;text-indent:-9999px } + +.notice a { padding:3px;background:#333;color:white} +.notice .action { text-align: right } + +/* Support Try Ruby! */ + + +a.trigger{ +position: absolute; +text-decoration: none; +top: 80px; right: 0; +font-size: 14px; +letter-spacing:-1px; +font-family: verdana, helvetica, arial, sans-serif; +color:#fff; +padding: 10px 10px 10px 10px; +font-weight: 500; +background:#333333 url(images/plus.png) 15% 55% no-repeat; +border:1px solid #444444; +-moz-border-radius-topleft: 20px; +-webkit-border-top-left-radius: 20px; +-moz-border-radius-bottomleft: 20px; +-webkit-border-bottom-left-radius: 20px; +-moz-border-radius-bottomright: 0px; +-webkit-border-bottom-right-radius: 0px; +display: block; +} + +a.trigger:hover{ +position: absolute; +text-decoration: none; +top: 80px; right: 0; +font-size: 14px; +letter-spacing:-1px; +font-family: verdana, helvetica, arial, sans-serif; +color:#fff; +padding: 10px 10px 10px 10px; +font-weight: 500; +background: green; +border:1px solid #444444; +-moz-border-radius-topleft: 20px; +-webkit-border-top-left-radius: 20px; +-moz-border-radius-bottomleft: 20px; +-webkit-border-bottom-left-radius: 20px; +-moz-border-radius-bottomright: 0px; +-webkit-border-bottom-right-radius: 0px; +display: block; +} + +a.active.trigger { +background:#222222 url(images/minus.png) 15% 55% no-repeat; +} + + \ No newline at end of file diff --git a/tryruby/Gemfile b/tryruby/Gemfile index 649c6bb..668de60 100644 --- a/tryruby/Gemfile +++ b/tryruby/Gemfile @@ -1,16 +1,46 @@ -source :rubygems +source 'http://rubygems.org' -gem "rails", '2.3.5' +gem 'rails', '3.0.5' -# Specify your favourite web server (only one). -#gem 'unicorn', :group => :development -#gem 'mongrel', :group => :development +# Bundle edge Rails instead: +# gem 'rails', :git => 'git://github.com/rails/rails.git' +gem 'mysql2' +gem 'fakefs', '0.2.1', :git => "http://github.com/defunkt/fakefs.git", :ref => "aa0cb96b8ebc81287a2e", :require => 'fakefs/safe' +# Use unicorn as the web server +# gem 'unicorn' +gem 'i18n' +# Deploy with Capistrano +# gem 'capistrano' + +gem 'devise' + +# To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+) +# gem 'ruby-debug' +# gem 'ruby-debug19', :require => 'ruby-debug' +gem 'jquery-rails' gem 'ruby_parser' +gem 'racc' + +#gem 'ruby2ruby' +#gem 'newrelic_rpm' +#gem 'madmimi' +#gem 'delayed_job' +#gem 'dalli' +# Bundle the extra gems: +# gem 'bj' + +gem 'nokogiri' +# gem 'sqlite3-ruby', :require => 'sqlite3' +# gem 'aws-s3', :require => 'aws/s3' -group :test do - gem 'test-unit', '2.1.1' - gem 'fakefs', '0.2.1', :git => "http://github.com/defunkt/fakefs.git", :ref => "aa0cb96b8ebc81287a2e", :require => 'fakefs/safe' - gem 'hpricot' - gem 'ruby-debug19' -end \ No newline at end of file +# Bundle gems for the local environment. Make sure to +# put test-only gems in this group so their generators +# and rake tasks are available in development mode: +# group :development, :test do + gem 'capybara' + gem 'rspec-rails' + gem 'cucumber-rails' + #gem 'factory-girl' + +# end diff --git a/tryruby/README b/tryruby/README index 79bc20e..fe7013d 100644 --- a/tryruby/README +++ b/tryruby/README @@ -1,21 +1,256 @@ -=README +== Welcome to Rails -This is a highly experimental version of tryruby -I realize putting try ruby is rails is going to be controversial. -My origional idea was to use sinatra. Maybe I will use both. +Rails is a web-application framework that includes everything needed to create +database-backed web applications according to the Model-View-Control pattern. -What is driving this decision is: -* the desire to move away from explictly needing to state where the ruby interpreter is -* an admission that I need to become rack based, rails is a cheap way out -* more people know rails within the ruby world then not (sorry its true) -* I need to be able to extend this eaiser, if I tried to add even 30 lessons in its current form, it would not be as fun as inside rails -* rspec-rails I understand way better than rspec some random script. +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. -so why am I not using camping, simple, I am not sure how maintained camping is. -What about merb? I admit, I don't know merb and with rails 3.0 won't I be getting the benefits of merb anyway? +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. -I will contact everyone who has a fork of this project to notify them of if and when I plan on making the architectual switch before hand. +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. -Thanks. -In the meanwhile, ignore this branch. +== Getting Started + +1. At the command prompt, create a new Rails application: + rails new myapp (where myapp is the application name) + +2. Change directory to myapp and start the web server: + cd myapp; rails server (run with --help for options) + +3. Go to http://localhost:3000/ and you'll see: + "Welcome aboard: You're riding Ruby on Rails!" + +4. Follow the guidelines to start developing your application. You can find +the following resources handy: + +* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html +* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ + + +== 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/. There are +several books available online as well: + +* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) +* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) + +These two 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 sudo 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", "body"=>"Only ten..", "id"=>"2"}>]" + >> @posts.first.title = "hello from a debugger" + => "hello from a debugger" + +...and even better, 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 can enter "cont". + + +== Console + +The console is a Ruby shell, which allows you to interact with your +application's domain model. 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. + +To start the console, run rails console from the application +directory. + +Options: + +* Passing the -s, --sandbox argument will rollback any modifications + made to the database. +* Passing an environment name as an argument will load the corresponding + environment. Example: rails console production. + +To reload your controllers and models after launching the console run +reload! + +More information about irb can be found at: +link:http://www.rubycentral.com/pickaxe/irb.html + + +== dbconsole + +You can go to the command line of your database directly through rails +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 rails dbconsole production. Currently works for MySQL, +PostgreSQL and SQLite 3. + +== Description of Contents + +The default directory structure of a generated Ruby on Rails application: + + |-- app + | |-- controllers + | |-- helpers + | |-- mailers + | |-- models + | `-- views + | `-- layouts + |-- config + | |-- environments + | |-- initializers + | `-- locales + |-- db + |-- doc + |-- lib + | `-- tasks + |-- log + |-- public + | |-- images + | |-- javascripts + | `-- stylesheets + |-- script + |-- test + | |-- fixtures + | |-- functional + | |-- integration + | |-- performance + | `-- unit + |-- tmp + | |-- cache + | |-- pids + | |-- sessions + | `-- sockets + `-- vendor + `-- plugins + +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. Models descend from + ActiveRecord::Base by default. + +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 by default. + +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 generators 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 rails generate + command, 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/tryruby/Rakefile b/tryruby/Rakefile index 3bb0e85..79dd5f3 100644 --- a/tryruby/Rakefile +++ b/tryruby/Rakefile @@ -1,10 +1,7 @@ # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. -require(File.join(File.dirname(__FILE__), 'config', 'boot')) - +require File.expand_path('../config/application', __FILE__) require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' -require 'tasks/rails' +TryRuby::Application.load_tasks diff --git a/tryruby/app/controllers/application_controller.rb b/tryruby/app/controllers/application_controller.rb index 02c13e5..b0f4608 100644 --- a/tryruby/app/controllers/application_controller.rb +++ b/tryruby/app/controllers/application_controller.rb @@ -1,15 +1,24 @@ # Filters added to this controller apply to all controllers in the application. # Likewise, all the methods added will be available for all controllers. +require './lib/tryruby' class ApplicationController < ActionController::Base +# attr_accessor :past_commands, :current_statement, :start_time +=begin + #attr_accessor :session + TryRuby.session = session + + TryRuby.session['start_time'] ||= Time.now + TryRuby.session['current_statement'] ||= '' + TryRuby.session['past_commands'] ||= '' + helper :all # include all helpers, all the time protect_from_forgery # See ActionController::RequestForgeryProtection for details # Scrub sensitive parameters from your log # filter_parameter_logging :password #class << self - attr_accessor :session - TryRuby.session = TryRuby::Session.new +=end # not needed #end end diff --git a/tryruby/app/controllers/tryruby_controller.rb b/tryruby/app/controllers/tryruby_controller.rb index 88f6513..912866a 100644 --- a/tryruby/app/controllers/tryruby_controller.rb +++ b/tryruby/app/controllers/tryruby_controller.rb @@ -1,11 +1,14 @@ -require './lib/tryruby' class TryrubyController < ApplicationController layout 'tryruby' - attr_accessor :past_commands, :current_statement, :start_time def run - render :text => run_script(params[:cmd]) - end + @cmd=params[:cmd] + + @a= run_script(@cmd) +@b = "handleJSON({\"type\": #{@a.type.to_json}, \"output\":#{@a.output.to_json},\"result\":#{@a.result.inspect.to_json}, \"error\": #{@a.error.inspect.to_json}})" + + render :json => @b + end private @@ -15,7 +18,7 @@ def run_script(command) # rescue StandardError => e # e.message + ". On the " - TryRuby.run_line(command).format + TryRuby.run_line(command) # end # return "=> #{output}" + ", says yoda" diff --git a/tryruby/app/helpers/application_helper.rb b/tryruby/app/helpers/application_helper.rb index 22a7940..d995b05 100644 --- a/tryruby/app/helpers/application_helper.rb +++ b/tryruby/app/helpers/application_helper.rb @@ -1,3 +1,18 @@ -# Methods added to this helper will be available to all templates in the application. module ApplicationHelper + + def google_analytics_js + ua_code = "UA-2365371-3" + '' + + end + end diff --git a/tryruby/app/views/layouts/tryruby.rhtml b/tryruby/app/views/layouts/tryruby.rhtml index 7d7fadc..2600200 100644 --- a/tryruby/app/views/layouts/tryruby.rhtml +++ b/tryruby/app/views/layouts/tryruby.rhtml @@ -4,40 +4,81 @@ try ruby! (in your browser) - - - - - + + + + - - + + + + + + + + + + + + + -
      - -
      +
      +
      +
      + + + <%= render :partial => 'tryruby/donate' %> + +

      A Popup Browser

      [x]

      - <%= yield %> +
      -
      + -
      -
      -
      + +
      + + +
      +
      +
      +
      +
      +
      +
      +
      -
      +

      Got 15 minutes? Give Ruby a shot right now!

      Ruby is a programming language from Japan (available at ruby-lang.org) @@ -79,8 +120,7 @@ Allows you to skip to the next section of a lesson.

      - - + +Please Support
      Try Ruby!
      + diff --git a/tryruby/app/views/tryruby/index.rhtml b/tryruby/app/views/tryruby/index.rhtml index 091d348..c95b8f7 100644 --- a/tryruby/app/views/tryruby/index.rhtml +++ b/tryruby/app/views/tryruby/index.rhtml @@ -1 +1,11 @@ - \ No newline at end of file + +
      +
      +
      +
      +
      +
      +
      +
      + + diff --git a/tryruby/config/boot.rb b/tryruby/config/boot.rb index 9ee835a..4489e58 100644 --- a/tryruby/config/boot.rb +++ b/tryruby/config/boot.rb @@ -1,124 +1,6 @@ -# Don't change this file! -# Configure your app in config/environment.rb and config/environments/*.rb +require 'rubygems' -RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT) +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -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 - $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.) - exit 1 - end - - class << self - def rubygems_version - Gem::RubyGemsVersion 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 - -class Rails::Boot - def run - load_initializer - - Rails::Initializer.class_eval do - def load_gems - @bundler_loaded ||= Bundler.require :default, Rails.env - end - end - - Rails::Initializer.run(:set_load_path) - end -end - -# All that for this: -Rails.boot! +require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) diff --git a/tryruby/config/database.yml b/tryruby/config/database.yml index 854cff5..48e775d 100644 --- a/tryruby/config/database.yml +++ b/tryruby/config/database.yml @@ -1,20 +1,32 @@ -# SQLite version 3.x -# gem install sqlite3-ruby (not necessary on OS X Leopard) -development: - +development: + adapter: mysql2 + database: tryruby_development + username: root + password: root + pool: 5 + timeout: 5000 + host: 127.0.0.1 # 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 + adapter: mysql2 + database: tryruby_test + username: root + password: root + pool: 5 + timeout: 5000 + host: 127.0.0.1 production: - adapter: sqlite3 - database: db/production.sqlite3 - pool: 5 - timeout: 5000 + adapter: mysql2 + database: tryruby_production + username: root + password: root + pool: 5 + timeout: 5000 + host: localhost +cucumber: + <<: *test \ No newline at end of file diff --git a/tryruby/config/environment.rb b/tryruby/config/environment.rb index dec1ee9..1c5d67b 100644 --- a/tryruby/config/environment.rb +++ b/tryruby/config/environment.rb @@ -1,45 +1,5 @@ -# Be sure to restart your server when you modify this file +# Load the rails application +require File.expand_path('../application', __FILE__) -# Specifies gem version of Rails to use when vendor/rails is not present -RAILS_GEM_VERSION = '2.3.5' 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| - config.frameworks -= [:active_record] - # 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.load_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 - -#equire 'irb' -#require 'tryruby_runner' +# Initialize the rails application +TryRuby::Application.initialize! diff --git a/tryruby/config/environments/development.rb b/tryruby/config/environments/development.rb index 85c9a60..43463ef 100644 --- a/tryruby/config/environments/development.rb +++ b/tryruby/config/environments/development.rb @@ -1,17 +1,26 @@ -# Settings specified here will take precedence over those in config/environment.rb +TryRuby::Application.configure do + # Settings specified here will take precedence over those in config/application.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 + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the webserver when you make code changes. + config.cache_classes = false -# Log error messages when you accidentally call methods on nil. -config.whiny_nils = true + # Log error messages when you accidentally call methods on nil. + config.whiny_nils = true -# Show full error reports and disable caching -config.action_controller.consider_all_requests_local = true -config.action_view.debug_rjs = true -config.action_controller.perform_caching = false + # Show full error reports and disable caching + config.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 + + # Print deprecation notices to the Rails logger + config.active_support.deprecation = :log + + # Only use best-standards-support built into browsers + config.action_dispatch.best_standards_support = :builtin +end -# 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/tryruby/config/environments/production.rb b/tryruby/config/environments/production.rb index 27119d2..3827776 100644 --- a/tryruby/config/environments/production.rb +++ b/tryruby/config/environments/production.rb @@ -1,28 +1,49 @@ -# Settings specified here will take precedence over those in config/environment.rb +TryRuby::Application.configure do + # Settings specified here will take precedence over those in config/application.rb -# The production environment is meant for finished, "live" apps. -# Code is not reloaded between requests -config.cache_classes = true + # The production environment is meant for finished, "live" apps. + # Code is not reloaded between requests + config.cache_classes = true -# 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 + # Full error reports are disabled and caching is turned on + config.consider_all_requests_local = false + config.action_controller.perform_caching = true -# See everything in the log (default is :info) -# config.log_level = :debug + # Specifies the header that your server uses for sending files + config.action_dispatch.x_sendfile_header = "X-Sendfile" -# Use a different logger for distributed setups -# config.logger = SyslogLogger.new + # For nginx: + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' -# Use a different cache store in production -# config.cache_store = :mem_cache_store + # If you have no front-end server that supports something like X-Sendfile, + # just comment this out and Rails will serve the files -# Enable serving of images, stylesheets, and javascripts from an asset server -# config.action_controller.asset_host = "http://assets.example.com" + # See everything in the log (default is :info) + # config.log_level = :debug -# Disable delivery errors, bad email addresses will be ignored -# config.action_mailer.raise_delivery_errors = false + # Use a different logger for distributed setups + # config.logger = SyslogLogger.new -# Enable threaded mode -# config.threadsafe! \ No newline at end of file + # Use a different cache store in production + # config.cache_store = :mem_cache_store + + # Disable Rails's static asset server + # In production, Apache or nginx will already do this + config.serve_static_assets = false + + # 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! + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation can not be found) + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners + config.active_support.deprecation = :notify +end diff --git a/tryruby/config/environments/test.rb b/tryruby/config/environments/test.rb index d6f80a4..7127cbe 100644 --- a/tryruby/config/environments/test.rb +++ b/tryruby/config/environments/test.rb @@ -1,28 +1,35 @@ -# 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 +TryRuby::Application.configure do + # Settings specified here will take precedence over those in config/application.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.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates + config.action_dispatch.show_exceptions = false + + # 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 + + # Print deprecation notices to the stderr + config.active_support.deprecation = :stderr +end diff --git a/tryruby/config/initializers/backtrace_silencers.rb b/tryruby/config/initializers/backtrace_silencers.rb index c2169ed..59385cd 100644 --- a/tryruby/config/initializers/backtrace_silencers.rb +++ b/tryruby/config/initializers/backtrace_silencers.rb @@ -3,5 +3,5 @@ # 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 +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/tryruby/config/initializers/inflections.rb b/tryruby/config/initializers/inflections.rb index d531b8b..d250860 100644 --- a/tryruby/config/initializers/inflections.rb +++ b/tryruby/config/initializers/inflections.rb @@ -1,10 +1,10 @@ # Be sure to restart your server when you modify this file. -# Add new inflection rules using the following format +# Add new inflection rules using the following format # (all these examples are active by default): -# ActiveSupport::Inflector.inflections do |inflect| + ActiveSupport::Inflector.inflections do |inflect| # inflect.plural /^(ox)$/i, '\1en' # inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' + inflect.irregular 'irb', 'irb' # inflect.uncountable %w( fish sheep ) -# end + end diff --git a/tryruby/config/initializers/session_store.rb b/tryruby/config/initializers/session_store.rb index b8817bb..5551e13 100644 --- a/tryruby/config/initializers/session_store.rb +++ b/tryruby/config/initializers/session_store.rb @@ -1,15 +1,8 @@ # 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 => '_tryruby_session', - :secret => '522523ceaba8ca180200d1931128947224663751e03dc442c9cba14d758b80fe166eb4bb2c0b2521a78152e0ae5c3e29237bc826aadb55b8182b1b293af75163' -} +TryRuby::Application.config.session_store :cookie_store, :key => '_TryRuby_session' # 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 +# (create the session table with "rails generate session_migration") +# TryRuby::Application.config.session_store :active_record_store diff --git a/tryruby/config/locales/en.yml b/tryruby/config/locales/en.yml index f265c06..a747bfa 100644 --- a/tryruby/config/locales/en.yml +++ b/tryruby/config/locales/en.yml @@ -2,4 +2,4 @@ # See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. en: - hello: "Hello world" \ No newline at end of file + hello: "Hello world" diff --git a/tryruby/config/routes.rb b/tryruby/config/routes.rb index cc9cbdb..3d5bd3f 100644 --- a/tryruby/config/routes.rb +++ b/tryruby/config/routes.rb @@ -1,7 +1,64 @@ -ActionController::Routing::Routes.draw do |map| - # You can have the root of your site routed with map.root -- just remember to delete public/index.html. - map.root :controller => "tryruby" - - map.connect ':controller/:action/:id' - map.connect ':controller/:action/:id.:format' +TryRuby::Application.routes.draw do + resources :irb + + # The priority is based upon order of creation: + # first created -> highest priority. + root :to => "tryruby#index" + match '/tryruby/run' => 'tryruby#run' + # connect ':controller/:action/:id' + # connect ':controller/:action/:id.:format' + + # Sample of regular route: + # match 'products/:id' => 'catalog#view' + # Keep in mind you can assign values other than :controller and :action + + # Sample of named route: + # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase + # This route can be invoked with purchase_url(:id => product.id) + + # Sample resource route (maps HTTP verbs to controller actions automatically): + # resources :products + + # Sample resource route with options: + # resources :products do + # member do + # get 'short' + # post 'toggle' + # end + # + # collection do + # get 'sold' + # end + # end + + # Sample resource route with sub-resources: + # resources :products do + # resources :comments, :sales + # resource :seller + # end + + # Sample resource route with more complex sub-resources + # resources :products do + # resources :comments + # resources :sales do + # get 'recent', :on => :collection + # end + # end + + # Sample resource route within a namespace: + # namespace :admin do + # # Directs /admin/products/* to Admin::ProductsController + # # (app/controllers/admin/products_controller.rb) + # resources :products + # end + + # You can have the root of your site routed with "root" + # just remember to delete public/index.html. + # root :to => "welcome#index" + + # See how all your routes lay out with "rake routes" + + # This is a legacy wild controller route that's not recommended for RESTful applications. + # Note: This route will make all actions in every controller accessible via GET requests. + # match ':controller(/:action(/:id(.:format)))' end diff --git a/tryruby/db/seeds.rb b/tryruby/db/seeds.rb index 3174d0c..664d8c7 100644 --- a/tryruby/db/seeds.rb +++ b/tryruby/db/seeds.rb @@ -2,6 +2,6 @@ # 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) +# Mayor.create(:name => 'Daley', :city => cities.first) diff --git a/tryruby/lib/tryruby.rb b/tryruby/lib/tryruby.rb index e6933a8..4bd5b04 100644 --- a/tryruby/lib/tryruby.rb +++ b/tryruby/lib/tryruby.rb @@ -3,6 +3,8 @@ require 'setup.rb' require 'fakefs/safe' + + module FakeFS class Dir @@ -252,7 +254,7 @@ def run_line(code) result = Thread.new { eval cmd, TOPLEVEL_BINDING }.value rescue SecurityError => e puts e - return Output.illegal + return Output.illegal :illegal => e, :output => get_stdout rescue Exception => e return Output.error :error => e, :output => get_stdout ensure diff --git a/tryruby/public/images/header.png b/tryruby/public/images/header.png index 1a6a3f243063ba8e2a93933357efbd3174f581ae..d6b9aa0f3ce2e738d3d12c68b52c78175322741c 100644 GIT binary patch literal 51873 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 diff --git a/tryruby/public/javascripts/jquery.console.js b/tryruby/public/javascripts/jquery.console.js index 651da47..d45390d 100644 --- a/tryruby/public/javascripts/jquery.console.js +++ b/tryruby/public/javascripts/jquery.console.js @@ -1,7 +1,7 @@ // JQuery Console 1.0 // Sun Feb 21 20:28:47 GMT 2010 // -// Copyright 2010 Chris Done. All rights reserved. +// Copyright 2010 Chris Done, Simon David Pratt. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions @@ -10,43 +10,86 @@ // 1. Redistributions of source code must retain the above // copyright notice, this list of conditions and the following // disclaimer. - +// // 2. Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials // provided with the distribution. // -// THIS SOFTWARE IS PROVIDED BY CHRIS DONE ``AS IS'' AND ANY EXPRESS -// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL CHRIS DONE OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. - -// The views and conclusions contained in the software and -// documentation are those of the authors and should not be -// interpreted as representing official policies, either expressed or -// implied, of Chris Done. -// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + // TESTED ON // Internet Explorer 6 // Opera 10.01 // Chromium 4.0.237.0 (Ubuntu build 31094) -// Firefox 3.5.8 +// Firefox 3.5.8, 3.6.2 (Mac) +// Safari 4.0.5 (6531.22.7) (Mac) +// Google Chrome 5.0.375.55 (Mac) (function($){ $.fn.console = function(config){ //////////////////////////////////////////////////////////////////////// // Constants // Some are enums, data types, others just for optimisation - var keyCodes = { left:37,right:39,up:38,down:40,back:8,del:46, - end:35,start:36,ret:13 }; + var keyCodes = { + // left + 37: moveBackward, + // right + 39: moveForward, + // up + 38: previousHistory, + // down + 40: nextHistory, + // backspace + 8: backDelete, + // delete + 46: forwardDelete, + // end + 35: moveToEnd, + // start + 36: moveToStart, + // return + 13: commandTrigger, + // tab + 18: doNothing + }; + var ctrlCodes = { + // C-a + 65: moveToStart, + // C-e + 69: moveToEnd, + // C-d + 68: forwardDelete, + // C-n + 78: nextHistory, + // C-p + 80: previousHistory, + // C-b + 66: moveBackward, + // C-f + 70: moveForward, + // C-k + 75: deleteUntilEnd + }; + var altCodes = { + // M-f + 70: moveToNextWord, + // M-b + 66: moveToPreviousWord, + // M-d + 68: deleteNextWord + }; var cursor = ' '; // Opera only works with this character, not or ­, // but IE6 displays this character, which is bad, so just use @@ -76,6 +119,10 @@ // variable below to ignore the keypress event if the keydown // event succeeds. var cancelKeyPress = 0; + // When this value is false, the prompt will not respond to input + var acceptInput = true; + // When this value is true, the command has been canceled + var cancelCommand = false; // External exports object var extern = {}; @@ -98,6 +145,7 @@ },100); } extern.inner = inner; + extern.typer = typer; extern.scrollToBottom = scrollToBottom; })(); @@ -152,6 +200,8 @@ function newPromptBox() { column = 0; promptText = ''; + ringn = 0; // Reset the position of the history ring + enableInput(); promptBox = $('
      '); var label = $(''); promptBox.append(label.text(promptLabel).show()); @@ -185,93 +235,50 @@ typer.keydown(function(e){ cancelKeyPress = 0; var keyCode = e.keyCode; - if (isControlCharacter(keyCode)) { - cancelKeyPress = keyCode; - if (!typer.consoleControl(keyCode)) { - return false; - } - } + // C-c: cancel the execution + if(e.ctrlKey && keyCode == 67) { + cancelKeyPress = keyCode; + cancelExecution(); + return false; + } + if (acceptInput) { + if (keyCode in keyCodes) { + cancelKeyPress = keyCode; + (keyCodes[keyCode])(); + return false; + } else if (e.ctrlKey && keyCode in ctrlCodes) { + cancelKeyPress = keyCode; + (ctrlCodes[keyCode])(); + return false; + } else if (e.altKey && keyCode in altCodes) { + cancelKeyPress = keyCode; + (altCodes[keyCode])(); + return false; + } + } }); //////////////////////////////////////////////////////////////////////// // Handle key press typer.keypress(function(e){ var keyCode = e.keyCode || e.which; - if (cancelKeyPress != keyCode && keyCode >= 32){ + if (isIgnorableKey(e)) { + return false; + } + if (acceptInput && cancelKeyPress != keyCode && keyCode >= 32){ if (cancelKeyPress) return false; - if (typeof config.charInsertTrigger == 'function' && - config.charInsertTrigger(keyCode)) + if (typeof config.charInsertTrigger == 'undefined' || + (typeof config.charInsertTrigger == 'function' && + config.charInsertTrigger(keyCode,promptText))) typer.consoleInsert(keyCode); } if ($.browser.webkit) return false; }); - // Is a keycode a contorl character? - // E.g. up, down, left, right, backspc, return, etc. - function isControlCharacter(keyCode){ - // TODO: Make more precise/fast. - return ( - (keyCode >= keyCodes.left && keyCode <= keyCodes.down) - || keyCode == keyCodes.back || keyCode == keyCodes.del - || keyCode == keyCodes.end || keyCode == keyCodes.start - || keyCode == keyCodes.ret - ); - }; - - //////////////////////////////////////////////////////////////////////// - // Handle console control keys - // E.g. up, down, left, right, backspc, return, etc. - typer.consoleControl = function(keyCode){ - switch (keyCode){ - case keyCodes.left:{ - moveColumn(-1); - updatePromptDisplay(); - return false; - break; - } - case keyCodes.right:{ - moveColumn(1); - updatePromptDisplay(); - return false; - break; - } - case keyCodes.back:{ - if (moveColumn(-1)){ - deleteCharAtPos(); - updatePromptDisplay(); - } - return false; - break; - } - case keyCodes.del:{ - if (deleteCharAtPos()) - updatePromptDisplay(); - return false; - break; - } - case keyCodes.end:{ - if (moveColumn(promptText.length-column)) - updatePromptDisplay(); - return false; - break; - } - case keyCodes.start:{ - if (moveColumn(-column)) - updatePromptDisplay(); - return false; - break; - } - case keyCodes.ret:{ - commandTrigger(); return false; - } - case keyCodes.up:{ - rotateHistory(-1); return false; - } - case keyCodes.down:{ - rotateHistory(1); return false; - } - default: //alert("Unknown control character: " + keyCode); - } + function isIgnorableKey(e) { + // for now just filter alt+tab that we receive on some platforms when + // user switches windows (goes away from the browser) + return ((e.keyCode == keyCodes.tab || e.keyCode == 192) && e.altKey); }; //////////////////////////////////////////////////////////////////////// @@ -301,6 +308,14 @@ updatePromptDisplay(); }; + function previousHistory() { + rotateHistory(-1); + }; + + function nextHistory() { + rotateHistory(1); + }; + // Add something to the history ring function addToHistory(line){ history.push(line); @@ -309,7 +324,7 @@ // Delete the character at the current position function deleteCharAtPos(){ - if (promptText != ''){ + if (column < promptText.length){ promptText = promptText.substring(0,column) + promptText.substring(column+1); @@ -318,6 +333,41 @@ } else return false; }; + function backDelete() { + if (moveColumn(-1)){ + deleteCharAtPos(); + updatePromptDisplay(); + } + }; + + function forwardDelete() { + if (deleteCharAtPos()) + updatePromptDisplay(); + }; + + function deleteUntilEnd() { + while(deleteCharAtPos()) { + updatePromptDisplay(); + } + }; + + function deleteNextWord() { + // A word is defined within this context as a series of alphanumeric + // characters. + // Delete up to the next alphanumeric character + while(column < promptText.length && + !isCharAlphanumeric(promptText[column])) { + deleteCharAtPos(); + updatePromptDisplay(); + } + // Then, delete until the next non-alphanumeric character + while(column < promptText.length && + isCharAlphanumeric(promptText[column])) { + deleteCharAtPos(); + updatePromptDisplay(); + } + }; + //////////////////////////////////////////////////////////////////////// // Validate command and trigger it if valid, or show a validation error function commandTrigger() { @@ -341,35 +391,48 @@ inner.attr({ scrollTop: inner.attr("scrollHeight") });; }; + function cancelExecution() { + if(typeof config.cancelHandle == 'function') { + config.cancelHandle(); + } + } + //////////////////////////////////////////////////////////////////////// // Handle a command function handleCommand() { if (typeof config.commandHandle == 'function') { + disableInput(); + addToHistory(promptText); var ret = config.commandHandle(promptText,function(msgs){ commandResult(msgs); }); if (typeof ret == 'boolean') { if (ret) { // Command succeeded without a result. - addToHistory(promptText); commandResult(); } else { - addToHistory(promptText); commandResult('Command failed.', "jquery-console-message-error"); } } else if (typeof ret == "string") { - addToHistory(promptText); commandResult(ret,"jquery-console-message-success"); - } else if (typeof ret == 'undefined') { - addToHistory(promptText); - } else if (ret.length) { - addToHistory(promptText); + } else if (typeof ret == 'object' && ret.length) { commandResult(ret); } } }; + //////////////////////////////////////////////////////////////////////// + // Disable input + function disableInput() { + acceptInput = false; + }; + + // Enable input + function enableInput() { + acceptInput = true; + } + //////////////////////////////////////////////////////////////////////// // Reset the prompt in invalid command function commandResult(msg,className) { @@ -419,6 +482,78 @@ } else return false; }; + function moveForward() { + if(moveColumn(1)) { + updatePromptDisplay(); + return true; + } + return false; + }; + + function moveBackward() { + if(moveColumn(-1)) { + updatePromptDisplay(); + return true; + } + return false; + }; + + function moveToStart() { + if (moveColumn(-column)) + updatePromptDisplay(); + }; + + function moveToEnd() { + if (moveColumn(promptText.length-column)) + updatePromptDisplay(); + }; + + function moveToNextWord() { + while(column < promptText.length && + !isCharAlphanumeric(promptText[column]) && + moveForward()) { + } + while(column < promptText.length && + isCharAlphanumeric(promptText[column]) && + moveForward()) { + } + }; + + function moveToPreviousWord() { + // Move backward until we find the first alphanumeric + while(column -1 >= 0 && + !isCharAlphanumeric(promptText[column-1]) && + moveBackward()) { + } + // Move until we find the first non-alphanumeric + while(column -1 >= 0 && + isCharAlphanumeric(promptText[column-1]) && + moveBackward()) { + } + }; + + function isCharAlphanumeric(charToTest) { + if(typeof charToTest == 'string') { + var code = charToTest.charCodeAt(); + return (code >= 'A'.charCodeAt() && code <= 'Z'.charCodeAt()) || + (code >= 'a'.charCodeAt() && code <= 'z'.charCodeAt()) || + (code >= '0'.charCodeAt() && code <= '9'.charCodeAt()); + } + return false; + }; + + function doNothing() {}; + + extern.promptText = function(text){ + if (text) { + promptText = text; + if (column > promptText.length) + column = promptText.length; + updatePromptDisplay(); + } + return promptText; + }; + //////////////////////////////////////////////////////////////////////// // Update the prompt display function updatePromptDisplay(){ diff --git a/tryruby/public/stylesheets/site.css b/tryruby/public/stylesheets/site.css index 307347e..62b1526 100755 --- a/tryruby/public/stylesheets/site.css +++ b/tryruby/public/stylesheets/site.css @@ -5,7 +5,7 @@ body { } h1, h2, h3, h4 { font-family: georgia, serif; - margin: 10px 16px; padding: 0; + margin: 10px 45px; padding: 0; } h1 { color: #333; @@ -37,12 +37,12 @@ input.keyboard-selector-input { _top: expression(eval(document.body.scrollTop)); left: -300px; } -#container { +.container { width: 678px; margin: 0 auto; text-align: left; } -#content { +.content { width: 712px; background: url(/images/tile.png) repeat-y; } @@ -73,7 +73,7 @@ p#lbClose { p#lbClose a { display: inline; } -#shellwin { +.shellwin { width: 712px; padding-left: 12px; background: url(/images/background.png) no-repeat; @@ -149,26 +149,25 @@ code strong { background-color: #dcffb9; padding: 1px 3px; } ul.commands li strong { display: block; float: left; width: 60px; border: none; } /* irb terminal */ -#terminal { - background-color: #f2f2f0; +.terminal { + background-color: #ffffff; border: solid 1px #204a87; width: 678px; height: 240px; overflow: auto; } -#irb { - visibility: hidden; - padding: 4px; margin: 0; +.console { + padding: 4px; margin-left: -50px; font-family: "Andale Mono", courier, fixed, monospace; font-size: 14px; line-height: 16px; color: #204a87; text-align: left; + width: 664px; + height:220px; } -#irb div { - margin: 0; padding: 0; -} -#irb div b { + +.console div b { background-color: #874a20; color: #fedeac; } @@ -209,3 +208,171 @@ span.back_purple { background-color: #5c3566; } span.back_lt_purple { background-color: #ad7fa8; } span.back_brown { background-color: #8f5902; } span.back_lt_brown { background-color: #b9b96e; } + +/** no ways***/ + +div.main-wrapper-bottom { + height:4px;background-position: 0px -80px; background-repeat: no-repeat; + font-size:0 /* IE6, go figure */ +} +div.main-wrapper-borders { + background-position: -1731px 0; padding:15px; + background-repeat:repeat-y; +} +div.console-wrapper { + margin:0px auto ;width:566px; + cursor:text; + font-family:monospace; +} +div.console-wrapper-top { + height:3px;background-position: -50px -48px; background-repeat: no-repeat; + font-size:0 /* IE6, go figure */ +} +div.console-wrapper-bottom { + height:3px;background-position: -50px -51px; background-repeat: no-repeat; + font-size:0 /* IE6, go figure */ +} +div.console-wrapper-borders { + background-position: 0px 0px; padding:1px; + background-repeat:repeat-y; +} +div.guide-wrapper { + color:#fff; width:566px;margin-left:2px +} +div.guide-wrapper-top { + height:4px;background-position: 0 -65px; + font-size:0 /* IE6, go figure */ +} +div.guide-wrapper-bottom { + height:4px;background-position: 0 -70px; + font-size:0 /* IE6, go figure */ +} +div.guide-wrapper-borders { + background-position: -1166px 0px;padding:15px; + background-repeat:repeat-y; +} + +div.footer-wrapper-borders { + background-position: -566px 0px; padding:10px; + background-repeat:repeat-y; + font-size:12px +} +h1.main-header { + text-indent:-9999px; background-position: -49px 0px; + background-repeat: no-repeat; + width:318px; height:48px; margin-bottom:20px; + float:left +} + +div.footer { + line-height: 1.3em; +} + div.console div.jquery-console-inner +{ height:100%; overflow:auto; background:white} +div.console div.jquery-console-prompt-box +{ color:#437375; font-family:monospace; margin-top:0.5em; } +div.console div.jquery-console-prompt-box .prompt-done +{ cursor: pointer } +div.console div.jquery-console-prompt-box .prompt-done:hover +{ background:#453D5B; color: white; } +div.console div.jquery-console-focus span.jquery-console-cursor +{ background:#666; color:#fff; } +div.console div.jquery-console-message-error { + color:#ef0505; font-family:sans-serif; font-weight:bold; + padding-top:0.25em +} +div.console div.jquery-console-message-value +{ color:#000; font-family:monospace;padding-top:0.25em; font-weight: bold; } +div.console div.jquery-console-message-type +{ color:#382567; font-family:monospace;padding-left:0em;padding-top:0.25em; font-size:.9em } +div.console span.jquery-console-prompt-label { font-weight:bold } +div.console div.jquery-console-welcome { font-family:"DejaVu Sans",sans-serif; } + +div.share-wrapper { font-size:12px;padding:10px 0em 0em 10px } +div.share-wrapper strong { font-weight: bold } + +.clearfix:after { content:"."; display:block; height:0; clear:both; visibility:hidden } +div.menu { + float:right; + margin-right:2px; + margin-top:40px; + margin-bottom:5px +} +a.reset-btn { + float:left; + display:block; + width:59px; + height:24px; + background-position: -427px -18px; background-repeat: no-repeat; +} +a.reset-btn span { display:none } + +div.clear { clear:both } + + +div.console-wrapper .notice { + position:absolute; + bottom:0;right:0; + margin:1px; + background:#eee; + color:black; + padding:10px; + font-size:12px; + font-family:sans-serif; + font-weight:bold; +} +p.ajax-loader { background:url(../images/ajax-loader.gif); width:16px; height:16px;text-indent:-9999px } + +.notice a { padding:3px;background:#333;color:white} +.notice .action { text-align: right } + +/* Support Try Ruby! */ + + +a.trigger{ +position: absolute; +text-decoration: none; +top: 80px; right: 0; +font-size: 14px; +letter-spacing:-1px; +font-family: verdana, helvetica, arial, sans-serif; +color:#fff; +padding: 10px 10px 10px 10px; +font-weight: 500; +background:#333333 url(images/plus.png) 15% 55% no-repeat; +border:1px solid #444444; +-moz-border-radius-topleft: 20px; +-webkit-border-top-left-radius: 20px; +-moz-border-radius-bottomleft: 20px; +-webkit-border-bottom-left-radius: 20px; +-moz-border-radius-bottomright: 0px; +-webkit-border-bottom-right-radius: 0px; +display: block; +} + +a.trigger:hover{ +position: absolute; +text-decoration: none; +top: 80px; right: 0; +font-size: 14px; +letter-spacing:-1px; +font-family: verdana, helvetica, arial, sans-serif; +color:#fff; +padding: 10px 10px 10px 10px; +font-weight: 500; +background: green; +border:1px solid #444444; +-moz-border-radius-topleft: 20px; +-webkit-border-top-left-radius: 20px; +-moz-border-radius-bottomleft: 20px; +-webkit-border-bottom-left-radius: 20px; +-moz-border-radius-bottomright: 0px; +-webkit-border-bottom-right-radius: 0px; +display: block; +} + +a.active.trigger { +background:#222222 url(images/minus.png) 15% 55% no-repeat; +} + + \ No newline at end of file diff --git a/tryruby/test/performance/browsing_test.rb b/tryruby/test/performance/browsing_test.rb index 4b60558..867fc8c 100644 --- a/tryruby/test/performance/browsing_test.rb +++ b/tryruby/test/performance/browsing_test.rb @@ -1,8 +1,8 @@ require 'test_helper' -require 'performance_test_help' +require 'rails/performance_test_help' # Profiling results for each test method are written to tmp/performance. -class BrowsingTest < ActionController::PerformanceTest +class BrowsingTest < ActionDispatch::PerformanceTest def test_homepage get '/' end diff --git a/tryruby/test/test_helper.rb b/tryruby/test/test_helper.rb index 24632d8..8bf1192 100644 --- a/tryruby/test/test_helper.rb +++ b/tryruby/test/test_helper.rb @@ -1,60 +1,13 @@ ENV["RAILS_ENV"] = "test" -require File.expand_path(File.dirname(__FILE__) + "/../config/environment") -require 'test_help' -require 'rexml/document' -require 'hpricot' - -class Integer - def weeks - self * 7*24*60*60 - end -end - - -def run_script(session,line) - TryRuby.run_line(line) -end - -class TryRubyTestSession < TryRuby::Session - def initialize - @current_includes = [] - end - - attr_accessor :start_time, :current_statement - attr_accessor :nesting_level, :past_commands, :current_includes -end +require File.expand_path('../../config/environment', __FILE__) +require 'rails/test_help' class ActiveSupport::TestCase - # Transactional fixtures accelerate your tests by wrapping each test method - # in a transaction that's rolled back on completion. This ensures that the - # test database remains unchanged so your fixtures don't have to be reloaded - # between every test method. Fewer database queries means faster tests. - # - # Read Mike Clark's excellent walkthrough at - # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting - # - # Every Active Record database supports transactions except MyISAM tables - # in MySQL. Turn off transactional fixtures in this case; however, if you - # don't care one way or the other, switching from MyISAM to InnoDB tables - # is recommended. - # - # The only drawback to using transactional fixtures is when you actually - # need to test transactions. Since your test is bracketed by a transaction, - # any transactions started in your code will be automatically rolled back. - #self.use_transactional_fixtures = true - - # Instantiated fixtures are slow, but give you @david where otherwise you - # would need people(:david). If you don't want to migrate your existing - # test cases which use the @david style and don't mind the speed hit (each - # instantiated fixtures translates to a database query per test method), - # then set this back to true. - #self.use_instantiated_fixtures = false - # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. # # Note: You'll currently still have to declare fixtures explicitly in integration tests # -- they do not yet inherit this setting - #fixtures :all + fixtures :all # Add more helper methods to be used by all tests here... end diff --git a/tryruby/tmp/pids/server.pid b/tryruby/tmp/pids/server.pid index ade6218..3acadad 100644 --- a/tryruby/tmp/pids/server.pid +++ b/tryruby/tmp/pids/server.pid @@ -1 +1 @@ -4772 \ No newline at end of file +48625 \ No newline at end of file