From 185d018ecd6eec3453b6032222d4be24010b9a56 Mon Sep 17 00:00:00 2001 From: John Duff Date: Mon, 26 Sep 2011 23:35:23 -0400 Subject: [PATCH] Initial commit --- .gitignore | 4 + Gemfile | 4 + README.md | 73 + Rakefile | 1 + batman-rails.gemspec | 26 + lib/batman-rails.rb | 1 + lib/batman/rails.rb | 6 + lib/batman/rails/engine.rb | 6 + lib/batman/rails/version.rb | 6 + lib/generators/batman/common.rb | 45 + lib/generators/batman/controller_generator.rb | 33 + lib/generators/batman/helper_generator.rb | 14 + lib/generators/batman/install_generator.rb | 49 + lib/generators/batman/model_generator.rb | 29 + lib/generators/batman/scaffold_generator.rb | 19 + .../batman/templates/batman_app.coffee | 25 + .../batman/templates/controller.coffee | 5 + lib/generators/batman/templates/helper.coffee | 5 + lib/generators/batman/templates/model.coffee | 7 + test/controller_generator_test.rb | 40 + test/fixtures/application.js | 9 + test/install_generator_test.rb | 82 + test/model_generator_test.rb | 44 + test/sample/.gitignore | 5 + test/sample/Gemfile | 30 + test/sample/README | 261 + test/sample/Rakefile | 7 + test/sample/app/assets/images/rails.png | Bin 0 -> 6646 bytes .../app/assets/javascripts/application.js | 9 + .../app/assets/stylesheets/application.css | 7 + .../app/controllers/application_controller.rb | 3 + test/sample/app/helpers/application_helper.rb | 2 + test/sample/app/mailers/.gitkeep | 0 test/sample/app/models/.gitkeep | 0 .../app/views/layouts/application.html.erb | 14 + test/sample/config.ru | 4 + test/sample/config/application.rb | 48 + test/sample/config/boot.rb | 6 + test/sample/config/database.yml | 25 + test/sample/config/environment.rb | 5 + .../sample/config/environments/development.rb | 30 + test/sample/config/environments/production.rb | 60 + test/sample/config/environments/test.rb | 42 + .../initializers/backtrace_silencers.rb | 7 + .../sample/config/initializers/inflections.rb | 10 + test/sample/config/initializers/mime_types.rb | 5 + .../config/initializers/secret_token.rb | 7 + .../config/initializers/session_store.rb | 8 + .../config/initializers/wrap_parameters.rb | 14 + test/sample/config/locales/en.yml | 5 + test/sample/config/routes.rb | 58 + test/sample/db/seeds.rb | 7 + test/sample/doc/README_FOR_APP | 2 + test/sample/lib/assets/.gitkeep | 0 test/sample/lib/tasks/.gitkeep | 0 test/sample/log/.gitkeep | 0 test/sample/public/404.html | 26 + test/sample/public/422.html | 26 + test/sample/public/500.html | 26 + test/sample/public/favicon.ico | 0 test/sample/public/index.html | 241 + test/sample/public/robots.txt | 5 + test/sample/script/rails | 6 + test/sample/test/fixtures/.gitkeep | 0 test/sample/test/functional/.gitkeep | 0 test/sample/test/integration/.gitkeep | 0 test/sample/test/performance/browsing_test.rb | 12 + test/sample/test/test_helper.rb | 13 + test/sample/test/unit/.gitkeep | 0 .../sample/vendor/assets/stylesheets/.gitkeep | 0 test/sample/vendor/plugins/.gitkeep | 0 test/test_helper.rb | 24 + .../regular_users_controller.js.coffee | 3 + .../javascripts/batman/batman.jquery.js | 73 + vendor/assets/javascripts/batman/batman.js | 4717 +++++++++++++++++ .../assets/javascripts/batman/batman.rails.js | 58 + .../assets/javascripts/batman/batman.solo.js | 415 ++ 77 files changed, 6859 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 README.md create mode 100644 Rakefile create mode 100644 batman-rails.gemspec create mode 100644 lib/batman-rails.rb create mode 100644 lib/batman/rails.rb create mode 100644 lib/batman/rails/engine.rb create mode 100644 lib/batman/rails/version.rb create mode 100644 lib/generators/batman/common.rb create mode 100644 lib/generators/batman/controller_generator.rb create mode 100644 lib/generators/batman/helper_generator.rb create mode 100644 lib/generators/batman/install_generator.rb create mode 100644 lib/generators/batman/model_generator.rb create mode 100644 lib/generators/batman/scaffold_generator.rb create mode 100644 lib/generators/batman/templates/batman_app.coffee create mode 100644 lib/generators/batman/templates/controller.coffee create mode 100644 lib/generators/batman/templates/helper.coffee create mode 100644 lib/generators/batman/templates/model.coffee create mode 100644 test/controller_generator_test.rb create mode 100644 test/fixtures/application.js create mode 100644 test/install_generator_test.rb create mode 100644 test/model_generator_test.rb create mode 100644 test/sample/.gitignore create mode 100644 test/sample/Gemfile create mode 100644 test/sample/README create mode 100644 test/sample/Rakefile create mode 100644 test/sample/app/assets/images/rails.png create mode 100644 test/sample/app/assets/javascripts/application.js create mode 100644 test/sample/app/assets/stylesheets/application.css create mode 100644 test/sample/app/controllers/application_controller.rb create mode 100644 test/sample/app/helpers/application_helper.rb create mode 100644 test/sample/app/mailers/.gitkeep create mode 100644 test/sample/app/models/.gitkeep create mode 100644 test/sample/app/views/layouts/application.html.erb create mode 100644 test/sample/config.ru create mode 100644 test/sample/config/application.rb create mode 100644 test/sample/config/boot.rb create mode 100644 test/sample/config/database.yml create mode 100644 test/sample/config/environment.rb create mode 100644 test/sample/config/environments/development.rb create mode 100644 test/sample/config/environments/production.rb create mode 100644 test/sample/config/environments/test.rb create mode 100644 test/sample/config/initializers/backtrace_silencers.rb create mode 100644 test/sample/config/initializers/inflections.rb create mode 100644 test/sample/config/initializers/mime_types.rb create mode 100644 test/sample/config/initializers/secret_token.rb create mode 100644 test/sample/config/initializers/session_store.rb create mode 100644 test/sample/config/initializers/wrap_parameters.rb create mode 100644 test/sample/config/locales/en.yml create mode 100644 test/sample/config/routes.rb create mode 100644 test/sample/db/seeds.rb create mode 100644 test/sample/doc/README_FOR_APP create mode 100644 test/sample/lib/assets/.gitkeep create mode 100644 test/sample/lib/tasks/.gitkeep create mode 100644 test/sample/log/.gitkeep create mode 100644 test/sample/public/404.html create mode 100644 test/sample/public/422.html create mode 100644 test/sample/public/500.html create mode 100644 test/sample/public/favicon.ico create mode 100644 test/sample/public/index.html create mode 100644 test/sample/public/robots.txt create mode 100755 test/sample/script/rails create mode 100644 test/sample/test/fixtures/.gitkeep create mode 100644 test/sample/test/functional/.gitkeep create mode 100644 test/sample/test/integration/.gitkeep create mode 100644 test/sample/test/performance/browsing_test.rb create mode 100644 test/sample/test/test_helper.rb create mode 100644 test/sample/test/unit/.gitkeep create mode 100644 test/sample/vendor/assets/stylesheets/.gitkeep create mode 100644 test/sample/vendor/plugins/.gitkeep create mode 100644 test/test_helper.rb create mode 100644 tmp/app/assets/javascripts/controllers/regular_users_controller.js.coffee create mode 100644 vendor/assets/javascripts/batman/batman.jquery.js create mode 100644 vendor/assets/javascripts/batman/batman.js create mode 100644 vendor/assets/javascripts/batman/batman.rails.js create mode 100644 vendor/assets/javascripts/batman/batman.solo.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4040c6c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.gem +.bundle +Gemfile.lock +pkg/* diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..8c8c833 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source "http://rubygems.org" + +# Specify your gem's dependencies in batman-rails.gemspec +gemspec diff --git a/README.md b/README.md new file mode 100644 index 0000000..aaf46d6 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# Batman-Rails + +Easily setup and use batman.js (0.6.0) with rails 3.1 + +## Rails 3.1 setup +This gem requires the use of rails 3.1, coffeescript and the new rails asset pipeline provided by sprockets. + +This gem vendors the latest version of batman.js for Rails 3.1 and greater. The files will be added to the asset pipeline and available for you to use. + +### Installation + +In your Gemfile, add this line: + + gem "batman-rails" + +Then run the following commands: + + bundle install + rails g batman:install + +### Layout and namespacing + +Running `rails g batman:install` will create the following directory structure under `app/assets/javascripts/`: + + controllers/ + models/ + helpers/ + +It will also create a toplevel app_name.coffee file to setup namespacing and setup initial requires. + +## Generators +batman-rails provides 3 simple generators to help get you started using batman.js with rails 3.1. +The generators will only create client side code (javascript). + +### Model Generator + + rails g batman:model + +This generator creates a batman model and collection inside `app/assets/javascript/models` to be used to talk to the rails backend. + +### Controllers + + rails g batman:controller + +This generator creates a batman controller for the given actions provided. + +### Scaffolding + + rails g batman:scaffold + +This generator creates a controller, helper and mode to create a simple crud single page app + +## Example Usage + +Created a new rails 3.1 application called `blog`. + + rails new blog + +Edit your Gemfile and add + + gem 'batman-rails' + +Install the gem and generate scaffolding. + + bundle install + rails g batman:install + rails g scaffold Post title:string content:string + rake db:migrate + rails g batman:scaffold Post title:string content:string + +You now have installed the batman-rails gem, setup a default directory structure for your frontend batman code. +Then you generated the usual rails server side crud scaffolding and finally generated batman.js code to provide a simple single page crud app. +You have one last step: diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..c702cfc --- /dev/null +++ b/Rakefile @@ -0,0 +1 @@ +require 'bundler/gem_tasks' diff --git a/batman-rails.gemspec b/batman-rails.gemspec new file mode 100644 index 0000000..432bc4a --- /dev/null +++ b/batman-rails.gemspec @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +$:.push File.expand_path("../lib", __FILE__) +require "batman/rails/version" + +Gem::Specification.new do |s| + s.name = "batman-rails" + s.version = Batman::Rails::VERSION + s.authors = ["John Duff"] + s.email = ["john.duff@jadedpixel.com"] + s.homepage = "" + s.summary = %q{} + s.description = %q{} + + s.rubyforge_project = "batman-rails" + + s.add_dependency "railties", "~> 3.1.0" + s.add_dependency "thor", "~> 0.14" + s.add_development_dependency "bundler", "~> 1.0.0" + s.add_development_dependency "rails", "~> 3.1.0" + s.add_development_dependency "mocha" + + s.files = `git ls-files`.split("\n") + s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + s.require_paths = ["lib"] +end diff --git a/lib/batman-rails.rb b/lib/batman-rails.rb new file mode 100644 index 0000000..3bffa61 --- /dev/null +++ b/lib/batman-rails.rb @@ -0,0 +1 @@ +require "batman/rails" diff --git a/lib/batman/rails.rb b/lib/batman/rails.rb new file mode 100644 index 0000000..4fcac76 --- /dev/null +++ b/lib/batman/rails.rb @@ -0,0 +1,6 @@ +require "batman/rails/version" +require "batman/rails/engine" +module Batman + module Rails + end +end diff --git a/lib/batman/rails/engine.rb b/lib/batman/rails/engine.rb new file mode 100644 index 0000000..d94f8af --- /dev/null +++ b/lib/batman/rails/engine.rb @@ -0,0 +1,6 @@ +module Batman + module Rails + class Engine < ::Rails::Engine + end + end +end diff --git a/lib/batman/rails/version.rb b/lib/batman/rails/version.rb new file mode 100644 index 0000000..78e7140 --- /dev/null +++ b/lib/batman/rails/version.rb @@ -0,0 +1,6 @@ +module Batman + module Rails + VERSION = "0.0.1" + BATMAN_VERSION = "0.6.0" + end +end diff --git a/lib/generators/batman/common.rb b/lib/generators/batman/common.rb new file mode 100644 index 0000000..b6944ef --- /dev/null +++ b/lib/generators/batman/common.rb @@ -0,0 +1,45 @@ +module Batman + module Generators + module Common + def self.included(base) + base.send(:extend, ClassMethods) + base.source_root File.expand_path("../templates", __FILE__) + end + + protected + def with_app_name + raise "Batman application name must be given" unless app_name + yield + end + + def js_app_name + app_name.camelize + end + + def app_name + @app_name ||= options[:app_name] || application_name + end + + def application_name + if defined?(::Rails) && ::Rails.application + ::Rails.application.class.name.split('::').first.underscore + end + end + + def js_path + "app/assets/javascripts" + end + + def singular_model_name + singular_name.camelize + end + + module ClassMethods + def requires_app_name + class_option :app_name, :type => :string, :optional => true, + :desc => "Name of the Batman app (defaults to the Rails app name" + end + end + end + end +end diff --git a/lib/generators/batman/controller_generator.rb b/lib/generators/batman/controller_generator.rb new file mode 100644 index 0000000..17431b7 --- /dev/null +++ b/lib/generators/batman/controller_generator.rb @@ -0,0 +1,33 @@ +require 'generators/batman/common' +module Batman + module Generators + class ControllerGenerator < ::Rails::Generators::NamedBase + include Common + requires_app_name + + desc "This generator creates a Batman controller" + argument :actions, :type => :array, :default => [], :banner => "action action" + + + RESERVED_JS_WORDS = %w{ + break case catch continue debugger default delete do else finally for + function if in instanceof new return switch this throw try typeof var void while with + } + + def validate_no_reserved_words + actions.each do |action| + if RESERVED_JS_WORDS.include? action + raise Thor::Error, "The name '#{action}' is reserved by javascript " << + "Please choose an alternative action name and run this generator again." + end + end + end + + def create_batman_controller + with_app_name do + template "controller.coffee", "#{js_path}/controllers/#{plural_name.downcase}_controller.js.coffee" + end + end + end + end +end diff --git a/lib/generators/batman/helper_generator.rb b/lib/generators/batman/helper_generator.rb new file mode 100644 index 0000000..e32019e --- /dev/null +++ b/lib/generators/batman/helper_generator.rb @@ -0,0 +1,14 @@ +require 'generators/batman/common' +module Batman + module Generators + class HelperGenerator < ::Rails::Generators::NamedBase + include Common + + desc "This generator creates a Batman helper" + + def create_batman_helper + template "helper.coffee", "#{js_path}/helpers/#{plural_name.downcase}_helper.js.coffee" + end + end + end +end diff --git a/lib/generators/batman/install_generator.rb b/lib/generators/batman/install_generator.rb new file mode 100644 index 0000000..929c69d --- /dev/null +++ b/lib/generators/batman/install_generator.rb @@ -0,0 +1,49 @@ +require 'generators/batman/common' +module Batman + module Generators + class InstallGenerator < ::Rails::Generators::Base + include Common + requires_app_name + + desc "This generator installs Batman.js with a default folder layout" + + class_option :skip_git, :type => :boolean, :aliases => "-G", :default => false, + :desc => "Skip Git ignores and keeps" + + def create_batman_app + with_app_name do + template "batman_app.coffee", "#{js_path}/#{app_name}.js.coffee" + end + end + + def create_directories + %w(models controllers helpers).each do |dir| + empty_directory "#{js_path}/#{dir}" + create_file "#{js_path}/#{dir}/.gitkeep" unless options[:skip_git] + end + end + + def inject_batman + with_app_name do + inject_into_file "#{js_path}/application.js", :after=>/\/\/=(?!.*\/\/=).*?$/m do +<<-CODE +\n\n//= require batman/batman +//= require batman/batman.jquery +//= require batman/batman.rails + +//= require #{app_name} + +//= require_tree ./models +//= require_tree ./controllers +//= require_tree ./helpers + +$(document).ready(function(){ + #{js_app_name}.run(); +}); +CODE + end + end + end + end + end +end diff --git a/lib/generators/batman/model_generator.rb b/lib/generators/batman/model_generator.rb new file mode 100644 index 0000000..9cebdc5 --- /dev/null +++ b/lib/generators/batman/model_generator.rb @@ -0,0 +1,29 @@ +require 'generators/batman/common' +module Batman + module Generators + class ModelGenerator < ::Rails::Generators::NamedBase + include Common + requires_app_name + + desc "This generator creates a Batman model" + argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + + def create_batman_model + with_app_name do + template "model.coffee", "#{js_path}/models/#{file_name.downcase}.js.coffee" + end + end + + protected + def render_attribute(attribute) + type = case attribute.type.to_s + when 'date', 'datetime' + "Batman.Encoders.railsDate" + when 'string', 'integer', 'float', 'decimal', 'boolean', 'text' + end + + ["'#{attribute.name}'", type].compact.join(', ') + end + end + end +end diff --git a/lib/generators/batman/scaffold_generator.rb b/lib/generators/batman/scaffold_generator.rb new file mode 100644 index 0000000..6e41376 --- /dev/null +++ b/lib/generators/batman/scaffold_generator.rb @@ -0,0 +1,19 @@ +require 'generators/batman/common' +module Batman + module Generators + class ScaffoldGenerator < ::Rails::Generators::NamedBase + include Common + requires_app_name + + desc "This generator creates the client side CRUD scaffolding" + + def create_batman_model + with_app_name do + generate "batman:model #{singular_model_name} --app_name #{app_name}" + generate "batman:controller #{singular_model_name} index show create update destroy --app_name #{app_name}" + generate "batman:helper #{singular_model_name}" + end + end + end + end +end diff --git a/lib/generators/batman/templates/batman_app.coffee b/lib/generators/batman/templates/batman_app.coffee new file mode 100644 index 0000000..00ab416 --- /dev/null +++ b/lib/generators/batman/templates/batman_app.coffee @@ -0,0 +1,25 @@ +window.<%= js_app_name %> = class <%= js_app_name %> extends Batman.App + + # @root 'controller#all' + # @route '/controller/:id', 'controller#show', resource: 'model', action: 'show' + + @run -> + console.log "Running..." + true + + @ready -> + console.log "<%= js_app_name %> ready for use." + + @flash: Batman() + @flash.accessor + get: (key) -> @[key] + set: (key, value) -> + @[key] = value + if value isnt '' + setTimeout => + @set(key, '') + , 2000 + value + + @flashSuccess: (message) -> @set 'flash.success', message + @flashError: (message) -> @set 'flash.error', message diff --git a/lib/generators/batman/templates/controller.coffee b/lib/generators/batman/templates/controller.coffee new file mode 100644 index 0000000..e9801a2 --- /dev/null +++ b/lib/generators/batman/templates/controller.coffee @@ -0,0 +1,5 @@ +class <%= js_app_name %>.<%= plural_name.camelize %>Controller extends Batman.Controller +<% actions.each do |action| -%> + <%= action %>: (params) -> + +<% end -%> diff --git a/lib/generators/batman/templates/helper.coffee b/lib/generators/batman/templates/helper.coffee new file mode 100644 index 0000000..7cc7755 --- /dev/null +++ b/lib/generators/batman/templates/helper.coffee @@ -0,0 +1,5 @@ +# <%= plural_name.camelize %> helper file + +# Batman.mixin Batman.Filters, +# helper: (input) -> +# return input diff --git a/lib/generators/batman/templates/model.coffee b/lib/generators/batman/templates/model.coffee new file mode 100644 index 0000000..75ed5b9 --- /dev/null +++ b/lib/generators/batman/templates/model.coffee @@ -0,0 +1,7 @@ +class <%= js_app_name %>.<%= singular_model_name %> extends Batman.Model + @storageKey: '<%= plural_name %>' + @persist Batman.RailsStorage + +<% attributes.each do |attribute| -%> + @encode <%= render_attribute(attribute) %> +<% end -%> diff --git a/test/controller_generator_test.rb b/test/controller_generator_test.rb new file mode 100644 index 0000000..5ff8bc9 --- /dev/null +++ b/test/controller_generator_test.rb @@ -0,0 +1,40 @@ +require 'test_helper' +require 'generators/batman/controller_generator' + +class ControllerGeneratorTest < Rails::Generators::TestCase + tests Batman::Generators::ControllerGenerator + + test "simple controller" do + run_generator %w(Task index show) + + assert_file "#{javascripts_path}/controllers/tasks_controller.js.coffee" do |controller| + controller_class = Regexp.escape("class Sample.TasksController extends Batman.Controller") + + assert_match /#{controller_class}/, controller + assert_match %r{ index: \(params\) ->}, controller + assert_match %r{ show: \(params\) ->}, controller + end + end + + test "two word controller is camelcased" do + run_generator %w(RegularUser index) + + assert_file "#{javascripts_path}/controllers/regular_users_controller.js.coffee" do |controller| + controller_class = Regexp.escape("class Sample.RegularUsersController extends Batman.Controller") + + assert_match /#{controller_class}/, controller + assert_match %r{ index: \(params\) ->}, controller + end + end + + test "simple controller with app_name" do + run_generator %w(Task index --app_name MyApp) + + assert_file "#{javascripts_path}/controllers/tasks_controller.js.coffee" do |controller| + controller_class = Regexp.escape("class MyApp.TasksController extends Batman.Controller") + + assert_match /#{controller_class}/, controller + assert_match %r{ index: \(params\) ->}, controller + end + end +end diff --git a/test/fixtures/application.js b/test/fixtures/application.js new file mode 100644 index 0000000..37c7bfc --- /dev/null +++ b/test/fixtures/application.js @@ -0,0 +1,9 @@ +// This is a manifest file that'll be compiled into including all the files listed below. +// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically +// be included in the compiled file accessible from http://example.com/assets/application.js +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// the compiled file. +// +//= require jquery +//= require jquery_ujs +//= require_tree . diff --git a/test/install_generator_test.rb b/test/install_generator_test.rb new file mode 100644 index 0000000..a9e0daf --- /dev/null +++ b/test/install_generator_test.rb @@ -0,0 +1,82 @@ +require 'mocha' +require 'test_helper' +require 'generators/batman/install_generator' + +class InstallGeneratorTest < Rails::Generators::TestCase + tests Batman::Generators::InstallGenerator + + def setup + mkdir_p "#{destination_root}/app/assets/javascripts" + cp fixture("application.js"), "#{destination_root}/app/assets/javascripts" + Rails.application.class.stubs(:name).returns("Dummy::Application") + + super + end + + test "Assert Batman application file is created" do + run_generator + + assert_file "#{javascripts_path}/dummy.js.coffee" do |app| + assert_match /window\.Dummy = class Dummy extends Batman\.App/, app + assert_match /@run ->/, app + end + end + + test "Assert Batman application file is created for two word application name" do + Rails.application.class.stubs(:name).returns("FooBar::Application") + run_generator + + assert_file "#{javascripts_path}/foo_bar.js.coffee" do |app| + assert_match /window\.FooBar = class FooBar extends Batman\.App/, app + end + end + + test "Assert application require is properly setup for two word application name" do + Rails.application.class.stubs(:name).returns("FooBar::Application") + run_generator + + assert_file "#{javascripts_path}/application.js", /require foo_bar/ + end + + test "Assert Batman directory structure is created" do + run_generator + + %W{controllers models helpers}.each do |dir| + assert_directory "#{javascripts_path}/#{dir}" + assert_file "#{javascripts_path}/#{dir}/.gitkeep" + end + end + + test "Assert no gitkeep files are created when skipping git" do + run_generator [destination_root, "--skip-git"] + + %W{controllers models helpers}.each do |dir| + assert_directory "#{javascripts_path}/#{dir}" + assert_no_file "#{javascripts_path}/#{dir}/.gitkeep" + end + end + + test "Assert application.js require batman, batman.jquery, batman.rails and dummy.js" do + run_generator + + assert_file "#{javascripts_path}/application.js" do |app| + %W{batman batman.jquery batman.rails}.each do |require| + assert_match /require batman\/#{require}/, app + end + + assert_match /require dummy/, app + + %W{models controllers helpers}.each do |require| + assert_match /require_tree \.\/#{require}/, app + end + + assert_match /Dummy\.run\(\)/, app + end + end + + private + + def fixture(file) + File.expand_path("fixtures/#{file}", File.dirname(__FILE__)) + end +end diff --git a/test/model_generator_test.rb b/test/model_generator_test.rb new file mode 100644 index 0000000..45a9f1f --- /dev/null +++ b/test/model_generator_test.rb @@ -0,0 +1,44 @@ +require 'test_helper' +require 'generators/batman/model_generator' + +class ModelGeneratorTest < Rails::Generators::TestCase + tests Batman::Generators::ModelGenerator + + test "simple model" do + run_generator %w(Task title:string created_at:date) + + assert_file "#{javascripts_path}/models/task.js.coffee" do |model| + model_class = Regexp.escape("class Sample.Task extends Batman.Model") + + assert_match /#{model_class}/, model + + assert_match /@storageKey: 'tasks'/, model + assert_match /@persist Batman.RailsStorage/, model + + assert_match /@encode 'title'/, model + assert_match /@encode 'created_at', Batman.Encoders.railsDate/, model + end + end + + test "two word model is camelcased" do + run_generator %w(RegularUser name:string) + + assert_file "#{javascripts_path}/models/regular_user.js.coffee" do |model| + model_class = Regexp.escape("class Sample.RegularUser extends Batman.Model") + + assert_match /#{model_class}/, model + + assert_match /@storageKey: 'regular_users'/, model + end + end + + test "simple model with app_name" do + run_generator %w(Task title:string created_at:date --app_name MyApp) + + assert_file "#{javascripts_path}/models/task.js.coffee" do |model| + model_class = Regexp.escape("class MyApp.Task extends Batman.Model") + + assert_match /#{model_class}/, model + end + end +end diff --git a/test/sample/.gitignore b/test/sample/.gitignore new file mode 100644 index 0000000..923b697 --- /dev/null +++ b/test/sample/.gitignore @@ -0,0 +1,5 @@ +.bundle +db/*.sqlite3 +log/*.log +tmp/ +.sass-cache/ diff --git a/test/sample/Gemfile b/test/sample/Gemfile new file mode 100644 index 0000000..6ce21fe --- /dev/null +++ b/test/sample/Gemfile @@ -0,0 +1,30 @@ +source 'http://rubygems.org' + +gem 'rails', '3.1.0' + +# Bundle edge Rails instead: +# gem 'rails', :git => 'git://github.com/rails/rails.git' + +gem 'sqlite3' + +gem 'json' + +# Gems used only for assets and not required +# in production environments by default. +group :assets do + gem 'sass-rails', " ~> 3.1.0" + gem 'coffee-rails', "~> 3.1.0" + gem 'uglifier' +end + +gem 'jquery-rails' + +# Use unicorn as the web server +# gem 'unicorn' + +# Deploy with Capistrano +# gem 'capistrano' + +# To use debugger +# gem 'ruby-debug' + diff --git a/test/sample/README b/test/sample/README new file mode 100644 index 0000000..7c36f23 --- /dev/null +++ b/test/sample/README @@ -0,0 +1,261 @@ +== Welcome to Rails + +Rails is a web-application framework that includes everything needed to create +database-backed web applications according to the Model-View-Control pattern. + +This pattern splits the view (also called the presentation) into "dumb" +templates that are primarily responsible for inserting pre-built data in between +HTML tags. The model contains the "smart" domain objects (such as Account, +Product, Person, Post) that holds all the business logic and knows how to +persist themselves to a database. The controller handles the incoming requests +(such as Save New Account, Update Product, Show Post) by manipulating the model +and directing data to the view. + +In Rails, the model is handled by what's called an object-relational mapping +layer entitled Active Record. This layer allows you to present the data from +database rows as objects and embellish these data objects with business logic +methods. You can read more about Active Record in +link:files/vendor/rails/activerecord/README.html. + +The controller and view are handled by the Action Pack, which handles both +layers by its two parts: Action View and Action Controller. These two layers +are bundled in a single package due to their heavy interdependence. This is +unlike the relationship between the Active Record and Action Pack that is much +more separate. Each of these packages can be used independently outside of +Rails. You can read more about Action Pack in +link:files/vendor/rails/actionpack/README.html. + + +== Getting Started + +1. At the command prompt, 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.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.org/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 + | |-- assets + | |-- images + | |-- javascripts + | `-- stylesheets + | |-- controllers + | |-- helpers + | |-- mailers + | |-- models + | `-- views + | `-- layouts + |-- config + | |-- environments + | |-- initializers + | `-- locales + |-- db + |-- doc + |-- lib + | `-- tasks + |-- log + |-- public + |-- script + |-- test + | |-- fixtures + | |-- functional + | |-- integration + | |-- performance + | `-- unit + |-- tmp + | |-- cache + | |-- pids + | |-- sessions + | `-- sockets + `-- vendor + |-- assets + `-- stylesheets + `-- plugins + +app + Holds all the code that's specific to this particular application. + +app/assets + Contains subdirectories for images, stylesheets, and JavaScript files. + +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. 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/test/sample/Rakefile b/test/sample/Rakefile new file mode 100644 index 0000000..4287d59 --- /dev/null +++ b/test/sample/Rakefile @@ -0,0 +1,7 @@ +#!/usr/bin/env rake +# 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.expand_path('../config/application', __FILE__) + +Sample::Application.load_tasks diff --git a/test/sample/app/assets/images/rails.png b/test/sample/app/assets/images/rails.png new file mode 100644 index 0000000000000000000000000000000000000000..d5edc04e65f555e3ba4dcdaad39dc352e75b575e GIT binary patch literal 6646 zcmVpVcQya!6@Dsmj@#jv7C*qh zIhOJ6_K0n?*d`*T7TDuW-}m`9Kz3~>+7`DUkbAraU%yi+R{N~~XA2B%zt-4=tLimUer9!2M~N{G5bftFij_O&)a zsHnOppFIzebQ`RA0$!yUM-lg#*o@_O2wf422iLnM6cU(ktYU8#;*G!QGhIy9+ZfzKjLuZo%@a z-i@9A`X%J{^;2q&ZHY3C(B%gqCPW!8{9C0PMcNZccefK){s|V5-xxtHQc@uf>XqhD z7#N^siWqetgq29aX>G^olMf=bbRF6@Y(}zYxw6o!9WBdG1unP}<(V;zKlcR2p86fq zYjaqB^;Ycq>Wy@5T1xOzG3tucG3e%nPvajaN{CrFbnzv^9&K3$NrDm*eQe4`BGQ2bI;dFEwyt>hK%X!L6)82aOZp zsrGcJ#7PoX7)s|~t6is?FfX*7vWdREi58tiY4S)t6u*|kv?J)d_$r+CH#eZ?Ef+I_ z(eVlX8dh~4QP?o*E`_MgaNFIKj*rtN(0Raj3ECjSXcWfd#27NYs&~?t`QZFT}!Zaf=ldZIhi}LhQlqLo+o5(Pvui&{7PD__^53f9j>HW`Q z_V8X5j~$|GP9qXu0C#!@RX2}lXD35@3N5{BkUi%jtaPQ*H6OX2zIz4QPuqmTv3`vG{zc>l3t0B9E75h< z8&twGh%dp7WPNI+tRl%#gf2}Epg8st+~O4GjtwJsXfN;EjAmyr6z5dnaFU(;IV~QK zW62fogF~zA``(Q>_SmD!izc6Y4zq*97|NAPHp1j5X7Op2%;GLYm>^HEMyObo6s7l) zE3n|aOHi5~B84!}b^b*-aL2E)>OEJX_tJ~t<#VJ?bT?lDwyDB&5SZ$_1aUhmAY}#* zs@V1I+c5md9%R-o#_DUfqVtRk>59{+Opd5Yu%dAU#VQW}^m}x-30ftBx#527{^pI4 z6l2C6C7QBG$~NLYb3rVdLD#Z{+SleOp`(Lg5J}`kxdTHe(nV5BdpLrD=l|)e$gEqA zwI6vuX-PFCtcDIH>bGY2dwq&^tf+&R?)nY-@7_j%4CMRAF}C9w%p86W<2!aSY$p+k zrkFtG=cGo38RnrG28;?PNk%7a@faaXq&MS*&?1Z`7Ojw7(#>}ZG4nMAs3VXxfdW>i zY4VX02c5;f7jDPY_7@Oa)CHH}cH<3y#}_!nng^W+h1e-RL*YFYOteC@h?BtJZ+?sE zy)P5^8Mregx{nQaw1NY-|3>{Z)|0`?zc?G2-acYiSU`tj#sSGfm7k86ZQ0SQgPevcklHxM9<~4yW zR796sisf1|!#{Z=e^)0;_8iUhL8g(;j$l=02FTPZ(dZV@s#aQ`DHkLM6=YsbE4iQ!b#*374l0Jw5;jD%J;vQayq=nD8-kHI~f9Ux|32SJUM`> zGp2UGK*4t?cRKi!2he`zI#j0f${I#f-jeT?u_C7S4WsA0)ryi-1L0(@%pa^&g5x=e z=KW9+Nn(=)1T&S8g_ug%dgk*~l2O-$r9#zEGBdQsweO%t*6F4c8JC36JtTizCyy+E4h%G(+ z5>y$%0txMuQ$e~wjFgN(xrAndHQo`Za+K*?gUVDTBV&Ap^}|{w#CIq{DRe}+l@(Ec zCCV6f_?dY_{+f{}6XGn!pL_up?}@>KijT^$w#Lb6iHW&^8RP~g6y=vZBXx~B9nI^i zGexaPjcd(%)zGw!DG_dDwh-7x6+ST#R^${iz_M$uM!da8SxgB_;Z0G%Y*HpvLjKw; zX=ir7i1O$-T|*TBoH$dlW+TLf5j5sep^DlDtkox;Kg{Q%EXWedJq@J@%VAcK)j3y1 zShM!CS#qax;D@RND%2t3W6kv+#Ky0F9<3YKDbV^XJ=^$s(Vtza8V72YY)577nnldI zHMA0PUo!F3j(ubV*CM@PiK<^|RM2(DuCbG7`W}Rg(xdYC>C~ z;1KJGLN&$cRxSZunjXcntykmpFJ7;dk>shY(DdK&3K_JDJ6R%D`e~6Qv67@Rwu+q9 z*|NG{r}4F8f{Dfzt0+cZMd$fvlX3Q`dzM46@r?ISxr;9gBTG2rmfiGOD*#c*3f)cc zF+PFZobY$-^}J8 z%n=h4;x2}cP!@SiVd!v;^Wwo0(N??-ygDr7gG^NKxDjSo{5T{?$|Qo5;8V!~D6O;F*I zuY!gd@+2j_8Rn=UWDa#*4E2auWoGYDddMW7t0=yuC(xLWky?vLimM~!$3fgu!dR>p z?L?!8z>6v$|MsLb&dU?ob)Zd!B)!a*Z2eTE7 zKCzP&e}XO>CT%=o(v+WUY`Az*`9inbTG& z_9_*oQKw;sc8{ipoBC`S4Tb7a%tUE)1fE+~ib$;|(`|4QbXc2>VzFi%1nX%ti;^s3~NIL0R}!!a{0A zyCRp0F7Y&vcP&3`&Dzv5!&#h}F2R-h&QhIfq*ts&qO13{_CP}1*sLz!hI9VoTSzTu zok5pV0+~jrGymE~{TgbS#nN5+*rF7ij)cnSLQw0Ltc70zmk|O!O(kM<3zw-sUvkx~ z2`y+{xAwKSa-0}n7{$I@Zop7CWy%_xIeN1e-7&OjQ6vZZPbZ^3_ z(~=;ZSP98S2oB#35b1~_x`2gWiPdIVddEf`AD9<@c_s)TM;3J$T_l?pr{<7PTgdiy zBc5IGx)g~n=s+Z$RzYCmv8PlJu%gkh^;%mTGMc)UwRINVD~K;`Rl!5@hhGg;y>5qj zq|u-Yf0q_~Y+Mbivkkfa0nAOzB1acnytogsj_m7FB(-FjihMek#GAU4M!iXCgdK8a zjoKm?*|iz7;dHm4$^hh(`Ufl>yb>$hjIA-;>{>C}G0Di%bGvUsJkfLAV|xq32c>RqJqTBJ3Dx zYC;*Dt|S$b6)aCJFnK(Eey$M1DpVV~_MIhwK> zygo(jWC|_IRw|456`roEyXtkNLWNAt-4N1qyN$I@DvBzt;e|?g<*HK1%~cq|^u*}C zmMrwh>{QAq?Ar~4l^DqT%SQ)w)FA(#7#u+N;>E975rYML>)LgE`2<7nN=C1pC{IkV zVw}_&v6j&S?QVh*)wF3#XmE@0($^BVl1969csLKUBNer{suVd!a~B!0MxWY?=(GD6 zy$G&ERFR#i6G4=2F?R4}Mz3B?3tnpoX3)qFF2sh9-Jn*e%9F>i{WG7$_~XyOO2!+@ z6k+38KyD@-0=uee54D0!Z1@B^ilj~StchdOn(*qvg~s5QJpWGc!6U^Aj!xt-HZn_V zS%|fyQ5YS@EP2lBIodXCLjG_+a)%En+7jzngk@J>6D~^xbxKkvf-R0-c%mX+o{?&j zZZ%RxFeav8Y0gkwtdtrwUb-i0Egd2C=ADu%w5VV-hNJvl)GZ?M;y$!?b=S+wKRK7Q zcOjPT!p<*#8m;TsBih=@Xc&c)?Vy`Ys>IvK@|1%N+M6J-^RCRaZcPP2eQh9DEGZr+ z?8B~wF14mk4Xkuen{wY^CWwS1PI<8gikY*)3?RSo5l8es4*J z43k_BIwc}of=6Pfs%xIxlMDGOJN zvl!a>G)52XMqA%fbgkZi%)%bN*ZzZw2!rn4@+J)2eK#kWuEW{)W~-`y1vhA5-7p%R z&f5N!a9f8cK1Xa=O}=9{wg%}Ur^+8Y(!UCeqw>%wj@|bYHD-bZO~mk3L$9_^MmF3G zvCiK^e@q6G?tHkM8%GqsBMZaB20W$UEt_5r~jc#WlR>Bv{6W>A=!#InoY zLOd04@Rz?*7PpW8u|+}bt`?+Z(GsX{Br4A2$ZZ(26Degmr9`O=t2KgHTL*==R3xcP z&Y(J7hC@6_x8zVz!CX3l4Xtss6i7r#E6kXMNN1~>9KTRzewfp))ij%)SBBl0fZdYP zd!zzQD5u8yk-u|41|Rqz7_tCFUMThZJVj)yQf6^Cwtn|Ew6cm5J|u1Bq>MWX-AfB&NE;C z62@=-0le`E6-CurMKjoIy)BuUmhMGJb}pPx!@GLWMT+wH2R?wA=MEy)o57~feFp8P zY@YXAyt4<1FD<|iw{FGQu~GEI<4C64)V*QiVk+VzOV^9GWf4ir#oYgHJz!wq>iZV#_6@_{)&lum)4x z_Of*CLVQ7wdT#XT-(h0qH%mcIF7yzMIvvTN3bPceK>PpJi(=3Nny zbSn}p$dGKQUlX&-t~RR)#F7I<8NCD^yke(vdf#4^aAh}M-{tS9-&^tC4`KU_pToXy z+|K8sx}a)Kh{h{;*V1#hs1xB%(?j>)g~`Wv(9F)f=Qn)(daVB7hZtcp^#LrEr1T1J zZSJ*lVyVVjhy)mkex9Whn=EinKDHe@KlfQI-Fl7M?-c~HnW0;C;+MbUY8?FToy;A+ zs&Nc7VZ=Of+e!G6s#+S5WBU)kgQq_I1@!uH74GJ-+O|%0HXm9Mqlvp|j%0`T>fr9^ zK;qo>XdwZW<>%tTA+<(1^6(>=-2N;hRgBnjvEjN;VbKMbFg--WrGy|XESoH1p|M4` z86(gC^vB4qScASZ&cdpT{~QDN-jC|GJ(RYoW1VW4!SSn- zhQds9&RBKn6M&GVK_Aayt(Hekbnw=tr>f z^o@v9_*iQO1*zeOrts9Q-$pc@!StS&kz$cF`s@pM`rmJXTP&h5G)A74!0e%ZJbl}( zssI|_!%~_hZFypv*S^JE5N&Kvmx7KiG<|fGMO=WrH+@Yhuj+KwiS#l4>@%2nl zS)mDikfmokO4q2A)hRVZBq2-5q&XC>%HOLkOYxZ66(s86?=0s4z5xbiOV)}L-&6b)h6(~CIaR#JNw~46+WBiU7IhB zq!NuR4!TsYnyBg>@G=Ib*cMq^k<}AMpCeYEf&dzfiGI-wOQ7hb+nA zkN7_){y&c3xC0 AQ~&?~ literal 0 HcmV?d00001 diff --git a/test/sample/app/assets/javascripts/application.js b/test/sample/app/assets/javascripts/application.js new file mode 100644 index 0000000..37c7bfc --- /dev/null +++ b/test/sample/app/assets/javascripts/application.js @@ -0,0 +1,9 @@ +// This is a manifest file that'll be compiled into including all the files listed below. +// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically +// be included in the compiled file accessible from http://example.com/assets/application.js +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// the compiled file. +// +//= require jquery +//= require jquery_ujs +//= require_tree . diff --git a/test/sample/app/assets/stylesheets/application.css b/test/sample/app/assets/stylesheets/application.css new file mode 100644 index 0000000..fc25b57 --- /dev/null +++ b/test/sample/app/assets/stylesheets/application.css @@ -0,0 +1,7 @@ +/* + * This is a manifest file that'll automatically include all the stylesheets available in this directory + * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at + * the top of the compiled file, but it's generally better to create a new file per style scope. + *= require_self + *= require_tree . +*/ \ No newline at end of file diff --git a/test/sample/app/controllers/application_controller.rb b/test/sample/app/controllers/application_controller.rb new file mode 100644 index 0000000..e8065d9 --- /dev/null +++ b/test/sample/app/controllers/application_controller.rb @@ -0,0 +1,3 @@ +class ApplicationController < ActionController::Base + protect_from_forgery +end diff --git a/test/sample/app/helpers/application_helper.rb b/test/sample/app/helpers/application_helper.rb new file mode 100644 index 0000000..de6be79 --- /dev/null +++ b/test/sample/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/test/sample/app/mailers/.gitkeep b/test/sample/app/mailers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/app/models/.gitkeep b/test/sample/app/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/app/views/layouts/application.html.erb b/test/sample/app/views/layouts/application.html.erb new file mode 100644 index 0000000..e90a16d --- /dev/null +++ b/test/sample/app/views/layouts/application.html.erb @@ -0,0 +1,14 @@ + + + + Sample + <%= stylesheet_link_tag "application" %> + <%= javascript_include_tag "application" %> + <%= csrf_meta_tags %> + + + +<%= yield %> + + + diff --git a/test/sample/config.ru b/test/sample/config.ru new file mode 100644 index 0000000..047f4ea --- /dev/null +++ b/test/sample/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 Sample::Application diff --git a/test/sample/config/application.rb b/test/sample/config/application.rb new file mode 100644 index 0000000..22d3ee4 --- /dev/null +++ b/test/sample/config/application.rb @@ -0,0 +1,48 @@ +require File.expand_path('../boot', __FILE__) + +require 'rails/all' + +if defined?(Bundler) + # If you precompile assets before deploying to production, use this line + Bundler.require *Rails.groups(:assets => %w(development test)) + # If you want your assets lazily compiled in production, use this line + # Bundler.require(:default, :assets, Rails.env) +end + +module Sample + 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 + + # 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] + + # Enable the asset pipeline + config.assets.enabled = true + + # Version of your assets, change this if you want to expire all your assets + config.assets.version = '1.0' + end +end diff --git a/test/sample/config/boot.rb b/test/sample/config/boot.rb new file mode 100644 index 0000000..4489e58 --- /dev/null +++ b/test/sample/config/boot.rb @@ -0,0 +1,6 @@ +require 'rubygems' + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) + +require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) diff --git a/test/sample/config/database.yml b/test/sample/config/database.yml new file mode 100644 index 0000000..51a4dd4 --- /dev/null +++ b/test/sample/config/database.yml @@ -0,0 +1,25 @@ +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +development: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: sqlite3 + database: db/test.sqlite3 + pool: 5 + timeout: 5000 + +production: + adapter: sqlite3 + database: db/production.sqlite3 + pool: 5 + timeout: 5000 diff --git a/test/sample/config/environment.rb b/test/sample/config/environment.rb new file mode 100644 index 0000000..c9071f8 --- /dev/null +++ b/test/sample/config/environment.rb @@ -0,0 +1,5 @@ +# Load the rails application +require File.expand_path('../application', __FILE__) + +# Initialize the rails application +Sample::Application.initialize! diff --git a/test/sample/config/environments/development.rb b/test/sample/config/environments/development.rb new file mode 100644 index 0000000..9fa7cbf --- /dev/null +++ b/test/sample/config/environments/development.rb @@ -0,0 +1,30 @@ +Sample::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 web server when you make code changes. + config.cache_classes = false + + # Log error messages when you accidentally call methods on nil. + config.whiny_nils = true + + # Show full error reports and disable caching + config.consider_all_requests_local = 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 + + # Do not compress assets + config.assets.compress = false + + # Expands the lines which load the assets + config.assets.debug = true +end diff --git a/test/sample/config/environments/production.rb b/test/sample/config/environments/production.rb new file mode 100644 index 0000000..f46bd4f --- /dev/null +++ b/test/sample/config/environments/production.rb @@ -0,0 +1,60 @@ +Sample::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # Code is not reloaded between requests + config.cache_classes = true + + # Full error reports are disabled and caching is turned on + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Disable Rails's static asset server (Apache or nginx will already do this) + config.serve_static_assets = false + + # Compress JavaScripts and CSS + config.assets.compress = true + + # Don't fallback to assets pipeline if a precompiled asset is missed + config.assets.compile = false + + # Generate digests for assets URLs + config.assets.digest = true + + # Defaults to Rails.root.join("public/assets") + # config.assets.manifest = YOUR_PATH + + # Specifies the header that your server uses for sending files + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # See everything in the log (default is :info) + # config.log_level = :debug + + # Use a different logger for distributed setups + # config.logger = SyslogLogger.new + + # Use a different cache store in production + # config.cache_store = :mem_cache_store + + # Enable serving of images, stylesheets, and JavaScripts from an asset server + # config.action_controller.asset_host = "http://assets.example.com" + + # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) + # config.assets.precompile += %w( search.js ) + + # 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/test/sample/config/environments/test.rb b/test/sample/config/environments/test.rb new file mode 100644 index 0000000..fc73ada --- /dev/null +++ b/test/sample/config/environments/test.rb @@ -0,0 +1,42 @@ +Sample::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 + + # Configure static asset server for tests with Cache-Control for performance + config.serve_static_assets = true + config.static_cache_control = "public, max-age=3600" + + # 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 + + # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets + config.assets.allow_debugging = true +end diff --git a/test/sample/config/initializers/backtrace_silencers.rb b/test/sample/config/initializers/backtrace_silencers.rb new file mode 100644 index 0000000..59385cd --- /dev/null +++ b/test/sample/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/test/sample/config/initializers/inflections.rb b/test/sample/config/initializers/inflections.rb new file mode 100644 index 0000000..9e8b013 --- /dev/null +++ b/test/sample/config/initializers/inflections.rb @@ -0,0 +1,10 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format +# (all these examples are active by default): +# ActiveSupport::Inflector.inflections do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end diff --git a/test/sample/config/initializers/mime_types.rb b/test/sample/config/initializers/mime_types.rb new file mode 100644 index 0000000..72aca7e --- /dev/null +++ b/test/sample/config/initializers/mime_types.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf +# Mime::Type.register_alias "text/html", :iphone diff --git a/test/sample/config/initializers/secret_token.rb b/test/sample/config/initializers/secret_token.rb new file mode 100644 index 0000000..c0d18fb --- /dev/null +++ b/test/sample/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. +Sample::Application.config.secret_token = 'd99a3616e0e024b85f627f569863e8056b1b060f510a07e28efd6b46a3031b7bf61da8926619beb99bf80c6337a1c897fafe2116dbc7b1b1c8f84071fd14d657' diff --git a/test/sample/config/initializers/session_store.rb b/test/sample/config/initializers/session_store.rb new file mode 100644 index 0000000..58b38b7 --- /dev/null +++ b/test/sample/config/initializers/session_store.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +Sample::Application.config.session_store :cookie_store, :key => '_sample_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 "rails generate session_migration") +# Sample::Application.config.session_store :active_record_store diff --git a/test/sample/config/initializers/wrap_parameters.rb b/test/sample/config/initializers/wrap_parameters.rb new file mode 100644 index 0000000..da4fb07 --- /dev/null +++ b/test/sample/config/initializers/wrap_parameters.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters :format => [:json] +end + +# Disable root element in JSON by default. +ActiveSupport.on_load(:active_record) do + self.include_root_in_json = false +end diff --git a/test/sample/config/locales/en.yml b/test/sample/config/locales/en.yml new file mode 100644 index 0000000..179c14c --- /dev/null +++ b/test/sample/config/locales/en.yml @@ -0,0 +1,5 @@ +# Sample localization file for English. Add more files in this directory for other locales. +# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. + +en: + hello: "Hello world" diff --git a/test/sample/config/routes.rb b/test/sample/config/routes.rb new file mode 100644 index 0000000..66d2bc3 --- /dev/null +++ b/test/sample/config/routes.rb @@ -0,0 +1,58 @@ +Sample::Application.routes.draw do + # The priority is based upon order of creation: + # first created -> highest priority. + + # 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/test/sample/db/seeds.rb b/test/sample/db/seeds.rb new file mode 100644 index 0000000..d34dfa0 --- /dev/null +++ b/test/sample/db/seeds.rb @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). +# +# Examples: +# +# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) +# Mayor.create(:name => 'Emanuel', :city => cities.first) diff --git a/test/sample/doc/README_FOR_APP b/test/sample/doc/README_FOR_APP new file mode 100644 index 0000000..fe41f5c --- /dev/null +++ b/test/sample/doc/README_FOR_APP @@ -0,0 +1,2 @@ +Use this README file to introduce your application and point to useful places in the API for learning more. +Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. diff --git a/test/sample/lib/assets/.gitkeep b/test/sample/lib/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/lib/tasks/.gitkeep b/test/sample/lib/tasks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/log/.gitkeep b/test/sample/log/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/public/404.html b/test/sample/public/404.html new file mode 100644 index 0000000..9a48320 --- /dev/null +++ b/test/sample/public/404.html @@ -0,0 +1,26 @@ + + + + The page you were looking for doesn't exist (404) + + + + + +
+

