Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

updated to padrino 0.11.4

  • Loading branch information...
commit 4038cefd2ab09e968f8102cfddd31e0ae00fa642 1 parent 8d0c33a
@williamn williamn authored
Showing with 5,075 additions and 435 deletions.
  1. +7 −3 .components
  2. +3 −1 .gitignore
  3. +27 −11 Gemfile
  4. +98 −0 Gemfile.lock
  5. +6 −0 Rakefile
  6. +42 −29 admin/app.rb
  7. +56 −13 admin/controllers/accounts.rb
  8. +2 −4 admin/controllers/base.rb
  9. +55 −14 admin/controllers/posts.rb
  10. +9 −5 admin/controllers/sessions.rb
  11. +42 −39 admin/views/accounts/_form.haml
  12. +7 −14 admin/views/accounts/edit.haml
  13. +53 −30 admin/views/accounts/index.haml
  14. +6 −13 admin/views/accounts/new.haml
  15. +11 −24 admin/views/base/index.haml
  16. +3 −0  admin/views/errors/403.haml
  17. +3 −0  admin/views/errors/404.haml
  18. +3 −0  admin/views/errors/500.haml
  19. +47 −29 admin/views/layouts/application.haml
  20. +18 −0 admin/views/layouts/error.haml
  21. +18 −15 admin/views/posts/_form.haml
  22. +7 −14 admin/views/posts/edit.haml
  23. +55 −28 admin/views/posts/index.haml
  24. +6 −13 admin/views/posts/new.haml
  25. +35 −23 admin/views/sessions/new.haml
  26. +14 −23 app/app.rb
  27. +1 −1  app/controllers/posts.rb
  28. +1 −1  app/helpers/posts_helper.rb
  29. +2 −2 app/views/posts/index.haml
  30. +9 −2 config.ru
  31. +35 −18 config/apps.rb
  32. +40 −13 config/boot.rb
  33. +21 −11 config/database.rb
  34. BIN  db/development.db
  35. +8 −6 db/schema.rb
  36. +28 −0 db/seeds.old
  37. +5 −5 db/seeds.rb
  38. +11 −0 lib/sass_initializer.rb
  39. +38 −0 models/account.rb
  40. BIN  public/admin/images/favicon.ico
  41. BIN  public/admin/images/font/FontAwesome.otf
  42. BIN  public/admin/images/font/fontawesome-webfont.eot
  43. +399 −0 public/admin/images/font/fontawesome-webfont.svg
  44. BIN  public/admin/images/font/fontawesome-webfont.ttf
  45. BIN  public/admin/images/font/fontawesome-webfont.woff
  46. BIN  public/admin/images/logo.png
  47. +117 −0 public/admin/javascripts/application.js
  48. +126 −0 public/admin/javascripts/bootstrap/affix.js
  49. +98 −0 public/admin/javascripts/bootstrap/alert.js
  50. +6 −0 public/admin/javascripts/bootstrap/bootstrap.min.js
  51. +109 −0 public/admin/javascripts/bootstrap/button.js
  52. +217 −0 public/admin/javascripts/bootstrap/carousel.js
  53. +179 −0 public/admin/javascripts/bootstrap/collapse.js
  54. +154 −0 public/admin/javascripts/bootstrap/dropdown.js
  55. +246 −0 public/admin/javascripts/bootstrap/modal.js
  56. +117 −0 public/admin/javascripts/bootstrap/popover.js
  57. +158 −0 public/admin/javascripts/bootstrap/scrollspy.js
  58. +135 −0 public/admin/javascripts/bootstrap/tab.js
  59. +386 −0 public/admin/javascripts/bootstrap/tooltip.js
  60. +56 −0 public/admin/javascripts/bootstrap/transition.js
  61. +4 −0 public/admin/javascripts/jquery-1.9.0.min.js
  62. +347 −0 public/admin/stylesheets/application.css
  63. +1,238 −0 public/admin/stylesheets/bootstrap.css
  64. BIN  public/favicon.ico
  65. +109 −0 public/javascripts/jquery-ujs.js
  66. +4 −19 public/javascripts/jquery.js
  67. +13 −0 test/app/controllers/posts_controller_test.rb
  68. +10 −3 test/test.rake
  69. +15 −9 test/test_config.rb
