You can confirm your account through the link below:
4 |
5 |
<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
6 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 | # Add your own tasks in files placed in lib/tasks ending in .rake,
3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4 |
5 | require File.expand_path('../config/application', __FILE__)
6 |
7 | Kanshabox::Application.load_tasks
8 |
--------------------------------------------------------------------------------
/db/migrate/20110907054247_create_thanks.rb:
--------------------------------------------------------------------------------
1 | class CreateThanks < ActiveRecord::Migration
2 | def change
3 | create_table :thanks do |t|
4 | t.integer :user_id
5 | t.string :what
6 | t.string :message
7 | t.datetime :date_at
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | APP_PATH = File.expand_path('../../config/application', __FILE__)
5 | require File.expand_path('../../config/boot', __FILE__)
6 | require 'rails/commands'
7 |
--------------------------------------------------------------------------------
/config/initializers/ext.rb:
--------------------------------------------------------------------------------
1 | module ThisMonth
2 | def this_month?
3 | ((Date.today.beginning_of_month)..(Date.today.end_of_month)).include?(self.to_date)
4 | end
5 | end
6 |
7 | class Date
8 | include ThisMonth
9 | end
10 | class DateTime
11 | include ThisMonth
12 | end
13 | class Time
14 | include ThisMonth
15 | end
16 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/unlock_instructions.html.erb:
--------------------------------------------------------------------------------
1 |
Hello <%= @resource.email %>!
2 |
3 |
Your account has been locked due to an excessive amount of unsuccessful sign in attempts.
4 |
5 |
Click the link below to unlock your account:
6 |
7 |
<%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>
8 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7 | # Mayor.create(name: 'Emanuel', city: cities.first)
8 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll automatically include all the stylesheets available in this directory
3 | * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4 | * the top of the compiled file, but it's generally better to create a new file per style scope.
5 | *= require_self
6 | *= require_tree .
7 | */
--------------------------------------------------------------------------------
/test/fixtures/thanks.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
2 |
3 | # This model initially had no columns defined. If you add columns to the
4 | # model remove the '{}' from the fixture names and add the columns immediately
5 | # below each fixture, per the syntax in the comments below
6 | #
7 | one: {}
8 | # column: value
9 | #
10 | two: {}
11 | # column: value
12 |
--------------------------------------------------------------------------------
/test/fixtures/users.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
2 |
3 | # This model initially had no columns defined. If you add columns to the
4 | # model remove the '{}' from the fixture names and add the columns immediately
5 | # below each fixture, per the syntax in the comments below
6 | #
7 | one: {}
8 | # column: value
9 | #
10 | two: {}
11 | # column: value
12 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format
4 | # (all these examples are active by default):
5 | # ActiveSupport::Inflector.inflections do |inflect|
6 | # inflect.plural /^(ox)$/i, '\1en'
7 | # inflect.singular /^(ox)en/i, '\1'
8 | # inflect.irregular 'person', 'people'
9 | # inflect.uncountable %w( fish sheep )
10 | # end
11 |
--------------------------------------------------------------------------------
/test/performance/browsing_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 | require 'rails/performance_test_help'
3 |
4 | class BrowsingTest < ActionDispatch::PerformanceTest
5 | # Refer to the documentation for all available options
6 | # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory]
7 | # :output => 'tmp/performance', :formats => [:flat] }
8 |
9 | def test_homepage
10 | get '/'
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/views/devise/unlocks/new.html.erb:
--------------------------------------------------------------------------------
1 |
10 | <% end %>
11 |
12 | <%= render :partial => "devise/shared/links" %>
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Kanshabox::Application.config.session_store :cookie_store, key: '_kanshabox_session'
4 |
5 | # Use the database for sessions instead of the cookie-based default,
6 | # which shouldn't be used to store highly confidential information
7 | # (create the session table with "rails generate session_migration")
8 | # Kanshabox::Application.config.session_store :active_record_store
9 |
--------------------------------------------------------------------------------
/app/views/devise/confirmations/new.html.erb:
--------------------------------------------------------------------------------
1 |
We've been notified about this issue and we'll take a look at it shortly.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/models/thank.rb:
--------------------------------------------------------------------------------
1 | class Thank < ActiveRecord::Base
2 |
3 | belongs_to :user
4 | validates :what, :length => { :in => 1..20 }
5 | validates :message, :length => { :in => 1..140 }
6 |
7 | def self.filter(params)
8 | thanks = self.where('')
9 | if params[:date]
10 | date = Date.parse(params[:date])
11 | thanks = where(:date_at => (date.beginning_of_day)..(date.end_of_day))
12 | end
13 | thanks
14 | end
15 |
16 | # def to_json_with_ext
17 | # {
18 | # :id => self.id,
19 | # :date_at => self.date_at,
20 | # :what => self.what,
21 | # :message => self.message,
22 | # :user_id => self.user_id,
23 | # # :total_count => Thank.where(:user_id => self.user_id).count,
24 | # # :date_count
25 | # }.to_json
26 | # end
27 | # alias_method_chain :to_json, :ext
28 |
29 | end
30 |
--------------------------------------------------------------------------------
/app/views/devise/sessions/new.html.haml:
--------------------------------------------------------------------------------
1 | - title '5Thanksへログイン'
2 | .page-header
3 | %h2 5Thanksへログイン
4 |
5 | .row
6 | .span10
7 | = form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
8 |
9 | %fieldset
10 | .clearfix
11 | = f.label :email, 'メールアドレス'
12 | .input
13 | = f.text_field :email
14 | .clearfix
15 | = f.label :password, 'パスワード'
16 | .input
17 | = f.password_field :password
18 | - if devise_mapping.rememberable?
19 | %ul.inputs-list
20 | %li
21 | %label
22 | = f.check_box :remember_me
23 | %span パスワードを覚えておく
24 | .actions
25 | = f.submit "ログイン", :class => 'btn primary'
26 | .span6
27 | = render :partial => "devise/shared/links"
28 |
--------------------------------------------------------------------------------
/public/assets/application-1256e2bab720fa68d08bf74acc61842c.css:
--------------------------------------------------------------------------------
1 | .footer{margin-top:25px;padding:10px;border-top:1px solid #CCC}.homes-index-page .hero-unit{margin-top:5px}.thanks-index-page p.empty-message{padding:60px 0;font-size:20px;font-weight:bold;line-height:150%}dl dt.date{position:relative;padding-left:60px;min-height:50px}dl dt.date time{display:block;font-size:30px;line-height:30px;padding:4px}dl dt.date .count{position:absolute;display:block;left:0px;width:50px;height:40px;background:#ff115c;color:#FFF}dl dt.date .count .num{display:block;font-size:18px;padding:5px 10px 0;text-align:center}dl dt.date .count .label{display:block;margin-top:-2px;text-align:center;font-size:11px;font-weight:normal}dl dd.thanks{border-bottom:1px dashed #CCC;margin-bottom:10px}dl dd.thanks ol li{margin-bottom:10px}dl dd.thanks ol li:hover{background:#EEE}dl dd.thanks ol li b{font-size:15px}dl dd.thanks ol li p.message{font-size:13px}
2 |
--------------------------------------------------------------------------------
/config/locales/translation_ja.yml:
--------------------------------------------------------------------------------
1 | ja:
2 | activerecord:
3 | models:
4 | user: "ユーザー" #g
5 | thank: "ありがとうございます" #g
6 |
7 | attributes:
8 | user:
9 | email: "メール" #g
10 | encrypted_password: "暗号化されたパスワード" #g
11 | reset_password_token: "パスワードリセットトークン" #g
12 | reset_password_sent_at: "で送信されたパスワードをリセットする" #g
13 | remember_created_at: "で作成した覚えている" #g
14 | current_sign_in_at: "での現在の記号" #g
15 | last_sign_in_at: "での最後の記号" #g
16 | current_sign_in_ip: "IPの現在の記号" #g
17 | last_sign_in_ip: "IPの最後の記号" #g
18 | username: "ユーザー名" #g
19 | thanks: "感謝" #g
20 | password: "パスワード"
21 | password_confirmation: "パスワード確認"
22 | current_password: "現在のパスワード"
23 |
24 | thank:
25 | what: "誰 or 何に?" #g
26 | message: "メッセージ" #g
27 | date_at: "日付" #g
28 | user: "ユーザー" #g
29 |
--------------------------------------------------------------------------------
/app/views/thanks/_form.html.haml:
--------------------------------------------------------------------------------
1 | = form_for @thank do |f|
2 | -if @thank.errors.any?
3 | .alert-message.block-message.error
4 | %h3= "#{pluralize(@thank.errors.count, "つのエラー")}のため保存できませんでした。"
5 | %ul
6 | - @thank.errors.full_messages.each do |msg|
7 | %li= msg
8 |
9 | .fieldset
10 | .clearfix
11 | %label 誰 or 何に?
12 | .input
13 | = f.text_field :what
14 | へ
15 | .help-block 例:両親、子ども、先生、友人、お天気、音楽 etc
16 | .clearfix
17 | %label 感謝の言葉
18 | .input
19 | = f.text_area :message, :size => '70x2', :class => 'xxlarge'
20 | .help-block
21 | 自分の人生でありがたいと思うことはなんですか?
22 | %br
23 | 書きながら思い浮かべてみよう。
24 | .clearfix(style='display:none;')
25 | %label いつの感謝?
26 | .input
27 | = f.date_select :date_at
28 | .clearfix
29 | .input
30 | = f.submit '感謝をメモする', :class => 'btn primary'
31 |
--------------------------------------------------------------------------------
/db/migrate/20110907082955_devise_create_users.rb:
--------------------------------------------------------------------------------
1 | class DeviseCreateUsers < ActiveRecord::Migration
2 | def self.up
3 | create_table(:users) do |t|
4 | t.database_authenticatable :null => false
5 | t.recoverable
6 | t.rememberable
7 | t.trackable
8 |
9 | # t.encryptable
10 | # t.confirmable
11 | # t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
12 | # t.token_authenticatable
13 |
14 | t.string :username
15 |
16 | t.timestamps
17 | end
18 |
19 | add_index :users, :email, :unique => true
20 | add_index :users, :reset_password_token, :unique => true
21 | # add_index :users, :confirmation_token, :unique => true
22 | # add_index :users, :unlock_token, :unique => true
23 | # add_index :users, :authentication_token, :unique => true
24 | end
25 |
26 | def self.down
27 | drop_table :users
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'http://rubygems.org'
2 |
3 | gem 'rails', '3.1.0'
4 |
5 | # Bundle edge Rails instead:
6 | # gem 'rails', :git => 'git://github.com/rails/rails.git'
7 |
8 | gem 'sqlite3'
9 | # for heroku
10 | group :production do
11 | gem 'pg'
12 | gem 'therubyracer-heroku'
13 | end
14 |
15 | # Gems used only for assets and not required
16 | # in production environments by default.
17 | group :assets do
18 | gem 'sass-rails', " ~> 3.1.0"
19 | gem 'coffee-rails', "~> 3.1.0"
20 | gem 'uglifier'
21 | end
22 |
23 | gem 'devise'
24 | gem 'haml-rails'
25 | gem 'kaminari'
26 | gem 'jquery-rails'
27 | gem 'i18n_generators'
28 | gem 'nifty-generators'
29 |
30 | # Use unicorn as the web server
31 | # gem 'unicorn'
32 |
33 | # Deploy with Capistrano
34 | # gem 'capistrano'
35 |
36 | # To use debugger
37 | # gem 'ruby-debug19', :require => 'ruby-debug'
38 |
39 | group :test do
40 | # Pretty printed test output
41 | gem 'turn', :require => false
42 | end
43 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/edit.html.haml:
--------------------------------------------------------------------------------
1 | - title 'パスワードを変更する - 5Thanks'
2 |
3 | .page-header
4 | %h2 パスワードを変更する
5 |
6 | .row
7 | .span10
8 | = form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f|
9 | -if resource.errors.any?
10 | .alert-message.block-message.error
11 | %h3= "#{resource.errors.count}つのエラーのため保存できませんでした。"
12 | %ul
13 | - resource.errors.full_messages.each do |msg|
14 | %li= msg
15 | = f.hidden_field :reset_password_token
16 |
17 | %fieldset
18 | .clearfix
19 | = f.label :password, 'パスワード'
20 | .input
21 | = f.password_field :password
22 | .clearfix
23 | = f.label :password_confirmation, 'パスワード確認'
24 | .input
25 | = f.password_field :password_confirmation
26 | .actions
27 | = f.submit "パスワードを変更", :class => 'btn primary'
28 |
29 | .span6
30 | = render :partial => "devise/shared/links"
31 |
32 |
--------------------------------------------------------------------------------
/app/helpers/error_messages_helper.rb:
--------------------------------------------------------------------------------
1 | module ErrorMessagesHelper
2 | # Render error messages for the given objects. The :message and :header_message options are allowed.
3 | def error_messages_for(*objects)
4 | options = objects.extract_options!
5 | options[:header_message] ||= I18n.t(:"activerecord.errors.header", :default => "Invalid Fields")
6 | options[:message] ||= I18n.t(:"activerecord.errors.message", :default => "Correct the following errors and try again.")
7 | messages = objects.compact.map { |o| o.errors.full_messages }.flatten
8 | unless messages.empty?
9 | content_tag(:div, :class => "error_messages") do
10 | list_items = messages.map { |msg| content_tag(:li, msg) }
11 | content_tag(:h2, options[:header_message]) + content_tag(:p, options[:message]) + content_tag(:ul, list_items.join.html_safe)
12 | end
13 | end
14 | end
15 |
16 | module FormBuilderAdditions
17 | def error_messages(options = {})
18 | @template.error_messages_for(@object, options)
19 | end
20 | end
21 | end
22 |
23 | ActionView::Helpers::FormBuilder.send(:include, ErrorMessagesHelper::FormBuilderAdditions)
24 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/new.html.haml:
--------------------------------------------------------------------------------
1 | - title '5Thanksへ新規登録する'
2 | .page-header
3 | %h2 5Thanksへ新規登録する
4 |
5 | .row
6 | .span10
7 | = form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
8 | -if resource.errors.any?
9 | .alert-message.block-message.error
10 | %h3= "#{resource.errors.count}つのエラーのため保存できませんでした。"
11 | %ul
12 | - resource.errors.full_messages.each do |msg|
13 | %li= msg
14 |
15 | %fieldset
16 | .clearfix
17 | = f.label :email, 'メールアドレス'
18 | .input
19 | = f.text_field :email
20 | .clearfix
21 | = f.label :username, 'ユーザー名'
22 | .input
23 | = f.text_field :username
24 | %span.help-inline 半角英数字3文字〜15文字まで。
25 | .clearfix
26 | = f.label :password, 'パスワード'
27 | .input
28 | = f.password_field :password
29 | .clearfix
30 | = f.label :password_confirmation, 'パスワード確認'
31 | .input
32 | = f.password_field :password_confirmation
33 | .actions
34 | = f.submit "登録", :class => 'btn primary'
35 | .span6
36 | = render :partial => "devise/shared/links"
37 |
--------------------------------------------------------------------------------
/test/functional/thanks_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ThanksControllerTest < ActionController::TestCase
4 | setup do
5 | @thank = thanks(:one)
6 | end
7 |
8 | test "should get index" do
9 | get :index
10 | assert_response :success
11 | assert_not_nil assigns(:thanks)
12 | end
13 |
14 | test "should get new" do
15 | get :new
16 | assert_response :success
17 | end
18 |
19 | test "should create thank" do
20 | assert_difference('Thank.count') do
21 | post :create, thank: @thank.attributes
22 | end
23 |
24 | assert_redirected_to thank_path(assigns(:thank))
25 | end
26 |
27 | test "should show thank" do
28 | get :show, id: @thank.to_param
29 | assert_response :success
30 | end
31 |
32 | test "should get edit" do
33 | get :edit, id: @thank.to_param
34 | assert_response :success
35 | end
36 |
37 | test "should update thank" do
38 | put :update, id: @thank.to_param, thank: @thank.attributes
39 | assert_redirected_to thank_path(assigns(:thank))
40 | end
41 |
42 | test "should destroy thank" do
43 | assert_difference('Thank.count', -1) do
44 | delete :destroy, id: @thank.to_param
45 | end
46 |
47 | assert_redirected_to thanks_path
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/thanks.css.scss:
--------------------------------------------------------------------------------
1 | .thanks-index-page {
2 | p.empty-message {
3 | padding: 60px 0;
4 | font-size: 20px;
5 | font-weight: bold;
6 | line-height: 150%;
7 | }
8 | }
9 |
10 | dl {
11 | dt.date {
12 | position: relative;
13 | padding-left: 60px;
14 | min-height: 50px;
15 | time {
16 | display: block;
17 | font-size: 30px;
18 | line-height: 30px;
19 | padding: 4px;
20 | }
21 | .count {
22 | position: absolute;
23 | display: block;
24 | left: 0px;
25 | width: 50px;
26 | height: 40px;
27 | background: #ff115c;
28 | color: #FFF;
29 | .num {
30 | display: block;
31 | font-size: 18px;
32 | padding: 5px 10px 0;
33 | text-align: center;
34 | }
35 | .label {
36 | display: block;
37 | margin-top: -2px;
38 | text-align: center;
39 | font-size: 11px;
40 | font-weight: normal;
41 | }
42 | }
43 | }
44 | dd.thanks {
45 | border-bottom: 1px dashed #CCC;
46 | margin-bottom: 10px;
47 | ol {
48 | li {
49 | margin-bottom: 10px;
50 | &:hover { background: #EEE; }
51 | b { font-size: 15px;}
52 | p.message { font-size: 13px; }
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Kanshabox::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Log error messages when you accidentally call methods on nil.
10 | config.whiny_nils = true
11 |
12 | # Show full error reports and disable caching
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger
20 | config.active_support.deprecation = :log
21 |
22 | # Only use best-standards-support built into browsers
23 | config.action_dispatch.best_standards_support = :builtin
24 |
25 | # Do not compress assets
26 | config.assets.compress = false
27 |
28 | # Expands the lines which load the assets
29 | config.assets.debug = true
30 |
31 | config.action_mailer.default_url_options = {
32 | :host => 'localhost:3000'
33 | }
34 | end
35 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/edit.html.haml:
--------------------------------------------------------------------------------
1 | - title '設定 - 5Thanks'
2 | .page-header
3 | %h2 設定
4 |
5 | .row
6 | .span10
7 | = form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f|
8 | -if resource.errors.any?
9 | .alert-message.block-message.error
10 | %h3= "#{resource.errors.count}つのエラーのため保存できませんでした。"
11 | %ul
12 | - resource.errors.full_messages.each do |msg|
13 | %li= msg
14 | %fieldset
15 | .clearfix
16 | = f.label :email
17 | .input
18 | = f.email_field :email
19 | .clearfix
20 | = f.label :username
21 | .input
22 | = f.text_field :username
23 | .clearfix
24 | = f.label :password
25 | .input
26 | = f.password_field :password
27 | .clearfix
28 | = f.label :password_confirmation
29 | .input
30 | = f.password_field :password_confirmation
31 | .clearfix
32 | = f.label :current_password
33 | .input
34 | = f.password_field :current_password
35 | .help-block 設定を変更するためには現在のパスワードが必要です。
36 | .actions
37 | = f.submit "更新する", :class => 'btn primary'
38 |
39 | .span6
40 | %h3 退会
41 | %p
42 | = link_to "このアカウントを削除する", registration_path(resource_name), :confirm => "お気に召さずにごめんなさい。\n退会するとあなたの#{current_user.thanks.count}つの感謝がシステム上から消えてしまいます。\nでもあなたの心に残っているのでよろしいですよね?", :method => :delete
43 |
44 |
--------------------------------------------------------------------------------
/app/assets/javascripts/thanks.js.coffee:
--------------------------------------------------------------------------------
1 | $ ->
2 | if $('.thanks-new-page').is(':visible')
3 |
4 | # 入力フォーム監視
5 | observeInputs = (e)->
6 | values = $('#thank_what,#thank_message').map (index,node)-> $(node).val()
7 | if _.all(values, (value)-> value != '')
8 | $('input[name=commit]').removeAttr('disabled')
9 | else
10 | $('input[name=commit]').attr('disabled','disabled')
11 | observeInputs()
12 | $('#thank_what,#thank_message')
13 | .bind('keyup', observeInputs)
14 | .bind('change', observeInputs)
15 |
16 |
17 | if $('.thanks-index-page').is(':visible')
18 |
19 | $('.thanks li')
20 | .bind('mouseover', (e)->
21 | $(this).find('.destroy').show()
22 | )
23 | .bind('mouseout', (e)->
24 | $(this).find('.destroy').hide()
25 | )
26 |
27 | updateSystemMessage = (thanksNode)->
28 | count = $(thanksNode).find('li:visible').size()
29 | $(thanksNode).find('.system-message p').hide()
30 | if count == 0
31 | $(thanksNode).find('.empty-thanks-message').show()
32 | else if count >= 5
33 | $(thanksNode).find('.full-thanks-message').show()
34 | else
35 | $(thanksNode).find('.notyet-thanks-message').show()
36 | $(thanksNode).find('.notyet-thanks-message .num').text(5 - count)
37 |
38 | $('dd.thanks').each((i,node)->updateSystemMessage($(node)))
39 |
40 | # Thank 削除
41 | $('.btn-destroy')
42 | .bind('ajax:success',(e,res,status)->
43 | console.log(res)
44 | if status == 'success'
45 | thank = $(this).parents('li')
46 | thank.fadeOut(1000, ()->
47 | date = thank.parents('dd').data('date')
48 | count = $("dd[data-date=#{date}] li:visible").size()
49 | $("dt[data-date=#{date}]").find('.num').text(count)
50 | updateSystemMessage($("dd[data-date=#{date}]"))
51 | )
52 | )
53 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Kanshabox::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Configure static asset server for tests with Cache-Control for performance
11 | config.serve_static_assets = true
12 | config.static_cache_control = "public, max-age=3600"
13 |
14 | # Log error messages when you accidentally call methods on nil
15 | config.whiny_nils = true
16 |
17 | # Show full error reports and disable caching
18 | config.consider_all_requests_local = true
19 | config.action_controller.perform_caching = false
20 |
21 | # Raise exceptions instead of rendering exception templates
22 | config.action_dispatch.show_exceptions = false
23 |
24 | # Disable request forgery protection in test environment
25 | config.action_controller.allow_forgery_protection = false
26 |
27 | # Tell Action Mailer not to deliver emails to the real world.
28 | # The :test delivery method accumulates sent emails in the
29 | # ActionMailer::Base.deliveries array.
30 | config.action_mailer.delivery_method = :test
31 |
32 | # Use SQL instead of Active Record's schema dumper when creating the test database.
33 | # This is necessary if your schema can't be completely dumped by the schema dumper,
34 | # like if you have constraints or database-specific column types
35 | # config.active_record.schema_format = :sql
36 |
37 | # Print deprecation notices to the stderr
38 | config.active_support.deprecation = :stderr
39 |
40 | # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets
41 | config.assets.allow_debugging = true
42 | end
43 |
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | # This file is auto-generated from the current state of the database. Instead
3 | # of editing this file, please use the migrations feature of Active Record to
4 | # incrementally modify your database, and then regenerate this schema definition.
5 | #
6 | # Note that this schema.rb definition is the authoritative source for your
7 | # database schema. If you need to create the application database on another
8 | # system, you should be using db:schema:load, not running all the migrations
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 | # you'll amass, the slower it'll run and the greater likelihood for issues).
11 | #
12 | # It's strongly recommended to check this file into your version control system.
13 |
14 | ActiveRecord::Schema.define(:version => 20110907082955) do
15 |
16 | create_table "thanks", :force => true do |t|
17 | t.integer "user_id"
18 | t.string "what"
19 | t.string "message"
20 | t.datetime "date_at"
21 | t.datetime "created_at"
22 | t.datetime "updated_at"
23 | end
24 |
25 | create_table "users", :force => true do |t|
26 | t.string "email", :default => "", :null => false
27 | t.string "encrypted_password", :limit => 128, :default => "", :null => false
28 | t.string "reset_password_token"
29 | t.datetime "reset_password_sent_at"
30 | t.datetime "remember_created_at"
31 | t.integer "sign_in_count", :default => 0
32 | t.datetime "current_sign_in_at"
33 | t.datetime "last_sign_in_at"
34 | t.string "current_sign_in_ip"
35 | t.string "last_sign_in_ip"
36 | t.string "username"
37 | t.datetime "created_at"
38 | t.datetime "updated_at"
39 | end
40 |
41 | add_index "users", ["email"], :name => "index_users_on_email", :unique => true
42 | add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
43 |
44 | end
45 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | if defined?(Bundler)
6 | # If you precompile assets before deploying to production, use this line
7 | Bundler.require *Rails.groups(:assets => %w(development test))
8 | # If you want your assets lazily compiled in production, use this line
9 | # Bundler.require(:default, :assets, Rails.env)
10 | end
11 |
12 | module Kanshabox
13 | class Application < Rails::Application
14 | # Settings in config/environments/* take precedence over those specified here.
15 | # Application configuration should go into files in config/initializers
16 | # -- all .rb files in that directory are automatically loaded.
17 |
18 | # Custom directories with classes and modules you want to be autoloadable.
19 | # config.autoload_paths += %W(#{config.root}/extras)
20 |
21 | # Only load the plugins named here, in the order given (default is alphabetical).
22 | # :all can be used as a placeholder for all plugins not explicitly named.
23 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24 |
25 | # Activate observers that should always be running.
26 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
27 |
28 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
30 | config.time_zone = 'Tokyo'
31 |
32 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
33 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34 | config.i18n.default_locale = :ja
35 |
36 | # Configure the default encoding used in templates for Ruby 1.9.
37 | config.encoding = "utf-8"
38 |
39 | # Configure sensitive parameters which will be filtered from the log file.
40 | config.filter_parameters += [:password]
41 |
42 | # Enable the asset pipeline
43 | config.assets.enabled = true
44 |
45 | # Version of your assets, change this if you want to expire all your assets
46 | config.assets.version = '1.0'
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.haml:
--------------------------------------------------------------------------------
1 | !!!
2 | %html
3 | %head
4 | %title
5 | = yield(:title) || "5Thanks"
6 | %meta{"http-equiv"=>"Content-Type", :content=>"text/html; charset=utf-8"}/
7 | = stylesheet_link_tag 'http://twitter.github.com/bootstrap/assets/css/bootstrap-1.2.0.min.css'
8 | = stylesheet_link_tag "application"
9 | = javascript_include_tag "application"
10 | = csrf_meta_tag
11 | = yield(:head)
12 | - if Rails.env.production?
13 | :javascript
14 | var _gaq = _gaq || [];
15 | _gaq.push(['_setAccount', 'UA-845066-11']);
16 | _gaq.push(['_trackPageview']);
17 |
18 | (function() {
19 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
20 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
21 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
22 | })();
23 | %body(class='#{controller_name}-#{action_name}-page')
24 |
25 | .topbar
26 | .fill
27 | .container
28 | %h3
29 | %a(href='/')
30 | 5Thanks
31 | - if user_signed_in?
32 | %ul
33 | %li= link_to 'あなたの感謝', thanks_path
34 | %li= link_to '新しい感謝', new_thank_path
35 | %ul.nav.secondary-nav
36 | %li= link_to '設定', edit_user_registration_path
37 | %li= link_to 'ログアウト', destroy_user_session_path, :method => :delete
38 | - else
39 | %ul.nav.secondary-nav
40 | %li= link_to '新規登録', new_user_registration_path
41 | %li= link_to 'ログイン', new_user_session_path
42 |
43 | .container(style='margin-top: 40px; padding-top: 10px')
44 | - flash.each do |name, msg|
45 | .alert-message.small(class='#{name}')= msg
46 |
47 | .container
48 | .pageContent
49 | = yield
50 |
51 | .container.footer
52 | Powered by Heroku, Rails3.1, Twitter Bootstrap
53 | Create by @func09. Github
54 |
55 | :javascript
56 | if($('.alert-message.small').is(':visible')){
57 | setTimeout(function(){
58 | $('.alert-message.small').fadeOut()
59 | }, 3000);
60 | }
61 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Kanshabox::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # Code is not reloaded between requests
5 | config.cache_classes = true
6 |
7 | # Full error reports are disabled and caching is turned on
8 | config.consider_all_requests_local = false
9 | config.action_controller.perform_caching = true
10 |
11 | # Disable Rails's static asset server (Apache or nginx will already do this)
12 | config.serve_static_assets = false
13 |
14 | # Compress JavaScripts and CSS
15 | config.assets.compress = true
16 |
17 | # Don't fallback to assets pipeline if a precompiled asset is missed
18 | config.assets.compile = false
19 |
20 | # Generate digests for assets URLs
21 | config.assets.digest = true
22 |
23 | # Defaults to Rails.root.join("public/assets")
24 | # config.assets.manifest = YOUR_PATH
25 |
26 | # Specifies the header that your server uses for sending files
27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
29 |
30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31 | # config.force_ssl = true
32 |
33 | # See everything in the log (default is :info)
34 | # config.log_level = :debug
35 |
36 | # Use a different logger for distributed setups
37 | # config.logger = SyslogLogger.new
38 |
39 | # Use a different cache store in production
40 | # config.cache_store = :mem_cache_store
41 |
42 | # Enable serving of images, stylesheets, and JavaScripts from an asset server
43 | # config.action_controller.asset_host = "http://assets.example.com"
44 |
45 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
46 | # config.assets.precompile += %w( search.js )
47 |
48 | # Disable delivery errors, bad email addresses will be ignored
49 | # config.action_mailer.raise_delivery_errors = false
50 |
51 | # Enable threaded mode
52 | # config.threadsafe!
53 |
54 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
55 | # the I18n.default_locale when a translation can not be found)
56 | config.i18n.fallbacks = true
57 |
58 | # Send deprecation notices to registered listeners
59 | config.active_support.deprecation = :notify
60 |
61 | config.action_mailer.default_url_options = {
62 | :host => 'fivethanks.heroku.com'
63 | }
64 |
65 | ActionMailer::Base.smtp_settings = {
66 | :address => "smtp.sendgrid.net",
67 | :port => "25",
68 | :authentication => :plain,
69 | :user_name => ENV['SENDGRID_USERNAME'],
70 | :password => ENV['SENDGRID_PASSWORD'],
71 | :domain => ENV['SENDGRID_DOMAIN']
72 | }
73 |
74 | end
75 |
--------------------------------------------------------------------------------
/app/views/homes/index.html.haml:
--------------------------------------------------------------------------------
1 | - title '5Thanks - 感謝できることを毎日5つメモするツール'
2 | .hero-unit
3 | %h1 Welcome, 5Thanks
4 | %p
5 | 5Thanksは、毎日5つの感謝できることをメモして、ポジティブに生きたい人のためのツールです。
6 | .row
7 | .span9
8 |
9 | %h2 5Thanksについて
10 | %p
11 | 書籍「ハーバードの人生を変える授業」の中にある
12 | %blockquote
13 | ちょっとした事でもいいので、毎日感謝できることを5つ書く
14 | %p
15 | を実践するためのツールです。「ハーバードの人生を変える授業」によれば、これを続けた人は人生を肯定的に評価できるようになり、幸福感が高くなり、ポジティブな気分を味わうようになったそうです。
16 |
17 | %h3 サービスの特徴
18 | %ol
19 | %li
20 | %b 簡単なメモ登録
21 | %p
22 | 誰、何に対して、どんな感謝をしたのか書くだけのシンプルなツールです。
23 | %br
24 | = image_tag 'capture-2.png'
25 | %li
26 | %b 振り返りやすい
27 | %p
28 | メモは日毎に整理されて、過去のメモを読み返しやすいです。
29 | = image_tag 'capture-1.png'
30 | %li
31 | %b プライベートなメモ
32 | %p
33 | 感謝のメモは他の人から見られることはありません。
34 |
35 | .span7
36 | %h3 お知らせ
37 | %p
38 | %b 2011-09-12
39 | %br 「クリック募金dff」のバナーを表示するようにしました。何か良い事したいなと思った時にクリックしてみてください。
40 | %p
41 | %b 2011-09-08
42 | %br 5Thanksリリースしました!
43 |
44 | %hr
45 | %p これまで#{Thank.count}の感謝がメモされました
46 | %p #{User.count}人が5Thanksを利用しています。
47 | %p
48 | = link_to 'ユーザー登録して感謝の気持ちをメモし始める', new_user_registration_path, :class => 'btn primary xlarge'
49 |
50 | .share
51 |
52 | ツイート
53 |
54 |
--------------------------------------------------------------------------------
/app/controllers/thanks_controller.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | class ThanksController < ApplicationController
3 | before_filter :authenticate_user!
4 | # GET /thanks
5 | # GET /thanks.json
6 | def index
7 | if params[:m]
8 | @month = Date.parse(params[:m].concat('-01')).beginning_of_month if params[:m]
9 | else
10 | @month = Date.today.beginning_of_month
11 | end
12 | # メモした月リストを作成
13 | @months = current_user.thanks.group(:date_at).count
14 | .keys.group_by{|key,value| key.strftime('%Y-%m') }.keys.map{|d| Date.parse("#{d}-01") }.sort.reverse
15 | @thanks = current_user.thanks
16 | .where(:date_at => (@month.beginning_of_month)..(@month.end_of_month))
17 | .order('date_at DESC')
18 | .group_by{|t| t.date_at}
19 |
20 | respond_to do |format|
21 | format.html # index.html.erb
22 | end
23 | end
24 |
25 | # GET /thanks/1
26 | # GET /thanks/1.json
27 | def show
28 | @thank = Thank.find(params[:id])
29 |
30 | respond_to do |format|
31 | format.html # show.html.erb
32 | format.json { render json: @thank }
33 | end
34 | end
35 |
36 | # GET /thanks/new
37 | # GET /thanks/new.json
38 | def new
39 | date = Date.today
40 | if params[:date]
41 | date = DateTime.parse(params[:date])
42 | end
43 |
44 | @thank = Thank.new :date_at => date
45 |
46 | respond_to do |format|
47 | format.html # new.html.erb
48 | format.json { render json: @thank }
49 | end
50 | end
51 |
52 | # GET /thanks/1/edit
53 | def edit
54 | @thank = Thank.find(params[:id])
55 | end
56 |
57 | # POST /thanks
58 | # POST /thanks.json
59 | def create
60 | @thank = current_user.thanks.build(params[:thank])
61 |
62 | respond_to do |format|
63 | if @thank.save
64 | format.html { redirect_to thanks_path, notice: '感謝の気持ちが増えました!' }
65 | format.json { render json: @thank, status: :created, location: @thank }
66 | else
67 | format.html { render action: "new" }
68 | format.json { render json: @thank.errors, status: :unprocessable_entity }
69 | end
70 | end
71 | end
72 |
73 | # PUT /thanks/1
74 | # PUT /thanks/1.json
75 | def update
76 | @thank = Thank.find(params[:id])
77 |
78 | respond_to do |format|
79 | if @thank.update_attributes(params[:thank])
80 | format.html { redirect_to @thank, notice: 'Thank was successfully updated.' }
81 | format.json { head :ok }
82 | else
83 | format.html { render action: "edit" }
84 | format.json { render json: @thank.errors, status: :unprocessable_entity }
85 | end
86 | end
87 | end
88 |
89 | # DELETE /thanks/1
90 | # DELETE /thanks/1.json
91 | def destroy
92 | @thank = Thank.find(params[:id])
93 | @thank.destroy
94 |
95 | respond_to do |format|
96 | format.html { redirect_to thanks_url }
97 | format.json { render json: @thank, status: :created }
98 | end
99 | end
100 | end
101 |
--------------------------------------------------------------------------------
/config/locales/devise.en.yml:
--------------------------------------------------------------------------------
1 | # Additional translations at http://github.com/plataformatec/devise/wiki/I18n
2 |
3 | en:
4 | errors:
5 | messages:
6 | expired: "has expired, please request a new one"
7 | not_found: "not found"
8 | already_confirmed: "was already confirmed, please try signing in"
9 | not_locked: "was not locked"
10 | not_saved:
11 | one: "1 error prohibited this %{resource} from being saved:"
12 | other: "%{count} errors prohibited this %{resource} from being saved:"
13 |
14 | devise:
15 | failure:
16 | already_authenticated: 'You are already signed in.'
17 | unauthenticated: 'You need to sign in or sign up before continuing.'
18 | unconfirmed: 'You have to confirm your account before continuing.'
19 | locked: 'Your account is locked.'
20 | invalid: 'Invalid email or password.'
21 | invalid_token: 'Invalid authentication token.'
22 | timeout: 'Your session expired, please sign in again to continue.'
23 | inactive: 'Your account was not activated yet.'
24 | sessions:
25 | signed_in: 'Signed in successfully.'
26 | signed_out: 'Signed out successfully.'
27 | passwords:
28 | send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
29 | updated: 'Your password was changed successfully. You are now signed in.'
30 | send_paranoid_instructions: "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
31 | confirmations:
32 | send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
33 | send_paranoid_instructions: 'If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes.'
34 | confirmed: 'Your account was successfully confirmed. You are now signed in.'
35 | registrations:
36 | signed_up: 'Welcome! You have signed up successfully.'
37 | inactive_signed_up: 'You have signed up successfully. However, we could not sign you in because your account is %{reason}.'
38 | updated: 'You updated your account successfully.'
39 | destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
40 | reasons:
41 | inactive: 'inactive'
42 | unconfirmed: 'unconfirmed'
43 | locked: 'locked'
44 | unlocks:
45 | send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
46 | unlocked: 'Your account was successfully unlocked. You are now signed in.'
47 | send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.'
48 | omniauth_callbacks:
49 | success: 'Successfully authorized from %{kind} account.'
50 | failure: 'Could not authorize you from %{kind} because "%{reason}".'
51 | mailer:
52 | confirmation_instructions:
53 | subject: 'Confirmation instructions'
54 | reset_password_instructions:
55 | subject: 'Reset password instructions'
56 | unlock_instructions:
57 | subject: 'Unlock Instructions'
58 |
--------------------------------------------------------------------------------
/app/views/thanks/index.html.haml:
--------------------------------------------------------------------------------
1 | - if @month.this_month?
2 | - title "今月のあなたの感謝の言葉 - 5Thanks"
3 | - else
4 | - title "#{@month.strftime("%Y年%m月")}のあなたの感謝の言葉 - 5Thanks"
5 |
6 | .page-header
7 | %h1
8 | - if @month.this_month?
9 | 今月のあなたの感謝の言葉
10 | - else
11 | #{@month.strftime("%Y年%m月")}のあなたの感謝の言葉
12 | %p
13 | - unless current_user.thanks.count == 0
14 | あなたは今日までに#{current_user.thanks.count}個の感謝メモを残しました
15 |
16 | .row
17 | .span10
18 | - unless @thanks.count > 0
19 | %p.empty-message
20 | - if @month.end_of_month.future?
21 | 今月はまだ感謝メモをつけていません。
22 | %br
23 | = link_to "さぁ、感謝できることをメモしましょう!", new_thank_path
24 | - else
25 | この月は感謝メモがありませんでした
26 | - else
27 | %dl
28 | - if @month.this_month? and not @thanks.keys.first.today?
29 | - @thanks[Date.today] = []
30 | - for date in @thanks.keys.sort.reverse
31 | - thanks = @thanks[date]
32 | %dt.date(data-date='#{date.strftime("%Y-%m-%d")}')
33 | %span.count
34 | %span.num= thanks.count
35 | %span.label thx
36 | - if date.today?
37 | - label_when = "今日"
38 | %time(datetime='') 今日
39 | - else
40 | - label_when = "この日"
41 | %time(datetime='#{date.strftime("%Y-%m-%d")}')= date.strftime("%Y/%m/%d")
42 | %dd.thanks(data-date='#{date.strftime("%Y-%m-%d")}')
43 | %ol
44 | - for thank in thanks
45 | %li(data-thank-id='#{thank.id}')
46 | %b= "#{thank.what}へ"
47 | %span.destroy(style='display:none;')
48 | = link_to 'x', thank_path(thank,:format => :json), :remote => true, :class => 'btn-destroy', :method => :delete
49 | %br
50 | %p.message= thank.message
51 | .system-message
52 | - if date.today?
53 | %p.empty-thanks-message(style='display:none;')
54 | 今日はまだ感謝をメモしてないよ。
55 | = link_to "ちょっとした事でいいんだよ", new_thank_path
56 | %p.full-thanks-message(style='display:none;')
57 | 今日もいっぱい良いことがあったね!
58 | = link_to "もっと感謝したことはある?", new_thank_path
59 | %p.notyet-thanks-message(style='display:none;')
60 | 今日はあと#{5-thanks.size}個の感謝をメモろう。
61 | = link_to "あとちょっと!", new_thank_path
62 | - else
63 | %p.full-thanks-message(style='display:none;')
64 | いっぱい良いことがあったね!
65 | = link_to "もっと感謝したいことはある?", new_thank_path(:date => date.strftime("%Y%m%d"))
66 | %p.notyet-thanks-message(style='display:none;')
67 | もう少し感謝のメモを書こう。
68 | = link_to "思い出してみて!", new_thank_path(:date => date.strftime("%Y%m%d"))
69 | .span6
70 | %p
71 |
72 | %ul
73 | - for date in @months
74 | %li
75 | = link_to date.strftime('%Y年%m月'), thanks_path(:m => date.strftime('%Y-%m'))
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/config/locales/devise.ja.yml:
--------------------------------------------------------------------------------
1 | ja:
2 | errors:
3 | messages:
4 | not_found: "は見つかりませんでした"
5 | # not_found: "not found"
6 | already_confirmed: "は既に登録済みです"
7 | # already_confirmed: "was already confirmed"
8 | not_locked: "は凍結されていません"
9 | # not_locked: "was not locked"
10 | not_saved:
11 | one: "1つのエラーがありました。"
12 | other: "%{count}つのエラーがありました。"
13 |
14 | devise:
15 | omniauth_callbacks:
16 | # success: 'Successfully authorized from %{kind} account.'
17 | success: '%{kind} アカウントの認証に成功しました'
18 | # failure: 'Could not authorize you from %{kind} because "%{reason}".'
19 | failure: '%{kind} からの認証に失敗しました。 理由: "%{reason}".'
20 | failure:
21 | already_authenticated: 'すでにログインしています。'
22 | # already_authenticated: 'You are already signed in.'
23 | unauthenticated: 'ログインしてください。'
24 | # unauthenticated: 'You need to sign in or sign up before continuing.'
25 | unconfirmed: '本登録を行ってください。'
26 | # unconfirmed: 'You have to confirm your account before continuing.'
27 | locked: 'あなたのアカウントは凍結されています。'
28 | # locked: 'Your account is locked.'
29 | invalid: 'メールアドレスかパスワードが違います。'
30 | # invalid: 'Invalid email or password.'
31 | invalid_token: '認証キーが不正です。'
32 | # invalid_token: 'Invalid authentication token.'
33 | timeout: 'セッションがタイムアウトしました。もう一度ログインしてください。'
34 | # timeout: 'Your session expired, please sign in again to continue.'
35 | inactive: 'アカウントがアクティベートされていません。'
36 | # inactive: 'Your account was not activated yet.'
37 | sessions:
38 | signed_in: 'ログインしました。'
39 | # signed_in: 'Signed in successfully.'
40 | signed_out: 'ログアウトしました。'
41 | # signed_out: 'Signed out successfully.'
42 | passwords:
43 | send_instructions: 'パスワードのリセット方法を数分以内にメールでご連絡します。'
44 | # send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
45 | updated: 'パスワードを変更しました。'
46 | # updated: 'Your password was changed successfully. You are now signed in.'
47 | confirmations:
48 | send_instructions: '登録方法を数分以内にメールでご連絡します。'
49 | # send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
50 | confirmed: 'アカウントを登録しました。'
51 | # confirmed: 'Your account was successfully confirmed. You are now signed in.'
52 | registrations:
53 | signed_up: 'アカウント登録を受け付けました。確認のメールをお送りします。'
54 | # signed_up: 'You have signed up successfully. If enabled, a confirmation was sent to your e-mail.'
55 | updated: 'アカウントを更新しました。'
56 | # updated: 'You updated your account successfully.'
57 | destroyed: 'アカウントを削除しました。またのご利用をお待ちしております。'
58 | # destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
59 | inactive_signed_up: '新規登録に成功しました。 しかしアカウントにログインしていません。理由: %{reason}.'
60 | # inactive_signed_up: 'You have signed up successfully. However, we could not sign you in because your account is %{reason}.'
61 | unlocks:
62 | send_instructions: 'アカウントの凍結解除方法を数分以内にメールでご連絡します。'
63 | # send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
64 | unlocked: 'アカウントを凍結解除しました。'
65 | # unlocked: 'Your account was successfully unlocked. You are now signed in.'
66 | mailer:
67 | confirmation_instructions:
68 | subject: 'アカウントの登録方法'
69 | # subject: 'Confirmation instructions'
70 | reset_password_instructions:
71 | subject: 'パスワードの再設定'
72 | # subject: 'Reset password instructions'
73 | unlock_instructions:
74 | subject: 'アカウントの凍結解除'
75 | # subject: 'Unlock Instructions'
76 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | specs:
4 | actionmailer (3.1.0)
5 | actionpack (= 3.1.0)
6 | mail (~> 2.3.0)
7 | actionpack (3.1.0)
8 | activemodel (= 3.1.0)
9 | activesupport (= 3.1.0)
10 | builder (~> 3.0.0)
11 | erubis (~> 2.7.0)
12 | i18n (~> 0.6)
13 | rack (~> 1.3.2)
14 | rack-cache (~> 1.0.3)
15 | rack-mount (~> 0.8.2)
16 | rack-test (~> 0.6.1)
17 | sprockets (~> 2.0.0)
18 | activemodel (3.1.0)
19 | activesupport (= 3.1.0)
20 | bcrypt-ruby (~> 3.0.0)
21 | builder (~> 3.0.0)
22 | i18n (~> 0.6)
23 | activerecord (3.1.0)
24 | activemodel (= 3.1.0)
25 | activesupport (= 3.1.0)
26 | arel (~> 2.2.1)
27 | tzinfo (~> 0.3.29)
28 | activeresource (3.1.0)
29 | activemodel (= 3.1.0)
30 | activesupport (= 3.1.0)
31 | activesupport (3.1.0)
32 | multi_json (~> 1.0)
33 | ansi (1.3.0)
34 | arel (2.2.1)
35 | bcrypt-ruby (3.0.0)
36 | builder (3.0.0)
37 | coffee-rails (3.1.0)
38 | coffee-script (>= 2.2.0)
39 | railties (~> 3.1.0.rc1)
40 | coffee-script (2.2.0)
41 | coffee-script-source
42 | execjs
43 | coffee-script-source (1.1.2)
44 | devise (1.4.4)
45 | bcrypt-ruby (~> 3.0)
46 | orm_adapter (~> 0.0.3)
47 | warden (~> 1.0.3)
48 | erubis (2.7.0)
49 | execjs (1.2.4)
50 | multi_json (~> 1.0)
51 | haml (3.1.2)
52 | haml-rails (0.3.4)
53 | actionpack (~> 3.0)
54 | activesupport (~> 3.0)
55 | haml (~> 3.0)
56 | railties (~> 3.0)
57 | hike (1.2.1)
58 | i18n (0.6.0)
59 | i18n_generators (1.0.3)
60 | jquery-rails (1.0.13)
61 | railties (~> 3.0)
62 | thor (~> 0.14)
63 | kaminari (0.12.4)
64 | rails (>= 3.0.0)
65 | mail (2.3.0)
66 | i18n (>= 0.4.0)
67 | mime-types (~> 1.16)
68 | treetop (~> 1.4.8)
69 | mime-types (1.16)
70 | multi_json (1.0.3)
71 | nifty-generators (0.4.6)
72 | orm_adapter (0.0.5)
73 | pg (0.11.0)
74 | polyglot (0.3.2)
75 | rack (1.3.2)
76 | rack-cache (1.0.3)
77 | rack (>= 0.4)
78 | rack-mount (0.8.3)
79 | rack (>= 1.0.0)
80 | rack-ssl (1.3.2)
81 | rack
82 | rack-test (0.6.1)
83 | rack (>= 1.0)
84 | rails (3.1.0)
85 | actionmailer (= 3.1.0)
86 | actionpack (= 3.1.0)
87 | activerecord (= 3.1.0)
88 | activeresource (= 3.1.0)
89 | activesupport (= 3.1.0)
90 | bundler (~> 1.0)
91 | railties (= 3.1.0)
92 | railties (3.1.0)
93 | actionpack (= 3.1.0)
94 | activesupport (= 3.1.0)
95 | rack-ssl (~> 1.3.2)
96 | rake (>= 0.8.7)
97 | rdoc (~> 3.4)
98 | thor (~> 0.14.6)
99 | rake (0.9.2)
100 | rdoc (3.9.4)
101 | sass (3.1.7)
102 | sass-rails (3.1.0)
103 | actionpack (~> 3.1.0)
104 | railties (~> 3.1.0)
105 | sass (>= 3.1.4)
106 | sprockets (2.0.0)
107 | hike (~> 1.2)
108 | rack (~> 1.0)
109 | tilt (~> 1.1, != 1.3.0)
110 | sqlite3 (1.3.4)
111 | therubyracer-heroku (0.8.1.pre3)
112 | thor (0.14.6)
113 | tilt (1.3.3)
114 | treetop (1.4.10)
115 | polyglot
116 | polyglot (>= 0.3.1)
117 | turn (0.8.2)
118 | ansi (>= 1.2.2)
119 | tzinfo (0.3.29)
120 | uglifier (1.0.3)
121 | execjs (>= 0.3.0)
122 | multi_json (>= 1.0.2)
123 | warden (1.0.5)
124 | rack (>= 1.0)
125 |
126 | PLATFORMS
127 | ruby
128 |
129 | DEPENDENCIES
130 | coffee-rails (~> 3.1.0)
131 | devise
132 | haml-rails
133 | i18n_generators
134 | jquery-rails
135 | kaminari
136 | nifty-generators
137 | pg
138 | rails (= 3.1.0)
139 | sass-rails (~> 3.1.0)
140 | sqlite3
141 | therubyracer-heroku
142 | turn
143 | uglifier
144 |
--------------------------------------------------------------------------------
/public/assets/ICanHaz-b4b81e9871b46f7e192a5e1d3182010b.min.js:
--------------------------------------------------------------------------------
1 | (function(a){var b=function(){var a=function(){};return a.prototype={otag:"{{",ctag:"}}",pragmas:{},buffer:[],pragmas_implemented:{"IMPLICIT-ITERATOR":!0},context:{},render:function(a,b,c,d){d||(this.context=b,this.buffer=[]);if(!this.includes("",a)){if(d)return a;this.send(a);return}a=this.render_pragmas(a),a=this.render_section(a,b,c);if(d)return this.render_tags(a,b,c,d);this.render_tags(a,b,c,d)},send:function(a){a!=""&&this.buffer.push(a)},render_pragmas:function(a){if(!this.includes("%",a))return a;var b=this;return a.replace(RegExp(this.otag+"%([\\w-]+) ?([\\w]+=[\\w]+)?"+this.ctag),function(a,c,d){if(!b.pragmas_implemented[c])throw{message:"This implementation of mustache doesn't understand the '"+c+"' pragma"};return b.pragmas[c]={},d&&(a=d.split("="),b.pragmas[c][a[0]]=a[1]),""})},render_partial:function(a,b,c){a=this.trim(a);if(!c||c[a]===undefined)throw{message:"unknown_partial '"+a+"'"};return typeof b[a]!="object"?this.render(c[a],b,c,!0):this.render(c[a],b[a],c,!0)},render_section:function(a,b,c){if(!this.includes("#",a)&&!this.includes("^",a))return a;var d=this;return a.replace(RegExp(this.otag+"(\\^|\\#)\\s*(.+)\\s*"+this.ctag+"\n*([\\s\\S]+?)"+this.otag+"\\/\\s*\\2\\s*"+this.ctag+"\\s*","mg"),function(a,e,f,g){a=d.find(f,b);if(e=="^")return!a||d.is_array(a)&&a.length===0?d.render(g,b,c,!0):"";if(e=="#")return d.is_array(a)?d.map(a,function(a){return d.render(g,d.create_context(a),c,!0)}).join(""):d.is_object(a)?d.render(g,d.create_context(a),c,!0):typeof a=="function"?a.call(b,g,function(a){return d.render(a,b,c,!0)}):a?d.render(g,b,c,!0):""})},render_tags:function(a,b,c,d){var e=this,f=function(){return RegExp(e.otag+"(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?"+e.ctag+"+","g")},g=f(),h=function(a,d,h){switch(d){case"!":return"";case"=":return e.set_delimiters(h),g=f(),"";case">":return e.render_partial(h,b,c);case"{":return e.find(h,b);default:return e.escape(e.find(h,b))}};a=a.split("\n");for(var i=0;i\\]/g,function(a){switch(a){case"&":return"&";case"\\":return"\\\\";case'"':return'"';case"<":return"<";case">":return">";default:return a}})},create_context:function(a){if(this.is_object(a))return a;var b=".";this.pragmas["IMPLICIT-ITERATOR"]&&(b=this.pragmas["IMPLICIT-ITERATOR"].iterator);var c={};return c[b]=a,c},is_object:function(a){return a&&typeof a=="object"},is_array:function(a){return Object.prototype.toString.call(a)==="[object Array]"},trim:function(a){return a.replace(/^\s*|\s*$/g,"")},map:function(a,b){if(typeof a.map=="function")return a.map(b);for(var c=[],d=a.length,e=0;e|\\{|%)?([^\\/#\\^]+?)\\1?"+e.ctag+"+","g")},k=j(),h=function(o,m,l){switch(m){case "!":return"";case "=":e.set_delimiters(l);k=j();return"";case ">":return e.render_partial(l,b,c);case "{":return e.find(l,b);default:return e.escape(e.find(l,b))}};a=a.split("\n");for(var g=0;g\\]/g,function(b){switch(b){case "&":return"&";case "\\":return"\\\\";case '"':return'"';case "<":return"<";case ">":return">";default:return b}})},create_context:function(a){if(this.is_object(a))return a;else{var b=".";if(this.pragmas["IMPLICIT-ITERATOR"])b=this.pragmas["IMPLICIT-ITERATOR"].iterator;var c={};c[b]=a;return c}},
7 | is_object:function(a){return a&&typeof a=="object"},is_array:function(a){return Object.prototype.toString.call(a)==="[object Array]"},trim:function(a){return a.replace(/^\s*|\s*$/g,"")},map:function(a,b){if(typeof a.map=="function")return a.map(b);else{for(var c=[],d=a.length,e=0;e Mailer Configuration
5 | # Configure the e-mail address which will be shown in Devise::Mailer,
6 | # note that it will be overwritten if you use your own mailer class with default "from" parameter.
7 | config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com"
8 |
9 | # Configure the class responsible to send e-mails.
10 | # config.mailer = "Devise::Mailer"
11 |
12 | # ==> ORM configuration
13 | # Load and configure the ORM. Supports :active_record (default) and
14 | # :mongoid (bson_ext recommended) by default. Other ORMs may be
15 | # available as additional gems.
16 | require 'devise/orm/active_record'
17 |
18 | # ==> Configuration for any authentication mechanism
19 | # Configure which keys are used when authenticating a user. The default is
20 | # just :email. You can configure it to use [:username, :subdomain], so for
21 | # authenticating a user, both parameters are required. Remember that those
22 | # parameters are used only when authenticating and not when retrieving from
23 | # session. If you need permissions, you should implement that in a before filter.
24 | # You can also supply a hash where the value is a boolean determining whether
25 | # or not authentication should be aborted when the value is not present.
26 | # config.authentication_keys = [ :email ]
27 |
28 | # Configure parameters from the request object used for authentication. Each entry
29 | # given should be a request method and it will automatically be passed to the
30 | # find_for_authentication method and considered in your model lookup. For instance,
31 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
32 | # The same considerations mentioned for authentication_keys also apply to request_keys.
33 | # config.request_keys = []
34 |
35 | # Configure which authentication keys should be case-insensitive.
36 | # These keys will be downcased upon creating or modifying a user and when used
37 | # to authenticate or find a user. Default is :email.
38 | config.case_insensitive_keys = [ :email ]
39 |
40 | # Configure which authentication keys should have whitespace stripped.
41 | # These keys will have whitespace before and after removed upon creating or
42 | # modifying a user and when used to authenticate or find a user. Default is :email.
43 | config.strip_whitespace_keys = [ :email ]
44 |
45 | # Tell if authentication through request.params is enabled. True by default.
46 | # config.params_authenticatable = true
47 |
48 | # Tell if authentication through HTTP Basic Auth is enabled. False by default.
49 | # config.http_authenticatable = false
50 |
51 | # If http headers should be returned for AJAX requests. True by default.
52 | # config.http_authenticatable_on_xhr = true
53 |
54 | # The realm used in Http Basic Authentication. "Application" by default.
55 | # config.http_authentication_realm = "Application"
56 |
57 | # It will change confirmation, password recovery and other workflows
58 | # to behave the same regardless if the e-mail provided was right or wrong.
59 | # Does not affect registerable.
60 | # config.paranoid = true
61 |
62 | # ==> Configuration for :database_authenticatable
63 | # For bcrypt, this is the cost for hashing the password and defaults to 10. If
64 | # using other encryptors, it sets how many times you want the password re-encrypted.
65 | #
66 | # Limiting the stretches to just one in testing will increase the performance of
67 | # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
68 | # a value less than 10 in other environments.
69 | config.stretches = Rails.env.test? ? 1 : 10
70 |
71 | # Setup a pepper to generate the encrypted password.
72 | # config.pepper = "358597d57a823f59f16fae766ab11b37478dd71364604a8734fd27cd74f35cb5c3fb044b22c87a5e57f03c80068c42c47d21a68f75355802f53ec45fb643514a"
73 |
74 | # ==> Configuration for :confirmable
75 | # The time you want to give your user to confirm his account. During this time
76 | # he will be able to access your application without confirming. Default is 0.days
77 | # When confirm_within is zero, the user won't be able to sign in without confirming.
78 | # You can use this to let your user access some features of your application
79 | # without confirming the account, but blocking it after a certain period
80 | # (ie 2 days).
81 | # config.confirm_within = 2.days
82 |
83 | # Defines which key will be used when confirming an account
84 | # config.confirmation_keys = [ :email ]
85 |
86 | # ==> Configuration for :rememberable
87 | # The time the user will be remembered without asking for credentials again.
88 | # config.remember_for = 2.weeks
89 |
90 | # If true, a valid remember token can be re-used between multiple browsers.
91 | # config.remember_across_browsers = true
92 |
93 | # If true, extends the user's remember period when remembered via cookie.
94 | # config.extend_remember_period = false
95 |
96 | # If true, uses the password salt as remember token. This should be turned
97 | # to false if you are not using database authenticatable.
98 | config.use_salt_as_remember_token = true
99 |
100 | # Options to be passed to the created cookie. For instance, you can set
101 | # :secure => true in order to force SSL only cookies.
102 | # config.cookie_options = {}
103 |
104 | # ==> Configuration for :validatable
105 | # Range for password length. Default is 6..128.
106 | # config.password_length = 6..128
107 |
108 | # Email regex used to validate email formats. It simply asserts that
109 | # an one (and only one) @ exists in the given string. This is mainly
110 | # to give user feedback and not to assert the e-mail validity.
111 | # config.email_regexp = /\A[^@]+@[^@]+\z/
112 |
113 | # ==> Configuration for :timeoutable
114 | # The time you want to timeout the user session without activity. After this
115 | # time the user will be asked for credentials again. Default is 30 minutes.
116 | # config.timeout_in = 30.minutes
117 |
118 | # ==> Configuration for :lockable
119 | # Defines which strategy will be used to lock an account.
120 | # :failed_attempts = Locks an account after a number of failed attempts to sign in.
121 | # :none = No lock strategy. You should handle locking by yourself.
122 | # config.lock_strategy = :failed_attempts
123 |
124 | # Defines which key will be used when locking and unlocking an account
125 | # config.unlock_keys = [ :email ]
126 |
127 | # Defines which strategy will be used to unlock an account.
128 | # :email = Sends an unlock link to the user email
129 | # :time = Re-enables login after a certain amount of time (see :unlock_in below)
130 | # :both = Enables both strategies
131 | # :none = No unlock strategy. You should handle unlocking by yourself.
132 | # config.unlock_strategy = :both
133 |
134 | # Number of authentication tries before locking an account if lock_strategy
135 | # is failed attempts.
136 | # config.maximum_attempts = 20
137 |
138 | # Time interval to unlock the account if :time is enabled as unlock_strategy.
139 | # config.unlock_in = 1.hour
140 |
141 | # ==> Configuration for :recoverable
142 | #
143 | # Defines which key will be used when recovering the password for an account
144 | # config.reset_password_keys = [ :email ]
145 |
146 | # Time interval you can reset your password with a reset password key.
147 | # Don't put a too small interval or your users won't have the time to
148 | # change their passwords.
149 | config.reset_password_within = 2.hours
150 |
151 | # ==> Configuration for :encryptable
152 | # Allow you to use another encryption algorithm besides bcrypt (default). You can use
153 | # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1,
154 | # :authlogic_sha512 (then you should set stretches above to 20 for default behavior)
155 | # and :restful_authentication_sha1 (then you should set stretches to 10, and copy
156 | # REST_AUTH_SITE_KEY to pepper)
157 | # config.encryptor = :sha512
158 |
159 | # ==> Configuration for :token_authenticatable
160 | # Defines name of the authentication token params key
161 | # config.token_authentication_key = :auth_token
162 |
163 | # If true, authentication through token does not store user in session and needs
164 | # to be supplied on each request. Useful if you are using the token as API token.
165 | # config.stateless_token = false
166 |
167 | # ==> Scopes configuration
168 | # Turn scoped views on. Before rendering "sessions/new", it will first check for
169 | # "users/sessions/new". It's turned off by default because it's slower if you
170 | # are using only default views.
171 | # config.scoped_views = false
172 |
173 | # Configure the default scope given to Warden. By default it's the first
174 | # devise role declared in your routes (usually :user).
175 | # config.default_scope = :user
176 |
177 | # Configure sign_out behavior.
178 | # Sign_out action can be scoped (i.e. /users/sign_out affects only :user scope).
179 | # The default is true, which means any logout action will sign out all active scopes.
180 | # config.sign_out_all_scopes = true
181 |
182 | # ==> Navigation configuration
183 | # Lists the formats that should be treated as navigational. Formats like
184 | # :html, should redirect to the sign in page when the user does not have
185 | # access, but formats like :xml or :json, should return 401.
186 | #
187 | # If you have any extra navigational formats, like :iphone or :mobile, you
188 | # should add them to the navigational formats lists.
189 | #
190 | # The :"*/*" and "*/*" formats below is required to match Internet
191 | # Explorer requests.
192 | # config.navigational_formats = [:"*/*", "*/*", :html]
193 |
194 | # The default HTTP method used to sign out a resource. Default is :delete.
195 | config.sign_out_via = :delete
196 |
197 | # ==> OmniAuth
198 | # Add a new OmniAuth provider. Check the wiki for more information on setting
199 | # up on your models and hooks.
200 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'
201 |
202 | # ==> Warden configuration
203 | # If you want to use other strategies, that are not supported by Devise, or
204 | # change the failure app, you can configure them inside the config.warden block.
205 | #
206 | # config.warden do |manager|
207 | # manager.failure_app = AnotherApp
208 | # manager.intercept_401 = false
209 | # manager.default_strategies(:scope => :user).unshift :some_external_strategy
210 | # end
211 | end
212 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/underscore.js:
--------------------------------------------------------------------------------
1 | // Underscore.js 1.1.7
2 | // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
3 | // Underscore is freely distributable under the MIT license.
4 | // Portions of Underscore are inspired or borrowed from Prototype,
5 | // Oliver Steele's Functional, and John Resig's Micro-Templating.
6 | // For all details and documentation:
7 | // http://documentcloud.github.com/underscore
8 |
9 | (function() {
10 |
11 | // Baseline setup
12 | // --------------
13 |
14 | // Establish the root object, `window` in the browser, or `global` on the server.
15 | var root = this;
16 |
17 | // Save the previous value of the `_` variable.
18 | var previousUnderscore = root._;
19 |
20 | // Establish the object that gets returned to break out of a loop iteration.
21 | var breaker = {};
22 |
23 | // Save bytes in the minified (but not gzipped) version:
24 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
25 |
26 | // Create quick reference variables for speed access to core prototypes.
27 | var slice = ArrayProto.slice,
28 | unshift = ArrayProto.unshift,
29 | toString = ObjProto.toString,
30 | hasOwnProperty = ObjProto.hasOwnProperty;
31 |
32 | // All **ECMAScript 5** native function implementations that we hope to use
33 | // are declared here.
34 | var
35 | nativeForEach = ArrayProto.forEach,
36 | nativeMap = ArrayProto.map,
37 | nativeReduce = ArrayProto.reduce,
38 | nativeReduceRight = ArrayProto.reduceRight,
39 | nativeFilter = ArrayProto.filter,
40 | nativeEvery = ArrayProto.every,
41 | nativeSome = ArrayProto.some,
42 | nativeIndexOf = ArrayProto.indexOf,
43 | nativeLastIndexOf = ArrayProto.lastIndexOf,
44 | nativeIsArray = Array.isArray,
45 | nativeKeys = Object.keys,
46 | nativeBind = FuncProto.bind;
47 |
48 | // Create a safe reference to the Underscore object for use below.
49 | var _ = function(obj) { return new wrapper(obj); };
50 |
51 | // Export the Underscore object for **CommonJS**, with backwards-compatibility
52 | // for the old `require()` API. If we're not in CommonJS, add `_` to the
53 | // global object.
54 | if (typeof module !== 'undefined' && module.exports) {
55 | module.exports = _;
56 | _._ = _;
57 | } else {
58 | // Exported as a string, for Closure Compiler "advanced" mode.
59 | root['_'] = _;
60 | }
61 |
62 | // Current version.
63 | _.VERSION = '1.1.7';
64 |
65 | // Collection Functions
66 | // --------------------
67 |
68 | // The cornerstone, an `each` implementation, aka `forEach`.
69 | // Handles objects with the built-in `forEach`, arrays, and raw objects.
70 | // Delegates to **ECMAScript 5**'s native `forEach` if available.
71 | var each = _.each = _.forEach = function(obj, iterator, context) {
72 | if (obj == null) return;
73 | if (nativeForEach && obj.forEach === nativeForEach) {
74 | obj.forEach(iterator, context);
75 | } else if (obj.length === +obj.length) {
76 | for (var i = 0, l = obj.length; i < l; i++) {
77 | if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
78 | }
79 | } else {
80 | for (var key in obj) {
81 | if (hasOwnProperty.call(obj, key)) {
82 | if (iterator.call(context, obj[key], key, obj) === breaker) return;
83 | }
84 | }
85 | }
86 | };
87 |
88 | // Return the results of applying the iterator to each element.
89 | // Delegates to **ECMAScript 5**'s native `map` if available.
90 | _.map = function(obj, iterator, context) {
91 | var results = [];
92 | if (obj == null) return results;
93 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
94 | each(obj, function(value, index, list) {
95 | results[results.length] = iterator.call(context, value, index, list);
96 | });
97 | return results;
98 | };
99 |
100 | // **Reduce** builds up a single result from a list of values, aka `inject`,
101 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
102 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
103 | var initial = memo !== void 0;
104 | if (obj == null) obj = [];
105 | if (nativeReduce && obj.reduce === nativeReduce) {
106 | if (context) iterator = _.bind(iterator, context);
107 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
108 | }
109 | each(obj, function(value, index, list) {
110 | if (!initial) {
111 | memo = value;
112 | initial = true;
113 | } else {
114 | memo = iterator.call(context, memo, value, index, list);
115 | }
116 | });
117 | if (!initial) throw new TypeError("Reduce of empty array with no initial value");
118 | return memo;
119 | };
120 |
121 | // The right-associative version of reduce, also known as `foldr`.
122 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
123 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
124 | if (obj == null) obj = [];
125 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
126 | if (context) iterator = _.bind(iterator, context);
127 | return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
128 | }
129 | var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
130 | return _.reduce(reversed, iterator, memo, context);
131 | };
132 |
133 | // Return the first value which passes a truth test. Aliased as `detect`.
134 | _.find = _.detect = function(obj, iterator, context) {
135 | var result;
136 | any(obj, function(value, index, list) {
137 | if (iterator.call(context, value, index, list)) {
138 | result = value;
139 | return true;
140 | }
141 | });
142 | return result;
143 | };
144 |
145 | // Return all the elements that pass a truth test.
146 | // Delegates to **ECMAScript 5**'s native `filter` if available.
147 | // Aliased as `select`.
148 | _.filter = _.select = function(obj, iterator, context) {
149 | var results = [];
150 | if (obj == null) return results;
151 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
152 | each(obj, function(value, index, list) {
153 | if (iterator.call(context, value, index, list)) results[results.length] = value;
154 | });
155 | return results;
156 | };
157 |
158 | // Return all the elements for which a truth test fails.
159 | _.reject = function(obj, iterator, context) {
160 | var results = [];
161 | if (obj == null) return results;
162 | each(obj, function(value, index, list) {
163 | if (!iterator.call(context, value, index, list)) results[results.length] = value;
164 | });
165 | return results;
166 | };
167 |
168 | // Determine whether all of the elements match a truth test.
169 | // Delegates to **ECMAScript 5**'s native `every` if available.
170 | // Aliased as `all`.
171 | _.every = _.all = function(obj, iterator, context) {
172 | var result = true;
173 | if (obj == null) return result;
174 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
175 | each(obj, function(value, index, list) {
176 | if (!(result = result && iterator.call(context, value, index, list))) return breaker;
177 | });
178 | return result;
179 | };
180 |
181 | // Determine if at least one element in the object matches a truth test.
182 | // Delegates to **ECMAScript 5**'s native `some` if available.
183 | // Aliased as `any`.
184 | var any = _.some = _.any = function(obj, iterator, context) {
185 | iterator = iterator || _.identity;
186 | var result = false;
187 | if (obj == null) return result;
188 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
189 | each(obj, function(value, index, list) {
190 | if (result |= iterator.call(context, value, index, list)) return breaker;
191 | });
192 | return !!result;
193 | };
194 |
195 | // Determine if a given value is included in the array or object using `===`.
196 | // Aliased as `contains`.
197 | _.include = _.contains = function(obj, target) {
198 | var found = false;
199 | if (obj == null) return found;
200 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
201 | any(obj, function(value) {
202 | if (found = value === target) return true;
203 | });
204 | return found;
205 | };
206 |
207 | // Invoke a method (with arguments) on every item in a collection.
208 | _.invoke = function(obj, method) {
209 | var args = slice.call(arguments, 2);
210 | return _.map(obj, function(value) {
211 | return (method.call ? method || value : value[method]).apply(value, args);
212 | });
213 | };
214 |
215 | // Convenience version of a common use case of `map`: fetching a property.
216 | _.pluck = function(obj, key) {
217 | return _.map(obj, function(value){ return value[key]; });
218 | };
219 |
220 | // Return the maximum element or (element-based computation).
221 | _.max = function(obj, iterator, context) {
222 | if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
223 | var result = {computed : -Infinity};
224 | each(obj, function(value, index, list) {
225 | var computed = iterator ? iterator.call(context, value, index, list) : value;
226 | computed >= result.computed && (result = {value : value, computed : computed});
227 | });
228 | return result.value;
229 | };
230 |
231 | // Return the minimum element (or element-based computation).
232 | _.min = function(obj, iterator, context) {
233 | if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
234 | var result = {computed : Infinity};
235 | each(obj, function(value, index, list) {
236 | var computed = iterator ? iterator.call(context, value, index, list) : value;
237 | computed < result.computed && (result = {value : value, computed : computed});
238 | });
239 | return result.value;
240 | };
241 |
242 | // Sort the object's values by a criterion produced by an iterator.
243 | _.sortBy = function(obj, iterator, context) {
244 | return _.pluck(_.map(obj, function(value, index, list) {
245 | return {
246 | value : value,
247 | criteria : iterator.call(context, value, index, list)
248 | };
249 | }).sort(function(left, right) {
250 | var a = left.criteria, b = right.criteria;
251 | return a < b ? -1 : a > b ? 1 : 0;
252 | }), 'value');
253 | };
254 |
255 | // Groups the object's values by a criterion produced by an iterator
256 | _.groupBy = function(obj, iterator) {
257 | var result = {};
258 | each(obj, function(value, index) {
259 | var key = iterator(value, index);
260 | (result[key] || (result[key] = [])).push(value);
261 | });
262 | return result;
263 | };
264 |
265 | // Use a comparator function to figure out at what index an object should
266 | // be inserted so as to maintain order. Uses binary search.
267 | _.sortedIndex = function(array, obj, iterator) {
268 | iterator || (iterator = _.identity);
269 | var low = 0, high = array.length;
270 | while (low < high) {
271 | var mid = (low + high) >> 1;
272 | iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
273 | }
274 | return low;
275 | };
276 |
277 | // Safely convert anything iterable into a real, live array.
278 | _.toArray = function(iterable) {
279 | if (!iterable) return [];
280 | if (iterable.toArray) return iterable.toArray();
281 | if (_.isArray(iterable)) return slice.call(iterable);
282 | if (_.isArguments(iterable)) return slice.call(iterable);
283 | return _.values(iterable);
284 | };
285 |
286 | // Return the number of elements in an object.
287 | _.size = function(obj) {
288 | return _.toArray(obj).length;
289 | };
290 |
291 | // Array Functions
292 | // ---------------
293 |
294 | // Get the first element of an array. Passing **n** will return the first N
295 | // values in the array. Aliased as `head`. The **guard** check allows it to work
296 | // with `_.map`.
297 | _.first = _.head = function(array, n, guard) {
298 | return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
299 | };
300 |
301 | // Returns everything but the first entry of the array. Aliased as `tail`.
302 | // Especially useful on the arguments object. Passing an **index** will return
303 | // the rest of the values in the array from that index onward. The **guard**
304 | // check allows it to work with `_.map`.
305 | _.rest = _.tail = function(array, index, guard) {
306 | return slice.call(array, (index == null) || guard ? 1 : index);
307 | };
308 |
309 | // Get the last element of an array.
310 | _.last = function(array) {
311 | return array[array.length - 1];
312 | };
313 |
314 | // Trim out all falsy values from an array.
315 | _.compact = function(array) {
316 | return _.filter(array, function(value){ return !!value; });
317 | };
318 |
319 | // Return a completely flattened version of an array.
320 | _.flatten = function(array) {
321 | return _.reduce(array, function(memo, value) {
322 | if (_.isArray(value)) return memo.concat(_.flatten(value));
323 | memo[memo.length] = value;
324 | return memo;
325 | }, []);
326 | };
327 |
328 | // Return a version of the array that does not contain the specified value(s).
329 | _.without = function(array) {
330 | return _.difference(array, slice.call(arguments, 1));
331 | };
332 |
333 | // Produce a duplicate-free version of the array. If the array has already
334 | // been sorted, you have the option of using a faster algorithm.
335 | // Aliased as `unique`.
336 | _.uniq = _.unique = function(array, isSorted) {
337 | return _.reduce(array, function(memo, el, i) {
338 | if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el;
339 | return memo;
340 | }, []);
341 | };
342 |
343 | // Produce an array that contains the union: each distinct element from all of
344 | // the passed-in arrays.
345 | _.union = function() {
346 | return _.uniq(_.flatten(arguments));
347 | };
348 |
349 | // Produce an array that contains every item shared between all the
350 | // passed-in arrays. (Aliased as "intersect" for back-compat.)
351 | _.intersection = _.intersect = function(array) {
352 | var rest = slice.call(arguments, 1);
353 | return _.filter(_.uniq(array), function(item) {
354 | return _.every(rest, function(other) {
355 | return _.indexOf(other, item) >= 0;
356 | });
357 | });
358 | };
359 |
360 | // Take the difference between one array and another.
361 | // Only the elements present in just the first array will remain.
362 | _.difference = function(array, other) {
363 | return _.filter(array, function(value){ return !_.include(other, value); });
364 | };
365 |
366 | // Zip together multiple lists into a single array -- elements that share
367 | // an index go together.
368 | _.zip = function() {
369 | var args = slice.call(arguments);
370 | var length = _.max(_.pluck(args, 'length'));
371 | var results = new Array(length);
372 | for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
373 | return results;
374 | };
375 |
376 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
377 | // we need this function. Return the position of the first occurrence of an
378 | // item in an array, or -1 if the item is not included in the array.
379 | // Delegates to **ECMAScript 5**'s native `indexOf` if available.
380 | // If the array is large and already in sort order, pass `true`
381 | // for **isSorted** to use binary search.
382 | _.indexOf = function(array, item, isSorted) {
383 | if (array == null) return -1;
384 | var i, l;
385 | if (isSorted) {
386 | i = _.sortedIndex(array, item);
387 | return array[i] === item ? i : -1;
388 | }
389 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
390 | for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
391 | return -1;
392 | };
393 |
394 |
395 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
396 | _.lastIndexOf = function(array, item) {
397 | if (array == null) return -1;
398 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
399 | var i = array.length;
400 | while (i--) if (array[i] === item) return i;
401 | return -1;
402 | };
403 |
404 | // Generate an integer Array containing an arithmetic progression. A port of
405 | // the native Python `range()` function. See
406 | // [the Python documentation](http://docs.python.org/library/functions.html#range).
407 | _.range = function(start, stop, step) {
408 | if (arguments.length <= 1) {
409 | stop = start || 0;
410 | start = 0;
411 | }
412 | step = arguments[2] || 1;
413 |
414 | var len = Math.max(Math.ceil((stop - start) / step), 0);
415 | var idx = 0;
416 | var range = new Array(len);
417 |
418 | while(idx < len) {
419 | range[idx++] = start;
420 | start += step;
421 | }
422 |
423 | return range;
424 | };
425 |
426 | // Function (ahem) Functions
427 | // ------------------
428 |
429 | // Create a function bound to a given object (assigning `this`, and arguments,
430 | // optionally). Binding with arguments is also known as `curry`.
431 | // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
432 | // We check for `func.bind` first, to fail fast when `func` is undefined.
433 | _.bind = function(func, obj) {
434 | if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
435 | var args = slice.call(arguments, 2);
436 | return function() {
437 | return func.apply(obj, args.concat(slice.call(arguments)));
438 | };
439 | };
440 |
441 | // Bind all of an object's methods to that object. Useful for ensuring that
442 | // all callbacks defined on an object belong to it.
443 | _.bindAll = function(obj) {
444 | var funcs = slice.call(arguments, 1);
445 | if (funcs.length == 0) funcs = _.functions(obj);
446 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
447 | return obj;
448 | };
449 |
450 | // Memoize an expensive function by storing its results.
451 | _.memoize = function(func, hasher) {
452 | var memo = {};
453 | hasher || (hasher = _.identity);
454 | return function() {
455 | var key = hasher.apply(this, arguments);
456 | return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
457 | };
458 | };
459 |
460 | // Delays a function for the given number of milliseconds, and then calls
461 | // it with the arguments supplied.
462 | _.delay = function(func, wait) {
463 | var args = slice.call(arguments, 2);
464 | return setTimeout(function(){ return func.apply(func, args); }, wait);
465 | };
466 |
467 | // Defers a function, scheduling it to run after the current call stack has
468 | // cleared.
469 | _.defer = function(func) {
470 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
471 | };
472 |
473 | // Internal function used to implement `_.throttle` and `_.debounce`.
474 | var limit = function(func, wait, debounce) {
475 | var timeout;
476 | return function() {
477 | var context = this, args = arguments;
478 | var throttler = function() {
479 | timeout = null;
480 | func.apply(context, args);
481 | };
482 | if (debounce) clearTimeout(timeout);
483 | if (debounce || !timeout) timeout = setTimeout(throttler, wait);
484 | };
485 | };
486 |
487 | // Returns a function, that, when invoked, will only be triggered at most once
488 | // during a given window of time.
489 | _.throttle = function(func, wait) {
490 | return limit(func, wait, false);
491 | };
492 |
493 | // Returns a function, that, as long as it continues to be invoked, will not
494 | // be triggered. The function will be called after it stops being called for
495 | // N milliseconds.
496 | _.debounce = function(func, wait) {
497 | return limit(func, wait, true);
498 | };
499 |
500 | // Returns a function that will be executed at most one time, no matter how
501 | // often you call it. Useful for lazy initialization.
502 | _.once = function(func) {
503 | var ran = false, memo;
504 | return function() {
505 | if (ran) return memo;
506 | ran = true;
507 | return memo = func.apply(this, arguments);
508 | };
509 | };
510 |
511 | // Returns the first function passed as an argument to the second,
512 | // allowing you to adjust arguments, run code before and after, and
513 | // conditionally execute the original function.
514 | _.wrap = function(func, wrapper) {
515 | return function() {
516 | var args = [func].concat(slice.call(arguments));
517 | return wrapper.apply(this, args);
518 | };
519 | };
520 |
521 | // Returns a function that is the composition of a list of functions, each
522 | // consuming the return value of the function that follows.
523 | _.compose = function() {
524 | var funcs = slice.call(arguments);
525 | return function() {
526 | var args = slice.call(arguments);
527 | for (var i = funcs.length - 1; i >= 0; i--) {
528 | args = [funcs[i].apply(this, args)];
529 | }
530 | return args[0];
531 | };
532 | };
533 |
534 | // Returns a function that will only be executed after being called N times.
535 | _.after = function(times, func) {
536 | return function() {
537 | if (--times < 1) { return func.apply(this, arguments); }
538 | };
539 | };
540 |
541 |
542 | // Object Functions
543 | // ----------------
544 |
545 | // Retrieve the names of an object's properties.
546 | // Delegates to **ECMAScript 5**'s native `Object.keys`
547 | _.keys = nativeKeys || function(obj) {
548 | if (obj !== Object(obj)) throw new TypeError('Invalid object');
549 | var keys = [];
550 | for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
551 | return keys;
552 | };
553 |
554 | // Retrieve the values of an object's properties.
555 | _.values = function(obj) {
556 | return _.map(obj, _.identity);
557 | };
558 |
559 | // Return a sorted list of the function names available on the object.
560 | // Aliased as `methods`
561 | _.functions = _.methods = function(obj) {
562 | var names = [];
563 | for (var key in obj) {
564 | if (_.isFunction(obj[key])) names.push(key);
565 | }
566 | return names.sort();
567 | };
568 |
569 | // Extend a given object with all the properties in passed-in object(s).
570 | _.extend = function(obj) {
571 | each(slice.call(arguments, 1), function(source) {
572 | for (var prop in source) {
573 | if (source[prop] !== void 0) obj[prop] = source[prop];
574 | }
575 | });
576 | return obj;
577 | };
578 |
579 | // Fill in a given object with default properties.
580 | _.defaults = function(obj) {
581 | each(slice.call(arguments, 1), function(source) {
582 | for (var prop in source) {
583 | if (obj[prop] == null) obj[prop] = source[prop];
584 | }
585 | });
586 | return obj;
587 | };
588 |
589 | // Create a (shallow-cloned) duplicate of an object.
590 | _.clone = function(obj) {
591 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
592 | };
593 |
594 | // Invokes interceptor with the obj, and then returns obj.
595 | // The primary purpose of this method is to "tap into" a method chain, in
596 | // order to perform operations on intermediate results within the chain.
597 | _.tap = function(obj, interceptor) {
598 | interceptor(obj);
599 | return obj;
600 | };
601 |
602 | // Perform a deep comparison to check if two objects are equal.
603 | _.isEqual = function(a, b) {
604 | // Check object identity.
605 | if (a === b) return true;
606 | // Different types?
607 | var atype = typeof(a), btype = typeof(b);
608 | if (atype != btype) return false;
609 | // Basic equality test (watch out for coercions).
610 | if (a == b) return true;
611 | // One is falsy and the other truthy.
612 | if ((!a && b) || (a && !b)) return false;
613 | // Unwrap any wrapped objects.
614 | if (a._chain) a = a._wrapped;
615 | if (b._chain) b = b._wrapped;
616 | // One of them implements an isEqual()?
617 | if (a.isEqual) return a.isEqual(b);
618 | if (b.isEqual) return b.isEqual(a);
619 | // Check dates' integer values.
620 | if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
621 | // Both are NaN?
622 | if (_.isNaN(a) && _.isNaN(b)) return false;
623 | // Compare regular expressions.
624 | if (_.isRegExp(a) && _.isRegExp(b))
625 | return a.source === b.source &&
626 | a.global === b.global &&
627 | a.ignoreCase === b.ignoreCase &&
628 | a.multiline === b.multiline;
629 | // If a is not an object by this point, we can't handle it.
630 | if (atype !== 'object') return false;
631 | // Check for different array lengths before comparing contents.
632 | if (a.length && (a.length !== b.length)) return false;
633 | // Nothing else worked, deep compare the contents.
634 | var aKeys = _.keys(a), bKeys = _.keys(b);
635 | // Different object sizes?
636 | if (aKeys.length != bKeys.length) return false;
637 | // Recursive comparison of contents.
638 | for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
639 | return true;
640 | };
641 |
642 | // Is a given array or object empty?
643 | _.isEmpty = function(obj) {
644 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
645 | for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
646 | return true;
647 | };
648 |
649 | // Is a given value a DOM element?
650 | _.isElement = function(obj) {
651 | return !!(obj && obj.nodeType == 1);
652 | };
653 |
654 | // Is a given value an array?
655 | // Delegates to ECMA5's native Array.isArray
656 | _.isArray = nativeIsArray || function(obj) {
657 | return toString.call(obj) === '[object Array]';
658 | };
659 |
660 | // Is a given variable an object?
661 | _.isObject = function(obj) {
662 | return obj === Object(obj);
663 | };
664 |
665 | // Is a given variable an arguments object?
666 | _.isArguments = function(obj) {
667 | return !!(obj && hasOwnProperty.call(obj, 'callee'));
668 | };
669 |
670 | // Is a given value a function?
671 | _.isFunction = function(obj) {
672 | return !!(obj && obj.constructor && obj.call && obj.apply);
673 | };
674 |
675 | // Is a given value a string?
676 | _.isString = function(obj) {
677 | return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
678 | };
679 |
680 | // Is a given value a number?
681 | _.isNumber = function(obj) {
682 | return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
683 | };
684 |
685 | // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
686 | // that does not equal itself.
687 | _.isNaN = function(obj) {
688 | return obj !== obj;
689 | };
690 |
691 | // Is a given value a boolean?
692 | _.isBoolean = function(obj) {
693 | return obj === true || obj === false;
694 | };
695 |
696 | // Is a given value a date?
697 | _.isDate = function(obj) {
698 | return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
699 | };
700 |
701 | // Is the given value a regular expression?
702 | _.isRegExp = function(obj) {
703 | return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
704 | };
705 |
706 | // Is a given value equal to null?
707 | _.isNull = function(obj) {
708 | return obj === null;
709 | };
710 |
711 | // Is a given variable undefined?
712 | _.isUndefined = function(obj) {
713 | return obj === void 0;
714 | };
715 |
716 | // Utility Functions
717 | // -----------------
718 |
719 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
720 | // previous owner. Returns a reference to the Underscore object.
721 | _.noConflict = function() {
722 | root._ = previousUnderscore;
723 | return this;
724 | };
725 |
726 | // Keep the identity function around for default iterators.
727 | _.identity = function(value) {
728 | return value;
729 | };
730 |
731 | // Run a function **n** times.
732 | _.times = function (n, iterator, context) {
733 | for (var i = 0; i < n; i++) iterator.call(context, i);
734 | };
735 |
736 | // Add your own custom functions to the Underscore object, ensuring that
737 | // they're correctly added to the OOP wrapper as well.
738 | _.mixin = function(obj) {
739 | each(_.functions(obj), function(name){
740 | addToWrapper(name, _[name] = obj[name]);
741 | });
742 | };
743 |
744 | // Generate a unique integer id (unique within the entire client session).
745 | // Useful for temporary DOM ids.
746 | var idCounter = 0;
747 | _.uniqueId = function(prefix) {
748 | var id = idCounter++;
749 | return prefix ? prefix + id : id;
750 | };
751 |
752 | // By default, Underscore uses ERB-style template delimiters, change the
753 | // following template settings to use alternative delimiters.
754 | _.templateSettings = {
755 | evaluate : /<%([\s\S]+?)%>/g,
756 | interpolate : /<%=([\s\S]+?)%>/g
757 | };
758 |
759 | // JavaScript micro-templating, similar to John Resig's implementation.
760 | // Underscore templating handles arbitrary delimiters, preserves whitespace,
761 | // and correctly escapes quotes within interpolated code.
762 | _.template = function(str, data) {
763 | var c = _.templateSettings;
764 | var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
765 | 'with(obj||{}){__p.push(\'' +
766 | str.replace(/\\/g, '\\\\')
767 | .replace(/'/g, "\\'")
768 | .replace(c.interpolate, function(match, code) {
769 | return "'," + code.replace(/\\'/g, "'") + ",'";
770 | })
771 | .replace(c.evaluate || null, function(match, code) {
772 | return "');" + code.replace(/\\'/g, "'")
773 | .replace(/[\r\n\t]/g, ' ') + "__p.push('";
774 | })
775 | .replace(/\r/g, '\\r')
776 | .replace(/\n/g, '\\n')
777 | .replace(/\t/g, '\\t')
778 | + "');}return __p.join('');";
779 | var func = new Function('obj', tmpl);
780 | return data ? func(data) : func;
781 | };
782 |
783 | // The OOP Wrapper
784 | // ---------------
785 |
786 | // If Underscore is called as a function, it returns a wrapped object that
787 | // can be used OO-style. This wrapper holds altered versions of all the
788 | // underscore functions. Wrapped objects may be chained.
789 | var wrapper = function(obj) { this._wrapped = obj; };
790 |
791 | // Expose `wrapper.prototype` as `_.prototype`
792 | _.prototype = wrapper.prototype;
793 |
794 | // Helper function to continue chaining intermediate results.
795 | var result = function(obj, chain) {
796 | return chain ? _(obj).chain() : obj;
797 | };
798 |
799 | // A method to easily add functions to the OOP wrapper.
800 | var addToWrapper = function(name, func) {
801 | wrapper.prototype[name] = function() {
802 | var args = slice.call(arguments);
803 | unshift.call(args, this._wrapped);
804 | return result(func.apply(_, args), this._chain);
805 | };
806 | };
807 |
808 | // Add all of the Underscore functions to the wrapper object.
809 | _.mixin(_);
810 |
811 | // Add all mutator Array functions to the wrapper.
812 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
813 | var method = ArrayProto[name];
814 | wrapper.prototype[name] = function() {
815 | method.apply(this._wrapped, arguments);
816 | return result(this._wrapped, this._chain);
817 | };
818 | });
819 |
820 | // Add all accessor Array functions to the wrapper.
821 | each(['concat', 'join', 'slice'], function(name) {
822 | var method = ArrayProto[name];
823 | wrapper.prototype[name] = function() {
824 | return result(method.apply(this._wrapped, arguments), this._chain);
825 | };
826 | });
827 |
828 | // Start chaining a wrapped Underscore object.
829 | wrapper.prototype.chain = function() {
830 | this._chain = true;
831 | return this;
832 | };
833 |
834 | // Extracts the result from a wrapped and chained object.
835 | wrapper.prototype.value = function() {
836 | return this._wrapped;
837 | };
838 |
839 | })();
840 |
--------------------------------------------------------------------------------
/public/assets/jquery-8a50feed8d29566738ad005e19fe1c2d.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery JavaScript Library v1.6.2
3 | * http://jquery.com/
4 | *
5 | * Copyright 2011, John Resig
6 | * Dual licensed under the MIT or GPL Version 2 licenses.
7 | * http://jquery.org/license
8 | *
9 | * Includes Sizzle.js
10 | * http://sizzlejs.com/
11 | * Copyright 2011, The Dojo Foundation
12 | * Released under the MIT, BSD, and GPL Licenses.
13 | *
14 | * Date: Thu Jun 30 14:16:56 2011 -0400
15 | */
16 | (function(a,b){function c(a){return J.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function d(a){if(!cl[a]){var b=G.body,c=J("<"+a+">").appendTo(b),d=c.css("display");c.remove();if(d==="none"||d===""){cm||(cm=G.createElement("iframe"),cm.frameBorder=cm.width=cm.height=0),b.appendChild(cm);if(!cn||!cm.createElement)cn=(cm.contentWindow||cm.contentDocument).document,cn.write((G.compatMode==="CSS1Compat"?"":"")+""),cn.close();c=cn.createElement(a),cn.body.appendChild(c),d=J.css(c,"display"),b.removeChild(cm)}cl[a]=d}return cl[a]}function e(a,b){var c={};return J.each(cr.concat.apply([],cr.slice(0,b)),function(){c[this]=a}),c}function f(){cs=b}function g(){return setTimeout(f,0),cs=J.now()}function h(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function i(){try{return new a.XMLHttpRequest}catch(b){}}function j(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},f,g,h=d.length,i,j=d[0],k,l,m,n,o;for(f=1;f0)return c!=="border"&&J.each(e,function(){c||(d-=parseFloat(J.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(J.css(a,c+this))||0:d-=parseFloat(J.css(a,"border"+this+"Width"))||0}),d+"px";d=bK(a,b,b);if(d<0||d==null)d=a.style[b]||0;return d=parseFloat(d)||0,c&&J.each(e,function(){d+=parseFloat(J.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(J.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(J.css(a,c+this))||0)}),d+"px"}function p(a,b){b.src?J.ajax({url:b.src,async:!1,dataType:"script"}):J.globalEval((b.text||b.textContent||b.innerHTML||"").replace(by,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function q(a){J.nodeName(a,"input")?r(a):"getElementsByTagName"in a&&J.grep(a.getElementsByTagName("input"),r)}function r(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function s(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function t(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(J.expando)}}function u(a,b){if(b.nodeType===1&&!!J.hasData(a)){var c=J.expando,d=J.data(a),e=J.data(b,d);if(d=d[c]){var f=d.events;e=e[c]=J.extend({},d);if(f){delete e.handle,e.events={};for(var g in f)for(var h=0,i=f[g].length;h=0===c})}function x(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function y(a,b){return(a&&a!=="*"?a+".":"")+b.replace($,"`").replace(_,"&")}function z(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o=[],p=[],q=J._data(this,"events");if(!(a.liveFired===this||!q||!q.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(m=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var r=q.live.slice(0);for(h=0;hc)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,n=e.handleObj.origHandler.apply(e.elem,arguments);if(n===!1||a.isPropagationStopped()){c=e.level,n===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function A(a,c,d){var e=J.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,J.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function B(){return!0}function C(){return!1}function D(a,c,d){var e=c+"defer",f=c+"queue",g=c+"mark",h=J.data(a,e,b,!0);h&&(d==="queue"||!J.data(a,f,b,!0))&&(d==="mark"||!J.data(a,g,b,!0))&&setTimeout(function(){!J.data(a,f,b,!0)&&!J.data(a,g,b,!0)&&(J.removeData(a,e,!0),h.resolve())},0)}function E(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function F(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(N,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:J.isNaN(d)?M.test(d)?J.parseJSON(d):d:parseFloat(d)}catch(f){}J.data(a,c,d)}else d=b}return d}var G=a.document,H=a.navigator,I=a.location,J=function(){function c(){if(!d.isReady){try{G.documentElement.doScroll("left")}catch(a){setTimeout(c,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z])/ig,w=function(a,b){return b.toUpperCase()},x=H.userAgent,y,z,A,B=Object.prototype.toString,C=Object.prototype.hasOwnProperty,D=Array.prototype.push,E=Array.prototype.slice,F=String.prototype.trim,I=Array.prototype.indexOf,J={};return d.fn=d.prototype={constructor:d,init:function(a,c,e){var f,g,i,j;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(a==="body"&&!c&&G.body)return this.context=G,this[0]=G.body,this.selector=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?f=h.exec(a):f=[null,a,null];if(f&&(f[1]||!c)){if(f[1])return c=c instanceof d?c[0]:c,j=c?c.ownerDocument||c:G,i=m.exec(a),i?d.isPlainObject(c)?(a=[G.createElement(i[1])],d.fn.attr.call(a,c,!0)):a=[j.createElement(i[1])]:(i=d.buildFragment([f[1]],[j]),a=(i.cacheable?d.clone(i.fragment):i.fragment).childNodes),d.merge(this,a);g=G.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return e.find(a);this.length=1,this[0]=g}return this.context=G,this.selector=a,this}return!c||c.jquery?(c||e).find(a):this.constructor(c).find(a)}return d.isFunction(a)?e.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),d.makeArray(a,this))},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return E.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();return d.isArray(a)?D.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")"),e},each:function(a,b){return d.each(this,a,b)},ready:function(a){return d.bindReady(),z.done(a),this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(E.apply(this,arguments),"slice",E.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:D,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;z.resolveWith(G,[d]),d.fn.trigger&&d(G).trigger("ready").unbind("ready")}},bindReady:function(){if(!z){z=d._Deferred();if(G.readyState==="complete")return setTimeout(d.ready,1);if(G.addEventListener)G.addEventListener("DOMContentLoaded",A,!1),a.addEventListener("load",d.ready,!1);else if(G.attachEvent){G.attachEvent("onreadystatechange",A),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}G.documentElement.doScroll&&b&&c()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):J[B.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!C.call(a,"constructor")&&!C.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||C.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=d.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){return a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b),c},noop:function(){},globalEval:function(b){b&&i.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(v,w)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||d.isArray(a));if(k)for(;i1?L.call(arguments,0):b,--f||g.resolveWith(g,L.call(c,0))}}var c=arguments,d=0,e=c.length,f=e,g=e<=1&&a&&J.isFunction(a.promise)?a:J.Deferred();if(e>1){for(;d
a",c=a.getElementsByTagName("*"),d=a.getElementsByTagName("a")[0];if(!c||!c.length||!d)return{};e=G.createElement("select"),f=e.appendChild(G.createElement("option")),g=a.getElementsByTagName("input")[0],i={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.55$/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:g.value==="on",optSelected:f.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},g.checked=!0,i.noCloneChecked=g.cloneNode(!0).checked,e.disabled=!0,i.optDisabled=!f.disabled;try{delete a.test}catch(t){i.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){i.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),g=G.createElement("input"),g.value="t",g.setAttribute("type","radio"),i.radioValue=g.value==="t",g.setAttribute("checked","checked"),a.appendChild(g),j=G.createDocumentFragment(),j.appendChild(a.firstChild),i.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",k=G.getElementsByTagName("body")[0],m=G.createElement(k?"div":"body"),n={visibility:"hidden",width:0,height:0,border:0,margin:0},k&&J.extend(n,{position:"absolute",left:-1e3,top:-1e3});for(r in n)m.style[r]=n[r];m.appendChild(a),l=k||b,l.insertBefore(m,l.firstChild),i.appendChecked=g.checked,i.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,i.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="",i.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="