The page you were looking for doesn't exist.

+

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

+
+ + diff --git a/test/sample/public/422.html b/test/sample/public/422.html new file mode 100644 index 0000000..83660ab --- /dev/null +++ b/test/sample/public/422.html @@ -0,0 +1,26 @@ + + + + The change you wanted was rejected (422) + + + + + +
+

The change you wanted was rejected.

+

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

+
+ + diff --git a/test/sample/public/500.html b/test/sample/public/500.html new file mode 100644 index 0000000..b80307f --- /dev/null +++ b/test/sample/public/500.html @@ -0,0 +1,26 @@ + + + + We're sorry, but something went wrong (500) + + + + + +
+

We're sorry, but something went wrong.

+

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

+
+ + diff --git a/test/sample/public/favicon.ico b/test/sample/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/public/index.html b/test/sample/public/index.html new file mode 100644 index 0000000..9d9811a --- /dev/null +++ b/test/sample/public/index.html @@ -0,0 +1,241 @@ + + + + Ruby on Rails: Welcome aboard + + + + +
+ + +
+ + + + +
+

Getting started

+

Here’s how to get rolling:

+ +
    +
  1. +

    Use rails generate to create your models and controllers

    +

    To see all available options, run it without parameters.

    +
  2. + +
  3. +

    Set up a default route and remove public/index.html

    +

    Routes are set up in config/routes.rb.

    +
  4. + +
  5. +

    Create your database

    +

    Run rake db:create to create your database. If you're not using SQLite (the default), edit config/database.yml with your username and password.

    +
  6. +