View
10 .components
@@ -1,6 +1,10 @@
----
-:test: shoulda
+---
:orm: activerecord
-:mock: mocha
+:test: shoulda
+:mock: none
:script: jquery
:renderer: haml
+:stylesheet: sass
+:namespace: SampleBlog
+:migration_format: number
+:admin_renderer: haml
View
4 .gitignore
@@ -4,4 +4,6 @@ tmp/**/*
bin/*
vendor/gems/*
!vendor/gems/cache/
-.sass-cache/*
+.sass-cache/*
+db/*.db
+.*.sw*
View
38 Gemfile
@@ -1,20 +1,36 @@
-source :rubygems
+source 'https://rubygems.org'
+
+# Distribute your app as a gem
+# gemspec
+
+# Server requirements
+# gem 'thin' # or mongrel
+# gem 'trinidad', :platform => 'jruby'
+
+# Optional JSON codec (faster performance)
+# gem 'oj'
# Project requirements
-gem 'bundler', '>= 0.9.26'
-gem 'sinatra-flash', :require => 'sinatra/flash'
-gem 'thin' # or mongrel
+gem 'rake'
# Component requirements
-gem 'haml'
+gem 'bcrypt-ruby', :require => 'bcrypt'
gem 'sass'
-gem 'activerecord', :require => "active_record"
-gem 'sqlite3-ruby', :require => "sqlite3"
+gem 'haml'
+gem 'activerecord', '>= 3.1', :require => 'active_record'
+gem 'sqlite3'
# Test requirements
-gem 'mocha', :group => "test"
-gem 'shoulda', :group => "test"
+gem 'shoulda', :group => 'test'
gem 'rack-test', :require => 'rack/test', :group => 'test'
-# Padrino
-gem 'padrino'
+# Padrino Stable Gem
+gem 'padrino', '0.11.4'
+
+# Or Padrino Edge
+# gem 'padrino', :github => 'padrino/padrino-framework'
+
+# Or Individual Gems
+# %w(core gen helpers cache mailer admin).each do |g|
+# gem 'padrino-' + g, '0.11.4'
+# end
View
98 Gemfile.lock
@@ -0,0 +1,98 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ activemodel (3.2.16)
+ activesupport (= 3.2.16)
+ builder (~> 3.0.0)
+ activerecord (3.2.16)
+ activemodel (= 3.2.16)
+ activesupport (= 3.2.16)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activesupport (3.2.16)
+ i18n (~> 0.6, >= 0.6.4)
+ multi_json (~> 1.0)
+ arel (3.0.3)
+ bcrypt-ruby (3.1.2)
+ builder (3.0.4)
+ haml (4.0.4)
+ tilt
+ http_router (0.11.0)
+ rack (>= 1.0.0)
+ url_mount (~> 0.2.1)
+ i18n (0.6.9)
+ mail (2.5.4)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.25.1)
+ multi_json (1.8.2)
+ padrino (0.11.4)
+ padrino-admin (= 0.11.4)
+ padrino-cache (= 0.11.4)
+ padrino-core (= 0.11.4)
+ padrino-gen (= 0.11.4)
+ padrino-helpers (= 0.11.4)
+ padrino-mailer (= 0.11.4)
+ padrino-admin (0.11.4)
+ padrino-core (= 0.11.4)
+ padrino-helpers (= 0.11.4)
+ padrino-cache (0.11.4)
+ padrino-core (= 0.11.4)
+ padrino-helpers (= 0.11.4)
+ padrino-core (0.11.4)
+ activesupport (>= 3.1, < 4.0)
+ http_router (~> 0.11.0)
+ rack-protection (>= 1.5.0)
+ sinatra (~> 1.4.2)
+ thor (~> 0.17.0)
+ tilt (~> 1.4.1)
+ padrino-gen (0.11.4)
+ bundler (~> 1.0)
+ padrino-core (= 0.11.4)
+ padrino-helpers (0.11.4)
+ i18n (~> 0.6)
+ padrino-core (= 0.11.4)
+ padrino-mailer (0.11.4)
+ mail (~> 2.5.3)
+ padrino-core (= 0.11.4)
+ polyglot (0.3.3)
+ rack (1.5.2)
+ rack-protection (1.5.1)
+ rack
+ rack-test (0.6.2)
+ rack (>= 1.0)
+ rake (10.1.0)
+ sass (3.2.12)
+ shoulda (3.5.0)
+ shoulda-context (~> 1.0, >= 1.0.1)
+ shoulda-matchers (>= 1.4.1, < 3.0)
+ shoulda-context (1.1.6)
+ shoulda-matchers (2.4.0)
+ activesupport (>= 3.0.0)
+ sinatra (1.4.4)
+ rack (~> 1.4)
+ rack-protection (~> 1.4)
+ tilt (~> 1.3, >= 1.3.4)
+ sqlite3 (1.3.8)
+ thor (0.17.0)
+ tilt (1.4.1)
+ treetop (1.4.15)
+ polyglot
+ polyglot (>= 0.3.1)
+ tzinfo (0.3.38)
+ url_mount (0.2.1)
+ rack
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ activerecord (>= 3.1)
+ bcrypt-ruby
+ haml
+ padrino (= 0.11.4)
+ rack-test
+ rake
+ sass
+ shoulda
+ sqlite3
View
6 Rakefile
@@ -0,0 +1,6 @@
+require 'bundler/setup'
+require 'padrino-core/cli/rake'
+
+PadrinoTasks.use(:database)
+PadrinoTasks.use(:activerecord)
+PadrinoTasks.init
View
71 admin/app.rb
@@ -1,33 +1,46 @@
-class Admin < Padrino::Application
- register Padrino::Helpers
- register Padrino::Mailer
- register Padrino::Admin::AccessControl
- register Padrino::Rendering
-
- ##
- # Application-specific configuration options
- #
- # set :raise_errors, true # Show exceptions (default for development)
- # set :public, "foo/bar" # Location for static assets (default root/public)
- # set :sessions, false # Enabled by default
- # set :reload, false # Reload application files (default in development)
- # set :default_builder, "foo" # Set a custom form builder (default 'StandardFormBuilder')
- # set :locale_path, "bar" # Set path for I18n translations (default your_app/locales)
- # disable :flash # Disables rack-flash (enabled by default)
- # enable :authentication # Enable padrino-admin authentication (disabled by default)
- # layout :my_layout # Layout can be in views/layouts/foo.ext or views/foo.ext (default :application)
- #
- enable :authentication
- disable :store_location
- set :login_page, "/admin/sessions/new"
+module SampleBlog
+ class Admin < Padrino::Application
+ use ActiveRecord::ConnectionAdapters::ConnectionManagement
+ register Padrino::Rendering
+ register Padrino::Mailer
+ register Padrino::Helpers
+ register Padrino::Admin::AccessControl
- access_control.roles_for :any do |role|
- role.protect "/"
- role.allow "/sessions"
- end
+ ##
+ # Application configuration options
+ #
+ # set :raise_errors, true # Raise exceptions (will stop application) (default for test)
+ # set :dump_errors, true # Exception backtraces are written to STDERR (default for production/development)
+ # set :show_exceptions, true # Shows a stack trace in browser (default for development)
+ # set :logging, true # Logging in STDOUT for development and file for production (default only for development)
+ # set :public_folder, "foo/bar" # Location for static assets (default root/public)
+ # set :reload, false # Reload application files (default in development)
+ # set :default_builder, "foo" # Set a custom form builder (default 'StandardFormBuilder')
+ # set :locale_path, "bar" # Set path for I18n translations (default your_app/locales)
+ # disable :sessions # Disabled sessions by default (enable if needed)
+ # disable :flash # Disables sinatra-flash (enabled by default if Sinatra::Flash is defined)
+ # layout :my_layout # Layout can be in views/layouts/foo.ext or views/foo.ext (default :application)
+ #
+
+ set :admin_model, 'Account'
+ set :login_page, '/admin/sessions/new'
+
+ enable :sessions
+ disable :store_location
+
+ access_control.roles_for :any do |role|
+ role.protect '/'
+ role.allow '/sessions'
+ end
+
+ access_control.roles_for :admin do |role|
+ role.project_module :posts, '/posts'
+ role.project_module :accounts, '/accounts'
+ end
- access_control.roles_for :admin do |role, account|
- role.project_module :posts, "/posts"
- role.project_module :accounts, "/accounts"
+ # Custom error management
+ error(403) { @title = "Error 403"; render('errors/403', :layout => :error) }
+ error(404) { @title = "Error 404"; render('errors/404', :layout => :error) }
+ error(500) { @title = "Error 500"; render('errors/500', :layout => :error) }
end
end
View
69 admin/controllers/accounts.rb
@@ -1,11 +1,12 @@
-Admin.controllers :accounts do
-
+SampleBlog::Admin.controllers :accounts do
get :index do
+ @title = "Accounts"
@accounts = Account.all
render 'accounts/index'
end
get :new do
+ @title = pat(:new_title, :model => 'account')
@account = Account.new
render 'accounts/new'
end
@@ -13,35 +14,77 @@
post :create do
@account = Account.new(params[:account])
if @account.save
- flash[:notice] = 'Account was successfully created.'
- redirect url(:accounts, :edit, :id => @account.id)
+ @title = pat(:create_title, :model => "account #{@account.id}")
+ flash[:success] = pat(:create_success, :model => 'Account')
+ params[:save_and_continue] ? redirect(url(:accounts, :index)) : redirect(url(:accounts, :edit, :id => @account.id))
else
+ @title = pat(:create_title, :model => 'account')
+ flash.now[:error] = pat(:create_error, :model => 'account')
render 'accounts/new'
end
end
get :edit, :with => :id do
+ @title = pat(:edit_title, :model => "account #{params[:id]}")
@account = Account.find(params[:id])
- render 'accounts/edit'
+ if @account
+ render 'accounts/edit'
+ else
+ flash[:warning] = pat(:create_error, :model => 'account', :id => "#{params[:id]}")
+ halt 404
+ end
end
put :update, :with => :id do
+ @title = pat(:update_title, :model => "account #{params[:id]}")
@account = Account.find(params[:id])
- if @account.update_attributes(params[:account])
- flash[:notice] = 'Account was successfully updated.'
- redirect url(:accounts, :edit, :id => @account.id)
+ if @account
+ if @account.update_attributes(params[:account])
+ flash[:success] = pat(:update_success, :model => 'Account', :id => "#{params[:id]}")
+ params[:save_and_continue] ?
+ redirect(url(:accounts, :index)) :
+ redirect(url(:accounts, :edit, :id => @account.id))
+ else
+ flash.now[:error] = pat(:update_error, :model => 'account')
+ render 'accounts/edit'
+ end
else
- render 'accounts/edit'
+ flash[:warning] = pat(:update_warning, :model => 'account', :id => "#{params[:id]}")
+ halt 404
end
end
delete :destroy, :with => :id do
+ @title = "Accounts"
account = Account.find(params[:id])
- if account.destroy
- flash[:notice] = 'Account was successfully destroyed.'
+ if account
+ if account != current_account && account.destroy
+ flash[:success] = pat(:delete_success, :model => 'Account', :id => "#{params[:id]}")
+ else
+ flash[:error] = pat(:delete_error, :model => 'account')
+ end
+ redirect url(:accounts, :index)
else
- flash[:error] = 'Impossible destroy Account!'
+ flash[:warning] = pat(:delete_warning, :model => 'account', :id => "#{params[:id]}")
+ halt 404
+ end
+ end
+
+ delete :destroy_many do
+ @title = "Accounts"
+ unless params[:account_ids]
+ flash[:error] = pat(:destroy_many_error, :model => 'account')
+ redirect(url(:accounts, :index))
+ end
+ ids = params[:account_ids].split(',').map(&:strip)
+ accounts = Account.find(ids)
+
+ if accounts.include? current_account
+ flash[:error] = pat(:delete_error, :model => 'account')
+ elsif Account.destroy accounts
+
+ flash[:success] = pat(:destroy_many_success, :model => 'Accounts', :ids => "#{ids.to_sentence}")
end
redirect url(:accounts, :index)
end
-end
+end
View
6 admin/controllers/base.rb
@@ -1,7 +1,5 @@
-Admin.controllers :base do
-
+SampleBlog::Admin.controllers :base do
get :index, :map => "/" do
render "base/index"
end
-
-end
+end
View
69 admin/controllers/posts.rb
@@ -1,11 +1,12 @@
-Admin.controllers :posts do
-
+SampleBlog::Admin.controllers :posts do
get :index do
- @posts = Post.all
+ @title = "Posts"
+ @posts = Post.order("created_at DESC")
render 'posts/index'
end
get :new do
+ @title = pat(:new_title, :model => 'post')
@post = Post.new
render 'posts/new'
end
@@ -14,35 +15,75 @@
@post = Post.new(params[:post])
@post.account = current_account
if @post.save
- flash[:notice] = 'Post was successfully created.'
- redirect url(:posts, :edit, :id => @post.id)
+ @title = pat(:create_title, :model => "post #{@post.id}")
+ flash[:success] = pat(:create_success, :model => 'Post')
+ params[:save_and_continue] ? redirect(url(:posts, :index)) : redirect(url(:posts, :edit, :id => @post.id))
else
+ @title = pat(:create_title, :model => 'post')
+ flash.now[:error] = pat(:create_error, :model => 'post')
render 'posts/new'
end
end
get :edit, :with => :id do
+ @title = pat(:edit_title, :model => "post #{params[:id]}")
@post = Post.find(params[:id])
- render 'posts/edit'
+ if @post
+ render 'posts/edit'
+ else
+ flash[:warning] = pat(:create_error, :model => 'post', :id => "#{params[:id]}")
+ halt 404
+ end
end
put :update, :with => :id do
+ @title = pat(:update_title, :model => "post #{params[:id]}")
@post = Post.find(params[:id])
- if @post.update_attributes(params[:post])
- flash[:notice] = 'Post was successfully updated.'
- redirect url(:posts, :edit, :id => @post.id)
+ if @post
+ if @post.update_attributes(params[:post])
+ flash[:success] = pat(:update_success, :model => 'Post', :id => "#{params[:id]}")
+ params[:save_and_continue] ?
+ redirect(url(:posts, :index)) :
+ redirect(url(:posts, :edit, :id => @post.id))
+ else
+ flash.now[:error] = pat(:update_error, :model => 'post')
+ render 'posts/edit'
+ end
else
- render 'posts/edit'
+ flash[:warning] = pat(:update_warning, :model => 'post', :id => "#{params[:id]}")
+ halt 404
end
end
delete :destroy, :with => :id do
+ @title = "Posts"
post = Post.find(params[:id])
- if post.destroy
- flash[:notice] = 'Post was successfully destroyed.'
+ if post
+ if post.destroy
+ flash[:success] = pat(:delete_success, :model => 'Post', :id => "#{params[:id]}")
+ else
+ flash[:error] = pat(:delete_error, :model => 'post')
+ end
+ redirect url(:posts, :index)
else
- flash[:error] = 'Impossible destroy Post!'
+ flash[:warning] = pat(:delete_warning, :model => 'post', :id => "#{params[:id]}")
+ halt 404
+ end
+ end
+
+ delete :destroy_many do
+ @title = "Posts"
+ unless params[:post_ids]
+ flash[:error] = pat(:destroy_many_error, :model => 'post')
+ redirect(url(:posts, :index))
+ end
+ ids = params[:post_ids].split(',').map(&:strip)
+ posts = Post.find(ids)
+
+ if Post.destroy posts
+
+ flash[:success] = pat(:destroy_many_success, :model => 'Posts', :ids => "#{ids.to_sentence}")
end
redirect url(:posts, :index)
end
-end
+end
View
14 admin/controllers/sessions.rb
@@ -1,5 +1,4 @@
-Admin.controllers :sessions do
-
+SampleBlog::Admin.controllers :sessions do
get :new do
render "/sessions/new", nil, :layout => false
end
@@ -8,14 +7,19 @@
if account = Account.authenticate(params[:email], params[:password])
set_current_account(account)
redirect url(:base, :index)
+ elsif Padrino.env == :development && params[:bypass]
+ account = Account.first
+ set_current_account(account)
+ redirect url(:base, :index)
else
- flash[:warning] = "Login or password wrong."
+ params[:email], params[:password] = h(params[:email]), h(params[:password])
+ flash[:error] = pat('login.error')
redirect url(:sessions, :new)
end
end
- get :destroy do
+ delete :destroy do
set_current_account(nil)
redirect url(:sessions, :new)
end
-end
+end
View
81 admin/views/accounts/_form.haml
@@ -1,40 +1,43 @@
-.group
- =f.label :name
- =f.error_message_on :name
- =f.text_field :name, :class => :text_field
- %span.description Ex: a simple text
+- error = @account.errors.include?(:name)
+%fieldset.control-group{:class => error ? 'has-error' : ''}
+ =f.label :name, :class => 'control-label'
+ .controls
+ =f.text_field :name, :class => 'form-control input-large input-with-feedback', :autofocus => true
+ %span.help-inline=error ? f.error_message_on(:name, :class => 'text-error') : pat(:example)
+- error = @account.errors.include?(:surname)
+%fieldset.control-group{:class => error ? 'has-error' : ''}
+ =f.label :surname, :class => 'control-label'
+ .controls
+ =f.text_field :surname, :class => 'form-control input-large input-with-feedback'
+ %span.help-inline=error ? f.error_message_on(:surname, :class => 'text-error') : pat(:example)
+- error = @account.errors.include?(:email)
+%fieldset.control-group{:class => error ? 'has-error' : ''}
+ =f.label :email, :class => 'control-label'
+ .controls
+ =f.text_field :email, :class => 'form-control input-large input-with-feedback'
+ %span.help-inline=error ? f.error_message_on(:email, :class => 'text-error') : pat(:example)
+- error = @account.errors.include?(:password)
+%fieldset.control-group{:class => error ? 'has-error' : ''}
+ =f.label :password, :class => 'control-label'
+ .controls
+ =f.password_field :password, :class => 'form-control input-large input-with-feedback'
+ %span.help-inline=error ? f.error_message_on(:password, :class => 'text-error') : pat(:example)
+- error = @account.errors.include?(:password_confirmation)
+%fieldset.control-group{:class => error ? 'has-error' : ''}
+ =f.label :password_confirmation, :class => 'control-label'
+ .controls
+ =f.password_field :password_confirmation, :class => 'form-control input-large input-with-feedback'
+ %span.help-inline=error ? f.error_message_on(:password_confirmation, :class => 'text-error') : pat(:example)
+- error = @account.errors.include?(:role)
+%fieldset.control-group{:class => error ? 'has-error' : ''}
+ =f.label :role, :class => 'control-label'
+ .controls
+ =f.text_field :role, :class => 'form-control input-large input-with-feedback'
+ %span.help-inline=error ? f.error_message_on(:role, :class => 'text-error') : pat(:example)
-.group
- =f.label :surname
- =f.error_message_on :surname
- =f.text_field :surname, :class => :text_field
- %span.description Ex: a simple text
-
-.group
- =f.label :email
- =f.error_message_on :email
- =f.text_field :email, :class => :text_field
- %span.description Ex: a simple text
-
-.group
- =f.label :password
- =f.error_message_on :password
- =f.text_field :password, :class => :text_field
- %span.description Ex: a simple text
-
-.group
- =f.label :password_confirmation
- =f.error_message_on :password_confirmation
- =f.text_field :password_confirmation, :class => :text_field
- %span.description Ex: a simple text
-
-.group
- =f.label :role
- =f.error_message_on :role
- =f.select :role, :options => access_control.roles
- %span.description Ex: a simple text
-
-
-.group.navform.wat-cf
- =f.submit pat(:save), :class => :button
- =f.submit pat(:cancel), :onclick => "window.location='#{url(:accounts, :index)}';return false", :class => :button
+.form-actions
+ =f.submit pat(:save), :class => 'btn btn-primary'
+ &nbsp;
+ =f.submit pat(:save_and_continue), :class => 'btn btn-info', :name => 'save_and_continue'
+ &nbsp;
+ =link_to pat(:cancel), url(:accounts, :index), :class => 'btn btn-default'
View
21 admin/views/accounts/edit.haml
@@ -1,15 +1,8 @@
-.block
- .secondary-navigation
- %ul.wat-cf
- %li.first=link_to pat(:list), url(:accounts, :index)
- %li=link_to pat(:new), url(:accounts, :new)
- %li.active=link_to pat(:edit), url(:accounts, :edit, :id => @account.id)
- .content
- %h2.title
- =pat(:edit)
- =mt(:account)
- .inner
- -form_for :account, url(:accounts, :update, :id => @account.id), :method => :put, :class => :form do |f|
- =partial "accounts/form", :locals => { :f => f }
+%ul.nav.nav-tabs
+ %li=link_to tag_icon(:list, pat(:list)), url(:accounts, :index)
+ %li=link_to tag_icon(:plus, pat(:new)), url(:accounts, :new)
+ %li.active=link_to tag_icon(:edit, pat(:edit)), url(:accounts, :edit, :id => @account.id)
--content_for :sidebar, partial("base/sidebar")
+.tabs-content
+ -form_for :account, url(:accounts, :update, :id => @account.id), :method => :put, :class => 'form-horizontal' do |f|
+ =partial 'accounts/form', :locals => { :f => f }
View
83 admin/views/accounts/index.haml
@@ -1,31 +1,54 @@
-.block
- .secondary-navigation
- %ul.wat-cf
- %li.first.active=link_to pat(:list), url(:accounts, :index)
- %li=link_to pat(:new), url(:accounts, :new)
- .content
- %h2.title
- =pat(:all)
- =mt(:account)
- .inner
- %table.table
- %tr
- %th.first=mat(:account, :id)
- %th=mat(:account, :name)
- %th=mat(:account, :surname)
- %th=mat(:account, :email)
- %th.last="&nbsp;"
- -@accounts.each do |account|
- %tr
- %td.first=account.id
- %td=account.name
- %td=account.surname
- %td=account.email
- %td.last
- =button_to pat(:edit), url(:accounts, :edit, :id => account.id), :method => :get, :class => :button_to
- ="|"
- =button_to pat(:delete), url(:accounts, :destroy, :id => account.id), :method => :delete, :class => :button_to
- .actions-bar.wat-cf
- .actions="&nbsp;"
+%ul.nav.nav-tabs
+ %li.active=link_to tag_icon(:list, pat(:list)), url(:accounts, :index)
+ %li=link_to tag_icon(:plus, pat(:new)), url(:accounts, :new)
--content_for :sidebar, partial("base/sidebar")
+.tabs-content
+ %table#list.table.table-striped.table-hover.table-condensed
+ %thead
+ %tr
+ %th.header.list-menu
+ .dropdown
+ %a.list-menu-toggle.dropdown-toggle{:href => '#', :'data-toggle' => :dropdown}
+ =tag_icon :magic
+ %span.caret
+ %ul.list-menu.dropdown-menu{:role => :menu}
+ %li.list-menu-wrapper=link_to tag_icon(:check, pat(:select_all)), '#', :id => 'select-all', :class => 'list-menu-link'
+ %li.list-menu-wrapper.list-menu-wrapper-disabled
+ =link_to tag_icon(:'check-empty', pat(:deselect_all)), '#', :id => 'deselect-all', :class => 'list-menu-link list-menu-link-disabled'
+ %li.list-menu-divider.divider
+ %li.list-menu-wrapper.list-menu-wrapper-disabled
+ =link_to tag_icon(:trash, pat(:delete_selected)), '#', :id => 'delete-selected', :class => 'list-menu-link list-menu-link-disabled'
+ .list-menu-popover-delete-selected.popover.right
+ .arrow
+ %h3.popover-title=pat(:delete_selected_title)
+ .popover-content
+ -form_tag url(:accounts, :destroy_many), :method => :delete do
+ =hidden_field_tag :account_ids, :'data-delete-many-ids' => true
+ =submit_tag pat(:delete), :class =>'list-menu-popover-delete-selected-btn btn btn-danger btn-small'
+ .btn.btn-default.btn-small.cancel=pat(:cancel)
+ %th.header= mat(:account, :id)
+ %th.header= mat(:account, :name)
+ %th.header= mat(:account, :surname)
+ %th.header= mat(:account, :email)
+ %th.header.list-row-action-header
+
+ %tbody
+ -@accounts.each do |account|
+ %tr.list-row
+ %td.list-column.list-selectable
+ =check_box_tag 'account_ids[]', :value => account.id, :class => 'list-selectable-checkbox'
+ %td.list-column=account.id
+ %td.list-column=account.name
+ %td.list-column=account.surname
+ %td.list-column=account.email
+ %td.list-column.list-row-action
+ .list-row-action-wrapper
+ =link_to tag_icon(:edit), :href => url(:accounts, :edit, :id => account.id), :rel => :tooltip, :title => "#{pat(:edit)} account", :class => 'list-row-action-wrapper-link'
+ =link_to tag_icon(:trash), :rel => :tooltip, :title => "#{pat(:delete)} account", :class => 'list-row-action-delete-one list-row-action-wrapper-link'
+ .popover.list-row-action-popover-delete-one.left
+ .arrow
+ %h3.popover-title=pat(:delete, :model => "account")
+ .popover-content
+ -form_tag url(:accounts, :destroy, :id => account.id), :method => :delete do
+ =submit_tag pat(:delete), :class =>'list-row-action-popover-delete-one-btn btn btn-danger btn-small'
+ .btn.btn-default.btn-small.cancel=pat(:cancel)
View
19 admin/views/accounts/new.haml
@@ -1,14 +1,7 @@
-.block
- .secondary-navigation
- %ul.wat-cf
- %li.first=link_to pat(:list), url(:accounts, :index)
- %li.active=link_to pat(:new), url(:accounts, :new)
- .content
- %h2.title
- =pat(:new)
- =mt(:account)
- .inner
- -form_for :account, url(:accounts, :create), :class => :form do |f|
- =partial "accounts/form", :locals => { :f => f }
+%ul.nav.nav-tabs
+ %li=link_to tag_icon(:list, pat(:list)), url(:accounts, :index)
+ %li.active=link_to tag_icon(:plus, pat(:new)), url(:accounts, :new)
--content_for :sidebar, partial("base/sidebar")
+.tabs-content
+ -form_for :account, url(:accounts, :create), :class => 'form-horizontal' do |f|
+ =partial 'accounts/form', :locals => { :f => f }
View
35 admin/views/base/index.haml
@@ -1,25 +1,12 @@
-#block-text.block
- .content
- %h2.title Dashboard
- .inner
- %p.first
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
- Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
- Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
- %span.hightlight
- Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
- %p
- %span.small
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore
- %p
- %span.gray
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore
- %hr
- %p
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
- Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
- Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
- %span.hightlight
- Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+.base-text
+ %p Padrino is a ruby framework built upon the Sinatra web library.
+ %p It was created to make it fun and easy to code more advanced web applications while still adhering to the spirit that makes Sinatra great!
+ %p Padrino comes shipped with a slick and beautiful Admin Interface, with the following features:
--content_for :sidebar, partial("base/sidebar")
+.base-icons
+ .btn-group
+ .btn.btn-primary{:title => "Adapters for datamapper, sequel, activerecord, mongomapper, mongoid, couchrest"}=tag_icon("cogs icon-2x", "Orm Agnostic")
+ .btn.btn-success{:title => "User Authentication Support, User Authorization Management"}=tag_icon("group icon-2x", "Authentication")
+ .btn.btn-info{:title => "Erb, Haml, Slim Rendering Support"}=tag_icon("tasks icon-2x", "Template Agnostic")
+ .btn.btn-warning{:title => "English, German, Russian, Danish, French, Brazilian and Italian localizations and many more!.."}=tag_icon("flag icon-2x", "Multi Language")
+ .btn.btn-danger{:title => "You can create a new 'admin interface' by providing a single Model"}=tag_icon("magic icon-2x", "Scaffold")
View
3  admin/views/errors/403.haml
@@ -0,0 +1,3 @@
+%p=tag_icon 'minus-sign icon-3x'
+%h1=pat('custom_errors.403.title')
+%p=pat('custom_errors.403.description')
View
3  admin/views/errors/404.haml
@@ -0,0 +1,3 @@
+%p=tag_icon 'warning-sign icon-3x'
+%h1=pat('custom_errors.404.title')
+%p=pat('custom_errors.404.description')
View
3  admin/views/errors/500.haml
@@ -0,0 +1,3 @@
+%p=tag_icon 'beaker icon-3x'
+%h1=pat('custom_errors.500.title')
+%p=pat('custom_errors.500.description')
View
76 admin/views/layouts/application.haml
@@ -1,31 +1,49 @@
-!!! Strict
-%html{:lang => "en", :xmlns => "http://www.w3.org/1999/xhtml"}
+!!! 5
+%html{:lang => 'en'}
%head
- %meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
- %title Padrino Admin
- =stylesheet_link_tag :base, "themes/default/style"
+ <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
+ <!--[if lt IE 9]>
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+ %meta{:content => 'text/html; charset=utf-8', 'http-equiv' => 'Content-Type'}
+ %meta{:name => :viewport, :content => 'width=device-width,initial-scale=1'}
+ %title= @title.present? ? "#{@title} | Padrino Admin" : "Padrino Admin"
+ =favicon_tag 'favicon.ico'
+ %link{:href => 'http://fonts.googleapis.com/css?family=Varela', :rel => :stylesheet}
+ =stylesheet_link_tag 'bootstrap', 'application'
+
%body
- #container
- #header
- %h1=link_to "Padrino Admin", url(:base_index)
- #user-navigation
- %ul.wat-cf
- %li=link_to pat(:profile), url(:accounts, :edit, :id => current_account.id)
- %li=link_to pat(:logout), url(:sessions, :destroy), :method => :delete
- #main-navigation
- %ul.wat-cf
- -project_modules.each do |project_module|
- %li{:class => ("active" if request.path_info =~ /^#{project_module.path}/)}
- =link_to project_module.human_name, project_module.path("/admin")
- #wrapper.wat-cf
- .flash=[:error, :warning, :notice].map { |type| flash_tag(type, :class => "message #{type}") }.join
- #main
- =yield
- #footer
- .block
- %p
- Copyright ©
- =Time.now.year
- Your Site - Powered by
- =link_to "Padrino v.#{Padrino.version}", "http://padrino.github.com", :target => :_blank
- #sidebar=yield_content :sidebar
+ .navbar.navbar-fixed-top
+ .navbar-inner
+ .container
+ =link_to 'Padrino', url(:base_index), :class => 'navbar-brand', :title => 'Padrino Admin'
+ %ul.nav.navbar-nav.pull-right
+ %li.navbar-edit-account=link_to tag_icon(:user), url(:accounts, :edit, :id => current_account.id), :title => pat(:profile), :class => 'navbar-nav-link'
+ %li.navbar-logout
+ =button_to :logout, url(:sessions, :destroy), :method => :delete, :class => 'navbar-nav-form' do
+ =content_tag :button, tag_icon(:off), :type => :submit, :title => pat(:logout), :class => 'navbar-nav-form-link'
+
+ %ul.nav.navbar-nav.pull-left
+ - project_modules.each do |project_module|
+ %li{:class => "navbar-module #{('active' if request.path_info =~ /^#{project_module.path}/)}"}
+ =link_to project_module.human_name, project_module.path('/admin')
+
+ .container.main
+ .main-wrapper
+ =[:error, :warning, :success, :notice].map { |type| flash_tag(type, :class => "alert alert-#{type} fade in", :bootstrap => true) }.join.html_safe
+ .row=yield
+ .main-wrapper-push
+
+ %footer
+ .footer-wrapper.container
+ %p.pull-left
+ Copyright &copy;
+ =Time.now.year
+ Your Site - Powered by Padrino v.#{Padrino.version}
+ %ul.pull-right.footer-links
+ %li= link_to tag_icon(:home, 'web'), 'http://www.padrinorb.com', :target => :_blank, :class => 'footer-links-link'
+ %li= link_to tag_icon(:heart, 'blog'), 'http://www.padrinorb.com/blog', :target => :_blank, :class => 'footer-links-link'
+ %li= link_to tag_icon(:github, 'code'), 'https://github.com/padrino/padrino-framework', :target => :_blank, :class => 'footer-links-link'
+ %li= link_to tag_icon(:twitter, 'twitter'), 'http://twitter.com/padrinorb', :target => :_blank, :class => 'footer-links-link'
+
+ =javascript_include_tag 'jquery-1.9.0.min', (Padrino.env == :production ? 'bootstrap/bootstrap.min' : %w[bootstrap/affix bootstrap/alert bootstrap/button bootstrap/carousel bootstrap/collapse bootstrap/dropdown bootstrap/tooltip bootstrap/transition bootstrap/modal bootstrap/popover bootstrap/scrollspy bootstrap/tab]), :application
View
18 admin/views/layouts/error.haml
@@ -0,0 +1,18 @@
+!!! 5
+%html{:lang => 'en'}
+ %head
+ <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
+ <!--[if lt IE 9]>
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+ %meta{:content => 'text/html; charset=utf-8', 'http-equiv' => 'Content-Type'}
+ %meta{:name => :viewport, :content => 'width=device-width,initial-scale=1'}
+ %title= @title.present? ? "#{@title} | Padrino Admin" : "#{pat(:custom_errors.title)} | Padrino Admin"
+ =favicon_tag 'favicon.ico'
+ %link{:href => 'http://fonts.googleapis.com/css?family=Varela', :rel => :stylesheet}
+ =stylesheet_link_tag 'bootstrap', 'application'
+ %body
+ .custom-error.text-center
+ .custom-error-body.muted= yield
+ .custom-error-footer
+ =link_to tag_icon('arrow-left'), "#", :onclick => "history.go(-1);return false;", :class =>"custom-error-go-back btn btn-default pull-left", :title => "Go back"
View
33 admin/views/posts/_form.haml
@@ -1,16 +1,19 @@
-.group
- =f.label :title
- =f.error_message_on :title
- =f.text_field :title, :class => :text_field
- %span.description Ex: a simple text
+- error = @post.errors.include?(:title)
+%fieldset.control-group{:class => error ? 'has-error' : ''}
+ =f.label :title, :class => 'control-label'
+ .controls
+ =f.text_field :title, :class => 'form-control input-large input-with-feedback', :autofocus => true
+ %span.help-inline=error ? f.error_message_on(:title, :class => 'text-error') : pat(:example)
+- error = @post.errors.include?(:body)
+%fieldset.control-group{:class => error ? 'has-error' : ''}
+ =f.label :body, :class => 'control-label'
+ .controls
+ ~f.text_area :body, :class => 'form-control input-large input-with-feedback'
+ %span.help-inline=error ? f.error_message_on(:body, :class => 'text-error') : pat(:example)
-.group
- =f.label :body
- =f.error_message_on :body
- =f.text_area :body, :class => :text_area
- %span.description Ex: a simple text
-
-
-.group.navform.wat-cf
- =f.submit pat(:save), :class => :button
- =f.submit pat(:cancel), :onclick => "window.location='#{url(:posts, :index)}';return false", :class => :button
+.form-actions
+ =f.submit pat(:save), :class => 'btn btn-primary'
+ &nbsp;
+ =f.submit pat(:save_and_continue), :class => 'btn btn-info', :name => 'save_and_continue'
+ &nbsp;
+ =link_to pat(:cancel), url(:posts, :index), :class => 'btn btn-default'
View
21 admin/views/posts/edit.haml
@@ -1,15 +1,8 @@
-.block
- .secondary-navigation
- %ul.wat-cf
- %li.first=link_to pat(:list), url(:posts, :index)
- %li=link_to pat(:new), url(:posts, :new)
- %li.active=link_to pat(:edit), url(:posts, :edit, :id => @post.id)
- .content
- %h2.title
- =pat(:edit)
- =mt(:post)
- .inner
- -form_for :post, url(:posts, :update, :id => @post.id), :method => :put, :class => :form do |f|
- =partial "posts/form", :locals => { :f => f }
+%ul.nav.nav-tabs
+ %li=link_to tag_icon(:list, pat(:list)), url(:posts, :index)
+ %li=link_to tag_icon(:plus, pat(:new)), url(:posts, :new)
+ %li.active=link_to tag_icon(:edit, pat(:edit)), url(:posts, :edit, :id => @post.id)
--content_for :sidebar, partial("base/sidebar")
+.tabs-content
+ -form_for :post, url(:posts, :update, :id => @post.id), :method => :put, :class => 'form-horizontal' do |f|
+ =partial 'posts/form', :locals => { :f => f }
View
83 admin/views/posts/index.haml
@@ -1,29 +1,56 @@
-.block
- .secondary-navigation
- %ul.wat-cf
- %li.first.active=link_to pat(:list), url(:posts, :index)
- %li=link_to pat(:new), url(:posts, :new)
- .content
- %h2.title
- =pat(:all)
- =mt(:post)
- .inner
- %table.table
- %tr
- %th.first=mat(:post, :id)
- %th=mat(:post, :title)
- %th=mat(:post, :body)
- %th.last="&nbsp;"
- -@posts.each do |post|
- %tr
- %td.first=post.id
- %td=post.title
- %td=post.body
- %td.last
- =button_to pat(:edit), url(:posts, :edit, :id => post.id), :method => :get, :class => :button_to
- ="|"
- =button_to pat(:delete), url(:posts, :destroy, :id => post.id), :method => :delete, :class => :button_to
- .actions-bar.wat-cf
- .actions="&nbsp;"
+%ul.nav.nav-tabs
+ %li.active=link_to tag_icon(:list, pat(:list)), url(:posts, :index)
+ %li=link_to tag_icon(:plus, pat(:new)), url(:posts, :new)
--content_for :sidebar, partial("base/sidebar")
+.tabs-content
+ %table#list.table.table-striped.table-hover.table-condensed
+ %thead
+ %tr
+ %th.header.list-menu
+ .dropdown
+ %a.list-menu-toggle.dropdown-toggle{:href => '#', :'data-toggle' => :dropdown}
+ =tag_icon :magic
+ %span.caret
+ %ul.list-menu.dropdown-menu{:role => :menu}
+ %li.list-menu-wrapper=link_to tag_icon(:check, pat(:select_all)), '#', :id => 'select-all', :class => 'list-menu-link'
+ %li.list-menu-wrapper.list-menu-wrapper-disabled
+ =link_to tag_icon(:'check-empty', pat(:deselect_all)), '#', :id => 'deselect-all', :class => 'list-menu-link list-menu-link-disabled'
+ %li.list-menu-divider.divider
+ %li.list-menu-wrapper.list-menu-wrapper-disabled
+ =link_to tag_icon(:trash, pat(:delete_selected)), '#', :id => 'delete-selected', :class => 'list-menu-link list-menu-link-disabled'
+ .list-menu-popover-delete-selected.popover.right
+ .arrow
+ %h3.popover-title=pat(:delete_selected_title)
+ .popover-content
+ -form_tag url(:posts, :destroy_many), :method => :delete do
+ =hidden_field_tag :post_ids, :'data-delete-many-ids' => true
+ =submit_tag pat(:delete), :class =>'list-menu-popover-delete-selected-btn btn btn-danger btn-small'
+ .btn.btn-default.btn-small.cancel=pat(:cancel)
+ %th.header= mat(:post, :id)
+ %th.header= mat(:post, :title)
+ %th.header= mat(:post, :created_at)
+ %th.header= mat(:post, :updated_at)
+ %th.header= mat(:post, :account_id)
+ %th.header.list-row-action-header
+
+ %tbody
+ -@posts.each do |post|
+ %tr.list-row
+ %td.list-column.list-selectable
+ =check_box_tag 'post_ids[]', :value => post.id, :class => 'list-selectable-checkbox'
+ %td.list-column=post.id
+ %td.list-column=post.title
+ %td.list-column=time_ago_in_words post.created_at
+ %td.list-column=time_ago_in_words post.updated_at
+ %td.list-column=post.account.email
+ %td.list-column.list-row-action
+ .list-row-action-wrapper
+ =link_to tag_icon(:edit), :href => url(:posts, :edit, :id => post.id), :rel => :tooltip, :title => "#{pat(:edit)} post", :class => 'list-row-action-wrapper-link'
+ =link_to tag_icon(:trash), :rel => :tooltip, :title => "#{pat(:delete)} post", :class => 'list-row-action-delete-one list-row-action-wrapper-link'
+ .popover.list-row-action-popover-delete-one.left
+ .arrow
+ %h3.popover-title=pat(:delete, :model => "post")
+ .popover-content
+ -form_tag url(:posts, :destroy, :id => post.id), :method => :delete do
+ =submit_tag pat(:delete), :class =>'list-row-action-popover-delete-one-btn btn btn-danger btn-small'
+ .btn.btn-default.btn-small.cancel=pat(:cancel)
View
19 admin/views/posts/new.haml
@@ -1,14 +1,7 @@
-.block
- .secondary-navigation
- %ul.wat-cf
- %li.first=link_to pat(:list), url(:posts, :index)
- %li.active=link_to pat(:new), url(:posts, :new)
- .content
- %h2.title
- =pat(:new)
- =mt(:post)
- .inner
- -form_for :post, url(:posts, :create), :class => :form do |f|
- =partial "posts/form", :locals => { :f => f }
+%ul.nav.nav-tabs
+ %li=link_to tag_icon(:list, pat(:list)), url(:posts, :index)
+ %li.active=link_to tag_icon(:plus, pat(:new)), url(:posts, :new)
--content_for :sidebar, partial("base/sidebar")
+.tabs-content
+ -form_for :post, url(:posts, :create), :class => 'form-horizontal' do |f|
+ =partial 'posts/form', :locals => { :f => f }
View
58 admin/views/sessions/new.haml
@@ -1,25 +1,37 @@
-!!! Strict
-%html{:lang => "en", :xmlns => "http://www.w3.org/1999/xhtml"}
+!!! 5
+%html{:lang => 'en'}
%head
- %meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
- %title Padrino Admin
- =stylesheet_link_tag :base, :override, "themes/default/style"
+ <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
+ <!--[if lt IE 9]>
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+ %meta{:content => 'text/html; charset=utf-8', 'http-equiv' => 'Content-Type'}
+ %meta{:name => :viewport, :content => 'width=device-width,initial-scale=1'}
+ %title= @title.present? ? "#{@title} | Padrino Admin" : "#{pat('login.title')} | Padrino Admin"
+ =favicon_tag 'favicon.ico'
+ =stylesheet_link_tag 'bootstrap', 'application'
%body
- #container
- #box
- %h1 Padrino Admin
- #block-login.block
- %h2 Login Box
- .content.login
- .flash=[:error, :warning, :notice].map { |type| flash_tag(type, :class => "message #{type}") }.join
- -form_tag(url(:sessions, :create), :class => 'form login') do
- .group.wat-cf
- .left
- %label.label.right Login
- .right=text_field_tag :email, :value => params[:email], :class => :text_field
- .group.wat-cf
- .left
- %label.label.right Password
- .right=password_field_tag :password, :value => params[:password], :class => :text_field
- .group.navform.wat-cf
- .right=submit_tag('Sign In', :class => :button)
+ - form_tag url(:sessions, :create), :class=>'login form-horizontal' do
+ .login-header.modal-header
+ .login-logo=image_tag('logo.png', :alt => "Padrino's logo", :height => 250, :width => 193)
+ .login-body.modal-body
+ = [:error, :warning, :notice].map { |type| flash_tag(type, :class => "alert alert-#{type} fade in", :bootstrap => true) }.join.html_safe
+
+ .form-group
+ %label.col-lg-2.control-label{:for => :email}= pat('login.email')
+ .col-lg-10=email_field_tag :email, :value => params[:email], :autofocus => true, :class =>'form-control'
+
+ .form-group
+ %label.col-lg-2.control-label{:for => :password}= pat('login.password')
+ .col-lg-10=password_field_tag :password, :value => params[:password], :class =>'form-control'
+
+ %fieldset.login-control-group-last.control-group
+ .login-controls.controls
+ %label.login-bypass-label.checkbox
+ =check_box_tag :bypass
+ =pat('login.bypass')
+
+ .login-footer.modal-footer
+ =submit_tag(pat('login.sign_in'), :class => 'btn btn-primary pull-right')
+
+ =javascript_include_tag 'jquery-1.9.0.min', (Padrino.env == :production ? 'bootstrap/bootstrap.min' : %w[bootstrap/affix bootstrap/alert bootstrap/button bootstrap/carousel bootstrap/collapse bootstrap/dropdown bootstrap/tooltip bootstrap/transition bootstrap/modal bootstrap/popover bootstrap/scrollspy bootstrap/tab]), :application
View
37 app/app.rb
@@ -1,28 +1,19 @@
-class SampleBlog < Padrino::Application
- register Padrino::Helpers
- register Padrino::Mailer
- register SassInitializer
+module SampleBlog
+ class App < Padrino::Application
+ register SassInitializer
+ use ActiveRecord::ConnectionAdapters::ConnectionManagement
+ register Padrino::Rendering
+ register Padrino::Mailer
+ register Padrino::Helpers
- ##
- # Application-specific configuration options
- #
- # set :raise_errors, true # Show exceptions (default for development)
- # set :public, "foo/bar" # Location for static assets (default root/public)
- # set :sessions, false # Enabled by default
- # set :reload, false # Reload application files (default in development)
- # set :default_builder, "foo" # Set a custom form builder (default 'StandardFormBuilder')
- # set :locale_path, "bar" # Set path for I18n translations (default your_app/locales)
- # disable :flash # Disables rack-flash (enabled by default)
- # enable :authentication # Enable padrino-admin authentication (disabled by default)
- # layout :my_layout # Layout can be in views/layouts/foo.ext or views/foo.ext (default :application)
- #
+ enable :sessions
- get "/" do
- "Hello World!"
- end
+ get "/" do
+ "Hello World!"
+ end
- get :about, :map => '/about_us' do
- render :haml, "%p This is a sample blog created to demonstrate the power of Padrino!"
+ get :about, :map => '/about_us' do
+ render :haml, "%p This is a sample blog created to demonstrate how Padrino works!"
+ end
end
-
end
View
2  app/controllers/posts.rb
@@ -1,4 +1,4 @@
-SampleBlog.controllers :posts do
+SampleBlog::App.controllers :posts do
get :index, :provides => [:html, :rss, :atom] do
@posts = Post.all(:order => 'created_at desc')
render 'posts/index'
View
2  app/helpers/posts_helper.rb
@@ -1,6 +1,6 @@
# Helper methods defined here can be accessed in any controller or view in the application
-SampleBlog.helpers do
+SampleBlog::App.helpers do
# feed_tag(:rss, :title => "RSS", :href => url(...))
# feed_tag(:atom, :title => "ATOM", :href => url(...))
def feed_tag(mime, options={})
View
4 app/views/posts/index.haml
@@ -1,7 +1,7 @@
- @title = "Welcome"
-- content_for :include do
+- content_for :assets do
= feed_tag(:rss, :title => "RSS", :href => url(:posts, :index, :format => :rss))
= feed_tag(:atom, :title => "ATOM", :href => url(:posts, :index, :format => :atom))
-
+
#posts= partial 'posts/post', :collection => @posts
View
11 config.ru
@@ -1,2 +1,9 @@
-require File.dirname(__FILE__) + '/config/boot.rb'
-run Padrino.application
+#!/usr/bin/env rackup
+# encoding: utf-8
+
+# This file can be used to start Padrino,
+# just execute it from the command line.
+
+require File.expand_path("../config/boot.rb", __FILE__)
+
+run Padrino.application
View
53 config/apps.rb
@@ -1,21 +1,38 @@
-=begin
+##
+# This file mounts each app in the Padrino project to a specified sub-uri.
+# You can mount additional applications using any of these commands below:
+#
+# Padrino.mount('blog').to('/blog')
+# Padrino.mount('blog', :app_class => 'BlogApp').to('/blog')
+# Padrino.mount('blog', :app_file => 'path/to/blog/app.rb').to('/blog')
+#
+# You can also map apps to a specified host:
+#
+# Padrino.mount('Admin').host('admin.example.org')
+# Padrino.mount('WebSite').host(/.*\.?example.org/)
+# Padrino.mount('Foo').to('/foo').host('bar.example.org')
+#
+# Note 1: Mounted apps (by default) should be placed into the project root at '/app_name'.
+# Note 2: If you use the host matching remember to respect the order of the rules.
+#
+# By default, this file mounts the primary app which was generated with this project.
+# However, the mounted app can be modified as needed:
+#
+# Padrino.mount('AppName', :app_file => 'path/to/file', :app_class => 'BlogApp').to('/')
+#
-This file mounts each application within the Padrino project to a specific path.
-You can mount additional applications using any of these below:
-
- Padrino.mount("blog").to('/blog')
- Padrino.mount("blog", :app_class => "BlogApp").to('/blog')
- Padrino.mount("blog", :app_file => "/path/to/blog/app.rb").to('/blog')
-
-Note that mounted apps by default should be placed into 'apps/app_name'.
-
-By default, this file simply mounts the core app which was generated with this project.
-However, the mounted core can be modified as needed:
-
- Padrino.mount_core(:app_file => "/path/to/file", :app_class => "Blog")
-
-=end
+##
+# Setup global project settings for your apps. These settings are inherited by every subapp. You can
+# override these settings in the subapps as needed.
+#
+Padrino.configure_apps do
+ # enable :sessions
+ set :session_secret, 'f00d3cc34e78391d1482d52a83132490026b046dbbfc03796605b47adc5afea1'
+ set :protection, true
+ set :protect_from_csrf, true
+end
# Mounts the core application for this project
-Padrino.mount("SampleBlog").to("/")
-Padrino.mount("Admin").to("/admin")
+Padrino.mount('SampleBlog::App', :app_file => Padrino.root('app/app.rb')).to('/')
+
+Padrino.mount("SampleBlog::Admin", :app_file => File.expand_path('../../admin/app.rb', __FILE__)).to("/admin")
View
53 config/boot.rb
@@ -1,18 +1,45 @@
# Defines our constants
-PADRINO_ENV = ENV["PADRINO_ENV"] ||= ENV["RACK_ENV"] ||= "development" unless defined?(PADRINO_ENV)
-PADRINO_ROOT = File.dirname(__FILE__) + '/..' unless defined? PADRINO_ROOT
+PADRINO_ENV = ENV['PADRINO_ENV'] ||= ENV['RACK_ENV'] ||= 'development' unless defined?(PADRINO_ENV)
+PADRINO_ROOT = File.expand_path('../..', __FILE__) unless defined?(PADRINO_ROOT)
-begin
- # Require the preresolved locked set of gems.
- require File.expand_path('../../.bundle/environment', __FILE__)
-rescue LoadError
- # Fallback on doing the resolve at runtime.
- require 'rubygems'
- require 'bundler'
- Bundler.setup
+# Load our dependencies
+require 'rubygems' unless defined?(Gem)
+require 'bundler/setup'
+Bundler.require(:default, PADRINO_ENV)
+
+##
+# ## Enable devel logging
+#
+# Padrino::Logger::Config[:development][:log_level] = :devel
+# Padrino::Logger::Config[:development][:log_static] = true
+#
+# ## Configure your I18n
+#
+# I18n.default_locale = :en
+#
+# ## Configure your HTML5 data helpers
+#
+# Padrino::Helpers::TagHelpers::DATA_ATTRIBUTES.push(:dialog)
+# text_field :foo, :dialog => true
+# Generates: <input type="text" data-dialog="true" name="foo" />
+#
+# ## Add helpers to mailer
+#
+# Mail::Message.class_eval do
+# include Padrino::Helpers::NumberHelpers
+# include Padrino::Helpers::TranslationHelpers
+# end
+
+##
+# Add your before (RE)load hooks here
+#
+Padrino.before_load do
end
-Bundler.require(:default, PADRINO_ENV)
-puts "=> Located #{Padrino.bundle} Gemfile for #{Padrino.env}"
+##
+# Add your after (RE)load hooks here
+#
+Padrino.after_load do
+end
-Padrino.load!
+Padrino.load!
View
32 config/database.rb
@@ -1,10 +1,10 @@
##
# You can use other adapters like:
-#
+#
# ActiveRecord::Base.configurations[:development] = {
-# :adapter => 'mysql',
+# :adapter => 'mysql2',
# :encoding => 'utf8',
-# :reconnect => false,
+# :reconnect => true,
# :database => 'your_database',
# :pool => 5,
# :username => 'root',
@@ -15,24 +15,34 @@
#
ActiveRecord::Base.configurations[:development] = {
:adapter => 'sqlite3',
- :database => Padrino.root('db', "development.db")
+ :database => Padrino.root('db', 'development.db')
+
}
ActiveRecord::Base.configurations[:production] = {
:adapter => 'sqlite3',
- :database => Padrino.root('db', "development.db")
+ :database => Padrino.root('db', 'production.db')
+
}
ActiveRecord::Base.configurations[:test] = {
:adapter => 'sqlite3',
- :database => Padrino.root('db', "test.db")
+ :database => Padrino.root('db', 'test.db')
+
}
# Setup our logger
ActiveRecord::Base.logger = logger
+# Raise exception on mass assignment protection for Active Record models.
+ActiveRecord::Base.mass_assignment_sanitizer = :strict
+
+# Log the query plan for queries taking more than this (works
+# with SQLite, MySQL, and PostgreSQL).
+ActiveRecord::Base.auto_explain_threshold_in_seconds = 0.5
+
# Include Active Record class name as root for JSON serialized output.
-ActiveRecord::Base.include_root_in_json = true
+ActiveRecord::Base.include_root_in_json = false
# Store the full class name (including module namespace) in STI type column.
ActiveRecord::Base.store_full_sti_class = true
@@ -40,9 +50,9 @@
# Use ISO 8601 format for JSON serialized times and dates.
ActiveSupport.use_standard_json_time_format = true
-# Don't escape HTML entities in JSON, leave that for the #json_escape helper.
-# if you're including raw json in an HTML page.
+# Don't escape HTML entities in JSON, leave that for the #json_escape helper
+# if you're including raw JSON in an HTML page.
ActiveSupport.escape_html_entities_in_json = false
-# Now we can estabilish connection with our db
-ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Padrino.env])
+# Now we can establish connection with our db.
+ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Padrino.env])
View
BIN  db/development.db
Binary file not shown
View
14 db/schema.rb
@@ -1,10 +1,12 @@
-# This file is auto-generated from the current state of the database. Instead of editing this file,
-# please use the migrations feature of Active Record to incrementally modify your database, and
-# then regenerate this schema definition.
+# encoding: UTF-8
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
#
-# Note that this schema.rb definition is the authoritative source for your database schema. If you need
-# to create the application database on another system, you should be using db:schema:load, not running
-# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended to check this file into your version control system.
View
28 db/seeds.old
@@ -0,0 +1,28 @@
+# Seed add you the ability to populate your db.
+# We provide you a basic shell for interaction with the end user.
+# So try some code like below:
+#
+# name = shell.ask("What's your name?")
+# shell.say name
+#
+email = shell.ask "Which email do you want use for loggin into admin?"
+password = shell.ask "Tell me the password to use:"
+
+shell.say ""
+
+account = Account.create(:email => email, :name => "Foo", :surname => "Bar", :password => password, :password_confirmation => password, :role => "admin")
+
+if account.valid?
+ shell.say "================================================================="
+ shell.say "Account has been successfully created, now you can login with:"
+ shell.say "================================================================="
+ shell.say " email: #{email}"
+ shell.say " password: #{password}"
+ shell.say "================================================================="
+else
+ shell.say "Sorry but some thing went worng!"
+ shell.say ""
+ account.errors.full_messages.each { |m| shell.say " - #{m}" }
+end
+
+shell.say ""
View
10 db/seeds.rb
@@ -1,11 +1,11 @@
# Seed add you the ability to populate your db.
# We provide you a basic shell for interaction with the end user.
# So try some code like below:
-#
+#
# name = shell.ask("What's your name?")
# shell.say name
-#
-email = shell.ask "Which email do you want use for loggin into admin?"
+#
+email = shell.ask "Which email do you want use for logging into admin?"
password = shell.ask "Tell me the password to use:"
shell.say ""
@@ -20,9 +20,9 @@
shell.say " password: #{password}"
shell.say "================================================================="
else
- shell.say "Sorry but some thing went worng!"
+ shell.say "Sorry but some thing went wrong!"
shell.say ""
account.errors.full_messages.each { |m| shell.say " - #{m}" }
end
-shell.say ""
+shell.say ""
View
11 lib/sass_initializer.rb
@@ -0,0 +1,11 @@
+module SassInitializer
+ def self.registered(app)
+ # Enables support for SASS template reloading in rack applications.
+ # See http://nex-3.com/posts/88-sass-supports-rack for more details.
+ # Store SASS files (by default) within 'app/stylesheets'.
+ require 'sass/plugin/rack'
+ Sass::Plugin.options[:template_location] = Padrino.root("app/stylesheets")
+ Sass::Plugin.options[:css_location] = Padrino.root("public/stylesheets")
+ app.use Sass::Plugin::Rack
+ end
+end
View
38 models/account.rb
@@ -0,0 +1,38 @@
+class Account < ActiveRecord::Base
+ attr_accessor :password, :password_confirmation
+
+ # Validations
+ validates_presence_of :email, :role
+ validates_presence_of :password, :if => :password_required
+ validates_presence_of :password_confirmation, :if => :password_required
+ validates_length_of :password, :within => 4..40, :if => :password_required
+ validates_confirmation_of :password, :if => :password_required
+ validates_length_of :email, :within => 3..100
+ validates_uniqueness_of :email, :case_sensitive => false
+ validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
+ validates_format_of :role, :with => /[A-Za-z]/
+
+ # Callbacks
+ before_save :encrypt_password, :if => :password_required
+
+ ##
+ # This method is for authentication purpose.
+ #
+ def self.authenticate(email, password)
+ account = first(:conditions => ["lower(email) = lower(?)", email]) if email.present?
+ account && account.has_password?(password) ? account : nil
+ end
+
+ def has_password?(password)
+ ::BCrypt::Password.new(crypted_password) == password
+ end
+
+ private
+ def encrypt_password
+ self.crypted_password = ::BCrypt::Password.create(password)
+ end
+
+ def password_required
+ crypted_password.blank? || password.present?
+ end
+end
View
BIN  public/admin/images/favicon.ico
Binary file not shown
View
BIN  public/admin/images/font/FontAwesome.otf
Binary file not shown
View
BIN  public/admin/images/font/fontawesome-webfont.eot
Binary file not shown
View
399 public/admin/images/font/fontawesome-webfont.svg
399 additions, 0 deletions not shown
View
BIN  public/admin/images/font/fontawesome-webfont.ttf
Binary file not shown
View
BIN  public/admin/images/font/fontawesome-webfont.woff
Binary file not shown
View
BIN  public/admin/images/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
117 public/admin/javascripts/application.js
@@ -0,0 +1,117 @@
+!function($) {
+ 'use strict';
+
+ $(function() {
+ function toggleAction(selector, disabled) {
+ var method = disabled ? 'addClass' : 'removeClass';
+ $(selector)[method]('list-menu-link-disabled').parent()[method]('list-menu-wrapper-disabled');
+ }
+ // Check/uncheck all functionality
+ function checkAll(base, checked) {
+ // Toggle all checkboxes on the table's body that exist on the first column.
+ base.find(listCheckboxesSelector).prop('checked', checked);
+ base.find('.list-row')[checked ? 'addClass' : 'removeClass']('list-row-selected');
+ toggleAction('#delete-selected', !checked);
+ }
+ function generalToggle() {
+ var checked = listCheckboxes.filter(':checked').length;
+ toggleAction('#delete-selected', checked === 0);
+ toggleAction('#deselect-all', checked === 0);
+ toggleAction('#select-all', checked === listCheckboxesLength);
+ }
+
+ var listCheckboxesSelector = '.list-selectable-checkbox', list = $('#list'), alertTimeout = 4000, listCheckboxes, listCheckboxesLength;
+
+ // Automatically close alerts if there was any present.
+ if ($('.alert').length > 0) {
+ setTimeout(function() { $('.alert').alert('close'); }, alertTimeout);
+ }
+
+ // Only process list-related JavaScript if there's a list!
+ if (list.length > 0) {
+ listCheckboxes = list.find(listCheckboxesSelector);
+ listCheckboxesLength = listCheckboxes.length;
+
+ // Confirm before deleting one item
+ $('.list-row-action-delete-one').on('click', function(ev) {
+ ev.preventDefault();
+ $(this).addClass('list-row-action-wrapper-link-active')
+ .siblings('.list-row-action-popover-delete-one').first().show()
+ .find('.cancel').on('click', function() {
+
+ $(this).parents('.list-row-action-popover-delete-one').hide()
+ .siblings('.list-row-action-delete-one').removeClass('list-row-action-wrapper-link-active');
+ });
+ });
+
+ // Select/deselect record on row's click
+ list.find('.list-row').on('click', function(ev) {
+ var checkbox, willBeChecked;
+ ev.stopPropagation();
+
+ if (ev.currentTarget.tagName == 'TR') {
+ checkbox = $(this).find('.list-selectable-checkbox');
+ willBeChecked = !checkbox.prop('checked');
+ checkbox.prop('checked', willBeChecked);
+ $(this)[willBeChecked ? 'addClass' : 'removeClass']('list-row-selected');
+ generalToggle();
+ }
+ });
+ // Select all action
+ $('#select-all').on('click', function(ev) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ if ($(this).is('.list-menu-link-disabled')) return;
+
+ // We assume we want to stay on the dropdown to delete all perhaps
+ ev.stopPropagation();
+ checkAll(list, true);
+ toggleAction('#select-all', true);
+ toggleAction('#deselect-all', false);
+ });
+ // Deselect all action
+ $('#deselect-all').on('click', function(ev) {
+ ev.preventDefault();
+ if ($(this).is('.list-menu-link-disabled')) return;
+
+ checkAll(list, false);
+ toggleAction('#deselect-all', true);
+ toggleAction('#select-all', false);
+ });
+ // Delete selected
+ $('#delete-selected').on('click', function(ev) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ if ($(this).is('.list-menu-link-disabled')) return;
+
+ // Open the popup to confirm deletion
+ $(this).parent().addClass('active').parent('.dropdown').addClass('open');
+ $(this).addClass('active')
+ .siblings('.list-menu-popover-delete-selected').first().show()
+ .find('.cancel').on('click', function() {
+
+ // Hide the popover on cancel
+ $(this).parents('.list-menu-popover-delete-selected').hide()
+ .siblings('#delete-selected').removeClass('active').parent().removeClass('active');
+ // and close the dropdown
+ $(this).parents('.dropdown').removeClass('open');
+ });
+
+ $(this).siblings('.list-menu-popover-delete-selected').find(':hidden[data-delete-many-ids=true]').
+ val(listCheckboxes.filter(':checked').map(function() { return $(this).val(); }).toArray().join(','));
+ });
+
+ // Catch checkboxes check/uncheck and enable/disable the delete selected functionality
+ listCheckboxes.on('click', function(ev) {
+ ev.stopPropagation();
+
+ $(this).parent('.list-row')[$(this).is(':checked') ? 'addClass' : 'removeClass']('list-row-selected');
+
+ generalToggle();
+ });
+ }
+
+ // Autofocus first field with an error. (usability)
+ $('.has-error :input').first().focus();
+ });
+}(window.jQuery);
View
126 public/admin/javascripts/bootstrap/affix.js
@@ -0,0 +1,126 @@
+/* ========================================================================
+ * Bootstrap: affix.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#affix
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // AFFIX CLASS DEFINITION
+ // ======================
+
+ var Affix = function (element, options) {
+ this.options = $.extend({}, Affix.DEFAULTS, options)
+ this.$window = $(window)
+ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
+
+ this.$element = $(element)
+ this.affixed =
+ this.unpin = null
+
+ this.checkPosition()
+ }
+
+ Affix.RESET = 'affix affix-top affix-bottom'
+
+ Affix.DEFAULTS = {
+ offset: 0
+ }
+
+ Affix.prototype.checkPositionWithEventLoop = function () {
+ setTimeout($.proxy(this.checkPosition, this), 1)
+ }
+
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
+
+ var scrollHeight = $(document).height()
+ var scrollTop = this.$window.scrollTop()
+ var position = this.$element.offset()
+ var offset = this.options.offset
+ var offsetTop = offset.top
+ var offsetBottom = offset.bottom
+
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top()
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
+
+ var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
+ offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
+ offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
+
+ if (this.affixed === affix) return
+ if (this.unpin) this.$element.css('top', '')
+
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? position.top - scrollTop : null
+
+ this.$element.removeClass(Affix.RESET).addClass('affix' + (affix ? '-' + affix : ''))
+
+ if (affix == 'bottom') {
+ this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })
+ }
+ }
+
+
+ // AFFIX PLUGIN DEFINITION
+ // =======================
+
+ var old = $.fn.affix
+
+ $.fn.affix = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.affix')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.affix.Constructor = Affix
+
+
+ // AFFIX NO CONFLICT
+ // =================
+
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
+
+
+ // AFFIX DATA-API
+ // ==============
+
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ var data = $spy.data()
+
+ data.offset = data.offset || {}
+
+ if (data.offsetBottom) data.offset.bottom = data.offsetBottom
+ if (data.offsetTop) data.offset.top = data.offsetTop
+
+ $spy.affix(data)
+ })
+ })
+
+}(window.jQuery);
View
98 public/admin/javascripts/bootstrap/alert.js
@@ -0,0 +1,98 @@
+/* ========================================================================
+ * Bootstrap: alert.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#alerts
+ * ========================================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // ALERT CLASS DEFINITION
+ // ======================
+
+ var dismiss = '[data-dismiss="alert"]'
+ var Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
+
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ var $parent = $(selector)
+
+ if (e) e.preventDefault()
+
+ if (!$parent.length) {
+ $parent = $this.hasClass('alert') ? $this : $this.parent()
+ }
+
+ $parent.trigger(e = $.Event('close.bs.alert'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent.removeClass('in')
+
+ function removeElement() {
+ $parent.trigger('closed.bs.alert').remove()
+ }
+
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent
+ .one($.support.transition.end, removeElement)
+ .emulateTransitionEnd(150) :
+ removeElement()
+ }
+
+
+ // ALERT PLUGIN DEFINITION
+ // =======================
+
+ var old = $.fn.alert
+
+ $.fn.alert = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.alert')
+
+ if (!data) $this.data('bs.alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.alert.Constructor = Alert
+
+
+ // ALERT NO CONFLICT
+ // =================
+
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
+
+
+ // ALERT DATA-API
+ // ==============
+
+ $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+
+}(window.jQuery);
View
6 public/admin/javascripts/bootstrap/bootstrap.min.js
@@ -0,0 +1,6 @@
+/**
+* bootstrap.js v3.0.0 by @fat and @mdo
+* Copyright 2013 Twitter Inc.
+* http://www.apache.org/licenses/LICENSE-2.0
+*/
+if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",b),f.trigger(d=a.Event("show.bs.dropdown")),d.isDefaultPrevented())return;f.toggleClass("open").trigger("shown.bs.dropdown"),e.focus()}return!1}},f.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var f=c(d),g=f.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&f.find(e).focus(),d.click();var h=a("[role=menu] li:not(.divider):visible a",f);if(h.length){var i=h.index(h.filter(":focus"));38==b.keyCode&&i>0&&i--,40==b.keyCode&&i<h.length-1&&i++,~i||(i=0),h.eq(i).focus()}}}};var g=a.fn.dropdown;a.fn.dropdown=function(b){return this.each(function(){var c=a(this),d=c.data("dropdown");d||c.data("dropdown",d=new f(this)),"string"==typeof b&&d[b].call(c)})},a.fn.dropdown.Constructor=f,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=g,this},a(document).on("click.bs.dropdown.data-api",b).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",e,f.prototype.toggle).on("keydown.bs.dropdown.data-api",e+", [role=menu]",f.prototype.keydown)}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.options=c,this.$element=a(b),this.$backdrop=this.isShown=null,this.options.remote&&this.$element.load(this.options.remote)};b.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},b.prototype.toggle=function(a){return this[this.isShown?"hide":"show"](a)},b.prototype.show=function(b){var c=this,d=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(d),this.isShown||d.isDefaultPrevented()||(this.isShown=!0,this.escape(),this.$element.on("click.dismiss.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.backdrop(function(){var d=a.support.transition&&c.$element.hasClass("fade");c.$element.parent().length||c.$element.appendTo(document.body),c.$element.show(),d&&c.$element[0].offsetWidth,c.$element.addClass("in").attr("aria-hidden",!1),c.enforceFocus();var e=a.Event("shown.bs.modal",{relatedTarget:b});d?c.$element.find(".modal-dialog").one(a.support.transition.end,function(){c.$element.focus().trigger(e)}).emulateTransitionEnd(300):c.$element.focus().trigger(e)}))},b.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one(a.support.transition.end,a.proxy(this.hideModal,this)).emulateTransitionEnd(300):this.hideModal())},b.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.focus()},this))},b.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keyup.dismiss.bs.modal")},b.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.removeBackdrop(),a.$element.trigger("hidden.bs.modal")})},b.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},b.prototype.backdrop=function(b){var c=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var d=a.support.transition&&c;if(this.$backdrop=a('<div class="modal-backdrop '+c+'" />').appendTo(document.body),this.$element.on("click.dismiss.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),d&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;d?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()):b&&b()};var c=a.fn.modal;a.fn.modal=function(c,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},b.DEFAULTS,e.data(),"object"==typeof c&&c);f||e.data("bs.modal",f=new b(this,g)),"string"==typeof c?f[c](d):g.show&&f.show(d)})},a.fn.modal.Constructor=b,a.fn.modal.noConflict=function(){return a.fn.modal=c,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());b.preventDefault(),e.modal(f,this).one("hide",function(){c.is(":visible")&&c.focus()})}),a(document).on("show.bs.modal",".modal",function(){a(document.body).addClass("modal-open")}).on("hidden.bs.modal",".modal",function(){a(document.body).removeClass("modal-open")})}(window.jQuery),+function(a){"use strict";var b=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};b.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},b.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focus",i="hover"==g?"mouseleave":"blur";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},b.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},b.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show),void 0):c.show()},b.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide),void 0):c.hide()},b.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){if(this.$element.trigger(b),b.isDefaultPrevented())return;var c=this.tip();this.setContent(),this.options.animation&&c.addClass("fade");var d="function"==typeof this.options.placement?this.options.placement.call(this,c[0],this.$element[0]):this.options.placement,e=/\s?auto?\s?/i,f=e.test(d);f&&(d=d.replace(e,"")||"top"),c.detach().css({top:0,left:0,display:"block"}).addClass(d),this.options.container?c.appendTo(this.options.container):c.insertAfter(this.$element);var g=this.getPosition(),h=c[0].offsetWidth,i=c[0].offsetHeight;if(f){var j=this.$element.parent(),k=d,l=document.documentElement.scrollTop||document.body.scrollTop,m="body"==this.options.container?window.innerWidth:j.outerWidth(),n="body"==this.options.container?window.innerHeight:j.outerHeight(),o="body"==this.options.container?0:j.offset().left;d="bottom"==d&&g.top+g.height+i-l>n?"top":"top"==d&&g.top-l-i<0?"bottom":"right"==d&&g.right+h>m?"left":"left"==d&&g.left-h<o?"right":d,c.removeClass(k).addClass(d)}var p=this.getCalculatedOffset(d,g,h,i);this.applyPlacement(p,d),this.$element.trigger("shown.bs."+this.type)}},b.prototype.applyPlacement=function(a,b){var c,d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),a.top=a.top+g,a.left=a.left+h,d.offset(a).addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;if("top"==b&&j!=f&&(c=!0,a.top=a.top+f-j),/bottom|top/.test(b)){var k=0;a.left<0&&(k=-2*a.left,a.left=0,d.offset(a),i=d[0].offsetWidth,j=d[0].offsetHeight),this.replaceArrow(k-e+i,i,"left")}else this.replaceArrow(j-f,j,"top");c&&d.offset(a)},b.prototype.replaceArrow=function(a,b,c){this.arrow().css(c,a?50*(1-a/b)+"%":"")},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},b.prototype.hide=function(){function b(){"in"!=c.hoverState&&d.detach()}var c=this,d=this.tip(),e=a.Event("hide.bs."+this.type);return this.$element.trigger(e),e.isDefaultPrevented()?void 0:(d.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d.one(a.support.transition.end,b).emulateTransitionEnd(150):b(),this.$element.trigger("hidden.bs."+this.type),this)},b.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},b.prototype.hasContent=function(){return this.getTitle()},b.prototype.getPosition=function(){var b=this.$element[0];return a.extend({},"function"==typeof b.getBoundingClientRect?b.getBoundingClientRect():{width:b.offsetWidth,height:b.offsetHeight},this.$element.offset())},b.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},b.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},b.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},b.prototype.validate=function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},b.prototype.enable=function(){this.enabled=!0},b.prototype.disable=function(){this.enabled=!1},b.prototype.toggleEnabled=function(){this.enabled=!this.enabled},b.prototype.toggle=function(b){var c=b?a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type):this;c.tip().hasClass("in")?c.leave(c):c.enter(c)},b.prototype.destroy=function(){this.hide().$element.off("."+this.type).removeData("bs."+this.type)};var c=a.fn.tooltip;a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof c&&c;e||d.data("bs.tooltip",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=c,this}}(window.jQuery),+function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");b.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery);
View
109 public/admin/javascripts/bootstrap/button.js
@@ -0,0 +1,109 @@
+/* ========================================================================
+ * Bootstrap: button.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#buttons
+ * ========================================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // BUTTON PUBLIC CLASS DEFINITION
+ // ==============================
+
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Button.DEFAULTS, options)
+ }
+
+ Button.DEFAULTS = {
+ loadingText: 'loading...'
+ }
+
+ Button.prototype.setState = function (state) {
+ var d = 'disabled'
+ var $el = this.$element
+ var val = $el.is('input') ? 'val' : 'html'
+ var data = $el.data()
+
+ state = state + 'Text'
+
+ if (!data.resetText) $el.data('resetText', $el[val]())
+
+ $el[val](data[state] || this.options[state])
+
+ // push to event loop to allow forms to submit
+ setTimeout(function () {
+ state == 'loadingText' ?
+ $el.addClass(d).attr(d, d) :
+ $el.removeClass(d).removeAttr(d);
+ }, 0)
+ }
+
+ Button.prototype.toggle = function () {
+ var $parent = this.$element.closest('[data-toggle="buttons"]')
+
+ if ($parent.length) {
+ var $input = this.$element.find('input')
+ .prop('checked', !this.$element.hasClass('active'))
+ .trigger('change')
+ if ($input.prop('type') === 'radio') $parent.find('.active').removeClass('active')
+ }
+
+ this.$element.toggleClass('active')
+ }
+
+
+ // BUTTON PLUGIN DEFINITION
+ // ========================
+
+ var old = $.fn.button
+
+ $.fn.button = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.button')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.button', (data = new Button(this, options)))
+
+ if (option == 'toggle') data.toggle()
+ else if (option) data.setState(option)
+ })
+ }
+
+ $.fn.button.Constructor = Button
+
+
+ // BUTTON NO CONFLICT
+ // ==================
+
+ $.fn.button.noConflict = function () {
+ $.fn.button = old
+ return this
+ }
+
+
+ // BUTTON DATA-API
+ // ===============
+
+ $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ $btn.button('toggle')
+ e.preventDefault()
+ })
+
+}(window.jQuery);
View
217 public/admin/javascripts/bootstrap/carousel.js
@@ -0,0 +1,217 @@
+/* ========================================================================
+ * Bootstrap: carousel.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#carousel
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // CAROUSEL CLASS DEFINITION
+ // =========================
+
+ var Carousel = function (element, options) {
+ this.$element = $(element)
+ this.$indicators = this.$element.find('.carousel-indicators')