+
+
+ + +
+ + diff --git a/test/sample/public/robots.txt b/test/sample/public/robots.txt new file mode 100644 index 0000000..085187f --- /dev/null +++ b/test/sample/public/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-Agent: * +# Disallow: / diff --git a/test/sample/script/rails b/test/sample/script/rails new file mode 100755 index 0000000..f8da2cf --- /dev/null +++ b/test/sample/script/rails @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. + +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) +require 'rails/commands' diff --git a/test/sample/test/fixtures/.gitkeep b/test/sample/test/fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/test/functional/.gitkeep b/test/sample/test/functional/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/test/integration/.gitkeep b/test/sample/test/integration/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/test/performance/browsing_test.rb b/test/sample/test/performance/browsing_test.rb new file mode 100644 index 0000000..3fea27b --- /dev/null +++ b/test/sample/test/performance/browsing_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' +require 'rails/performance_test_help' + +class BrowsingTest < ActionDispatch::PerformanceTest + # Refer to the documentation for all available options + # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory] + # :output => 'tmp/performance', :formats => [:flat] } + + def test_homepage + get '/' + end +end diff --git a/test/sample/test/test_helper.rb b/test/sample/test/test_helper.rb new file mode 100644 index 0000000..8bf1192 --- /dev/null +++ b/test/sample/test/test_helper.rb @@ -0,0 +1,13 @@ +ENV["RAILS_ENV"] = "test" +require File.expand_path('../../config/environment', __FILE__) +require 'rails/test_help' + +class ActiveSupport::TestCase + # 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 + + # Add more helper methods to be used by all tests here... +end diff --git a/test/sample/test/unit/.gitkeep b/test/sample/test/unit/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/vendor/assets/stylesheets/.gitkeep b/test/sample/vendor/assets/stylesheets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/sample/vendor/plugins/.gitkeep b/test/sample/vendor/plugins/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..d7877fc --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,24 @@ +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + +require File.expand_path("../sample/config/environment.rb", __FILE__) +require "rails/test_help" + +Rails.backtrace_cleaner.remove_silencers! + +# Load support files +Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } + +# For Generators +require 'rails/generators/test_case' + + +class Rails::Generators::TestCase + destination File.expand_path("../tmp", File.dirname(__FILE__)) + setup :prepare_destination + + def javascripts_path + "app/assets/javascripts/" + end + +end diff --git a/tmp/app/assets/javascripts/controllers/regular_users_controller.js.coffee b/tmp/app/assets/javascripts/controllers/regular_users_controller.js.coffee new file mode 100644 index 0000000..c8d59f2 --- /dev/null +++ b/tmp/app/assets/javascripts/controllers/regular_users_controller.js.coffee @@ -0,0 +1,3 @@ +class Sample.RegularUsersController extends Batman.Controller + index: (params) -> + diff --git a/vendor/assets/javascripts/batman/batman.jquery.js b/vendor/assets/javascripts/batman/batman.jquery.js new file mode 100644 index 0000000..70b9661 --- /dev/null +++ b/vendor/assets/javascripts/batman/batman.jquery.js @@ -0,0 +1,73 @@ +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + Batman.Request.prototype.send = function(data) { + var options, _ref; + options = { + url: this.get('url'), + type: this.get('method'), + dataType: this.get('type'), + data: data || this.get('data'), + username: this.get('username'), + password: this.get('password'), + beforeSend: __bind(function() { + return this.loading(true); + }, this), + success: __bind(function(response, textStatus, xhr) { + this.set('status', xhr.status); + this.set('response', response); + return this.success(response); + }, this), + error: __bind(function(xhr, status, error) { + this.set('status', xhr.status); + this.set('response', xhr.responseText); + xhr.request = this; + return this.error(xhr); + }, this), + complete: __bind(function() { + this.loading(false); + return this.loaded(true); + }, this) + }; + if ((_ref = this.get('method')) === 'PUT' || _ref === 'POST') { + if (!this.get('formData')) { + options.contentType = this.get('contentType'); + } else { + options.contentType = false; + options.processData = false; + options.data = this.constructor.objectToFormData(options.data); + } + } + return jQuery.ajax(options); + }; + Batman.mixins.animation = { + show: function(addToParent) { + var jq, show, _ref, _ref2; + jq = $(this); + show = function() { + return jq.show(600); + }; + if (addToParent) { + if ((_ref = addToParent.append) != null) { + _ref.appendChild(this); + } + if ((_ref2 = addToParent.before) != null) { + _ref2.parentNode.insertBefore(this, addToParent.before); + } + jq.hide(); + setTimeout(show, 0); + } else { + show(); + } + return this; + }, + hide: function(removeFromParent) { + $(this).hide(600, __bind(function() { + var _ref; + if (removeFromParent) { + return (_ref = this.parentNode) != null ? _ref.removeChild(this) : void 0; + } + }, this)); + return this; + } + }; +}).call(this); diff --git a/vendor/assets/javascripts/batman/batman.js b/vendor/assets/javascripts/batman/batman.js new file mode 100644 index 0000000..32f456c --- /dev/null +++ b/vendor/assets/javascripts/batman/batman.js @@ -0,0 +1,4717 @@ +(function() { + var $addEventListener, $block, $event, $eventOneShot, $extendsEnumerable, $findName, $functionName, $get, $isChildOf, $mixin, $passError, $preventDefault, $redirect, $removeEventListener, $typeOf, $unmixin, Batman, BatmanObject, Binding, RenderContext, Validators, buntUndefined, camelize_rx, capitalize_rx, container, developer, div, filters, helpers, isEmptyDataObject, k, mixins, underscore_rx1, underscore_rx2, _Batman, _fn, _i, _j, _len, _len2, _objectToString, _ref, _ref2, _stateMachine_setState; + var __slice = Array.prototype.slice, __hasProp = Object.prototype.hasOwnProperty, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + Batman = function() { + var mixins; + mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return (function(func, args, ctor) { + ctor.prototype = func.prototype; + var child = new ctor, result = func.apply(child, args); + return typeof result === "object" ? result : child; + })(Batman.Object, mixins, function() {}); + }; + Batman.typeOf = $typeOf = function(object) { + if (typeof object === 'undefined') { + return "Undefined"; + } + return _objectToString.call(object).slice(8, -1); + }; + _objectToString = Object.prototype.toString; + Batman.mixin = $mixin = function() { + var hasSet, key, mixin, mixins, to, value, _i, _len; + to = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + hasSet = typeof to.set === 'function'; + for (_i = 0, _len = mixins.length; _i < _len; _i++) { + mixin = mixins[_i]; + if ($typeOf(mixin) !== 'Object') { + continue; + } + for (key in mixin) { + if (!__hasProp.call(mixin, key)) continue; + value = mixin[key]; + if (key === 'initialize' || key === 'uninitialize' || key === 'prototype') { + continue; + } + if (hasSet) { + to.set(key, value); + } else if (to.nodeName != null) { + Batman.data(to, key, value); + } else { + to[key] = value; + } + } + if (typeof mixin.initialize === 'function') { + mixin.initialize.call(to); + } + } + return to; + }; + Batman.unmixin = $unmixin = function() { + var from, key, mixin, mixins, _i, _len; + from = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + for (_i = 0, _len = mixins.length; _i < _len; _i++) { + mixin = mixins[_i]; + for (key in mixin) { + if (key === 'initialize' || key === 'uninitialize') { + continue; + } + delete from[key]; + } + if (typeof mixin.uninitialize === 'function') { + mixin.uninitialize.call(from); + } + } + return from; + }; + Batman._block = $block = function(lengthOrFunction, fn) { + var argsLength, callbackEater; + if (fn != null) { + argsLength = lengthOrFunction; + } else { + fn = lengthOrFunction; + } + return callbackEater = function() { + var args, ctx, f; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + ctx = this; + f = function(callback) { + args.push(callback); + return fn.apply(ctx, args); + }; + if ((typeof args[args.length - 1] === 'function') || (argsLength && (args.length >= argsLength))) { + return f(args.pop()); + } else { + return f; + } + }; + }; + Batman._findName = $findName = function(f, context) { + var key, value; + if (!f.displayName) { + for (key in context) { + value = context[key]; + if (value === f) { + f.displayName = key; + break; + } + } + } + return f.displayName; + }; + Batman._functionName = $functionName = function(f) { + var _ref; + if (f.__name__) { + return f.__name__; + } + if (f.name) { + return f.name; + } + return (_ref = f.toString().match(/\W*function\s+([\w\$]+)\(/)) != null ? _ref[1] : void 0; + }; + Batman._preventDefault = $preventDefault = function(e) { + if (typeof e.preventDefault === "function") { + return e.preventDefault(); + } else { + return e.returnValue = false; + } + }; + Batman._isChildOf = $isChildOf = function(parentNode, childNode) { + var node; + node = childNode.parentNode; + while (node) { + if (node === parentNode) { + return true; + } + node = node.parentNode; + } + return false; + }; + developer = { + DevelopmentError: (function() { + var DevelopmentError; + DevelopmentError = function(message) { + this.message = message; + return this.name = "DevelopmentError"; + }; + DevelopmentError.prototype = Error.prototype; + return DevelopmentError; + })(), + _ie_console: function(f, args) { + var arg, _i, _len, _results; + if (args.length !== 1) { + if (typeof console !== "undefined" && console !== null) { + console[f]("..." + f + " of " + args.length + " items..."); + } + } + _results = []; + for (_i = 0, _len = args.length; _i < _len; _i++) { + arg = args[_i]; + _results.push(typeof console !== "undefined" && console !== null ? console[f](arg) : void 0); + } + return _results; + }, + log: function() { + if ((typeof console !== "undefined" && console !== null ? console.log : void 0) == null) { + return; + } + if (console.log.apply) { + return console.log.apply(console, arguments); + } else { + return developer._ie_console("log", arguments); + } + }, + warn: function() { + if ((typeof console !== "undefined" && console !== null ? console.warn : void 0) == null) { + return; + } + if (console.warn.apply) { + return console.warn.apply(console, arguments); + } else { + return developer._ie_console("warn", arguments); + } + }, + error: function(message) { + throw new developer.DevelopmentError(message); + }, + assert: function(result, message) { + if (!result) { + return developer.error(message); + } + }, + addFilters: function() { + return $mixin(Batman.Filters, { + log: function(value, key) { + if (typeof console !== "undefined" && console !== null) { + if (typeof console.log === "function") { + console.log(arguments); + } + } + return value; + }, + logStack: function(value) { + if (typeof console !== "undefined" && console !== null) { + if (typeof console.log === "function") { + console.log(developer.currentFilterStack); + } + } + return value; + }, + logContext: function(value) { + if (typeof console !== "undefined" && console !== null) { + if (typeof console.log === "function") { + console.log(developer.currentFilterContext); + } + } + return value; + } + }); + } + }; + Batman.developer = developer; + camelize_rx = /(?:^|_|\-)(.)/g; + capitalize_rx = /(^|\s)([a-z])/g; + underscore_rx1 = /([A-Z]+)([A-Z][a-z])/g; + underscore_rx2 = /([a-z\d])([A-Z])/g; + helpers = Batman.helpers = { + camelize: function(string, firstLetterLower) { + string = string.replace(camelize_rx, function(str, p1) { + return p1.toUpperCase(); + }); + if (firstLetterLower) { + return string.substr(0, 1).toLowerCase() + string.substr(1); + } else { + return string; + } + }, + underscore: function(string) { + return string.replace(underscore_rx1, '$1_$2').replace(underscore_rx2, '$1_$2').replace('-', '_').toLowerCase(); + }, + singularize: function(string) { + var len; + len = string.length; + if (string.substr(len - 3) === 'ies') { + return string.substr(0, len - 3) + 'y'; + } else if (string.substr(len - 1) === 's') { + return string.substr(0, len - 1); + } else { + return string; + } + }, + pluralize: function(count, string) { + var lastLetter, len; + if (string) { + if (count === 1) { + return string; + } + } else { + string = count; + } + len = string.length; + lastLetter = string.substr(len - 1); + if (lastLetter === 'y') { + return "" + (string.substr(0, len - 1)) + "ies"; + } else if (lastLetter === 's') { + return string; + } else { + return "" + string + "s"; + } + }, + capitalize: function(string) { + return string.replace(capitalize_rx, function(m, p1, p2) { + return p1 + p2.toUpperCase(); + }); + }, + trim: function(string) { + if (string) { + return string.trim(); + } else { + return ""; + } + } + }; + Batman.Property = (function() { + Property.defaultAccessor = { + get: function(key) { + return this[key]; + }, + set: function(key, val) { + return this[key] = val; + }, + unset: function(key) { + var x; + x = this[key]; + delete this[key]; + return x; + } + }; + Property.triggerTracker = null; + Property.forBaseAndKey = function(base, key) { + var properties, propertyClass, _base; + propertyClass = base.propertyClass || Batman.Keypath; + if (base._batman) { + Batman.initializeObject(base); + properties = (_base = base._batman).properties || (_base.properties = new Batman.SimpleHash); + return properties.get(key) || properties.set(key, new propertyClass(base, key)); + } else { + return new propertyClass(base, key); + } + }; + function Property(base, key) { + this.base = base; + this.key = key; + this.observers = new Batman.SimpleSet; + if (this.hasObserversToFire()) { + this.refreshTriggers(); + } + this._preventCount = 0; + } + Property.prototype.isProperty = true; + Property.prototype.isEqual = function(other) { + return this.constructor === other.constructor && this.base === other.base && this.key === other.key; + }; + Property.prototype.accessor = function() { + var accessors, val, _ref, _ref2; + accessors = (_ref = this.base._batman) != null ? _ref.get('keyAccessors') : void 0; + if (accessors && (val = accessors.get(this.key))) { + return val; + } else { + return ((_ref2 = this.base._batman) != null ? _ref2.getFirst('defaultAccessor') : void 0) || Batman.Property.defaultAccessor; + } + }; + Property.prototype.registerAsTrigger = function() { + var tracker; + if (tracker = Batman.Property.triggerTracker) { + return tracker.add(this); + } + }; + Property.prototype.getValue = function() { + var _ref; + this.registerAsTrigger(); + return (_ref = this.accessor()) != null ? _ref.get.call(this.base, this.key) : void 0; + }; + Property.prototype.setValue = function(val) { + var result, _ref; + this.cacheDependentValues(); + result = (_ref = this.accessor()) != null ? _ref.set.call(this.base, this.key, val) : void 0; + this.fireDependents(); + return result; + }; + Property.prototype.unsetValue = function() { + var result, _ref; + this.cacheDependentValues(); + result = (_ref = this.accessor()) != null ? _ref.unset.call(this.base, this.key) : void 0; + this.fireDependents(); + return result; + }; + Property.prototype.cacheDependentValues = function() { + if (this.dependents) { + return this.dependents.forEach(function(prop) { + return prop.cachedValue = prop.getValue(); + }); + } + }; + Property.prototype.fireDependents = function() { + if (this.dependents) { + return this.dependents.forEach(function(prop) { + if (typeof prop.hasObserversToFire === "function" ? prop.hasObserversToFire() : void 0) { + return prop.fire(prop.getValue(), prop.cachedValue); + } + }); + } + }; + Property.prototype.observe = function() { + var callback, currentValue, fireImmediately, _i; + fireImmediately = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), callback = arguments[_i++]; + fireImmediately = fireImmediately[0] === true; + currentValue = this.getValue(); + this.observers.add(callback); + this.refreshTriggers(); + if (fireImmediately) { + callback.call(this.base, currentValue, currentValue); + } + return this; + }; + Property.prototype.hasObserversToFire = function() { + if (this.observers.length > 0) { + return true; + } + if (this.base._batman) { + return this.base._batman.ancestors().some(__bind(function(ancestor) { + var _ref, _ref2; + return (typeof ancestor.property === "function" ? (_ref = ancestor.property(this.key)) != null ? (_ref2 = _ref.observers) != null ? _ref2.length : void 0 : void 0 : void 0) > 0; + }, this)); + } else { + return false; + } + }; + Property.prototype.prevent = function() { + return this._preventCount++; + }; + Property.prototype.allow = function() { + if (this._preventCount > 0) { + return this._preventCount--; + } + }; + Property.prototype.isAllowedToFire = function() { + return this._preventCount <= 0; + }; + Property.prototype.fire = function() { + var args, base, key, observerSets; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (!(this.isAllowedToFire() && this.hasObserversToFire())) { + return; + } + key = this.key; + base = this.base; + observerSets = [this.observers]; + this.observers.forEach(function(callback) { + return callback != null ? callback.apply(base, args) : void 0; + }); + if (this.base._batman) { + this.base._batman.ancestors(function(ancestor) { + return typeof ancestor.property === "function" ? ancestor.property(key).observers.forEach(function(callback) { + return callback != null ? callback.apply(base, args) : void 0; + }) : void 0; + }); + } + return this.refreshTriggers(); + }; + Property.prototype.forget = function(observer) { + if (observer) { + this.observers.remove(observer); + } else { + this.observers = new Batman.SimpleSet; + } + if (!this.hasObserversToFire()) { + return this.clearTriggers(); + } + }; + Property.prototype.refreshTriggers = function() { + Batman.Property.triggerTracker = new Batman.SimpleSet; + this.getValue(); + if (this.triggers) { + this.triggers.forEach(__bind(function(property) { + var _ref; + if (!Batman.Property.triggerTracker.has(property)) { + return (_ref = property.dependents) != null ? _ref.remove(this) : void 0; + } + }, this)); + } + this.triggers = Batman.Property.triggerTracker; + this.triggers.forEach(__bind(function(property) { + property.dependents || (property.dependents = new Batman.SimpleSet); + return property.dependents.add(this); + }, this)); + return delete Batman.Property.triggerTracker; + }; + Property.prototype.clearTriggers = function() { + if (this.triggers) { + this.triggers.forEach(__bind(function(property) { + return property.dependents.remove(this); + }, this)); + return this.triggers = new Batman.SimpleSet; + } + }; + return Property; + })(); + Batman.Keypath = (function() { + __extends(Keypath, Batman.Property); + function Keypath(base, key) { + if ($typeOf(key) === 'String') { + this.segments = key.split('.'); + this.depth = this.segments.length; + } else { + this.segments = [key]; + this.depth = 1; + } + Keypath.__super__.constructor.apply(this, arguments); + } + Keypath.prototype.slice = function(begin, end) { + var base, segment, _i, _len, _ref; + if (end == null) { + end = this.depth; + } + base = this.base; + _ref = this.segments.slice(0, begin); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + segment = _ref[_i]; + if (!((base != null) && (base = Batman.Property.forBaseAndKey(base, segment).getValue()))) { + return; + } + } + return Batman.Property.forBaseAndKey(base, this.segments.slice(begin, end).join('.')); + }; + Keypath.prototype.terminalProperty = function() { + return this.slice(-1); + }; + Keypath.prototype.getValue = function() { + var _ref; + this.registerAsTrigger(); + if (this.depth === 1) { + return Keypath.__super__.getValue.apply(this, arguments); + } else { + return (_ref = this.terminalProperty()) != null ? _ref.getValue() : void 0; + } + }; + Keypath.prototype.setValue = function(val) { + var _ref; + if (this.depth === 1) { + return Keypath.__super__.setValue.apply(this, arguments); + } else { + return (_ref = this.terminalProperty()) != null ? _ref.setValue(val) : void 0; + } + }; + Keypath.prototype.unsetValue = function() { + var _ref; + if (this.depth === 1) { + return Keypath.__super__.unsetValue.apply(this, arguments); + } else { + return (_ref = this.terminalProperty()) != null ? _ref.unsetValue() : void 0; + } + }; + return Keypath; + })(); + Batman.Observable = { + isObservable: true, + property: function(key) { + Batman.initializeObject(this); + return Batman.Property.forBaseAndKey(this, key); + }, + get: function(key) { + return this.property(key).getValue(); + }, + set: function(key, val) { + return this.property(key).setValue(val); + }, + unset: function(key) { + return this.property(key).unsetValue(); + }, + getOrSet: function(key, valueFunction) { + var currentValue; + currentValue = this.get(key); + if (!currentValue) { + currentValue = valueFunction(); + this.set(key, currentValue); + } + return currentValue; + }, + forget: function(key, observer) { + if (key) { + this.property(key).forget(observer); + } else { + this._batman.properties.forEach(function(key, property) { + return property.forget(); + }); + } + return this; + }, + allowed: function(key) { + return this.property(key).isAllowedToFire(); + } + }; + _ref = ['observe', 'prevent', 'allow', 'fire']; + _fn = function(k) { + return Batman.Observable[k] = function() { + var args, key, _ref2; + key = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + (_ref2 = this.property(key))[k].apply(_ref2, args); + return this; + }; + }; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + k = _ref[_i]; + _fn(k); + } + $get = Batman.get = function(object, key) { + if (object.get) { + return object.get(key); + } else { + return Batman.Observable.get.call(object, key); + } + }; + Batman.EventEmitter = { + event: $block(function(key, context, callback) { + var f; + if (!callback && typeof context !== 'undefined') { + callback = context; + context = null; + } + if (!callback && $typeOf(key) !== 'String') { + callback = key; + key = null; + } + f = function(observer) { + var args, fired, firings, value, _base, _ref2, _ref3; + if (!this.observe) { + developer.error("EventEmitter requires Observable"); + } + Batman.initializeObject(this); + key || (key = $findName(f, this)); + fired = (_ref2 = this._batman.oneShotFired) != null ? _ref2[key] : void 0; + if (typeof observer === 'function') { + this.observe(key, observer); + if (f.isOneShot && fired) { + return observer.apply(this, f._firedArgs); + } + } else if (this.allowed(key)) { + if (f.isOneShot && fired) { + return false; + } + value = callback != null ? callback.apply(this, arguments) : void 0; + if (value !== false) { + f._firedArgs = typeof value !== 'undefined' ? (_ref3 = [value]).concat.apply(_ref3, arguments) : arguments.length === 0 ? [] : Array.prototype.slice.call(arguments); + args = Array.prototype.slice.call(f._firedArgs); + args.unshift(key); + this.fire.apply(this, args); + if (f.isOneShot) { + firings = (_base = this._batman).oneShotFired || (_base.oneShotFired = {}); + firings[key] = true; + } + } + return value; + } else { + return false; + } + }; + if (context) { + f = f.bind(context); + } + if (key != null) { + this[key] = f; + } + return $mixin(f, { + isEvent: true, + action: callback + }); + }), + eventOneShot: function(callback) { + return $mixin(Batman.EventEmitter.event.apply(this, arguments), { + isOneShot: true, + oneShotFired: this.oneShotFired.bind(this) + }); + }, + oneShotFired: function(key) { + var firings, _base; + Batman.initializeObject(this); + firings = (_base = this._batman).oneShotFired || (_base.oneShotFired = {}); + return !!firings[key]; + } + }; + Batman.event = $event = function(callback) { + var context; + context = new Batman.Object; + return context.event('_event', context, callback); + }; + Batman.eventOneShot = $eventOneShot = function(callback) { + var context, oneShot; + context = new Batman.Object; + oneShot = context.eventOneShot('_event', context, callback); + oneShot.oneShotFired = function() { + return context.oneShotFired('_event'); + }; + return oneShot; + }; + Batman.initializeObject = function(object) { + if (object._batman != null) { + return object._batman.check(object); + } else { + return object._batman = new _Batman(object); + } + }; + Batman._Batman = _Batman = (function() { + function _Batman() { + var mixins, object; + object = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + this.object = object; + if (mixins.length > 0) { + $mixin.apply(null, [this].concat(__slice.call(mixins))); + } + } + _Batman.prototype.check = function(object) { + if (object !== this.object) { + object._batman = new _Batman(object); + return false; + } + return true; + }; + _Batman.prototype.get = function(key) { + var results; + results = this.getAll(key); + switch (results.length) { + case 0: + return; + case 1: + return results[0]; + default: + if (results[0].concat != null) { + results = results.reduceRight(function(a, b) { + return a.concat(b); + }); + } else if (results[0].merge != null) { + results = results.reduceRight(function(a, b) { + return a.merge(b); + }); + } + return results; + } + }; + _Batman.prototype.getFirst = function(key) { + var results; + results = this.getAll(key); + return results[0]; + }; + _Batman.prototype.getAll = function(keyOrGetter) { + var getter, results, val; + if (typeof keyOrGetter === 'function') { + getter = keyOrGetter; + } else { + getter = function(ancestor) { + var _ref2; + return (_ref2 = ancestor._batman) != null ? _ref2[keyOrGetter] : void 0; + }; + } + results = this.ancestors(getter); + if (val = getter(this.object)) { + results.unshift(val); + } + return results; + }; + _Batman.prototype.ancestors = function(getter) { + var isClass, parent, proto, results, val, _ref2; + if (getter == null) { + getter = function(x) { + return x; + }; + } + results = []; + isClass = !!this.object.prototype; + parent = isClass ? (_ref2 = this.object.__super__) != null ? _ref2.constructor : void 0 : (proto = Object.getPrototypeOf(this.object)) === this.object ? this.object.constructor.__super__ : proto; + if (parent != null) { + val = getter(parent); + if (val != null) { + results.push(val); + } + if (parent._batman != null) { + results = results.concat(parent._batman.ancestors(getter)); + } + } + return results; + }; + _Batman.prototype.set = function(key, value) { + return this[key] = value; + }; + return _Batman; + })(); + BatmanObject = (function() { + var getAccessorObject; + BatmanObject.global = function(isGlobal) { + if (isGlobal === false) { + return; + } + return container[$functionName(this)] = this; + }; + BatmanObject.classMixin = function() { + return $mixin.apply(null, [this].concat(__slice.call(arguments))); + }; + BatmanObject.mixin = function() { + return this.classMixin.apply(this.prototype, arguments); + }; + BatmanObject.prototype.mixin = BatmanObject.classMixin; + getAccessorObject = function(accessor) { + if (!accessor.get && !accessor.set && !accessor.unset) { + accessor = { + get: accessor + }; + } + return accessor; + }; + BatmanObject.classAccessor = function() { + var accessor, key, keys, _base, _j, _k, _len2, _results; + keys = 2 <= arguments.length ? __slice.call(arguments, 0, _j = arguments.length - 1) : (_j = 0, []), accessor = arguments[_j++]; + Batman.initializeObject(this); + if (keys.length === 0) { + return this._batman.defaultAccessor = getAccessorObject(accessor); + } else { + (_base = this._batman).keyAccessors || (_base.keyAccessors = new Batman.SimpleHash); + _results = []; + for (_k = 0, _len2 = keys.length; _k < _len2; _k++) { + key = keys[_k]; + _results.push(this._batman.keyAccessors.set(key, getAccessorObject(accessor))); + } + return _results; + } + }; + BatmanObject.accessor = function() { + return this.classAccessor.apply(this.prototype, arguments); + }; + BatmanObject.prototype.accessor = BatmanObject.classAccessor; + function BatmanObject() { + var mixins; + mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + this._batman = new _Batman(this); + this.mixin.apply(this, mixins); + } + BatmanObject.classMixin(Batman.Observable, Batman.EventEmitter); + BatmanObject.mixin(Batman.Observable, Batman.EventEmitter); + BatmanObject.observeAll = function() { + return this.prototype.observe.apply(this.prototype, arguments); + }; + BatmanObject.singleton = function(singletonMethodName) { + if (singletonMethodName == null) { + singletonMethodName = "sharedInstance"; + } + return this.classAccessor(singletonMethodName, { + get: function() { + var _name; + return this[_name = "_" + singletonMethodName] || (this[_name] = new this); + } + }); + }; + return BatmanObject; + })(); + Batman.Object = BatmanObject; + Batman.Accessible = (function() { + __extends(Accessible, Batman.Object); + function Accessible() { + this.accessor.apply(this, arguments); + } + return Accessible; + })(); + Batman.Enumerable = { + isEnumerable: true, + map: function(f, ctx) { + var r; + if (ctx == null) { + ctx = container; + } + r = []; + this.forEach(function() { + return r.push(f.apply(ctx, arguments)); + }); + return r; + }, + every: function(f, ctx) { + var r; + if (ctx == null) { + ctx = container; + } + r = true; + this.forEach(function() { + return r = r && f.apply(ctx, arguments); + }); + return r; + }, + some: function(f, ctx) { + var r; + if (ctx == null) { + ctx = container; + } + r = false; + this.forEach(function() { + return r = r || f.apply(ctx, arguments); + }); + return r; + }, + reduce: function(f, r) { + var count, self; + count = 0; + self = this; + this.forEach(function() { + if (r != null) { + return r = f.apply(null, [r].concat(__slice.call(arguments), [count], [self])); + } else { + return r = arguments[0]; + } + }); + return r; + }, + filter: function(f) { + var r, wrap; + r = new this.constructor; + if (r.add) { + wrap = function(r, e) { + if (f(e)) { + r.add(e); + } + return r; + }; + } else if (r.set) { + wrap = function(r, k, v) { + if (f(k, v)) { + r.set(k, v); + } + return r; + }; + } else { + if (!r.push) { + r = []; + } + wrap = function(r, e) { + if (f(e)) { + r.push(e); + } + return r; + }; + } + return this.reduce(wrap, r); + } + }; + $extendsEnumerable = function(onto) { + var k, v, _ref2, _results; + _ref2 = Batman.Enumerable; + _results = []; + for (k in _ref2) { + v = _ref2[k]; + _results.push(onto[k] = v); + } + return _results; + }; + Batman.SimpleHash = (function() { + function SimpleHash() { + this._storage = {}; + this.length = 0; + } + $extendsEnumerable(SimpleHash.prototype); + SimpleHash.prototype.propertyClass = Batman.Property; + SimpleHash.prototype.hasKey = function(key) { + var pair, pairs, _j, _len2; + if (pairs = this._storage[key]) { + for (_j = 0, _len2 = pairs.length; _j < _len2; _j++) { + pair = pairs[_j]; + if (this.equality(pair[0], key)) { + return true; + } + } + } + return false; + }; + SimpleHash.prototype.get = function(key) { + var pair, pairs, _j, _len2; + if (pairs = this._storage[key]) { + for (_j = 0, _len2 = pairs.length; _j < _len2; _j++) { + pair = pairs[_j]; + if (this.equality(pair[0], key)) { + return pair[1]; + } + } + } + }; + SimpleHash.prototype.set = function(key, val) { + var pair, pairs, _base, _j, _len2; + pairs = (_base = this._storage)[key] || (_base[key] = []); + for (_j = 0, _len2 = pairs.length; _j < _len2; _j++) { + pair = pairs[_j]; + if (this.equality(pair[0], key)) { + return pair[1] = val; + } + } + this.length++; + pairs.push([key, val]); + return val; + }; + SimpleHash.prototype.unset = function(key) { + var index, obj, pairs, value, _len2, _ref2; + if (pairs = this._storage[key]) { + for (index = 0, _len2 = pairs.length; index < _len2; index++) { + _ref2 = pairs[index], obj = _ref2[0], value = _ref2[1]; + if (this.equality(obj, key)) { + pairs.splice(index, 1); + this.length--; + return; + } + } + } + }; + SimpleHash.prototype.getOrSet = Batman.Observable.getOrSet; + SimpleHash.prototype.equality = function(lhs, rhs) { + if (lhs === rhs) { + return true; + } + if (lhs !== lhs && rhs !== rhs) { + return true; + } + if ((lhs != null ? typeof lhs.isEqual === "function" ? lhs.isEqual(rhs) : void 0 : void 0) && (rhs != null ? typeof rhs.isEqual === "function" ? rhs.isEqual(lhs) : void 0 : void 0)) { + return true; + } + return false; + }; + SimpleHash.prototype.forEach = function(iterator) { + var key, obj, value, values, _ref2, _results; + _ref2 = this._storage; + _results = []; + for (key in _ref2) { + values = _ref2[key]; + _results.push((function() { + var _j, _len2, _ref3, _results2; + _results2 = []; + for (_j = 0, _len2 = values.length; _j < _len2; _j++) { + _ref3 = values[_j], obj = _ref3[0], value = _ref3[1]; + _results2.push(iterator(obj, value)); + } + return _results2; + })()); + } + return _results; + }; + SimpleHash.prototype.keys = function() { + var result; + result = []; + Batman.SimpleHash.prototype.forEach.call(this, function(obj) { + return result.push(obj); + }); + return result; + }; + SimpleHash.prototype.clear = function() { + this._storage = {}; + return this.length = 0; + }; + SimpleHash.prototype.isEmpty = function() { + return this.length === 0; + }; + SimpleHash.prototype.merge = function() { + var hash, merged, others, _j, _len2; + others = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + merged = new this.constructor; + others.unshift(this); + for (_j = 0, _len2 = others.length; _j < _len2; _j++) { + hash = others[_j]; + hash.forEach(function(obj, value) { + return merged.set(obj, value); + }); + } + return merged; + }; + return SimpleHash; + })(); + Batman.Hash = (function() { + var k, _j, _len2, _ref2; + __extends(Hash, Batman.Object); + function Hash() { + var self; + Batman.SimpleHash.apply(this, arguments); + this.meta = new Batman.Object({ + length: 0 + }); + self = this; + this.meta.accessor('isEmpty', function() { + return self.isEmpty(); + }); + this.meta.accessor('keys', function() { + return self.keys(); + }); + Hash.__super__.constructor.apply(this, arguments); + } + $extendsEnumerable(Hash.prototype); + Hash.prototype.propertyClass = Batman.Property; + Hash.accessor({ + get: Batman.SimpleHash.prototype.get, + set: function() { + var results; + results = Batman.SimpleHash.prototype.set.apply(this, arguments); + this.meta.set('length', this.length); + return results; + }, + unset: function() { + var results; + results = Batman.SimpleHash.prototype.unset.apply(this, arguments); + this.meta.set('length', this.length); + return results; + } + }); + _ref2 = ['hasKey', 'equality', 'forEach', 'keys', 'isEmpty', 'merge']; + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + k = _ref2[_j]; + Hash.prototype[k] = Batman.SimpleHash.prototype[k]; + } + Hash.prototype.clear = function() { + var results; + results = Batman.SimpleHash.prototype.clear.apply(this, arguments); + this.meta.set('length', this.length); + return results; + }; + return Hash; + })(); + Batman.SimpleSet = (function() { + function SimpleSet() { + this._storage = new Batman.SimpleHash; + this._indexes = new Batman.SimpleHash; + this._sorts = new Batman.SimpleHash; + this.length = 0; + if (arguments.length > 0) { + this.add.apply(this, arguments); + } + } + $extendsEnumerable(SimpleSet.prototype); + SimpleSet.prototype.has = function(item) { + return this._storage.hasKey(item); + }; + SimpleSet.prototype.add = function() { + var addedItems, item, items, _j, _len2; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + addedItems = []; + for (_j = 0, _len2 = items.length; _j < _len2; _j++) { + item = items[_j]; + if (!this._storage.hasKey(item)) { + this._storage.set(item, true); + addedItems.push(item); + this.length++; + } + } + if (addedItems.length !== 0) { + this.itemsWereAdded.apply(this, addedItems); + } + return addedItems; + }; + SimpleSet.prototype.remove = function() { + var item, items, removedItems, _j, _len2; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + removedItems = []; + for (_j = 0, _len2 = items.length; _j < _len2; _j++) { + item = items[_j]; + if (this._storage.hasKey(item)) { + this._storage.unset(item); + removedItems.push(item); + this.length--; + } + } + if (removedItems.length !== 0) { + this.itemsWereRemoved.apply(this, removedItems); + } + return removedItems; + }; + SimpleSet.prototype.forEach = function(iterator) { + return this._storage.forEach(function(key, value) { + return iterator(key); + }); + }; + SimpleSet.prototype.isEmpty = function() { + return this.length === 0; + }; + SimpleSet.prototype.clear = function() { + var items; + items = this.toArray(); + this._storage = new Batman.SimpleHash; + this.length = 0; + this.itemsWereRemoved.apply(this, items); + return items; + }; + SimpleSet.prototype.toArray = function() { + return this._storage.keys(); + }; + SimpleSet.prototype.merge = function() { + var merged, others, set, _j, _len2; + others = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + merged = new this.constructor; + others.unshift(this); + for (_j = 0, _len2 = others.length; _j < _len2; _j++) { + set = others[_j]; + set.forEach(function(v) { + return merged.add(v); + }); + } + return merged; + }; + SimpleSet.prototype.indexedBy = function(key) { + return this._indexes.get(key) || this._indexes.set(key, new Batman.SetIndex(this, key)); + }; + SimpleSet.prototype.sortedBy = function(key) { + return this._sorts.get(key) || this._sorts.set(key, new Batman.SetSort(this, key)); + }; + SimpleSet.prototype.itemsWereAdded = function() {}; + SimpleSet.prototype.itemsWereRemoved = function() {}; + return SimpleSet; + })(); + Batman.Set = (function() { + var k, _fn2, _j, _k, _len2, _len3, _ref2, _ref3; + __extends(Set, Batman.Object); + function Set() { + Batman.SimpleSet.apply(this, arguments); + this.set('length', 0); + } + $extendsEnumerable(Set.prototype); + Set.prototype.itemsWereAdded = Set.event(function() {}); + Set.prototype.itemsWereRemoved = Set.event(function() {}); + _ref2 = ['has', 'forEach', 'isEmpty', 'toArray', 'indexedBy', 'sortedBy']; + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + k = _ref2[_j]; + Set.prototype[k] = Batman.SimpleSet.prototype[k]; + } + _ref3 = ['add', 'remove', 'clear', 'merge']; + _fn2 = __bind(function(k) { + return this.prototype[k] = function() { + var newLength, oldLength, results, _ref4; + oldLength = this.length; + this.prevent('length'); + results = Batman.SimpleSet.prototype[k].apply(this, arguments); + _ref4 = [this.length, oldLength], newLength = _ref4[0], this.length = _ref4[1]; + this.allow('length'); + if (newLength !== oldLength) { + this.set('length', newLength); + } + return results; + }; + }, Set); + for (_k = 0, _len3 = _ref3.length; _k < _len3; _k++) { + k = _ref3[_k]; + _fn2(k); + } + Set.accessor('indexedBy', function() { + return new Batman.Accessible(__bind(function(key) { + return this.indexedBy(key); + }, this)); + }); + Set.accessor('sortedBy', function() { + return new Batman.Accessible(__bind(function(key) { + return this.sortedBy(key); + }, this)); + }); + Set.accessor('isEmpty', function() { + return this.isEmpty(); + }); + return Set; + }).call(this); + Batman.SetObserver = (function() { + __extends(SetObserver, Batman.Object); + function SetObserver(base) { + this.base = base; + this._itemObservers = new Batman.Hash; + this._setObservers = new Batman.Hash; + this._setObservers.set("itemsWereAdded", this.itemsWereAdded.bind(this)); + this._setObservers.set("itemsWereRemoved", this.itemsWereRemoved.bind(this)); + this.observe('itemsWereAdded', this.startObservingItems.bind(this)); + this.observe('itemsWereRemoved', this.stopObservingItems.bind(this)); + } + SetObserver.prototype.itemsWereAdded = SetObserver.event(function() {}); + SetObserver.prototype.itemsWereRemoved = SetObserver.event(function() {}); + SetObserver.prototype.observedItemKeys = []; + SetObserver.prototype.observerForItemAndKey = function(item, key) {}; + SetObserver.prototype._getOrSetObserverForItemAndKey = function(item, key) { + return this._itemObservers.getOrSet(item, __bind(function() { + var observersByKey; + observersByKey = new Batman.Hash; + return observersByKey.getOrSet(key, __bind(function() { + return this.observerForItemAndKey(item, key); + }, this)); + }, this)); + }; + SetObserver.prototype.startObserving = function() { + this._manageItemObservers("observe"); + return this._manageSetObservers("observe"); + }; + SetObserver.prototype.stopObserving = function() { + this._manageItemObservers("forget"); + return this._manageSetObservers("forget"); + }; + SetObserver.prototype.startObservingItems = function() { + var item, items, _j, _len2, _results; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _results = []; + for (_j = 0, _len2 = items.length; _j < _len2; _j++) { + item = items[_j]; + _results.push(this._manageObserversForItem(item, "observe")); + } + return _results; + }; + SetObserver.prototype.stopObservingItems = function() { + var item, items, _j, _len2, _results; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _results = []; + for (_j = 0, _len2 = items.length; _j < _len2; _j++) { + item = items[_j]; + _results.push(this._manageObserversForItem(item, "forget")); + } + return _results; + }; + SetObserver.prototype._manageObserversForItem = function(item, method) { + var key, _j, _len2, _ref2; + if (!item.isObservable) { + return; + } + _ref2 = this.observedItemKeys; + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + key = _ref2[_j]; + item[method](key, this._getOrSetObserverForItemAndKey(item, key)); + } + if (method === "forget") { + return this._itemObservers.unset(item); + } + }; + SetObserver.prototype._manageItemObservers = function(method) { + return this.base.forEach(__bind(function(item) { + return this._manageObserversForItem(item, method); + }, this)); + }; + SetObserver.prototype._manageSetObservers = function(method) { + if (!this.base.isObservable) { + return; + } + return this._setObservers.forEach(__bind(function(key, observer) { + return this.base[method](key, observer); + }, this)); + }; + return SetObserver; + })(); + Batman.SetSort = (function() { + __extends(SetSort, Batman.Object); + function SetSort(base, key) { + var boundReIndex; + this.base = base; + this.key = key; + if (this.base.isObservable) { + this._setObserver = new Batman.SetObserver(this.base); + this._setObserver.observedItemKeys = [this.key]; + boundReIndex = this._reIndex.bind(this); + this._setObserver.observerForItemAndKey = function() { + return boundReIndex; + }; + this._setObserver.observe('itemsWereAdded', boundReIndex); + this._setObserver.observe('itemsWereRemoved', boundReIndex); + this.startObserving(); + } + this._reIndex(); + } + SetSort.prototype.startObserving = function() { + var _ref2; + return (_ref2 = this._setObserver) != null ? _ref2.startObserving() : void 0; + }; + SetSort.prototype.stopObserving = function() { + var _ref2; + return (_ref2 = this._setObserver) != null ? _ref2.stopObserving() : void 0; + }; + SetSort.prototype.toArray = function() { + return this.get('_storage'); + }; + SetSort.accessor('toArray', SetSort.prototype.toArray); + SetSort.prototype.forEach = function(iterator) { + var e, i, _len2, _ref2, _results; + _ref2 = this.get('_storage'); + _results = []; + for (i = 0, _len2 = _ref2.length; i < _len2; i++) { + e = _ref2[i]; + _results.push(iterator(e, i)); + } + return _results; + }; + SetSort.prototype.compare = function(a, b) { + var typeComparison; + if (a === b) { + return 0; + } + if (a === void 0) { + return 1; + } + if (b === void 0) { + return -1; + } + if (a === null) { + return 1; + } + if (b === null) { + return -1; + } + if ((typeof a.isEqual === "function" ? a.isEqual(b) : void 0) && (typeof b.isEqual === "function" ? b.isEqual(a) : void 0)) { + return 0; + } + typeComparison = Batman.SetSort.prototype.compare($typeOf(a), $typeOf(b)); + if (typeComparison !== 0) { + return typeComparison; + } + if (a !== a) { + return 1; + } + if (b !== b) { + return -1; + } + if (a > b) { + return 1; + } + if (a < b) { + return -1; + } + return 0; + }; + SetSort.prototype._reIndex = function() { + var newOrder, _ref2; + newOrder = this.base.toArray().sort(__bind(function(a, b) { + var valueA, valueB; + valueA = Batman.Observable.property.call(a, this.key).getValue(); + if (valueA != null) { + valueA = valueA.valueOf(); + } + valueB = Batman.Observable.property.call(b, this.key).getValue(); + if (valueB != null) { + valueB = valueB.valueOf(); + } + return this.compare.call(this, valueA, valueB); + }, this)); + if ((_ref2 = this._setObserver) != null) { + _ref2.startObservingItems.apply(_ref2, newOrder); + } + return this.set('_storage', newOrder); + }; + return SetSort; + })(); + Batman.SetIndex = (function() { + __extends(SetIndex, Batman.Object); + function SetIndex(base, key) { + this.base = base; + this.key = key; + this._storage = new Batman.Hash; + if (this.base.isObservable) { + this._setObserver = new Batman.SetObserver(this.base); + this._setObserver.observedItemKeys = [this.key]; + this._setObserver.observerForItemAndKey = this.observerForItemAndKey.bind(this); + this._setObserver.observe('itemsWereAdded', __bind(function() { + var item, items, _j, _len2, _results; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _results = []; + for (_j = 0, _len2 = items.length; _j < _len2; _j++) { + item = items[_j]; + _results.push(this._addItem(item)); + } + return _results; + }, this)); + this._setObserver.observe('itemsWereRemoved', __bind(function() { + var item, items, _j, _len2, _results; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _results = []; + for (_j = 0, _len2 = items.length; _j < _len2; _j++) { + item = items[_j]; + _results.push(this._removeItem(item)); + } + return _results; + }, this)); + } + this.base.forEach(this._addItem.bind(this)); + this.startObserving(); + } + SetIndex.accessor(function(key) { + return this._resultSetForKey(key); + }); + SetIndex.prototype.startObserving = function() { + var _ref2; + return (_ref2 = this._setObserver) != null ? _ref2.startObserving() : void 0; + }; + SetIndex.prototype.stopObserving = function() { + var _ref2; + return (_ref2 = this._setObserver) != null ? _ref2.stopObserving() : void 0; + }; + SetIndex.prototype.observerForItemAndKey = function(item, key) { + return __bind(function(newValue, oldValue) { + this._removeItemFromKey(item, oldValue); + return this._addItemToKey(item, newValue); + }, this); + }; + SetIndex.prototype._addItem = function(item) { + return this._addItemToKey(item, this._keyForItem(item)); + }; + SetIndex.prototype._addItemToKey = function(item, key) { + return this._resultSetForKey(key).add(item); + }; + SetIndex.prototype._removeItem = function(item) { + return this._removeItemFromKey(item, this._keyForItem(item)); + }; + SetIndex.prototype._removeItemFromKey = function(item, key) { + return this._resultSetForKey(key).remove(item); + }; + SetIndex.prototype._resultSetForKey = function(key) { + return this._storage.getOrSet(key, function() { + return new Batman.Set; + }); + }; + SetIndex.prototype._keyForItem = function(item) { + return Batman.Keypath.forBaseAndKey(item, this.key).getValue(); + }; + return SetIndex; + })(); + Batman.UniqueSetIndex = (function() { + __extends(UniqueSetIndex, Batman.SetIndex); + function UniqueSetIndex() { + this._uniqueIndex = new Batman.Hash; + UniqueSetIndex.__super__.constructor.apply(this, arguments); + } + UniqueSetIndex.accessor(function(key) { + return this._uniqueIndex.get(key); + }); + UniqueSetIndex.prototype._addItemToKey = function(item, key) { + this._resultSetForKey(key).add(item); + if (!this._uniqueIndex.hasKey(key)) { + return this._uniqueIndex.set(key, item); + } + }; + UniqueSetIndex.prototype._removeItemFromKey = function(item, key) { + var resultSet; + resultSet = this._resultSetForKey(key); + resultSet.remove(item); + if (resultSet.length === 0) { + return this._uniqueIndex.unset(key); + } else { + return this._uniqueIndex.set(key, resultSet.toArray()[0]); + } + }; + return UniqueSetIndex; + })(); + Batman.SortableSet = (function() { + var k, _fn2, _j, _len2, _ref2; + __extends(SortableSet, Batman.Set); + function SortableSet() { + SortableSet.__super__.constructor.apply(this, arguments); + this._sortIndexes = {}; + this.observe('activeIndex', __bind(function() { + return this.setWasSorted(this); + }, this)); + } + SortableSet.prototype.setWasSorted = SortableSet.event(function() { + if (this.length === 0) { + return false; + } + }); + _ref2 = ['add', 'remove', 'clear']; + _fn2 = __bind(function(k) { + return this.prototype[k] = function() { + var results; + results = Batman.Set.prototype[k].apply(this, arguments); + this._reIndex(); + return results; + }; + }, SortableSet); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + k = _ref2[_j]; + _fn2(k); + } + SortableSet.prototype.addIndex = function(index) { + return this._reIndex(index); + }; + SortableSet.prototype.removeIndex = function(index) { + this._sortIndexes[index] = null; + delete this._sortIndexes[index]; + if (this.activeIndex === index) { + this.unset('activeIndex'); + } + return index; + }; + SortableSet.prototype.forEach = function(iterator) { + var el, _k, _len3, _ref3, _results; + _ref3 = this.toArray(); + _results = []; + for (_k = 0, _len3 = _ref3.length; _k < _len3; _k++) { + el = _ref3[_k]; + _results.push(iterator(el)); + } + return _results; + }; + SortableSet.prototype.sortBy = function(index) { + if (!this._sortIndexes[index]) { + this.addIndex(index); + } + if (this.activeIndex !== index) { + this.set('activeIndex', index); + } + return this; + }; + SortableSet.prototype.isSorted = function() { + return this._sortIndexes[this.get('activeIndex')] != null; + }; + SortableSet.prototype.toArray = function() { + return this._sortIndexes[this.get('activeIndex')] || SortableSet.__super__.toArray.apply(this, arguments); + }; + SortableSet.prototype._reIndex = function(index) { + var ary, keypath, ordering, _ref3; + if (index) { + _ref3 = index.split(' '), keypath = _ref3[0], ordering = _ref3[1]; + ary = Batman.Set.prototype.toArray.call(this); + this._sortIndexes[index] = ary.sort(function(a, b) { + var valueA, valueB, _ref4, _ref5, _ref6; + valueA = (_ref4 = (Batman.Observable.property.call(a, keypath)).getValue()) != null ? _ref4.valueOf() : void 0; + valueB = (_ref5 = (Batman.Observable.property.call(b, keypath)).getValue()) != null ? _ref5.valueOf() : void 0; + if ((ordering != null ? ordering.toLowerCase() : void 0) === 'desc') { + _ref6 = [valueB, valueA], valueA = _ref6[0], valueB = _ref6[1]; + } + if (valueA < valueB) { + return -1; + } else if (valueA > valueB) { + return 1; + } else { + return 0; + } + }); + if (this.activeIndex === index) { + this.setWasSorted(this); + } + } else { + for (index in this._sortIndexes) { + this._reIndex(index); + } + this.setWasSorted(this); + } + return this; + }; + return SortableSet; + }).call(this); + Batman.StateMachine = { + initialize: function() { + Batman.initializeObject(this); + if (!this._batman.states) { + return this._batman.states = new Batman.SimpleHash; + } + }, + state: function(name, callback) { + var event; + Batman.StateMachine.initialize.call(this); + if (!name) { + return this._batman.getFirst('state'); + } + developer.assert(this.event, "StateMachine requires EventEmitter"); + event = this[name] || this.event(name, function() { + _stateMachine_setState.call(this, name); + return false; + }); + if (typeof callback === 'function') { + event.call(this, callback); + } + return event; + }, + transition: function(from, to, callback) { + var event, name, transitions; + Batman.StateMachine.initialize.call(this); + this.state(from); + this.state(to); + name = "" + from + "->" + to; + transitions = this._batman.states; + event = transitions.get(name) || transitions.set(name, $event(function() {})); + if (callback) { + event(callback); + } + return event; + } + }; + Batman.Object.actsAsStateMachine = function(includeInstanceMethods) { + if (includeInstanceMethods == null) { + includeInstanceMethods = true; + } + Batman.StateMachine.initialize.call(this); + Batman.StateMachine.initialize.call(this.prototype); + this.classState = function() { + return Batman.StateMachine.state.apply(this, arguments); + }; + this.state = function() { + return this.classState.apply(this.prototype, arguments); + }; + if (includeInstanceMethods) { + this.prototype.state = this.classState; + } + this.classTransition = function() { + return Batman.StateMachine.transition.apply(this, arguments); + }; + this.transition = function() { + return this.classTransition.apply(this.prototype, arguments); + }; + if (includeInstanceMethods) { + return this.prototype.transition = this.classTransition; + } + }; + _stateMachine_setState = function(newState) { + var event, name, oldState, _base, _j, _len2, _ref2, _ref3; + Batman.StateMachine.initialize.call(this); + if (this._batman.isTransitioning) { + ((_base = this._batman).nextState || (_base.nextState = [])).push(newState); + return false; + } + this._batman.isTransitioning = true; + oldState = this.state(); + this._batman.state = newState; + if (newState && oldState) { + name = "" + oldState + "->" + newState; + _ref2 = this._batman.getAll(function(ancestor) { + var _ref2, _ref3; + return (_ref2 = ancestor._batman) != null ? (_ref3 = _ref2.get('states')) != null ? _ref3.get(name) : void 0 : void 0; + }); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + event = _ref2[_j]; + if (event) { + event(newState, oldState); + } + } + } + if (newState) { + this.fire(newState, newState, oldState); + } + this._batman.isTransitioning = false; + if ((_ref3 = this._batman.nextState) != null ? _ref3.length : void 0) { + this[this._batman.nextState.shift()](); + } + return newState; + }; + Batman.Request = (function() { + __extends(Request, Batman.Object); + function Request() { + Request.__super__.constructor.apply(this, arguments); + } + Request.objectToFormData = function(data) { + var formData, key, pairForList, val, _j, _len2, _ref2, _ref3; + pairForList = function(key, object, first) { + var k, list, v; + if (first == null) { + first = false; + } + return list = (function() { + switch (Batman.typeOf(object)) { + case 'Object': + list = (function() { + var _results; + _results = []; + for (k in object) { + v = object[k]; + _results.push(pairForList((first ? k : "" + key + "[" + k + "]"), v)); + } + return _results; + })(); + return list.reduce(function(acc, list) { + return acc.concat(list); + }, []); + case 'Array': + return object.reduce(function(acc, element) { + return acc.concat(pairForList("" + key + "[]", element)); + }, []); + default: + return [[key, object]]; + } + })(); + }; + formData = new FormData(); + _ref2 = pairForList("", data, true); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + _ref3 = _ref2[_j], key = _ref3[0], val = _ref3[1]; + formData.append(key, val); + } + return formData; + }; + Request.prototype.url = ''; + Request.prototype.data = ''; + Request.prototype.method = 'get'; + Request.prototype.formData = false; + Request.prototype.response = null; + Request.prototype.status = null; + Request.prototype.contentType = 'application/x-www-form-urlencoded'; + Request.observeAll('url', function() { + return this._autosendTimeout = setTimeout((__bind(function() { + return this.send(); + }, this)), 0); + }); + Request.prototype.loading = Request.event(function() {}); + Request.prototype.loaded = Request.event(function() {}); + Request.prototype.success = Request.event(function() {}); + Request.prototype.error = Request.event(function() {}); + Request.prototype.send = function() { + return developer.error("Please source a dependency file for a request implementation"); + }; + Request.prototype.cancel = function() { + if (this._autosendTimeout) { + return clearTimeout(this._autosendTimeout); + } + }; + return Request; + })(); + Batman.App = (function() { + __extends(App, Batman.Object); + function App() { + App.__super__.constructor.apply(this, arguments); + } + App.requirePath = ''; + App.require = function() { + var base, name, names, path, _j, _len2; + path = arguments[0], names = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + base = this.requirePath + path; + for (_j = 0, _len2 = names.length; _j < _len2; _j++) { + name = names[_j]; + this.prevent('run'); + path = base + '/' + name + '.coffee'; + new Batman.Request({ + url: path, + type: 'html', + success: __bind(function(response) { + CoffeeScript.eval(response); + this.allow('run'); + return this.run(); + }, this) + }); + } + return this; + }; + App.controller = function() { + var names; + names = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + names = names.map(function(n) { + return n + '_controller'; + }); + return this.require.apply(this, ['controllers'].concat(__slice.call(names))); + }; + App.model = function() { + return this.require.apply(this, ['models'].concat(__slice.call(arguments))); + }; + App.view = function() { + return this.require.apply(this, ['views'].concat(__slice.call(arguments))); + }; + App.layout = void 0; + App.run = App.eventOneShot(function() { + if (Batman.currentApp) { + if (Batman.currentApp === this) { + return; + } + Batman.currentApp.stop(); + } + if (this.hasRun) { + return false; + } + Batman.currentApp = this; + if (typeof this.dispatcher === 'undefined') { + this.dispatcher || (this.dispatcher = new Batman.Dispatcher(this)); + } + if (typeof this.layout === 'undefined') { + this.set('layout', new Batman.View({ + contexts: [this], + node: document + })); + this.get('layout').ready(__bind(function() { + return this.ready(); + }, this)); + } + if (typeof this.historyManager === 'undefined' && this.dispatcher.routeMap) { + this.historyManager = Batman.historyManager = new Batman.HashHistory(this); + this.historyManager.start(); + } + this.hasRun = true; + return this; + }); + App.ready = App.eventOneShot(function() { + return true; + }); + App.stop = App.eventOneShot(function() { + var _ref2; + if ((_ref2 = this.historyManager) != null) { + _ref2.stop(); + } + Batman.historyManager = null; + this.hasRun = false; + return this; + }); + return App; + })(); + Batman.Route = (function() { + var escapeRegExp, namedOrSplat, namedParam, queryParam, splatParam; + __extends(Route, Batman.Object); + namedParam = /:([\w\d]+)/g; + splatParam = /\*([\w\d]+)/g; + queryParam = '(?:\\?.+)?'; + namedOrSplat = /[:|\*]([\w\d]+)/g; + escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g; + function Route() { + var array; + Route.__super__.constructor.apply(this, arguments); + this.pattern = this.url.replace(escapeRegExp, '\\$&'); + this.regexp = new RegExp('^' + this.pattern.replace(namedParam, '([^\/]*)').replace(splatParam, '(.*?)') + queryParam + '$'); + this.namedArguments = []; + while ((array = namedOrSplat.exec(this.pattern)) != null) { + if (array[1]) { + this.namedArguments.push(array[1]); + } + } + } + Route.accessor('action', { + get: function() { + var components, result, signature; + if (this.action) { + return this.action; + } + if (this.options) { + result = $mixin({}, this.options); + if (signature = result.signature) { + components = signature.split('#'); + result.controller = components[0]; + result.action = components[1] || 'index'; + } + result.target = this.dispatcher.get(result.controller); + return this.set('action', result); + } + }, + set: function(key, action) { + return this.action = action; + } + }); + Route.prototype.parameterize = function(url) { + var action, array, index, key, param, params, query, s, value, _j, _len2, _len3, _ref2, _ref3, _ref4, _ref5; + _ref2 = url.split('?'), url = _ref2[0], query = _ref2[1]; + array = (_ref3 = this.regexp.exec(url)) != null ? _ref3.slice(1) : void 0; + params = { + url: url + }; + action = this.get('action'); + if (typeof action === 'function') { + params.action = action; + } else { + $mixin(params, action); + } + if (array) { + for (index = 0, _len2 = array.length; index < _len2; index++) { + param = array[index]; + params[this.namedArguments[index]] = param; + } + } + if (query) { + _ref4 = query.split('&'); + for (_j = 0, _len3 = _ref4.length; _j < _len3; _j++) { + s = _ref4[_j]; + _ref5 = s.split('='), key = _ref5[0], value = _ref5[1]; + params[key] = value; + } + } + return params; + }; + Route.prototype.dispatch = function(url) { + var action, params, _ref2, _ref3; + if ($typeOf(url) === 'String') { + params = this.parameterize(url); + } + if (!(action = params.action) && url !== '/404') { + $redirect('/404'); + } + if (typeof action === 'function') { + return action(params); + } + if ((_ref2 = params.target) != null ? _ref2.dispatch : void 0) { + return params.target.dispatch(action, params); + } + return (_ref3 = params.target) != null ? _ref3[action](params) : void 0; + }; + return Route; + })(); + Batman.Dispatcher = (function() { + __extends(Dispatcher, Batman.Object); + function Dispatcher(app) { + var controller, key, _ref2; + this.app = app; + this.app.route(this); + this.app.controllers = new Batman.Object; + _ref2 = this.app; + for (key in _ref2) { + controller = _ref2[key]; + if (!((controller != null ? controller.prototype : void 0) instanceof Batman.Controller)) { + continue; + } + this.prepareController(controller); + } + } + Dispatcher.prototype.prepareController = function(controller) { + var getter, name; + name = helpers.underscore($functionName(controller).replace('Controller', '')); + if (!name) { + return; + } + getter = function() { + return this[name] = controller.get('sharedController'); + }; + this.accessor(name, getter); + return this.app.controllers.accessor(name, getter); + }; + Dispatcher.prototype.register = function(url, options) { + var route; + if (url.indexOf('/') !== 0) { + url = "/" + url; + } + route = $typeOf(options) === 'Function' ? new Batman.Route({ + url: url, + action: options, + dispatcher: this + }) : new Batman.Route({ + url: url, + options: options, + dispatcher: this + }); + this.routeMap || (this.routeMap = {}); + return this.routeMap[url] = route; + }; + Dispatcher.prototype.findRoute = function(url) { + var route, routeUrl, _ref2; + if (url.indexOf('/') !== 0) { + url = "/" + url; + } + if ((route = this.routeMap[url])) { + return route; + } + _ref2 = this.routeMap; + for (routeUrl in _ref2) { + route = _ref2[routeUrl]; + if (route.regexp.test(url)) { + return route; + } + } + }; + Dispatcher.prototype.findUrl = function(params) { + var action, controller, key, matches, options, route, url, value, _ref2, _ref3; + _ref2 = this.routeMap; + for (url in _ref2) { + route = _ref2[url]; + matches = false; + options = route.options; + if (params.resource) { + matches = options.resource === params.resource && options.action === params.action; + } else { + action = route.get('action'); + if (typeof action === 'function') { + continue; + } + _ref3 = action, controller = _ref3.controller, action = _ref3.action; + if (controller === params.controller && action === (params.action || 'index')) { + matches = true; + } + } + if (!matches) { + continue; + } + for (key in params) { + value = params[key]; + url = url.replace(new RegExp('[:|\*]' + key), value); + } + return url; + } + }; + Dispatcher.prototype.dispatch = function(url) { + var route; + route = this.findRoute(url); + if (route) { + route.dispatch(url); + } else if (url !== '/404') { + $redirect('/404'); + } + return this.app.set('currentURL', url); + }; + return Dispatcher; + })(); + Batman.HistoryManager = (function() { + function HistoryManager(app) { + this.app = app; + } + HistoryManager.prototype.dispatch = function(url) { + if (url.indexOf('/') !== 0) { + url = "/" + url; + } + this.app.dispatcher.dispatch(url); + return url; + }; + HistoryManager.prototype.redirect = function(url) { + if ($typeOf(url) !== 'String') { + url = this.app.dispatcher.findUrl(url); + } + return this.dispatch(url); + }; + return HistoryManager; + })(); + Batman.HashHistory = (function() { + __extends(HashHistory, Batman.HistoryManager); + function HashHistory() { + this.parseHash = __bind(this.parseHash, this); + this.stop = __bind(this.stop, this); + this.start = __bind(this.start, this); + HashHistory.__super__.constructor.apply(this, arguments); + } + HashHistory.prototype.HASH_PREFIX = '#!'; + HashHistory.prototype.start = function() { + if (typeof window === 'undefined') { + return; + } + if (this.started) { + return; + } + this.started = true; + if ('onhashchange' in window) { + $addEventListener(window, 'hashchange', this.parseHash); + } else { + this.interval = setInterval(this.parseHash, 100); + } + this.first = true; + Batman.currentApp.prevent('ready'); + return setTimeout(this.parseHash, 0); + }; + HashHistory.prototype.stop = function() { + if (this.interval) { + this.interval = clearInterval(this.interval); + } else { + $removeEventListener(window, 'hashchange', this.parseHash); + } + return this.started = false; + }; + HashHistory.prototype.urlFor = function(url) { + return this.HASH_PREFIX + url; + }; + HashHistory.prototype.parseHash = function() { + var hash, result; + hash = window.location.hash.replace(this.HASH_PREFIX, ''); + if (hash === this.cachedHash) { + return; + } + result = this.dispatch((this.cachedHash = hash)); + if (this.first) { + Batman.currentApp.allow('ready'); + Batman.currentApp.fire('ready'); + this.first = false; + } + return result; + }; + HashHistory.prototype.redirect = function(params) { + var url; + url = HashHistory.__super__.redirect.apply(this, arguments); + this.cachedHash = url; + return window.location.hash = this.HASH_PREFIX + url; + }; + return HashHistory; + })(); + Batman.redirect = $redirect = function(url) { + var _ref2; + return (_ref2 = Batman.historyManager) != null ? _ref2.redirect(url) : void 0; + }; + Batman.App.classMixin({ + route: function(url, signature, options) { + var dispatcher, key, value, _ref2; + if (options == null) { + options = {}; + } + if (!url) { + return; + } + if (url instanceof Batman.Dispatcher) { + dispatcher = url; + _ref2 = this._dispatcherCache; + for (key in _ref2) { + value = _ref2[key]; + dispatcher.register(key, value); + } + this._dispatcherCache = null; + return dispatcher; + } + if ($typeOf(signature) === 'String') { + options.signature = signature; + } else if ($typeOf(signature) === 'Function') { + options = signature; + } else if (signature) { + $mixin(options, signature); + } + this._dispatcherCache || (this._dispatcherCache = {}); + return this._dispatcherCache[url] = options; + }, + root: function(signature, options) { + return this.route('/', signature, options); + }, + resources: function(resource, options, callback) { + var app, controller, ops; + if (options == null) { + options = {}; + } + if (typeof options === 'function') { + callback = options; + options = {}; + } + resource = helpers.pluralize(resource); + controller = options.controller || resource; + if (options.index !== false) { + this.route(resource, "" + controller + "#index", { + resource: controller, + action: 'index' + }); + } + if (options["new"] !== false) { + this.route("" + resource + "/new", "" + controller + "#new", { + resource: controller, + action: 'new' + }); + } + if (options.show !== false) { + this.route("" + resource + "/:id", "" + controller + "#show", { + resource: controller, + action: 'show' + }); + } + if (options.edit !== false) { + this.route("" + resource + "/:id/edit", "" + controller + "#edit", { + resource: controller, + action: 'edit' + }); + } + if (callback) { + app = this; + ops = { + collection: function(collectionCallback) { + return collectionCallback != null ? collectionCallback.call({ + route: function(url, methodName) { + return app.route("" + resource + "/" + url, "" + controller + "#" + (methodName || url)); + } + }) : void 0; + }, + member: function(memberCallback) { + return memberCallback != null ? memberCallback.call({ + route: function(url, methodName) { + return app.route("" + resource + "/:id/" + url, "" + controller + "#" + (methodName || url)); + } + }) : void 0; + } + }; + return callback.call(ops); + } + }, + redirect: $redirect + }); + Batman.Controller = (function() { + __extends(Controller, Batman.Object); + function Controller() { + this.redirect = __bind(this.redirect, this); + Controller.__super__.constructor.apply(this, arguments); + } + Controller.singleton('sharedController'); + Controller.beforeFilter = function(nameOrFunction) { + var filters, _base; + Batman.initializeObject(this); + filters = (_base = this._batman).beforeFilters || (_base.beforeFilters = []); + if (filters.indexOf(nameOrFunction) === -1) { + return filters.push(nameOrFunction); + } + }; + Controller.accessor('controllerName', { + get: function() { + return this._controllerName || (this._controllerName = helpers.underscore($functionName(this.constructor).replace('Controller', ''))); + } + }); + Controller.afterFilter = function(nameOrFunction) { + var filters, _base; + Batman.initializeObject(this); + filters = (_base = this._batman).afterFilters || (_base.afterFilters = []); + if (filters.indexOf(nameOrFunction) === -1) { + return filters.push(nameOrFunction); + } + }; + Controller.accessor('action', { + get: function() { + return this._currentAction; + }, + set: function(key, value) { + return this._currentAction = value; + } + }); + Controller.prototype.dispatch = function(action, params) { + var filter, filters, oldRedirect, redirectTo, _j, _k, _len2, _len3, _ref2, _ref3, _ref4, _ref5, _ref6; + if (params == null) { + params = {}; + } + params.controller || (params.controller = this.get('controllerName')); + params.action || (params.action = action); + params.target || (params.target = this); + oldRedirect = (_ref2 = Batman.historyManager) != null ? _ref2.redirect : void 0; + if ((_ref3 = Batman.historyManager) != null) { + _ref3.redirect = this.redirect; + } + this._actedDuringAction = false; + this.set('action', action); + if (filters = (_ref4 = this.constructor._batman) != null ? _ref4.get('beforeFilters') : void 0) { + for (_j = 0, _len2 = filters.length; _j < _len2; _j++) { + filter = filters[_j]; + if (typeof filter === 'function') { + filter.call(this, params); + } else { + this[filter](params); + } + } + } + developer.assert(this[action], "Error! Controller action " + (this.get('controllerName')) + "." + action + " couldn't be found!"); + this[action](params); + if (!this._actedDuringAction) { + this.render(); + } + if (filters = (_ref5 = this.constructor._batman) != null ? _ref5.get('afterFilters') : void 0) { + for (_k = 0, _len3 = filters.length; _k < _len3; _k++) { + filter = filters[_k]; + if (typeof filter === 'function') { + filter.call(this, params); + } else { + this[filter](params); + } + } + } + delete this._actedDuringAction; + this.set('action', null); + if ((_ref6 = Batman.historyManager) != null) { + _ref6.redirect = oldRedirect; + } + redirectTo = this._afterFilterRedirect; + delete this._afterFilterRedirect; + if (redirectTo) { + return $redirect(redirectTo); + } + }; + Controller.prototype.redirect = function(url) { + if (this._actedDuringAction) { + throw 'DoubleRedirectError'; + } + if (this.get('action')) { + this._actedDuringAction = true; + return this._afterFilterRedirect = url; + } else { + if ($typeOf(url) === 'Object') { + if (!url.controller) { + url.controller = this; + } + } + return $redirect(url); + } + }; + Controller.prototype.render = function(options) { + var view, _ref2; + if (options == null) { + options = {}; + } + if (this._actedDuringAction) { + throw 'DoubleRenderError'; + } + this._actedDuringAction = true; + if (options === false) { + return; + } + if (!options.view) { + options.source || (options.source = helpers.underscore($functionName(this.constructor).replace('Controller', '')) + '/' + this._currentAction + '.html'); + options.view = new Batman.View(options); + } + if (view = options.view) { + if ((_ref2 = Batman.currentApp) != null) { + _ref2.prevent('ready'); + } + view.contexts.push(this); + view.ready(function() { + var _ref3, _ref4; + Batman.DOM.contentFor('main', view.get('node')); + if ((_ref3 = Batman.currentApp) != null) { + _ref3.allow('ready'); + } + return (_ref4 = Batman.currentApp) != null ? _ref4.fire('ready') : void 0; + }); + } + return view; + }; + return Controller; + })(); + Batman.Model = (function() { + var k, _j, _k, _len2, _len3, _ref2, _ref3; + __extends(Model, Batman.Object); + Model.primaryKey = 'id'; + Model.storageKey = null; + Model.persist = function() { + var mechanism, mechanisms, results, storage, _base; + mechanisms = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + Batman.initializeObject(this.prototype); + storage = (_base = this.prototype._batman).storage || (_base.storage = []); + results = (function() { + var _j, _len2, _results; + _results = []; + for (_j = 0, _len2 = mechanisms.length; _j < _len2; _j++) { + mechanism = mechanisms[_j]; + mechanism = mechanism.isStorageAdapter ? mechanism : new mechanism(this); + storage.push(mechanism); + _results.push(mechanism); + } + return _results; + }).call(this); + if (results.length > 1) { + return results; + } else { + return results[0]; + } + }; + Model.encode = function() { + var decoder, encoder, encoderOrLastKey, key, keys, _base, _base2, _j, _k, _len2, _results; + keys = 2 <= arguments.length ? __slice.call(arguments, 0, _j = arguments.length - 1) : (_j = 0, []), encoderOrLastKey = arguments[_j++]; + Batman.initializeObject(this.prototype); + (_base = this.prototype._batman).encoders || (_base.encoders = new Batman.SimpleHash); + (_base2 = this.prototype._batman).decoders || (_base2.decoders = new Batman.SimpleHash); + switch ($typeOf(encoderOrLastKey)) { + case 'String': + keys.push(encoderOrLastKey); + break; + case 'Function': + encoder = encoderOrLastKey; + break; + default: + encoder = encoderOrLastKey.encode; + decoder = encoderOrLastKey.decode; + } + if (typeof encoder === 'undefined') { + encoder = this.defaultEncoder.encode; + } + if (typeof decoder === 'undefined') { + decoder = this.defaultEncoder.decode; + } + _results = []; + for (_k = 0, _len2 = keys.length; _k < _len2; _k++) { + key = keys[_k]; + if (encoder) { + this.prototype._batman.encoders.set(key, encoder); + } + _results.push(decoder ? this.prototype._batman.decoders.set(key, decoder) : void 0); + } + return _results; + }; + Model.defaultEncoder = { + encode: function(x) { + return x; + }, + decode: function(x) { + return x; + } + }; + Model.observe('primaryKey', true, function(newPrimaryKey) { + return this.encode(newPrimaryKey, { + encode: false, + decode: this.defaultEncoder.decode + }); + }); + Model.validate = function() { + var keys, match, matches, options, optionsOrFunction, validator, validators, _base, _j, _k, _len2, _results; + keys = 2 <= arguments.length ? __slice.call(arguments, 0, _j = arguments.length - 1) : (_j = 0, []), optionsOrFunction = arguments[_j++]; + Batman.initializeObject(this.prototype); + validators = (_base = this.prototype._batman).validators || (_base.validators = []); + if (typeof optionsOrFunction === 'function') { + return validators.push({ + keys: keys, + callback: optionsOrFunction + }); + } else { + options = optionsOrFunction; + _results = []; + for (_k = 0, _len2 = Validators.length; _k < _len2; _k++) { + validator = Validators[_k]; + _results.push((function() { + var _l, _len3; + if ((matches = validator.matches(options))) { + for (_l = 0, _len3 = matches.length; _l < _len3; _l++) { + match = matches[_l]; + delete options[match]; + } + return validators.push({ + keys: keys, + validator: new validator(matches) + }); + } + })()); + } + return _results; + } + }; + Model.classAccessor('all', { + get: function() { + var _ref2; + if (this.prototype.hasStorage() && ((_ref2 = this.classState()) !== 'loaded' && _ref2 !== 'loading')) { + this.load(); + } + return this.get('loaded'); + }, + set: function(k, v) { + return this.set('loaded', v); + } + }); + Model.classAccessor('loaded', { + get: function() { + if (!this.all) { + this.all = new Batman.SortableSet; + this.all.sortBy("id asc"); + } + return this.all; + }, + set: function(k, v) { + return this.all = v; + } + }); + Model.classAccessor('first', function() { + return this.get('all').toArray()[0]; + }); + Model.classAccessor('last', function() { + var x; + x = this.get('all').toArray(); + return x[x.length - 1]; + }); + Model.find = function(id, callback) { + var newRecord, record; + developer.assert(callback, "Must call find with a callback!"); + record = new this(id); + newRecord = this._mapIdentity(record); + newRecord.load(callback); + return newRecord; + }; + Model.load = function(options, callback) { + if ($typeOf(options) === 'Function') { + callback = options; + options = {}; + } + developer.assert(this.prototype._batman.getAll('storage').length, "Can't load model " + ($functionName(this)) + " without any storage adapters!"); + this.loading(); + return this.prototype._doStorageOperation('readAll', options, __bind(function(err, records) { + var mappedRecords, record; + if (err != null) { + return typeof callback === "function" ? callback(err, []) : void 0; + } else { + mappedRecords = (function() { + var _j, _len2, _results; + _results = []; + for (_j = 0, _len2 = records.length; _j < _len2; _j++) { + record = records[_j]; + _results.push(this._mapIdentity(record)); + } + return _results; + }).call(this); + this.loaded(); + return typeof callback === "function" ? callback(err, mappedRecords) : void 0; + } + }, this)); + }; + Model.create = function(attrs, callback) { + var obj, _ref2; + if (!callback) { + _ref2 = [{}, attrs], attrs = _ref2[0], callback = _ref2[1]; + } + obj = new this(attrs); + obj.save(callback); + return obj; + }; + Model.findOrCreate = function(attrs, callback) { + var foundRecord, record; + record = new this(attrs); + if (record.isNew()) { + return record.save(callback); + } else { + foundRecord = this._mapIdentity(record); + foundRecord.updateAttributes(attrs); + return callback(void 0, foundRecord); + } + }; + Model._mapIdentity = function(record) { + var existing, id, _ref2; + if (typeof (id = record.get('id')) === 'undefined' || id === '') { + return record; + } else { + existing = (_ref2 = this.get("loaded.indexedBy.id").get(id)) != null ? _ref2.toArray()[0] : void 0; + if (existing) { + existing.updateAttributes(record._batman.attributes || {}); + return existing; + } else { + this.get('loaded').add(record); + return record; + } + } + }; + Model.accessor('id', { + get: function() { + var pk; + pk = this.constructor.get('primaryKey'); + if (pk === 'id') { + return this.id; + } else { + return this.get(pk); + } + }, + set: function(k, v) { + var pk; + pk = this.constructor.get('primaryKey'); + if (pk === 'id') { + return this.id = v; + } else { + return this.set(pk, v); + } + } + }); + Model.accessor('dirtyKeys', 'errors', Batman.Property.defaultAccessor); + Model.accessor('batmanState', { + get: function() { + return this.state(); + }, + set: function(k, v) { + return this.state(v); + } + }); + Model.accessor({ + get: function(k) { + var _base; + return ((_base = this._batman).attributes || (_base.attributes = {}))[k] || this[k]; + }, + set: function(k, v) { + var _base; + return ((_base = this._batman).attributes || (_base.attributes = {}))[k] = v; + }, + unset: function(k) { + var x, _base; + x = ((_base = this._batman).attributes || (_base.attributes = {}))[k]; + delete this._batman.attributes[k]; + return x; + } + }); + function Model(idOrAttributes) { + if (idOrAttributes == null) { + idOrAttributes = {}; + } + this.destroy = __bind(this.destroy, this); + this.save = __bind(this.save, this); + this.load = __bind(this.load, this); + developer.assert(this instanceof Batman.Object, "constructors must be called with new"); + this.dirtyKeys = new Batman.Hash; + this.errors = new Batman.ErrorsHash; + if ($typeOf(idOrAttributes) === 'Object') { + Model.__super__.constructor.call(this, idOrAttributes); + } else { + Model.__super__.constructor.call(this); + this.set('id', idOrAttributes); + } + if (!this.state()) { + this.empty(); + } + } + Model.prototype.set = function(key, value) { + var oldValue, result, _ref2; + oldValue = this.get(key); + if (oldValue === value) { + return; + } + result = Model.__super__.set.apply(this, arguments); + this.dirtyKeys.set(key, oldValue); + if ((_ref2 = this.state()) !== 'dirty' && _ref2 !== 'loading' && _ref2 !== 'creating') { + this.dirty(); + } + return result; + }; + Model.prototype.updateAttributes = function(attrs) { + this.mixin(attrs); + return this; + }; + Model.prototype.toString = function() { + return "" + ($functionName(this.constructor)) + ": " + (this.get('id')); + }; + Model.prototype.toJSON = function() { + var encoders, obj; + obj = {}; + encoders = this._batman.get('encoders'); + if (!(!encoders || encoders.isEmpty())) { + encoders.forEach(__bind(function(key, encoder) { + var encodedVal, val; + val = this.get(key); + if (typeof val !== 'undefined') { + encodedVal = encoder(this.get(key)); + if (typeof encodedVal !== 'undefined') { + return obj[key] = encodedVal; + } + } + }, this)); + } + return obj; + }; + Model.prototype.fromJSON = function(data) { + var decoders, key, obj, value; + obj = {}; + decoders = this._batman.get('decoders'); + if (!decoders || decoders.isEmpty()) { + for (key in data) { + value = data[key]; + obj[key] = value; + } + } else { + decoders.forEach(function(key, decoder) { + if (data[key]) { + return obj[key] = decoder(data[key]); + } + }); + } + return this.mixin(obj); + }; + Model.actsAsStateMachine(true); + _ref2 = ['empty', 'dirty', 'loading', 'loaded', 'saving', 'saved', 'creating', 'created', 'validating', 'validated', 'destroying', 'destroyed']; + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + k = _ref2[_j]; + Model.state(k); + } + _ref3 = ['loading', 'loaded']; + for (_k = 0, _len3 = _ref3.length; _k < _len3; _k++) { + k = _ref3[_k]; + Model.classState(k); + } + Model.prototype._doStorageOperation = function(operation, options, callback) { + var mechanism, mechanisms, _l, _len4; + developer.assert(this.hasStorage(), "Can't " + operation + " model " + ($functionName(this.constructor)) + " without any storage adapters!"); + mechanisms = this._batman.get('storage'); + for (_l = 0, _len4 = mechanisms.length; _l < _len4; _l++) { + mechanism = mechanisms[_l]; + mechanism[operation](this, options, callback); + } + return true; + }; + Model.prototype.hasStorage = function() { + return (this._batman.get('storage') || []).length > 0; + }; + Model.prototype.load = function(callback) { + var _ref4; + if ((_ref4 = this.state()) === 'destroying' || _ref4 === 'destroyed') { + if (typeof callback === "function") { + callback(new Error("Can't load a destroyed record!")); + } + return; + } + this.loading(); + return this._doStorageOperation('read', {}, __bind(function(err, record) { + if (!err) { + this.loaded(); + record = this.constructor._mapIdentity(record); + } + return typeof callback === "function" ? callback(err, record) : void 0; + }, this)); + }; + Model.prototype.save = function(callback) { + var _ref4; + if ((_ref4 = this.state()) === 'destroying' || _ref4 === 'destroyed') { + if (typeof callback === "function") { + callback(new Error("Can't save a destroyed record!")); + } + return; + } + return this.validate(__bind(function(isValid, errors) { + var creating; + if (!isValid) { + if (typeof callback === "function") { + callback(errors); + } + return; + } + creating = this.isNew(); + this.saving(); + if (creating) { + this.creating(); + } + return this._doStorageOperation((creating ? 'create' : 'update'), {}, __bind(function(err, record) { + if (!err) { + if (creating) { + this.created(); + } + this.saved(); + this.dirtyKeys.clear(); + record = this.constructor._mapIdentity(record); + } + return typeof callback === "function" ? callback(err, record) : void 0; + }, this)); + }, this)); + }; + Model.prototype.destroy = function(callback) { + this.destroying(); + return this._doStorageOperation('destroy', {}, __bind(function(err, record) { + if (!err) { + this.constructor.get('all').remove(this); + this.destroyed(); + } + return typeof callback === "function" ? callback(err) : void 0; + }, this)); + }; + Model.prototype.validate = function(callback) { + var count, finish, key, oldState, v, validationCallback, validator, validators, _l, _len4, _len5, _m, _ref4; + oldState = this.state(); + this.errors.clear(); + this.validating(); + finish = __bind(function() { + this.validated(); + this[oldState](); + return typeof callback === "function" ? callback(this.errors.length === 0, this.errors) : void 0; + }, this); + validators = this._batman.get('validators') || []; + if (!(validators.length > 0)) { + finish(); + } else { + count = validators.length; + validationCallback = __bind(function() { + if (--count === 0) { + return finish(); + } + }, this); + for (_l = 0, _len4 = validators.length; _l < _len4; _l++) { + validator = validators[_l]; + v = validator.validator; + _ref4 = validator.keys; + for (_m = 0, _len5 = _ref4.length; _m < _len5; _m++) { + key = _ref4[_m]; + if (v) { + v.validateEach(this.errors, this, key, validationCallback); + } else { + validator.callback(this.errors, this, key, validationCallback); + } + } + } + } + }; + Model.prototype.isNew = function() { + return typeof this.get('id') === 'undefined'; + }; + return Model; + })(); + Batman.ErrorsHash = (function() { + __extends(ErrorsHash, Batman.Hash); + function ErrorsHash() { + ErrorsHash.__super__.constructor.apply(this, arguments); + this.meta.observe('length', __bind(function(newLength) { + return this.length = newLength; + }, this)); + this.meta.set('messages', new Batman.Set); + } + ErrorsHash.accessor({ + get: function(key) { + var set; + if (!(set = Batman.SimpleHash.prototype.get.call(this, key))) { + set = new Batman.Set; + set.observe('itemsWereAdded', __bind(function() { + var items, _ref2; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + this.meta.set('length', this.meta.get('length') + items.length); + return (_ref2 = this.meta.get('messages')).add.apply(_ref2, items); + }, this)); + set.observe('itemsWereRemoved', __bind(function() { + var items, _ref2; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + this.meta.set('length', this.meta.get('length') - arguments.length); + return (_ref2 = this.meta.get('messages')).remove.apply(_ref2, items); + }, this)); + Batman.SimpleHash.prototype.set.call(this, key, set); + } + return set; + }, + set: function() { + return developer.error("Can't set on an errors hash, use add instead!"); + }, + unset: function() { + return developer.error("Can't unset on an errors hash, use clear instead!"); + } + }); + ErrorsHash.prototype.add = function(key, error) { + return this.get(key).add(error); + }; + ErrorsHash.prototype.clear = function() { + this.forEach(function(key, set) { + return set.clear(); + }); + return this; + }; + return ErrorsHash; + })(); + Batman.Validator = (function() { + __extends(Validator, Batman.Object); + function Validator() { + var mixins, options; + options = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + this.options = options; + Validator.__super__.constructor.apply(this, mixins); + } + Validator.prototype.validate = function(record) { + return developer.error("You must override validate in Batman.Validator subclasses."); + }; + Validator.options = function() { + var options; + options = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + Batman.initializeObject(this); + if (this._batman.options) { + return this._batman.options.concat(options); + } else { + return this._batman.options = options; + } + }; + Validator.matches = function(options) { + var key, results, shouldReturn, value, _ref2, _ref3; + results = {}; + shouldReturn = false; + for (key in options) { + value = options[key]; + if (~((_ref2 = this._batman) != null ? (_ref3 = _ref2.options) != null ? _ref3.indexOf(key) : void 0 : void 0)) { + results[key] = value; + shouldReturn = true; + } + } + if (shouldReturn) { + return results; + } + }; + return Validator; + })(); + Validators = Batman.Validators = [ + Batman.LengthValidator = (function() { + __extends(LengthValidator, Batman.Validator); + LengthValidator.options('minLength', 'maxLength', 'length', 'lengthWithin', 'lengthIn'); + function LengthValidator(options) { + var range; + if (range = options.lengthIn || options.lengthWithin) { + options.minLength = range[0]; + options.maxLength = range[1] || -1; + delete options.lengthWithin; + delete options.lengthIn; + } + LengthValidator.__super__.constructor.apply(this, arguments); + } + LengthValidator.prototype.validateEach = function(errors, record, key, callback) { + var options, value; + options = this.options; + value = record.get(key); + if (options.minLength && value.length < options.minLength) { + errors.add(key, "" + key + " must be at least " + options.minLength + " characters"); + } + if (options.maxLength && value.length > options.maxLength) { + errors.add(key, "" + key + " must be less than " + options.maxLength + " characters"); + } + if (options.length && value.length !== options.length) { + errors.add(key, "" + key + " must be " + options.length + " characters"); + } + return callback(); + }; + return LengthValidator; + })(), Batman.PresenceValidator = (function() { + __extends(PresenceValidator, Batman.Validator); + function PresenceValidator() { + PresenceValidator.__super__.constructor.apply(this, arguments); + } + PresenceValidator.options('presence'); + PresenceValidator.prototype.validateEach = function(errors, record, key, callback) { + var value; + value = record.get(key); + if (this.options.presence && !(value != null)) { + errors.add(key, "" + key + " must be present"); + } + return callback(); + }; + return PresenceValidator; + })() + ]; + Batman.StorageAdapter = (function() { + var k, time, _fn2, _j, _k, _len2, _len3, _ref2, _ref3; + __extends(StorageAdapter, Batman.Object); + function StorageAdapter(model) { + StorageAdapter.__super__.constructor.call(this, { + model: model, + modelKey: model.get('storageKey') || helpers.pluralize(helpers.underscore($functionName(model))) + }); + } + StorageAdapter.prototype.isStorageAdapter = true; + StorageAdapter.prototype._batman.check(StorageAdapter.prototype); + _ref2 = ['all', 'create', 'read', 'readAll', 'update', 'destroy']; + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + k = _ref2[_j]; + _ref3 = ['before', 'after']; + _fn2 = __bind(function(k, time) { + var key; + key = "" + time + (helpers.capitalize(k)); + return this.prototype[key] = function(filter) { + var _base, _name; + this._batman.check(this); + return ((_base = this._batman)[_name = "" + key + "Filters"] || (_base[_name] = [])).push(filter); + }; + }, StorageAdapter); + for (_k = 0, _len3 = _ref3.length; _k < _len3; _k++) { + time = _ref3[_k]; + _fn2(k, time); + } + } + StorageAdapter.prototype.before = function() { + var callback, k, keys, _l, _len4, _m, _results; + keys = 2 <= arguments.length ? __slice.call(arguments, 0, _l = arguments.length - 1) : (_l = 0, []), callback = arguments[_l++]; + _results = []; + for (_m = 0, _len4 = keys.length; _m < _len4; _m++) { + k = keys[_m]; + _results.push(this["before" + (helpers.capitalize(k))](callback)); + } + return _results; + }; + StorageAdapter.prototype.after = function() { + var callback, k, keys, _l, _len4, _m, _results; + keys = 2 <= arguments.length ? __slice.call(arguments, 0, _l = arguments.length - 1) : (_l = 0, []), callback = arguments[_l++]; + _results = []; + for (_m = 0, _len4 = keys.length; _m < _len4; _m++) { + k = keys[_m]; + _results.push(this["after" + (helpers.capitalize(k))](callback)); + } + return _results; + }; + StorageAdapter.prototype._filterData = function() { + var action, data, prefix; + prefix = arguments[0], action = arguments[1], data = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + return (this._batman.get("" + prefix + (helpers.capitalize(action)) + "Filters") || []).concat(this._batman.get("" + prefix + "AllFilters") || []).reduce(__bind(function(filteredData, filter) { + return filter.call(this, filteredData); + }, this), data); + }; + StorageAdapter.prototype.getRecordFromData = function(data) { + var record; + record = new this.model(); + record.fromJSON(data); + return record; + }; + return StorageAdapter; + }).call(this); + $passError = function(f) { + return function(filterables) { + var err; + if (filterables[0]) { + return filterables; + } else { + err = filterables.shift(); + filterables = f.call(this, filterables); + filterables.unshift(err); + return filterables; + } + }; + }; + Batman.LocalStorage = (function() { + __extends(LocalStorage, Batman.StorageAdapter); + function LocalStorage() { + if (typeof window.localStorage === 'undefined') { + return null; + } + LocalStorage.__super__.constructor.apply(this, arguments); + this.storage = localStorage; + this.key_re = new RegExp("^" + this.modelKey + "(\\d+)$"); + this.nextId = 1; + this._forAllRecords(function(k, v) { + var matches; + if (matches = this.key_re.exec(k)) { + return this.nextId = Math.max(this.nextId, parseInt(matches[1], 10) + 1); + } + }); + return; + } + LocalStorage.prototype.before('create', 'update', $passError(function(_arg) { + var options, record; + record = _arg[0], options = _arg[1]; + return [JSON.stringify(record), options]; + })); + LocalStorage.prototype.after('read', $passError(function(_arg) { + var attributes, options, record; + record = _arg[0], attributes = _arg[1], options = _arg[2]; + return [record.fromJSON(JSON.parse(attributes)), attributes, options]; + })); + LocalStorage.prototype._forAllRecords = function(f) { + var i, _ref2, _results; + _results = []; + for (i = 0, _ref2 = this.storage.length; 0 <= _ref2 ? i < _ref2 : i > _ref2; 0 <= _ref2 ? i++ : i--) { + k = this.storage.key(i); + _results.push(f.call(this, k, this.storage.getItem(k))); + } + return _results; + }; + LocalStorage.prototype.getRecordFromData = function(data) { + var record; + record = LocalStorage.__super__.getRecordFromData.apply(this, arguments); + this.nextId = Math.max(this.nextId, parseInt(record.get('id'), 10) + 1); + return record; + }; + LocalStorage.prototype.update = function(record, options, callback) { + var err, id, recordToSave, _ref2; + _ref2 = this._filterData('before', 'update', void 0, record, options), err = _ref2[0], recordToSave = _ref2[1]; + if (!err) { + id = record.get('id'); + if (id != null) { + this.storage.setItem(this.modelKey + id, recordToSave); + } else { + err = new Error("Couldn't get record primary key."); + } + } + return callback.apply(null, this._filterData('after', 'update', err, record, options)); + }; + LocalStorage.prototype.create = function(record, options, callback) { + var err, id, key, recordToSave, _ref2; + _ref2 = this._filterData('before', 'create', void 0, record, options), err = _ref2[0], recordToSave = _ref2[1]; + if (!err) { + id = record.get('id') || record.set('id', this.nextId++); + if (id != null) { + key = this.modelKey + id; + if (this.storage.getItem(key)) { + err = new Error("Can't create because the record already exists!"); + } else { + this.storage.setItem(key, recordToSave); + } + } else { + err = new Error("Couldn't set record primary key on create!"); + } + } + return callback.apply(null, this._filterData('after', 'create', err, record, options)); + }; + LocalStorage.prototype.read = function(record, options, callback) { + var attrs, err, id, _ref2; + _ref2 = this._filterData('before', 'read', void 0, record, options), err = _ref2[0], record = _ref2[1]; + id = record.get('id'); + if (!err) { + if (id != null) { + attrs = this.storage.getItem(this.modelKey + id); + if (!attrs) { + err = new Error("Couldn't find record!"); + } + } else { + err = new Error("Couldn't get record primary key."); + } + } + return callback.apply(null, this._filterData('after', 'read', err, record, attrs, options)); + }; + LocalStorage.prototype.readAll = function(_, options, callback) { + var err, records, _ref2; + records = []; + _ref2 = this._filterData('before', 'readAll', void 0, options), err = _ref2[0], options = _ref2[1]; + if (!err) { + this._forAllRecords(function(storageKey, data) { + var keyMatches; + if (keyMatches = this.key_re.exec(storageKey)) { + return records.push({ + data: data, + id: keyMatches[1] + }); + } + }); + } + return callback.apply(null, this._filterData('after', 'readAll', err, records, options)); + }; + LocalStorage.prototype.after('readAll', $passError(function(_arg) { + var allAttributes, attributes, data, options; + allAttributes = _arg[0], options = _arg[1]; + allAttributes = (function() { + var _j, _len2, _name, _results; + _results = []; + for (_j = 0, _len2 = allAttributes.length; _j < _len2; _j++) { + attributes = allAttributes[_j]; + data = JSON.parse(attributes.data); + data[_name = this.model.primaryKey] || (data[_name] = parseInt(attributes.id, 10)); + _results.push(data); + } + return _results; + }).call(this); + return [allAttributes, options]; + })); + LocalStorage.prototype.after('readAll', $passError(function(_arg) { + var allAttributes, data, k, match, matches, options, v, _j, _len2; + allAttributes = _arg[0], options = _arg[1]; + matches = []; + for (_j = 0, _len2 = allAttributes.length; _j < _len2; _j++) { + data = allAttributes[_j]; + match = true; + for (k in options) { + v = options[k]; + if (data[k] !== v) { + match = false; + break; + } + } + if (match) { + matches.push(data); + } + } + return [matches, options]; + })); + LocalStorage.prototype.after('readAll', $passError(function(_arg) { + var data, filteredAttributes, options; + filteredAttributes = _arg[0], options = _arg[1]; + return [ + (function() { + var _j, _len2, _results; + _results = []; + for (_j = 0, _len2 = filteredAttributes.length; _j < _len2; _j++) { + data = filteredAttributes[_j]; + _results.push(this.getRecordFromData(data)); + } + return _results; + }).call(this), filteredAttributes, options + ]; + })); + LocalStorage.prototype.destroy = function(record, options, callback) { + var err, id, key, _ref2; + _ref2 = this._filterData('before', 'destroy', void 0, record, options), err = _ref2[0], record = _ref2[1]; + if (!err) { + id = record.get('id'); + if (id != null) { + key = this.modelKey + id; + if (this.storage.getItem(key)) { + this.storage.removeItem(key); + } else { + err = new Error("Can't delete nonexistant record!"); + } + } else { + err = new Error("Can't delete record without an primary key!"); + } + } + return callback.apply(null, this._filterData('after', 'destroy', err, record, options)); + }; + return LocalStorage; + })(); + Batman.RestStorage = (function() { + __extends(RestStorage, Batman.StorageAdapter); + RestStorage.prototype.defaultOptions = { + type: 'json' + }; + RestStorage.prototype.recordJsonNamespace = false; + RestStorage.prototype.collectionJsonNamespace = false; + function RestStorage() { + RestStorage.__super__.constructor.apply(this, arguments); + this.recordJsonNamespace = helpers.singularize(this.modelKey); + this.collectionJsonNamespace = helpers.pluralize(this.modelKey); + } + RestStorage.prototype.before('create', 'update', $passError(function(_arg) { + var json, options, record, x; + record = _arg[0], options = _arg[1]; + json = record.toJSON(); + record = this.recordJsonNamespace ? (x = {}, x[this.recordJsonNamespace] = json, x) : json; + return [record, options]; + })); + RestStorage.prototype.after('create', 'read', 'update', $passError(function(_arg) { + var data, options, record; + record = _arg[0], data = _arg[1], options = _arg[2]; + if (data[this.recordJsonNamespace]) { + data = data[this.recordJsonNamespace]; + } + return [record, data, options]; + })); + RestStorage.prototype.after('create', 'read', 'update', $passError(function(_arg) { + var data, options, record; + record = _arg[0], data = _arg[1], options = _arg[2]; + record.fromJSON(data); + return [record, data, options]; + })); + RestStorage.prototype.optionsForRecord = function(record, idRequired, callback) { + var id, url; + if (record.url) { + url = typeof record.url === 'function' ? record.url() : record.url; + } else { + url = "/" + this.modelKey; + if (idRequired || !record.isNew()) { + id = record.get('id'); + if (!(id != null)) { + callback.call(this, new Error("Couldn't get record primary key!")); + return; + } + url = url + "/" + id; + } + } + if (!url) { + return callback.call(this, new Error("Couldn't get model url!")); + } else { + return callback.call(this, void 0, $mixin({}, this.defaultOptions, { + url: url + })); + } + }; + RestStorage.prototype.optionsForCollection = function(recordsOptions, callback) { + var url, _base; + url = (typeof (_base = this.model).url === "function" ? _base.url() : void 0) || this.model.url || ("/" + this.modelKey); + if (!url) { + return callback.call(this, new Error("Couldn't get collection url!")); + } else { + return callback.call(this, void 0, $mixin({}, this.defaultOptions, { + url: url, + data: $mixin({}, this.defaultOptions.data, recordsOptions) + })); + } + }; + RestStorage.prototype.create = function(record, recordOptions, callback) { + return this.optionsForRecord(record, false, function(err, options) { + var data, _ref2; + _ref2 = this._filterData('before', 'create', err, record, recordOptions), err = _ref2[0], data = _ref2[1]; + if (err) { + callback(err); + return; + } + return new Batman.Request($mixin(options, { + data: data, + method: 'POST', + success: __bind(function(data) { + return callback.apply(null, this._filterData('after', 'create', void 0, record, data, recordOptions)); + }, this), + error: __bind(function(error) { + return callback.apply(null, this._filterData('after', 'create', error, record, error.request.get('response'), recordOptions)); + }, this) + })); + }); + }; + RestStorage.prototype.update = function(record, recordOptions, callback) { + return this.optionsForRecord(record, true, function(err, options) { + var data, _ref2; + _ref2 = this._filterData('before', 'update', err, record, recordOptions), err = _ref2[0], data = _ref2[1]; + if (err) { + callback(err); + return; + } + return new Batman.Request($mixin(options, { + data: data, + method: 'PUT', + success: __bind(function(data) { + return callback.apply(null, this._filterData('after', 'update', void 0, record, data, recordOptions)); + }, this), + error: __bind(function(error) { + return callback.apply(null, this._filterData('after', 'update', error, record, error.request.get('response'), recordOptions)); + }, this) + })); + }); + }; + RestStorage.prototype.read = function(record, recordOptions, callback) { + return this.optionsForRecord(record, true, function(err, options) { + var _ref2; + _ref2 = this._filterData('before', 'read', err, record, recordOptions), err = _ref2[0], record = _ref2[1], recordOptions = _ref2[2]; + if (err) { + callback(err); + return; + } + return new Batman.Request($mixin(options, { + data: recordOptions, + method: 'GET', + success: __bind(function(data) { + return callback.apply(null, this._filterData('after', 'read', void 0, record, data, recordOptions)); + }, this), + error: __bind(function(error) { + return callback.apply(null, this._filterData('after', 'read', error, record, error.request.get('response'), recordOptions)); + }, this) + })); + }); + }; + RestStorage.prototype.readAll = function(_, recordsOptions, callback) { + return this.optionsForCollection(recordsOptions, function(err, options) { + var _ref2; + _ref2 = this._filterData('before', 'readAll', err, recordsOptions), err = _ref2[0], recordsOptions = _ref2[1]; + if (err) { + callback(err); + return; + } + if (recordsOptions && recordsOptions.url) { + options.url = recordsOptions.url; + delete recordsOptions.url; + } + return new Batman.Request($mixin(options, { + data: recordsOptions, + method: 'GET', + success: __bind(function(data) { + return callback.apply(null, this._filterData('after', 'readAll', void 0, data, recordsOptions)); + }, this), + error: __bind(function(error) { + return callback.apply(null, this._filterData('after', 'readAll', error, error.request.get('response'), recordsOptions)); + }, this) + })); + }); + }; + RestStorage.prototype.after('readAll', $passError(function(_arg) { + var data, options, recordData; + data = _arg[0], options = _arg[1]; + recordData = data[this.collectionJsonNamespace] ? data[this.collectionJsonNamespace] : data; + return [recordData, data, options]; + })); + RestStorage.prototype.after('readAll', $passError(function(_arg) { + var attributes, options, recordData, serverData; + recordData = _arg[0], serverData = _arg[1], options = _arg[2]; + return [ + (function() { + var _j, _len2, _results; + _results = []; + for (_j = 0, _len2 = recordData.length; _j < _len2; _j++) { + attributes = recordData[_j]; + _results.push(this.getRecordFromData(attributes)); + } + return _results; + }).call(this), serverData, options + ]; + })); + RestStorage.prototype.destroy = function(record, recordOptions, callback) { + return this.optionsForRecord(record, true, function(err, options) { + var _ref2; + _ref2 = this._filterData('before', 'destroy', err, record, recordOptions), err = _ref2[0], record = _ref2[1], recordOptions = _ref2[2]; + if (err) { + callback(err); + return; + } + return new Batman.Request($mixin(options, { + method: 'DELETE', + success: __bind(function(data) { + return callback.apply(null, this._filterData('after', 'destroy', void 0, record, data, recordOptions)); + }, this), + error: __bind(function(error) { + return callback.apply(null, this._filterData('after', 'destroy', error, record, error.request.get('response'), recordOptions)); + }, this) + })); + }); + }; + return RestStorage; + })(); + Batman.View = (function() { + var viewSources; + __extends(View, Batman.Object); + function View(options) { + var context; + this.contexts = []; + View.__super__.constructor.call(this, options); + if (context = this.get('context')) { + this.contexts.push(context); + this.unset('context'); + } + } + viewSources = {}; + View.prototype.source = ''; + View.prototype.html = ''; + View.prototype.node = null; + View.prototype.contentFor = null; + View.prototype.ready = View.eventOneShot(function() {}); + View.prototype.prefix = 'views'; + View.observeAll('source', function() { + return setTimeout((__bind(function() { + return this.reloadSource(); + }, this)), 0); + }); + View.prototype.reloadSource = function() { + var source, url; + source = this.get('source'); + if (!source) { + return; + } + if (viewSources[source]) { + return this.set('html', viewSources[source]); + } else { + return new Batman.Request({ + url: url = "" + this.prefix + "/" + this.source, + type: 'html', + success: __bind(function(response) { + viewSources[source] = response; + return this.set('html', response); + }, this), + error: function(response) { + throw new Error("Could not load view from " + url); + } + }); + } + }; + View.observeAll('html', function(html) { + var node; + node = this.node || document.createElement('div'); + node.innerHTML = html; + if (this.node !== node) { + return this.set('node', node); + } + }); + View.observeAll('node', function(node) { + if (!node) { + return; + } + this.ready.fired = false; + if (this._renderer) { + this._renderer.forgetAll(); + } + if (node) { + this._renderer = new Batman.Renderer(node, __bind(function() { + var content, _ref2; + content = this.contentFor; + if (typeof content === 'string') { + this.contentFor = (_ref2 = Batman.DOM._yields) != null ? _ref2[content] : void 0; + } + if (this.contentFor && node) { + this.contentFor.innerHTML = ''; + return this.contentFor.appendChild(node); + } + }, this), this.contexts); + return this._renderer.rendered(__bind(function() { + return this.ready(node); + }, this)); + } + }); + return View; + })(); + Batman.Renderer = (function() { + var bindingRegexp, sortBindings; + __extends(Renderer, Batman.Object); + function Renderer(node, callback, contexts) { + this.node = node; + if (contexts == null) { + contexts = []; + } + this.resume = __bind(this.resume, this); + this.start = __bind(this.start, this); + Renderer.__super__.constructor.call(this); + if (callback != null) { + this.parsed(callback); + } + this.context = contexts instanceof RenderContext ? contexts : (function(func, args, ctor) { + ctor.prototype = func.prototype; + var child = new ctor, result = func.apply(child, args); + return typeof result === "object" ? result : child; + })(RenderContext, contexts, function() {}); + setTimeout(this.start, 0); + } + Renderer.prototype.start = function() { + this.startTime = new Date; + return this.parseNode(this.node); + }; + Renderer.prototype.resume = function() { + this.startTime = new Date; + return this.parseNode(this.resumeNode); + }; + Renderer.prototype.finish = function() { + this.startTime = null; + this.fire('parsed'); + return this.fire('rendered'); + }; + Renderer.prototype.forgetAll = function() {}; + Renderer.prototype.parsed = Renderer.eventOneShot(function() {}); + Renderer.prototype.rendered = Renderer.eventOneShot(function() {}); + bindingRegexp = /data\-(.*)/; + sortBindings = function(a, b) { + if (a[0] === 'foreach') { + return -1; + } else if (b[0] === 'foreach') { + return 1; + } else if (a[0] === 'formfor') { + return -1; + } else if (b[0] === 'formfor') { + return 1; + } else if (a[0] === 'bind') { + return -1; + } else if (b[0] === 'bind') { + return 1; + } else { + return 0; + } + }; + Renderer.prototype.parseNode = function(node) { + var attr, bindings, key, name, nextNode, readerArgs, result, skipChildren, varIndex, _base, _base2, _j, _len2, _name, _name2, _ref2; + if (new Date - this.startTime > 50) { + this.resumeNode = node; + setTimeout(this.resume, 0); + return; + } + if (node.getAttribute && node.attributes) { + bindings = (function() { + var _j, _len2, _ref2, _ref3, _results; + _ref2 = node.attributes; + _results = []; + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + attr = _ref2[_j]; + name = (_ref3 = attr.nodeName.match(bindingRegexp)) != null ? _ref3[1] : void 0; + if (!name) { + continue; + } + _results.push(~(varIndex = name.indexOf('-')) ? [name.substr(0, varIndex), name.substr(varIndex + 1), attr.value] : [name, attr.value]); + } + return _results; + })(); + _ref2 = bindings.sort(sortBindings); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + readerArgs = _ref2[_j]; + key = readerArgs[1]; + result = readerArgs.length === 2 ? typeof (_base = Batman.DOM.readers)[_name = readerArgs[0]] === "function" ? _base[_name](node, key, this.context, this) : void 0 : typeof (_base2 = Batman.DOM.attrReaders)[_name2 = readerArgs[0]] === "function" ? _base2[_name2](node, key, readerArgs[2], this.context, this) : void 0; + if (result === false) { + skipChildren = true; + break; + } + } + } + if ((nextNode = this.nextNode(node, skipChildren))) { + return this.parseNode(nextNode); + } else { + return this.finish(); + } + }; + Renderer.prototype.nextNode = function(node, skipChildren) { + var children, nextParent, parentSibling, sibling, _base; + if (!skipChildren) { + children = node.childNodes; + if (children != null ? children.length : void 0) { + return children[0]; + } + } + if (typeof (_base = Batman.data(node, 'onParseExit')) === "function") { + _base(); + } + if (this.node === node) { + return; + } + sibling = node.nextSibling; + if (sibling) { + return sibling; + } + nextParent = node; + while (nextParent = nextParent.parentNode) { + if (typeof nextParent.onParseExit === "function") { + nextParent.onParseExit(); + } + if (this.node === nextParent) { + return; + } + parentSibling = nextParent.nextSibling; + if (parentSibling) { + return parentSibling; + } + } + }; + return Renderer; + })(); + Binding = (function() { + var get_dot_rx, get_rx, keypath_rx; + __extends(Binding, Batman.Object); + keypath_rx = /(^|,)\s*(?!(?:true|false)\s*(?:$|,))([a-zA-Z][\w\.]*)\s*($|,)/g; + get_dot_rx = /(?:\]\.)(.+?)(?=[\[\.]|\s*\||$)/; + get_rx = /(?!^\s*)\[(.*?)\]/g; + Binding.accessor('filteredValue', function() { + var ctx, result, unfilteredValue; + unfilteredValue = this.get('unfilteredValue'); + if (this.get('key')) { + ctx = this.get('keyContext'); + } + if (this.filterFunctions.length > 0) { + developer.currentFilterContext = ctx; + developer.currentFilterStack = this.renderContext; + result = this.filterFunctions.reduce(__bind(function(value, fn, i) { + var args; + args = this.filterArguments[i].map(function(argument) { + if (argument._keypath) { + return argument.context.get(argument._keypath); + } else { + return argument; + } + }); + args.unshift(value); + return fn.apply(ctx, args); + }, this), unfilteredValue); + developer.currentFilterContext = null; + developer.currentFilterStack = null; + return result; + } else { + return unfilteredValue; + } + }); + Binding.accessor('unfilteredValue', function() { + if (k = this.get('key')) { + return this.get("keyContext." + k); + } else { + return this.get('value'); + } + }); + Binding.accessor('keyContext', function() { + var unfilteredValue, _ref2; + if (!this._keyContext) { + _ref2 = this.renderContext.findKey(this.key), unfilteredValue = _ref2[0], this._keyContext = _ref2[1]; + } + return this._keyContext; + }); + function Binding() { + var shouldSet, _ref2, _ref3; + Binding.__super__.constructor.apply(this, arguments); + this.parseFilter(); + this.nodeChange || (this.nodeChange = __bind(function(node, context) { + if (this.key && this.filterFunctions.length === 0) { + return this.get('keyContext').set(this.key, this.node.value); + } + }, this)); + this.dataChange || (this.dataChange = function(value, node) { + return Batman.DOM.valueForNode(this.node, value); + }); + shouldSet = true; + if (((_ref2 = this.only) === false || _ref2 === 'nodeChange') && Batman.DOM.nodeIsEditable(this.node)) { + Batman.DOM.events.change(this.node, __bind(function() { + shouldSet = false; + this.nodeChange(this.node, this._keyContext || this.value, this); + return shouldSet = true; + }, this)); + } + if ((_ref3 = this.only) === false || _ref3 === 'dataChange') { + this.observe('filteredValue', true, __bind(function(value) { + if (shouldSet) { + return this.dataChange(value, this.node, this); + } + }, this)); + } + this; + } + Binding.prototype.parseFilter = function() { + var args, filter, filterName, filterString, filters, key, keyPath, orig, split; + this.filterFunctions = []; + this.filterArguments = []; + keyPath = this.keyPath; + while (get_dot_rx.test(keyPath)) { + keyPath = keyPath.replace(get_dot_rx, "]['$1']"); + } + filters = keyPath.replace(get_rx, " | get $1 ").replace(/'/g, '"').split(/(?!")\s+\|\s+(?!")/); + try { + key = this.parseSegment(orig = filters.shift())[0]; + } catch (e) { + developer.warn(e); + developer.error("Error! Couldn't parse keypath in \"" + orig + "\". Parsing error above."); + } + if (key && key._keypath) { + this.key = key._keypath; + } else { + this.value = key; + } + if (filters.length) { + while (filterString = filters.shift()) { + split = filterString.indexOf(' '); + if (~split) { + filterName = filterString.substr(0, split); + args = filterString.substr(split); + } else { + filterName = filterString; + } + if (filter = Batman.Filters[filterName]) { + this.filterFunctions.push(filter); + if (args) { + try { + this.filterArguments.push(this.parseSegment(args)); + } catch (e) { + developer.error("Bad filter arguments \"" + args + "\"!"); + } + } else { + this.filterArguments.push([]); + } + } else { + developer.error("Unrecognized filter '" + filterName + "' in key \"" + this.keyPath + "\"!"); + } + } + return this.filterArguments = this.filterArguments.map(__bind(function(argumentList) { + return argumentList.map(__bind(function(argument) { + var _, _ref2; + if (argument._keypath) { + _ref2 = this.renderContext.findKey(argument._keypath), _ = _ref2[0], argument.context = _ref2[1]; + } + return argument; + }, this)); + }, this)); + } + }; + Binding.prototype.parseSegment = function(segment) { + return JSON.parse("[" + segment.replace(keypath_rx, "$1{\"_keypath\": \"$2\"}$3") + "]"); + }; + return Binding; + })(); + RenderContext = (function() { + var BindingProxy; + function RenderContext() { + var contexts; + contexts = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + this.contexts = contexts; + this.storage = new Batman.Object; + this.defaultContexts = [this.storage]; + if (Batman.currentApp) { + this.defaultContexts.push(Batman.currentApp); + } + } + RenderContext.prototype.findKey = function(key) { + var base, context, contexts, i, val, _j, _len2, _ref2; + base = key.split('.')[0].split('|')[0].trim(); + _ref2 = [this.contexts, this.defaultContexts]; + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + contexts = _ref2[_j]; + i = contexts.length; + while (i--) { + context = contexts[i]; + if (context.get != null) { + val = context.get(base); + } else { + val = context[base]; + } + if (typeof val !== 'undefined') { + return [$get(context, key), context]; + } + } + } + return [container.get(key), container]; + }; + RenderContext.prototype.set = function() { + var args, _ref2; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return (_ref2 = this.storage).set.apply(_ref2, args); + }; + RenderContext.prototype.push = function(x) { + return this.contexts.push(x); + }; + RenderContext.prototype.pop = function() { + return this.contexts.pop(); + }; + RenderContext.prototype.clone = function() { + var context, newStorage; + context = (function(func, args, ctor) { + ctor.prototype = func.prototype; + var child = new ctor, result = func.apply(child, args); + return typeof result === "object" ? result : child; + })(this.constructor, this.contexts, function() {}); + newStorage = $mixin({}, this.storage); + context.setStorage(newStorage); + return context; + }; + RenderContext.prototype.setStorage = function(storage) { + return this.defaultContexts[0] = storage; + }; + RenderContext.BindingProxy = BindingProxy = (function() { + __extends(BindingProxy, Batman.Object); + BindingProxy.prototype.isBindingProxy = true; + function BindingProxy(binding, localKey) { + this.binding = binding; + this.localKey = localKey; + if (this.localKey) { + this.accessor(this.localKey, function() { + return this.binding.get('filteredValue'); + }); + } else { + this.accessor(function(key) { + return this.binding.get("filteredValue." + key); + }); + } + } + return BindingProxy; + })(); + RenderContext.prototype.addKeyToScopeForNode = function(node, key, localName) { + this.bind(node, key, __bind(function(value, node, binding) { + return this.push(new BindingProxy(binding, localName)); + }, this), function() { + return true; + }); + return Batman.data(node, 'onParseExit', __bind(function() { + return this.pop(); + }, this)); + }; + RenderContext.prototype.bind = function(node, key, dataChange, nodeChange, only) { + if (only == null) { + only = false; + } + return new Binding({ + renderContext: this, + keyPath: key, + node: node, + dataChange: dataChange, + nodeChange: nodeChange, + only: only + }); + }; + return RenderContext; + })(); + Batman.DOM = { + readers: { + target: function(node, key, context, renderer) { + return Batman.DOM.readers.bind(node, key, context, renderer, 'nodeChange'); + }, + source: function(node, key, context, renderer) { + return Batman.DOM.readers.bind(node, key, context, renderer, 'dataChange'); + }, + bind: function(node, key, context, renderer, only) { + var _ref2, _ref3, _ref4; + switch (node.nodeName.toLowerCase()) { + case 'input': + switch (node.getAttribute('type')) { + case 'checkbox': + return Batman.DOM.attrReaders.bind(node, 'checked', key, context, renderer, only); + case 'radio': + return (_ref2 = Batman.DOM.binders).radio.apply(_ref2, arguments); + case 'file': + return (_ref3 = Batman.DOM.binders).file.apply(_ref3, arguments); + } + break; + case 'select': + return (_ref4 = Batman.DOM.binders).select.apply(_ref4, arguments); + } + return context.bind(node, key, void 0, void 0, only); + }, + context: function(node, key, context) { + return context.addKeyToScopeForNode(node, key); + }, + mixin: function(node, key, context) { + context.push(Batman.mixins); + context.bind(node, key, function(mixin) { + return $mixin(node, mixin); + }, function() {}); + return context.pop(); + }, + showif: function(node, key, context, renderer, invert) { + var originalDisplay; + originalDisplay = node.style.display || ''; + return context.bind(node, key, function(value) { + var hide, _ref2; + if (!!value === !invert) { + if ((_ref2 = Batman.data(node, 'show')) != null) { + _ref2.call(node); + } + return node.style.display = originalDisplay; + } else { + if (typeof (hide = Batman.data(node, 'hide')) === 'function') { + return hide.call(node); + } else { + return node.style.display = 'none'; + } + } + }, function() {}); + }, + hideif: function() { + var args, _ref2; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return (_ref2 = Batman.DOM.readers).showif.apply(_ref2, __slice.call(args).concat([true])); + }, + route: function(node, key, context) { + var action, app, container, dispatcher, model, name, url, _ref2, _ref3, _ref4; + if (key.substr(0, 1) === '/') { + url = key; + } else { + _ref2 = key.split('/'), key = _ref2[0], action = _ref2[1]; + _ref3 = context.findKey('dispatcher'), dispatcher = _ref3[0], app = _ref3[1]; + _ref4 = context.findKey(key), model = _ref4[0], container = _ref4[1]; + dispatcher || (dispatcher = Batman.currentApp.dispatcher); + if (dispatcher && model instanceof Batman.Model) { + action || (action = 'show'); + name = helpers.underscore(helpers.pluralize($functionName(model.constructor))); + url = dispatcher.findUrl({ + resource: name, + id: model.get('id'), + action: action + }); + } else if (model != null ? model.prototype : void 0) { + action || (action = 'index'); + name = helpers.underscore(helpers.pluralize($functionName(model))); + url = dispatcher.findUrl({ + resource: name, + action: action + }); + } + } + if (!url) { + return; + } + if (node.nodeName.toUpperCase() === 'A') { + node.href = Batman.HashHistory.prototype.urlFor(url); + } + return Batman.DOM.events.click(node, (function() { + return $redirect(url); + })); + }, + partial: function(node, path, context, renderer) { + var view; + renderer.prevent('rendered'); + view = new Batman.View({ + source: path + '.html', + contentFor: node, + contexts: Array.prototype.slice.call(context.contexts) + }); + return view.ready(function() { + renderer.allow('rendered'); + return renderer.fire('rendered'); + }); + }, + yield: function(node, key) { + return setTimeout((function() { + return Batman.DOM.yield(key, node); + }), 0); + }, + contentfor: function(node, key) { + return setTimeout((function() { + return Batman.DOM.contentFor(key, node); + }), 0); + } + }, + attrReaders: { + _parseAttribute: function(value) { + if (value === 'false') { + value = false; + } + if (value === 'true') { + value = true; + } + return value; + }, + source: function(node, attr, key, context, renderer) { + return Batman.DOM.attrReaders.bind(node, attr, key, context, renderer, 'dataChange'); + }, + bind: function(node, attr, key, context, renderer, only) { + var dataChange, nodeChange; + switch (attr) { + case 'checked': + case 'disabled': + case 'selected': + dataChange = function(value) { + var _base; + node[attr] = !!value; + return typeof (_base = Batman.data(node.parentNode, 'updateBinding')) === "function" ? _base() : void 0; + }; + nodeChange = function(node, subContext) { + return subContext.set(key, Batman.DOM.attrReaders._parseAttribute(node[attr])); + }; + Batman.data(node, attr, { + context: context, + key: key + }); + break; + case 'value': + case 'style': + case 'href': + case 'src': + case 'size': + dataChange = function(value) { + return node[attr] = value; + }; + nodeChange = function(node, subContext) { + return subContext.set(key, Batman.DOM.attrReaders._parseAttribute(node[attr])); + }; + break; + case 'class': + dataChange = function(value) { + return node.className = value; + }; + nodeChange = function(node, subContext) { + return subContext.set(key, node.className); + }; + break; + default: + dataChange = function(value) { + return node.setAttribute(attr, value); + }; + nodeChange = function(node, subContext) { + return subContext.set(key, Batman.DOM.attrReaders._parseAttribute(node.getAttribute(attr))); + }; + } + return context.bind(node, key, dataChange, nodeChange, only); + }, + context: function(node, contextName, key, context) { + return context.addKeyToScopeForNode(node, key, contextName); + }, + event: function(node, eventName, key, context) { + var confirmText, props; + props = { + callback: null, + subContext: null + }; + context.bind(node, key, function(value, node, binding) { + var ks; + props.callback = value; + if (binding.get('key')) { + ks = binding.get('key').split('.'); + ks.pop(); + if (ks.length > 0) { + return props.subContext = binding.get('keyContext').get(ks.join('.')); + } else { + return props.subContext = binding.get('keyContext'); + } + } + }, function() {}); + confirmText = node.getAttribute('data-confirm'); + return Batman.DOM.events[eventName](node, function() { + var _ref2; + if (confirmText && !confirm(confirmText)) { + return; + } + return (_ref2 = props.callback) != null ? _ref2.apply(props.subContext, arguments) : void 0; + }); + }, + addclass: function(node, className, key, context, parentRenderer, invert) { + className = className.replace(/\|/g, ' '); + return context.bind(node, key, function(value) { + var currentName, includesClassName; + currentName = node.className; + includesClassName = currentName.indexOf(className) !== -1; + if (!!value === !invert) { + if (!includesClassName) { + return node.className = "" + currentName + " " + className; + } + } else { + if (includesClassName) { + return node.className = currentName.replace(className, ''); + } + } + }, function() {}); + }, + removeclass: function() { + var args, _ref2; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return (_ref2 = Batman.DOM.attrReaders).addclass.apply(_ref2, __slice.call(args).concat([true])); + }, + foreach: function(node, iteratorName, key, context, parentRenderer) { + var fragment, nodeMap, numPendingChildren, observers, oldCollection, parent, prototype, sibling; + prototype = node.cloneNode(true); + prototype.removeAttribute("data-foreach-" + iteratorName); + parent = node.parentNode; + sibling = node.nextSibling; + parentRenderer.parsed(function() { + return parent.removeChild(node); + }); + nodeMap = new Batman.SimpleHash; + fragment = document.createDocumentFragment(); + numPendingChildren = 0; + observers = {}; + oldCollection = false; + context.bind(node, key, function(collection) { + var array, k, v, _results; + if (oldCollection) { + if (collection === oldCollection) { + return; + } + nodeMap.forEach(function(item, node) { + var _ref2; + return (_ref2 = node.parentNode) != null ? _ref2.removeChild(node) : void 0; + }); + nodeMap.clear(); + if (oldCollection.forget) { + oldCollection.forget('itemsWereAdded', observers.add); + oldCollection.forget('itemsWereRemoved', observers.remove); + oldCollection.forget('setWasSorted', observers.reorder); + } + } + oldCollection = collection; + observers.add = function() { + var childRenderer, item, items, iteratorContext, localClone, newNode, _j, _len2, _results; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + numPendingChildren += items.length; + _results = []; + for (_j = 0, _len2 = items.length; _j < _len2; _j++) { + item = items[_j]; + parentRenderer.prevent('rendered'); + newNode = prototype.cloneNode(true); + nodeMap.set(item, newNode); + localClone = context.clone(); + iteratorContext = new Batman.Object; + iteratorContext[iteratorName] = item; + localClone.push(iteratorContext); + childRenderer = new Batman.Renderer(newNode, (function(newNode) { + return function() { + var show; + if (typeof (show = Batman.data(newNode, 'show')) === 'function') { + show.call(newNode, { + before: sibling + }); + } else { + fragment.appendChild(newNode); + } + if (--numPendingChildren === 0) { + parent.insertBefore(fragment, sibling); + if (typeof collection.isSorted === "function" ? collection.isSorted() : void 0) { + observers.reorder(); + } + return fragment = document.createDocumentFragment(); + } + }; + })(newNode), localClone); + _results.push(childRenderer.rendered(__bind(function() { + parentRenderer.allow('rendered'); + return parentRenderer.fire('rendered'); + }, this))); + } + return _results; + }; + observers.remove = function() { + var item, items, oldNode, _j, _len2, _ref2; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + for (_j = 0, _len2 = items.length; _j < _len2; _j++) { + item = items[_j]; + oldNode = nodeMap.get(item); + nodeMap.unset(item); + if ((oldNode != null) && typeof oldNode.hide === 'function') { + oldNode.hide(true); + } else { + if (oldNode != null) { + if ((_ref2 = oldNode.parentNode) != null) { + _ref2.removeChild(oldNode); + } + } + } + } + return true; + }; + observers.reorder = function() { + var item, items, show, thisNode, _j, _len2, _results; + items = collection.toArray(); + _results = []; + for (_j = 0, _len2 = items.length; _j < _len2; _j++) { + item = items[_j]; + thisNode = nodeMap.get(item); + show = Batman.data(thisNode, 'show'); + _results.push(typeof show === 'function' ? show.call(thisNode, { + before: sibling + }) : parent.insertBefore(thisNode, sibling)); + } + return _results; + }; + observers.arrayChange = function(array) { + observers.remove.apply(observers, array); + return observers.add.apply(observers, array); + }; + if (collection) { + if (collection.observe) { + collection.observe('itemsWereAdded', observers.add); + collection.observe('itemsWereRemoved', observers.remove); + if (collection.setWasSorted) { + collection.observe('setWasSorted', observers.reorder); + } else { + collection.observe('toArray', observers.arrayChange); + } + } + if (collection.forEach) { + return collection.forEach(function(item) { + return observers.add(item); + }); + } else if (collection.get && (array = collection.get('toArray'))) { + return observers.add.apply(observers, array); + } else { + _results = []; + for (k in collection) { + v = collection[k]; + _results.push(observers.add(k)); + } + return _results; + } + } else { + return developer.warn("Warning! data-foreach-" + iteratorName + " called with an undefined binding. Key was: " + key + "."); + } + }, function() {}); + return false; + }, + formfor: function(node, localName, key, context) { + var binding; + binding = context.addKeyToScopeForNode(node, key, localName); + return Batman.DOM.events.submit(node, function(node, e) { + return $preventDefault(e); + }); + } + }, + binders: { + select: function(node, key, context, renderer, only) { + var boundValue, container, updateOptionBindings, updateSelectBinding, _ref2; + _ref2 = context.findKey(key), boundValue = _ref2[0], container = _ref2[1]; + updateSelectBinding = __bind(function() { + var c, selections; + selections = node.multiple ? (function() { + var _j, _len2, _ref3, _results; + _ref3 = node.children; + _results = []; + for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) { + c = _ref3[_j]; + if (c.selected) { + _results.push(c.value); + } + } + return _results; + })() : node.value; + if (selections.length === 1) { + selections = selections[0]; + } + return container.set(key, selections); + }, this); + updateOptionBindings = __bind(function() { + var child, data, subBoundValue, subContainer, subContext, subKey, _j, _len2, _ref3, _ref4, _results; + _ref3 = node.children; + _results = []; + for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) { + child = _ref3[_j]; + _results.push((data = Batman.data(child, 'selected')) ? (subContext = data.context) && (subKey = data.key) ? ((_ref4 = subContext.findKey(subKey), subBoundValue = _ref4[0], subContainer = _ref4[1], _ref4), child.selected !== subBoundValue ? subContainer.set(subKey, child.selected) : void 0) : void 0 : void 0); + } + return _results; + }, this); + return renderer.rendered(function() { + var dataChange, nodeChange; + dataChange = function(newValue) { + var child, match, matches, value, valueToChild, _j, _k, _l, _len2, _len3, _len4, _ref3, _ref4; + if (newValue instanceof Array) { + valueToChild = {}; + _ref3 = node.children; + for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) { + child = _ref3[_j]; + child.selected = false; + matches = valueToChild[child.value]; + if (matches) { + matches.push(child); + } else { + matches = [child]; + } + valueToChild[child.value] = matches; + } + for (_k = 0, _len3 = newValue.length; _k < _len3; _k++) { + value = newValue[_k]; + _ref4 = valueToChild[value]; + for (_l = 0, _len4 = _ref4.length; _l < _len4; _l++) { + match = _ref4[_l]; + match.selected = true; + } + } + } else { + node.value = newValue; + } + return updateOptionBindings(); + }; + nodeChange = function() { + updateSelectBinding(); + return updateOptionBindings(); + }; + Batman.data(node, 'updateBinding', updateSelectBinding); + return context.bind(node, key, dataChange, nodeChange, only); + }); + }, + radio: function(node, key, context, renderer, only) { + var dataChange, nodeChange; + dataChange = function(value) { + var boundValue, container, _ref2; + _ref2 = context.findKey(key), boundValue = _ref2[0], container = _ref2[1]; + if (boundValue) { + return node.checked = boundValue === node.value; + } else if (node.checked) { + return container.set(key, node.value); + } + }; + nodeChange = function(newNode, subContext) { + return subContext.set(key, Batman.DOM.attrReaders._parseAttribute(node.value)); + }; + return context.bind(node, key, dataChange, nodeChange, only); + }, + file: function(node, key, context, renderer, only) { + return context.bind(node, key, function() { + return developer.warn("Can't write to file inputs! Tried to on key " + key + "."); + }, function(node, subContext) { + var actualObject, adapter, _j, _len2, _ref2; + if (subContext instanceof RenderContext.BindingProxy) { + actualObject = subContext.binding.get('filteredValue'); + } else { + actualObject = subContext; + } + if (actualObject.hasStorage && actualObject.hasStorage()) { + _ref2 = actualObject._batman.get('storage'); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + adapter = _ref2[_j]; + if (adapter instanceof Batman.RestStorage) { + adapter.defaultOptions.formData = true; + } + } + } + if (node.hasAttribute('multiple')) { + return subContext.set(key, Array.prototype.slice.call(node.files)); + } else { + return subContext.set(key, node.files[0]); + } + }, only); + } + }, + events: { + click: function(node, callback, eventName) { + if (eventName == null) { + eventName = 'click'; + } + $addEventListener(node, eventName, function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + callback.apply(null, [node].concat(__slice.call(args))); + return $preventDefault(args[0]); + }); + if (node.nodeName.toUpperCase() === 'A' && !node.href) { + node.href = '#'; + } + return node; + }, + doubleclick: function(node, callback) { + return Batman.DOM.events.click(node, callback, 'dblclick'); + }, + change: function(node, callback) { + var eventName, eventNames, oldCallback, _j, _len2, _results; + eventNames = (function() { + switch (node.nodeName.toUpperCase()) { + case 'TEXTAREA': + return ['keyup', 'change']; + case 'INPUT': + if (node.type.toUpperCase() === 'TEXT') { + oldCallback = callback; + callback = function(e) { + var _ref2; + if (e.type === 'keyup' && (13 <= (_ref2 = e.keyCode) && _ref2 <= 14)) { + return; + } + return oldCallback.apply(null, arguments); + }; + return ['keyup', 'change']; + } else { + return ['change']; + } + break; + default: + return ['change']; + } + })(); + _results = []; + for (_j = 0, _len2 = eventNames.length; _j < _len2; _j++) { + eventName = eventNames[_j]; + _results.push($addEventListener(node, eventName, function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return callback.apply(null, [node].concat(__slice.call(args))); + })); + } + return _results; + }, + submit: function(node, callback) { + if (Batman.DOM.nodeIsEditable(node)) { + $addEventListener(node, 'keyup', function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (args[0].keyCode === 13 || args[0].which === 13 || args[0].keyIdentifier === 'Enter' || args[0].key === 'Enter') { + $preventDefault(args[0]); + return callback.apply(null, [node].concat(__slice.call(args))); + } + }); + } else { + $addEventListener(node, 'submit', function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + $preventDefault(args[0]); + return callback.apply(null, [node].concat(__slice.call(args))); + }); + } + return node; + } + }, + yield: function(name, node) { + var content, yields, _base, _ref2; + yields = (_base = Batman.DOM)._yields || (_base._yields = {}); + yields[name] = node; + if ((content = (_ref2 = Batman.DOM._yieldContents) != null ? _ref2[name] : void 0)) { + node.innerHTML = ''; + if (content) { + return node.appendChild(content); + } + } + }, + contentFor: function(name, node) { + var content, contents, yield, _base, _ref2; + contents = (_base = Batman.DOM)._yieldContents || (_base._yieldContents = {}); + contents[name] = node; + if ((yield = (_ref2 = Batman.DOM._yields) != null ? _ref2[name] : void 0)) { + content = $isChildOf(yield, node) ? node.cloneNode(true) : node; + yield.innerHTML = ''; + if (content) { + return yield.appendChild(content); + } + } + }, + valueForNode: function(node, value) { + var isSetting; + if (value == null) { + value = ''; + } + isSetting = arguments.length > 1; + switch (node.nodeName.toUpperCase()) { + case 'INPUT': + if (isSetting) { + return node.value = value; + } else { + return node.value; + } + break; + case 'TEXTAREA': + if (isSetting) { + return node.innerHTML = node.value = value; + } else { + return node.innerHTML; + } + break; + case 'SELECT': + return node.value = value; + default: + if (isSetting) { + return node.innerHTML = value; + } else { + return node.innerHTML; + } + } + }, + nodeIsEditable: function(node) { + var _ref2; + return (_ref2 = node.nodeName.toUpperCase()) === 'INPUT' || _ref2 === 'TEXTAREA' || _ref2 === 'SELECT'; + }, + addEventListener: $addEventListener = (typeof window !== "undefined" && window !== null ? window.addEventListener : void 0) ? (function(node, eventName, callback) { + return node.addEventListener(eventName, callback, false); + }) : (function(node, eventName, callback) { + return node.attachEvent("on" + eventName, callback); + }), + removeEventListener: $removeEventListener = (typeof window !== "undefined" && window !== null ? window.removeEventListener : void 0) ? (function(elem, eventType, handler) { + return elem.removeEventListener(eventType, handler, false); + }) : (function(elem, eventType, handler) { + return elem.detachEvent('on' + eventType, handler); + }) + }; + buntUndefined = function(f) { + return function(value) { + if (typeof value === 'undefined') { + return; + } else { + return f.apply(this, arguments); + } + }; + }; + filters = Batman.Filters = { + get: buntUndefined(function(value, key) { + if (value.get != null) { + return value.get(key); + } else { + return value[key]; + } + }), + equals: buntUndefined(function(lhs, rhs) { + return lhs === rhs; + }), + not: function(value) { + return !!!value; + }, + truncate: buntUndefined(function(value, length, end) { + if (end == null) { + end = "..."; + } + if (value.length > length) { + value = value.substr(0, length - end.length) + end; + } + return value; + }), + "default": function(value, string) { + return value || string; + }, + prepend: function(value, string) { + return string + value; + }, + append: function(value, string) { + return value + string; + }, + downcase: buntUndefined(function(value) { + return value.toLowerCase(); + }), + upcase: buntUndefined(function(value) { + return value.toUpperCase(); + }), + pluralize: buntUndefined(function(string, count) { + return helpers.pluralize(count, string); + }), + join: buntUndefined(function(value, byWhat) { + if (byWhat == null) { + byWhat = ''; + } + return value.join(byWhat); + }), + sort: buntUndefined(function(value) { + return value.sort(); + }), + map: buntUndefined(function(value, key) { + return value.map(function(x) { + return x[key]; + }); + }), + first: buntUndefined(function(value) { + return value[0]; + }), + meta: buntUndefined(function(value, keypath) { + return value.meta.get(keypath); + }) + }; + _ref2 = ['capitalize', 'singularize', 'underscore', 'camelize']; + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + k = _ref2[_j]; + filters[k] = buntUndefined(helpers[k]); + } + developer.addFilters(); + $mixin(Batman, { + cache: {}, + uuid: 0, + expando: "batman" + Math.random().toString().replace(/\D/g, ''), + canDeleteExpando: true, + noData: { + "embed": true, + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + hasData: function(elem) { + elem = (elem.nodeType ? Batman.cache[elem[Batman.expando]] : elem[Batman.expando]); + return !!elem && !isEmptyDataObject(elem); + }, + data: function(elem, name, data, pvt) { + var cache, getByName, id, internalKey, isNode, ret, thisCache; + if (!Batman.acceptData(elem)) { + return; + } + internalKey = Batman.expando; + getByName = typeof name === "string"; + isNode = elem.nodeType; + cache = isNode ? Batman.cache : elem; + id = isNode ? elem[Batman.expando] : elem[Batman.expando] && Batman.expando; + if ((!id || (pvt && id && (cache[id] && !cache[id][internalKey]))) && getByName && data === void 0) { + return; + } + if (!id) { + if (isNode) { + elem[Batman.expando] = id = ++Batman.uuid; + } else { + id = Batman.expando; + } + } + if (!cache[id]) { + cache[id] = {}; + } + if (typeof name === "object" || typeof name === "function") { + if (pvt) { + cache[id][internalKey] = $mixin(cache[id][internalKey], name); + } else { + cache[id] = $mixin(cache[id], name); + } + } + thisCache = cache[id]; + if (pvt) { + if (!thisCache[internalKey]) { + thisCache[internalKey] = {}; + } + thisCache = thisCache[internalKey]; + } + if (data !== void 0) { + thisCache[helpers.camelize(name, true)] = data; + } + if (getByName) { + ret = thisCache[name]; + if (ret == null) { + ret = thisCache[helpers.camelize(name, true)]; + } + } else { + ret = thisCache; + } + return ret; + }, + removeData: function(elem, name, pvt) { + var cache, id, internalCache, internalKey, isNode, thisCache; + if (!Batman.acceptData(elem)) { + return; + } + internalKey = Batman.expando; + isNode = elem.nodeType; + cache = isNode ? Batman.cache : elem; + id = isNode ? elem[Batman.expando] : Batman.expando; + if (!cache[id]) { + return; + } + if (name) { + thisCache = pvt ? cache[id][internalKey] : cache[id]; + if (thisCache) { + if (!thisCache[name]) { + name = helpers.camelize(name, true); + } + delete thisCache[name]; + if (!isEmptyDataObject(thisCache)) { + return; + } + } + } + if (pvt) { + delete cache[id][internalKey]; + if (!isEmptyDataObject(cache[id])) { + return; + } + } + internalCache = cache[id][internalKey]; + if (Batman.canDeleteExpando || !cache.setInterval) { + delete cache[id]; + } else { + cache[id] = null; + } + if (internalCache) { + cache[id] = {}; + return cache[id][internalKey] = internalCache; + } else if (isNode) { + if (Batman.canDeleteExpando) { + return delete elem[Batman.expando]; + } else if (elem.removeAttribute) { + return elem.removeAttribute(Batman.expando); + } else { + return elem[Batman.expando] = null; + } + } + }, + _data: function(elem, name, data) { + return Batman.data(elem, name, data, true); + }, + acceptData: function(elem) { + var match; + if (elem.nodeName) { + match = Batman.noData[elem.nodeName.toLowerCase()]; + if (match) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + return true; + } + }); + isEmptyDataObject = function(obj) { + var name; + for (name in obj) { + return false; + } + return true; + }; + try { + div = document.createElement('div'); + delete div.test; + } catch (e) { + Batman.canDeleteExpando = false; + } + mixins = Batman.mixins = new Batman.Object(); + Batman.Encoders = { + railsDate: { + encode: function(value) { + return value; + }, + decode: function(value) { + var a; + a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); + } else { + return developer.error("Unrecognized rails date " + value + "!"); + } + } + } + }; + container = typeof exports !== "undefined" && exports !== null ? (module.exports = Batman, global) : (window.Batman = Batman, window); + $mixin(container, Batman.Observable); + Batman.exportHelpers = function(onto) { + var k, _k, _len3, _ref3; + _ref3 = ['mixin', 'unmixin', 'route', 'redirect', 'event', 'eventOneShot', 'typeOf', 'redirect']; + for (_k = 0, _len3 = _ref3.length; _k < _len3; _k++) { + k = _ref3[_k]; + onto["$" + k] = Batman[k]; + } + return onto; + }; + Batman.exportGlobals = function() { + return Batman.exportHelpers(container); + }; +}).call(this); diff --git a/vendor/assets/javascripts/batman/batman.rails.js b/vendor/assets/javascripts/batman/batman.rails.js new file mode 100644 index 0000000..70899bf --- /dev/null +++ b/vendor/assets/javascripts/batman/batman.rails.js @@ -0,0 +1,58 @@ +(function() { + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __slice = Array.prototype.slice; + Batman.RailsStorage = (function() { + __extends(RailsStorage, Batman.RestStorage); + function RailsStorage() { + RailsStorage.__super__.constructor.apply(this, arguments); + } + RailsStorage.prototype._addJsonExtension = function(options) { + return options.url += '.json'; + }; + RailsStorage.prototype.optionsForRecord = function() { + var args, callback, _i; + args = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), callback = arguments[_i++]; + return RailsStorage.__super__.optionsForRecord.apply(this, __slice.call(args).concat([function(err, options) { + if (!err) { + this._addJsonExtension(options); + } + return callback.call(this, err, options); + }])); + }; + RailsStorage.prototype.optionsForCollection = function() { + var args, callback, _i; + args = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), callback = arguments[_i++]; + return RailsStorage.__super__.optionsForCollection.apply(this, __slice.call(args).concat([function(err, options) { + if (!err) { + this._addJsonExtension(options); + } + return callback.call(this, err, options); + }])); + }; + RailsStorage.prototype.after('update', 'create', function(_arg) { + var err, key, record, recordOptions, response, validationError, validationErrors, _i, _len, _ref; + err = _arg[0], record = _arg[1], response = _arg[2], recordOptions = _arg[3]; + if (err) { + if (err.request.get('status') === 422) { + _ref = JSON.parse(err.request.get('response')); + for (key in _ref) { + validationErrors = _ref[key]; + for (_i = 0, _len = validationErrors.length; _i < _len; _i++) { + validationError = validationErrors[_i]; + record.get('errors').add(key, "" + key + " " + validationError); + } + } + return [record.get('errors'), record, response, recordOptions]; + } + } + return arguments[0]; + }); + return RailsStorage; + })(); +}).call(this); diff --git a/vendor/assets/javascripts/batman/batman.solo.js b/vendor/assets/javascripts/batman/batman.solo.js new file mode 100644 index 0000000..722bd59 --- /dev/null +++ b/vendor/assets/javascripts/batman/batman.solo.js @@ -0,0 +1,415 @@ +(function() { + +/*! + * Reqwest! A x-browser general purpose XHR connection manager + * copyright Dustin Diaz 2011 + * https://github.com/ded/reqwest + * license MIT + */ +! +function(context, win) { + + var twoHundo = /^20\d$/, + doc = document, + byTag = 'getElementsByTagName', + contentType = 'Content-Type', + head = doc[byTag]('head')[0], + uniqid = 0, + lastValue // data stored by the most recent JSONP callback + , xhr = ('XMLHttpRequest' in win) ? + function() { + return new XMLHttpRequest() + } : function() { + return new ActiveXObject('Microsoft.XMLHTTP') + } + + function readyState(o, success, error) { + return function() { + if (o && o.readyState == 4) { + if (twoHundo.test(o.status)) { + success(o) + } else { + error(o) + } + } + } + } + + function setHeaders(http, o) { + var headers = o.headers || {} + headers.Accept = headers.Accept || 'text/javascript, text/html, application/xml, text/xml, */*' + + // breaks cross-origin requests with legacy browsers + if (!o.crossOrigin) { + headers['X-Requested-With'] = headers['X-Requested-With'] || 'XMLHttpRequest' + } + + if (o.data) { + headers[contentType] = headers[contentType] || 'application/x-www-form-urlencoded' + } + for (var h in headers) { + headers.hasOwnProperty(h) && http.setRequestHeader(h, headers[h], false) + } + } + + function getCallbackName(o) { + var callbackVar = o.jsonpCallback || "callback" + if (o.url.slice(-(callbackVar.length + 2)) == (callbackVar + "=?")) { + // Generate a guaranteed unique callback name + var callbackName = "reqwest_" + uniqid++ + + // Replace the ? in the URL with the generated name + o.url = o.url.substr(0, o.url.length - 1) + callbackName + return callbackName + } else { + // Find the supplied callback name + var regex = new RegExp(callbackVar + "=([\\w]+)") + return o.url.match(regex)[1] + } + } + + // Store the data returned by the most recent callback + + + function generalCallback(data) { + lastValue = data + } + + function getRequest(o, fn, err) { + function onload() { + // Call the user callback with the last value stored + // and clean up values and scripts. + o.success && o.success(lastValue) + lastValue = undefined + head.removeChild(script); + } + if (o.type == 'jsonp') { + var script = doc.createElement('script') + + // Add the global callback + win[getCallbackName(o)] = generalCallback; + + // Setup our script element + script.type = 'text/javascript' + script.src = o.url + script.async = true + + script.onload = onload + // onload for IE + script.onreadystatechange = function() { + /^loaded|complete$/.test(script.readyState) && onload() + } + + // Add the script to the DOM head + head.appendChild(script) + } else { + var http = xhr() + http.open(o.method || 'GET', typeof o == 'string' ? o : o.url, true) + setHeaders(http, o) + http.onreadystatechange = readyState(http, fn, err) + o.before && o.before(http) + http.send(o.data || null) + return http + } + } + + function Reqwest(o, fn) { + this.o = o + this.fn = fn + init.apply(this, arguments) + } + + function setType(url) { + if (/\.json$/.test(url)) { + return 'json' + } + if (/\.jsonp$/.test(url)) { + return 'jsonp' + } + if (/\.js$/.test(url)) { + return 'js' + } + if (/\.html?$/.test(url)) { + return 'html' + } + if (/\.xml$/.test(url)) { + return 'xml' + } + return 'js' + } + + function init(o, fn) { + this.url = typeof o == 'string' ? o : o.url + this.timeout = null + var type = o.type || setType(this.url), + self = this + fn = fn || + function() {} + + if (o.timeout) { + this.timeout = setTimeout(function() { + self.abort() + error() + }, o.timeout) + } + + function complete(resp) { + o.complete && o.complete(resp) + } + + function success(resp) { + o.timeout && clearTimeout(self.timeout) && (self.timeout = null) + var r = resp.responseText + + if (r) { + switch (type) { + case 'json': + resp = win.JSON ? win.JSON.parse(r) : eval('(' + r + ')') + break; + case 'js': + resp = eval(r) + break; + case 'html': + resp = r + break; + } + } + + fn(resp) + o.success && o.success(resp) + complete(resp) + } + + function error(resp) { + o.error && o.error(resp) + complete(resp) + } + + this.request = getRequest(o, success, error) + } + + Reqwest.prototype = { + abort: function() { + this.request.abort() + } + + , + retry: function() { + init.call(this, this.o, this.fn) + } + } + + function reqwest(o, fn) { + return new Reqwest(o, fn) + } + + function enc(v) { + return encodeURIComponent(v) + } + + function serial(el) { + var n = el.name + // don't serialize elements that are disabled or without a name + if (el.disabled || !n) { + return '' + } + n = enc(n) + switch (el.tagName.toLowerCase()) { + case 'input': + switch (el.type) { + // silly wabbit + case 'reset': + case 'button': + case 'image': + case 'file': + return '' + case 'checkbox': + case 'radio': + return el.checked ? n + '=' + (el.value ? enc(el.value) : true) + '&' : '' + default: + // text hidden password submit + return n + '=' + (el.value ? enc(el.value) : '') + '&' + } + break; + case 'textarea': + return n + '=' + enc(el.value) + '&' + case 'select': + // @todo refactor beyond basic single selected value case + return n + '=' + enc(el.options[el.selectedIndex].value) + '&' + } + return '' + } + + reqwest.serialize = function(form) { + var fields = [form[byTag]('input'), form[byTag]('select'), form[byTag]('textarea')], + serialized = [] + + for (var i = 0, l = fields.length; i < l; ++i) { + for (var j = 0, l2 = fields[i].length; j < l2; ++j) { + serialized.push(serial(fields[i][j])) + } + } + return serialized.join('').replace(/&$/, '') + } + + reqwest.serializeArray = function(f) { + for (var pairs = this.serialize(f).split('&'), i = 0, l = pairs.length, r = [], o; i < l; i++) { + pairs[i] && (o = pairs[i].split('=')) && r.push({ + name: o[0], + value: o[1] + }) + } + return r + } + + var old = context.reqwest + reqwest.noConflict = function() { + context.reqwest = old + return this + } + + // defined as extern for Closure Compilation + if (typeof module !== 'undefined') module.exports = reqwest + context['reqwest'] = reqwest + +}(this, window) + +; + var buildParams, param, prefixes, r20, rbracket; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + (typeof exports !== "undefined" && exports !== null ? exports : this).reqwest = typeof window !== "undefined" && window !== null ? window.reqwest : reqwest; + rbracket = /\[\]$/; + r20 = /%20/g; + param = function(a) { + var add, k, name, s, v, value; + s = []; + add = function(key, value) { + value = (Batman.typeOf(value) === 'function' ? value() : value); + return s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value); + }; + if (Batman.typeOf(a) === 'Array') { + for (value in a) { + name = a[value]; + add(name, value); + } + } else { + for (k in a) { + v = a[k]; + buildParams(k, v, add); + } + } + return s.join("&").replace(r20, "+"); + }; + buildParams = function(prefix, obj, add) { + var i, name, v, _len, _results, _results2; + if (Batman.typeOf(obj) === 'Array') { + _results = []; + for (i = 0, _len = obj.length; i < _len; i++) { + v = obj[i]; + _results.push(rbracket.test(prefix) ? add(prefix, v) : buildParams(prefix + "[" + (typeof v === "object" || Batman.typeOf(v) === 'Array' ? i : "") + "]", v, add)); + } + return _results; + } else if ((obj != null) && typeof obj === "object") { + _results2 = []; + for (name in obj) { + _results2.push(buildParams(prefix + "[" + name + "]", obj[name], add)); + } + return _results2; + } else { + return add(prefix, obj); + } + }; + Batman.Request.prototype.send = function(data) { + var options, xhr, _ref; + this.loading(true); + options = { + url: this.get('url'), + method: this.get('method'), + type: this.get('type'), + headers: {}, + success: __bind(function(response) { + this.set('response', response); + this.set('status', xhr.status); + return this.success(response); + }, this), + error: __bind(function(xhr) { + this.set('response', xhr.responseText || xhr.content); + this.set('status', xhr.status); + xhr.request = this; + return this.error(xhr); + }, this), + complete: __bind(function() { + this.loading(false); + return this.loaded(true); + }, this) + }; + if ((_ref = options.method) === 'PUT' || _ref === 'POST') { + if (!this.get('formData')) { + options.headers['Content-type'] = this.get('contentType'); + data = data || this.get('data'); + if (options.method === 'GET') { + options.url += param(data); + } else { + options.data = param(data); + } + } else { + options.data = this.constructor.objectToFormData(options.data); + } + } + return xhr = (reqwest(options)).request; + }; + prefixes = ['Webkit', 'Moz', 'O', 'ms', '']; + Batman.mixins.animation = { + initialize: function() { + var prefix, _i, _len; + for (_i = 0, _len = prefixes.length; _i < _len; _i++) { + prefix = prefixes[_i]; + this.style["" + prefix + "Transform"] = 'scale(0, 0)'; + this.style.opacity = 0; + this.style["" + prefix + "TransitionProperty"] = "" + (prefix ? '-' + prefix.toLowerCase() + '-' : '') + "transform, opacity"; + this.style["" + prefix + "TransitionDuration"] = "0.8s, 0.55s"; + this.style["" + prefix + "TransformOrigin"] = "left top"; + } + return this; + }, + show: function(addToParent) { + var show, _ref, _ref2; + show = __bind(function() { + var prefix, _i, _len; + this.style.opacity = 1; + for (_i = 0, _len = prefixes.length; _i < _len; _i++) { + prefix = prefixes[_i]; + this.style["" + prefix + "Transform"] = 'scale(1, 1)'; + } + return this; + }, this); + if (addToParent) { + if ((_ref = addToParent.append) != null) { + _ref.appendChild(this); + } + if ((_ref2 = addToParent.before) != null) { + _ref2.parentNode.insertBefore(this, addToParent.before); + } + setTimeout(show, 0); + } else { + show(); + } + return this; + }, + hide: function(shouldRemove) { + var prefix, _i, _len; + this.style.opacity = 0; + for (_i = 0, _len = prefixes.length; _i < _len; _i++) { + prefix = prefixes[_i]; + this.style["" + prefix + "Transform"] = 'scale(0, 0)'; + } + if (shouldRemove) { + setTimeout((__bind(function() { + var _ref; + return (_ref = this.parentNode) != null ? _ref.removeChild(this) : void 0; + }, this)), 600); + } + return this; + } + }; +}).call(this);