diff --git a/.gitignore b/.gitignore index d89b7f8e..2ded5f16 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,8 @@ pkg/* *.swp *~ .rvmrc +coverage/* +.ruby-version +.ruby-gemset + +.idea/ \ No newline at end of file diff --git a/.rspec b/.rspec new file mode 100644 index 00000000..5f164763 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--format progress diff --git a/.travis.yml b/.travis.yml index 44e518a9..a3c1be68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: ruby rvm: - - 1.8.7 - - 1.9.2 - 1.9.3 - - ree + - 2.0.0 + - 2.1.1 +script: bundle exec rake spec diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..7a44faaf --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,54 @@ + \ No newline at end of file diff --git a/CONTRIBUTERS.md b/CONTRIBUTERS.md new file mode 100644 index 00000000..c252cfdd --- /dev/null +++ b/CONTRIBUTERS.md @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e56daa34 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +twitter-bootstrap-rails is a volunteer effort. We encourage you to pitch in. + +__*We only accept bug reports and pull requests in GitHub*__. Join the team! + +* If you have a support question about how to use twitter-bootstrap-rails, please [ask on StackOverflow](http://stackoverflow.com/search?tab=newest&q=twitter-bootstrap-rails). + +* Bug reports should include the following: + - Minimal example of code to reproduce the bug + - Stacktrace + - What you expected to see, and what you actually saw. + +* Feature requests should be accompanied by a patch, that includes tests. +* We won't accept any feature requests that come without a patch. + +Thanks for the your contributions! \ No newline at end of file diff --git a/Gemfile b/Gemfile index 53db076a..33e13c85 100644 --- a/Gemfile +++ b/Gemfile @@ -3,10 +3,21 @@ source "http://rubygems.org" # Specify your gem's dependencies in twitter-bootstrap-rails.gemspec gemspec gem 'less-rails', :path => ENV['LESS_RAILS_SOURCE'] if ENV['LESS_RAILS_SOURCE'] +gem 'activesupport', '< 4.0.0' if RUBY_VERSION < '1.9.3' +group :development, :test do + gem 'rb-readline' + gem 'guard' + gem 'guard-rspec' + gem 'pry' +end group :test do gem 'minitest' gem 'mocha' gem 'rake' gem 'turn' + gem 'coveralls' + gem 'rspec' + gem 'rspec-html-matchers' + gem 'rspec-activemodel-mocks' end diff --git a/Guardfile b/Guardfile new file mode 100644 index 00000000..e92770c8 --- /dev/null +++ b/Guardfile @@ -0,0 +1,61 @@ +# Note: The cmd option is now required due to the increasing number of ways +# rspec may be run, below are examples of the most common uses. +# * bundler: 'bundle exec rspec' +# * bundler binstubs: 'bin/rspec' +# * spring: 'bin/rsspec' (This will use spring if running and you have +# installed the spring binstubs per the docs) +# * zeus: 'zeus rspec' (requires the server to be started separetly) +# * 'just' rspec: 'rspec' +guard :rspec, cmd: 'bundle exec rspec' do + watch(%r{^spec/.+_spec\.rb$}) + watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } + watch('spec/spec_helper.rb') { "spec" } + + # Rails example + watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } + watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } + watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } + watch(%r{^spec/support/(.+)\.rb$}) { "spec" } + watch('config/routes.rb') { "spec/routing" } + watch('app/controllers/application_controller.rb') { "spec/controllers" } + watch('spec/rails_helper.rb') { "spec" } + + # Capybara features specs + watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" } + + # Turnip features and steps + watch(%r{^spec/acceptance/(.+)\.feature$}) + watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } +end + + +# Note: The cmd option is now required due to the increasing number of ways +# rspec may be run, below are examples of the most common uses. +# * bundler: 'bundle exec rspec' +# * bundler binstubs: 'bin/rspec' +# * spring: 'bin/rsspec' (This will use spring if running and you have +# installed the spring binstubs per the docs) +# * zeus: 'zeus rspec' (requires the server to be started separetly) +# * 'just' rspec: 'rspec' +guard :rspec, cmd: 'bundle exec rspec' do + watch(%r{^spec/.+_spec\.rb$}) + watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } + watch('spec/spec_helper.rb') { "spec" } + + # Rails example + watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } + watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } + watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } + watch(%r{^spec/support/(.+)\.rb$}) { "spec" } + watch('config/routes.rb') { "spec/routing" } + watch('app/controllers/application_controller.rb') { "spec/controllers" } + watch('spec/rails_helper.rb') { "spec" } + + # Capybara features specs + watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" } + + # Turnip features and steps + watch(%r{^spec/acceptance/(.+)\.feature$}) + watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } +end + diff --git a/README.md b/README.md index 5c1321da..288a7dfd 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ -# Twitter Bootstrap for Rails 3.1 Asset Pipeline +# Twitter Bootstrap 3.2 for Rails 4 Asset Pipeline Bootstrap is a toolkit from Twitter designed to kickstart development of webapps and sites. It includes base CSS and HTML for typography, forms, buttons, tables, grids, navigation, and more. -twitter-bootstrap-rails project integrates Bootstrap CSS toolkit for Rails 3.1 Asset Pipeline (Rails 3.2 supported) - -[![Build Status](https://secure.travis-ci.org/seyhunak/twitter-bootstrap-rails.png)](http://travis-ci.org/seyhunak/twitter-bootstrap-rails) -[![Dependency Status](https://gemnasium.com/seyhunak/twitter-bootstrap-rails.png)](https://gemnasium.com/seyhunak/twitter-bootstrap-rails) -[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/seyhunak/twitter-bootstrap-rails) -[![Still Maintained](https://a248.e.akamai.net/camo.github.com/9c977523be7fce95c026a1b7d9673903f82e59cd/687474703a2f2f7374696c6c6d61696e7461696e65642e636f6d2f7374696c6c6d61696e7461696e65642f7374696c6c6d61696e7461696e65642e706e67)](http://stillmaintained.com/seyhunak/twitter-bootstrap-rails) +twitter-bootstrap-rails project integrates Bootstrap CSS toolkit for Rails Asset Pipeline (Rails 4, 3.1, 3.2 are supported) +[![Gem Version](https://badge.fury.io/rb/twitter-bootstrap-rails.svg)](http://badge.fury.io/rb/twitter-bootstrap-rails) +[![Build Status](https://travis-ci.org/seyhunak/twitter-bootstrap-rails.svg?branch=master)](https://secure.travis-ci.org/seyhunak/twitter-bootstrap-rails?branch=master) +[![Dependency Status](https://gemnasium.com/seyhunak/twitter-bootstrap-rails.svg?travis)](https://gemnasium.com/seyhunak/twitter-bootstrap-rails?travis) +[![Code Climate](https://codeclimate.com/github/seyhunak/twitter-bootstrap-rails/badges/gpa.svg)](https://codeclimate.com/github/seyhunak/twitter-bootstrap-rails?branch=master) +[![Coverage Status](https://coveralls.io/repos/seyhunak/twitter-bootstrap-rails/badge.png?branch=master)](https://coveralls.io/repos/seyhunak/twitter-bootstrap-rails/badge.png?branch=master) +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/seyhunak/twitter-bootstrap-rails/trend.png)](https://bitdeli.com/free "Bitdeli Badge") ## Screencasts #### Installing twitter-bootstrap-rails, generators, usage and more @@ -16,29 +17,24 @@ twitter-bootstrap-rails project integrates Bootstrap CSS toolkit for Rails 3.1 A Screencasts provided by Railscasts (Ryan Bates) [Twitter Bootstrap Basics](http://railscasts.com/episodes/328-twitter-bootstrap-basics "Twitter Bootstrap Basics") -in this episode you will learn how to include Twitter Bootstrap into Rails application with the twitter-bootstrap-rails gem. +in this episode you will learn how to include Bootstrap into Rails application with the twitter-bootstrap-rails gem. [More on Twitter Bootstrap](http://railscasts.com/episodes/329-more-on-twitter-bootstrap "More on Twitter Bootstrap") -in this episode continues on the Twitter Bootstrap project showing how to display flash messages, add form validations with SimpleForm, customize layout with variables, and switch to using Sass. +in this episode continues on the Bootstrap project showing how to display flash messages, add form validations with SimpleForm, customize layout with variables, and switch to using Sass. (Note: This episode is pro episode) - -## Example Application -An example application is available at [toadkicker/teststrap](https://github.com/toadkicker/teststrap). You can view it running on heroku [here.](http://teststrap.herokuapp.com/) Contributions welcome. - - ## Installing the Gem -The [Twitter Bootstrap Rails gem](http://rubygems.org/gems/twitter-bootstrap-rails) can provide the Twitter Bootstrap stylesheets in two ways. +The [Twitter Bootstrap Rails gem](http://rubygems.org/gems/twitter-bootstrap-rails) can provide the Bootstrap stylesheets in two ways. -The plain CSS way is how Twitter Bootstrap is provided on [the official website](http://twitter.github.com/bootstrap/). +The plain CSS way is how Bootstrap is provided on [the official website](http://twbs.github.io/bootstrap/). -The [Less](http://lesscss.org/) way provides more customisation options, like changing theme colors, and provides useful Less mixins for your code, but requires the +The [Less](http://lesscss.org/) way provides more customization options, like changing theme colors, and provides useful Less mixins for your code, but requires the Less gem and the Ruby Racer Javascript runtime (not available on Microsoft Windows). ### Installing the Less stylesheets -To use Less stylesheets, you'll need the [less-rails gem](http://rubygems.org/gems/less-rails), and one of [Javascript runtimes supported by CommonJS](https://github.com/cowboyd/commonjs.rb#supported-runtimes). +To use Less stylesheets, you'll need the [less-rails gem](http://rubygems.org/gems/less-rails), and one of [JavaScript runtimes supported by CommonJS](https://github.com/cowboyd/commonjs.rb#supported-runtimes). Include these lines in the Gemfile to install the gems from [RubyGems.org](http://rubygems.org): @@ -62,6 +58,10 @@ Then run the bootstrap generator to add Bootstrap includes into your assets: rails generate bootstrap:install less +If you need to skip coffeescript replacement into app generators, use: + + rails generate bootstrap:install --no-coffeescript + ### Installing the CSS stylesheets If you don't need to customize the stylesheets using Less, the only gem you need is the `twitter-bootstrap-rails` gem: @@ -76,31 +76,19 @@ After running `bundle install`, run the generator: ## Generating layouts and views -You can run following generators to get started with Twitter Bootstrap quickly. +You can run following generators to get started with Bootstrap quickly. -Layout (generates Twitter Bootstrap compatible layout) - (Haml and Slim supported) +Layout (generates Bootstrap compatible layout) - (Haml and Slim supported) Usage: - rails g bootstrap:layout [LAYOUT_NAME] [*fixed or fluid] - - -Example of a fixed layout: - - - rails g bootstrap:layout application fixed + rails g bootstrap:layout [LAYOUT_NAME] -Example of a responsive layout: - - - rails g bootstrap:layout application fluid - - -Themed (generates Twitter Bootstrap compatible scaffold views.) - (Haml and Slim supported) +Themed (generates Bootstrap compatible scaffold views.) - (Haml and Slim supported) Usage: @@ -137,54 +125,132 @@ You have to require Bootstrap LESS (bootstrap_and_overrides.css.less) in your ap To use individual components from bootstrap, your bootstrap_and_overrides.less could look like this: ```css -@import "twitter/bootstrap/reset.less"; +// Core variables and mixins @import "twitter/bootstrap/variables.less"; @import "twitter/bootstrap/mixins.less"; + +// Reset and dependencies +@import "twitter/bootstrap/normalize.less"; +@import "twitter/bootstrap/print.less"; +//@import "twitter/bootstrap/glyphicons.less"; // Excludes glyphicons + +// Core CSS @import "twitter/bootstrap/scaffolding.less"; -@import "twitter/bootstrap/grid.less"; -@import "twitter/bootstrap/layouts.less"; @import "twitter/bootstrap/type.less"; +@import "twitter/bootstrap/code.less"; +@import "twitter/bootstrap/grid.less"; +@import "twitter/bootstrap/tables.less"; @import "twitter/bootstrap/forms.less"; -@import "twitter/bootstrap/wells.less"; -@import "twitter/bootstrap/component-animations.less"; @import "twitter/bootstrap/buttons.less"; -@import "twitter/bootstrap/close.less"; + +// Components +@import "twitter/bootstrap/component-animations.less"; +@import "twitter/bootstrap/dropdowns.less"; +@import "twitter/bootstrap/button-groups.less"; +@import "twitter/bootstrap/input-groups.less"; @import "twitter/bootstrap/navs.less"; @import "twitter/bootstrap/navbar.less"; -@import "twitter/bootstrap/labels-badges.less"; -@import "twitter/bootstrap/hero-unit.less"; +@import "twitter/bootstrap/breadcrumbs.less"; +@import "twitter/bootstrap/pagination.less"; +@import "twitter/bootstrap/pager.less"; +@import "twitter/bootstrap/labels.less"; +@import "twitter/bootstrap/badges.less"; +@import "twitter/bootstrap/jumbotron.less"; +@import "twitter/bootstrap/thumbnails.less"; +@import "twitter/bootstrap/alerts.less"; +@import "twitter/bootstrap/progress-bars.less"; +@import "twitter/bootstrap/media.less"; +@import "twitter/bootstrap/list-group.less"; +@import "twitter/bootstrap/panels.less"; +@import "twitter/bootstrap/responsive-embed.less"; +@import "twitter/bootstrap/wells.less"; +@import "twitter/bootstrap/close.less"; + +// Components w/ JavaScript +@import "twitter/bootstrap/modals.less"; +@import "twitter/bootstrap/tooltip.less"; +@import "twitter/bootstrap/popovers.less"; +@import "twitter/bootstrap/carousel.less"; + +// Utility classes @import "twitter/bootstrap/utilities.less"; -@import "twitter/bootstrap/responsive"; +@import "twitter/bootstrap/responsive-utilities.less"; ``` If you'd like to alter Bootstrap's own variables, or define your LESS styles inheriting Bootstrap's mixins, you can do so inside bootstrap_and_overrides.css.less: + ```css -@linkColor: #ff0000; +@link-color: #ff0000; ``` +### SASS + +If you are using SASS to compile your application.css (e.g. your manifest file is application.css.sass or application.css.scss) you may get this: + +``` +Invalid CSS after "*": expected "{", was "= require twitt..." +(in app/assets/stylesheets/application.css) +(sass) +``` + +If this is the case, you **must** use @import instead of `*=` in your manifest file, or don't compile your manifest with SASS. + ### Icons By default, this gem overrides standard Bootstraps's Glyphicons with Font Awesome (http://fortawesome.github.com/Font-Awesome/). -If you would like to restore the default Glyphicons, inside the _bootstrap_and_overrides.css.less_ remove the FontAwesome declaration and uncomment the line: + +This should appear inside _bootstrap_and_overrides *(based on you twitter-bootstrap-rails version)* + +**From 2.2.7** + +```css +// Font Awesome +@fontAwesomeEotPath: asset-url("fontawesome-webfont.eot"); +@fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot?#iefix"); +@fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff"); +@fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf"); +@fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular"); +@import "fontawesome/font-awesome"; +``` + +**Before 2.2.7** ```css // Font Awesome -// @import "fontawesome"; +@fontAwesomeEotPath: "/assets/fontawesome-webfont.eot"; +@fontAwesomeEotPath_iefix: "/assets/fontawesome-webfont.eot?#iefix"; +@fontAwesomeWoffPath: "/assets/fontawesome-webfont.woff"; +@fontAwesomeTtfPath: "/assets/fontawesome-webfont.ttf"; +@fontAwesomeSvgPath: "/assets/fontawesome-webfont.svg#fontawesomeregular"; +@import "fontawesome"; +``` + +If you would like to restore the default Glyphicons, inside the _bootstrap_and_overrides.css.less_ remove the FontAwesome declaration and uncomment the line: + +```less +// Font Awesome +// @fontAwesomeEotPath: asset-url("fontawesome-webfont.eot"); +// @fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot?#iefix"); +// @fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff"); +// @fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf"); +// @fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular"); +// @import "fontawesome/font-awesome"; + // Glyphicons -@import "twitter/bootstrap/sprites.less"; +@import "twitter/bootstrap/glyphicons.less"; ``` -## Using Javascripts +## Using JavaScript -You have to require Bootstrap JS (bootstrap.js) in your application.js +Require Bootstrap JS (bootstrap.js) in your application.js ```js //= require twitter/bootstrap $(function(){ - /* Your javascripts goes here... */ + /* Your JavaScript goes here... */ }); ``` @@ -193,37 +259,333 @@ If you want to customize what is loaded, your application.js would look somethin ```js #= require jquery #= require jquery_ujs -#= require twitter/bootstrap/bootstrap-transition -#= require twitter/bootstrap/bootstrap-alert -#= require twitter/bootstrap/bootstrap-modal -#= require twitter/bootstrap/bootstrap-button -#= require twitter/bootstrap/bootstrap-collapse +#= require twitter/bootstrap/transition +#= require twitter/bootstrap/alert +#= require twitter/bootstrap/modal +#= require twitter/bootstrap/button +#= require twitter/bootstrap/collapse ``` ...and so on for each bootstrap js component. -## Using Coffeescript (optionally) +## Using CoffeeScript (optionally) -Using Twitter Bootstrap with the CoffeeScript is easy. +Using Bootstrap with the CoffeeScript is easy. twitter-bootstrap-rails generates a "bootstrap.js.coffee" file for you to /app/assets/javascripts/ folder. ```coffee jQuery -> - $("a[rel=popover]").popover() - $(".tooltip").tooltip() - $("a[rel=tooltip]").tooltip() + $("a[rel~=popover], .has-popover").popover() + $("a[rel~=tooltip], .has-tooltip").tooltip() ``` ## Using Helpers +### Modal Helper +You can create modals easily using the following example. The header, body, and footer all accept content_tag or plain html. +The href of the button to launch the modal must match the id of the modal dialog. It also accepts a block for the header, body, and footer. If you are getting a complaint about the modal_helper unable to merge a hash it is due to this. + +```` +<%= content_tag :a, "Modal", href: "#modal", class: 'btn', data: {toggle: 'modal'} %> + +<%= modal_dialog id: "modal", + header: { show_close: true, dismiss: 'modal', title: 'Modal header' }, + body: { content: 'This is the body' }, + footer: { content: content_tag(:button, 'Save', class: 'btn') } %> + +```` + +### Navbar Helper +It should let you write things like: + +```` + +<%= nav_bar fixed: :top, brand: "Fashionable Clicheizr 2.0", responsive: true do %> + <%= menu_group do %> + <%= menu_item "Home", root_path %> + <%= menu_divider %> + <%= drop_down "Products" do %> + <%= menu_item "Things you can't afford", expensive_products_path %> + <%= menu_item "Things that won't suit you anyway", harem_pants_path %> + <%= menu_item "Things you're not even cool enough to buy anyway", hipster_products_path %> + <% if current_user.lives_in_hackney? %> + <%= menu_item "Bikes", fixed_wheel_bikes_path %> + <% end %> + <% end %> + <%= menu_item "About Us", about_us_path %> + <%= menu_item "Contact", contact_path %> + <% end %> + <%= menu_group pull: :right do %> + <% if current_user %> + <%= menu_item "Log Out", log_out_path %> + <% else %> + <%= form_for @user, url: session_path(:user), html => {class: "navbar-form pull-right"} do |f| -%> +

<%= f.text_field :email %>

+

<%= f.password_field :password %>

+

<%= f.submit "Sign in" %>

+ <% end -%> + <% end %> + <% end %> +<% end %> + + +```` + +### Navbar scaffolding + +In your view file (most likely application.html.erb) to get a basic navbar set up you need to do this: + +```` +<%= nav_bar %> +```` + +Which will render: + + + + +### Fixed navbar + +If you want the navbar to stick to the top of the screen, pass in the option like this: + +```` +<%= nav_bar fixed: :top %> +```` + +To render: + + + +### Static navbar + +If you want a full-width navbar that scrolls away with the page, pass in the option like this: + +```` +<%= nav_bar static: :top %> +```` + +To render: + + + + +### Brand name + +Add the name of your site on the left hand edge of the navbar. By default, it will link to root_url. Passing a brand_link option will set the url to whatever you want. + +```` +<%= nav_bar brand: "We're sooo web 2.0alizr", brand_link: account_dashboard_path %> +```` + +Which will render: + + + + +### Optional responsive variation + +If you want the responsive version of the navbar to work (One that shrinks down on mobile devices etc.), you need to pass this option: + +```` +<%= nav_bar responsive: true %> +```` +Which renders the html quite differently: + + + + + +### Nav links + +This is the 'meat' of the code where you define your menu items. + +You can group menu items in theoretical boxes which you can apply logic to - e.g. show different collections for logged in users/logged out users, or simply right align a group. + +The active menu item will be inferred from the link for now. + +The important methods here are menu_group and menu_item. + +menu_group only takes one argument - :pull - this moves the group left or right when passed :left or :right. + +menu_item generates a link wrapped in an li tag. It takes two arguments and an options hash. The first argument is the name (the text that will appear in the menu), and the path (which defaults to "#" if left blank). The rest of the options are passed straight through to the link_to helper, so that you can add classes, ids, methods or data tags etc. + +```` + +<%= nav_bar fixed: :top, brand: "Ninety Ten" do %> + <%= menu_group do %> + <%= menu_item "Home", root_path %> + <%= menu_item "About Us", about_us_path %> + <%= menu_item "Contact", contact_path %> + <% end %> + <% if current_user %> + <%= menu_item "Log Out", log_out_path %> + <% else %> + <%= menu_group pull: :right do %> + <%= menu_item "Sign Up", registration_path %> + <%= form_for @user, url: session_path(:user) do |f| -%> +

<%= f.text_field :email %>

+

<%= f.password_field :password %>

+

<%= f.submit "Sign in" %>

+ <% end -%> + <% end %> + <% end %> +<% end %> + +```` + +### Dropdown menus + +For multi-level list options, where it makes logical sense to group menu items, or simply to save space if you have a lot of pages, you can group menu items into drop down lists like this: + +```` +<%= nav_bar do %> + <%= menu_item "Home", root_path %> + + <%= drop_down "Products" do %> + <%= menu_item "Latest", latest_products_path %> + <%= menu_item "Top Sellers", popular_products_path %> + <%= drop_down_divider %> + <%= menu_item "Discount Items", discounted_products_path %> + <% end %> + + <%= menu_item "About Us", about_us_path %> + <%= menu_item "Contact", contact_path %> +<% end %> + +```` + +### Dividers + +Dividers are just vertical bars that visually separate logically disparate groups of menu items + +```` + +<%= nav_bar fixed: :bottom do %> + <%= menu_item "Home", root_path %> + <%= menu_item "About Us", about_us_path %> + <%= menu_item "Contact", contact_path %> + + <%= menu_divider %> + + <%= menu_item "Edit Profile", edit_user_path(current_user) %> + <%= menu_item "Account Settings", edit_user_account_path(current_user, @account) %> + <%= menu_item "Log Out", log_out_path %> +<% end %> + +```` + +### Forms in navbar + +At the moment - this is just a how to... + +You need to add this class to the form itself (Different form builders do this in different ways - please check out the relevant docs) + +````css +.navbar-form +```` +To pull the form left or right, add either of these classes: +````css +.pull-left +.pull-right +```` + +If you want the Bootstrap search box (I think it just rounds the corners), use: +````css +.navbar-search +```` +Instead of: +````css +.navbar-form +```` + +To change the size of the form fields, use .span2 (or however many span widths you want) to the input itself. + +### Component alignment + +You can shift things to the left or the right across the nav bar. It's easiest to do this on grouped menu items: + +```` +<%= nav_bar fixed: :bottom do %> + <% menu_group do %> + <%= menu_item "Home", root_path %> + <%= menu_item "About Us", about_us_path %> + <%= menu_item "Contact", contact_path %> + <% end %> + <% menu_group pull: :right do %> + <%= menu_item "Edit Profile", edit_user_path(current_user) %> + <%= menu_item "Account Settings", edit_user_account_path(current_user, @account) %> + <%= menu_item "Log Out", log_out_path %> + <% end %> +<% end %> + +```` + +### Text in the navbar + +If you want to put regular plain text in the navbar anywhere, you do it like this: + +```` +<%= nav_bar brand: "Apple" do %> + <%= menu_text "We make shiny things" %> + <%= menu_item "Home", root_path %> + <%= menu_item "About Us", about_us_path %> +<% end %> + +```` +It also takes the :pull option to drag it to the left or right. + + ### Flash helper -Add flash helper <%= bootstrap_flash %> to your layout (built-in with layout generator) + +Add flash helper `<%= bootstrap_flash %>` to your layout (built-in with layout generator) ### Breadcrumbs Helpers + +*Notice* If your application is using [breadcrumbs-on-rails](https://github.com/weppos/breadcrumbs_on_rails) you will have a namespace collision with the add_breadcrumb method. +You do not need to use these breadcrumb gems since this gem provides the same functionality out of the box without the additional dependency. + +Add breadcrumbs helper `<%= render_breadcrumbs %>` to your layout. +You can also specify a divider for it like this: `<%= render_breadcrumbs('>') %>` (default divider is `/`). + +Full example: +```ruby + +render_breadcrumbs(" / ", { class: '', item_class: '', divider_class: '', active_class: 'active' }) + +``` + ```ruby class ApplicationController - add_breadcrumb :index, :root_path + add_breadcrumb :root # 'root_path' will be used as url end ``` @@ -231,149 +593,65 @@ end class ExamplesController < ApplicationController add_breadcrumb :index, :examples_path - def index - end - - def show + def edit @example = Example.find params[:id] - add_breadcrumb @example.name, example_path(@example) - # add_breadcrumb :show, example_path(@example) + add_breadcrumb @example # @example.to_s as name, example_path(@example) as url + add_breadcrumb :edit, edit_example_path(@example) end end ``` +All symbolic names translated with I18n. See [I18n Internationalization Support](#i18n-internationalization-support) +section. -Add I18n translations +### Element utility helpers -```yml -en: - breadcrumbs: - application: - index: "Index" - examples: - index: "Examples" - show: "Example" +Badge: +```erb +<%= badge(12, :warning) %> 12 ``` -Add breadcrumbs helper <%= render_breadcrumbs %> to your layout - -## Changelog - - - -## Contributors & Patches & Forks - - - -### Future - - -## About Me -Lead/ Senior Developer - Programmer @useful (Usefulideas) Istanbul / Turkey +Label: +```erb +<%= tag_label('Gut!', :success) %> Gut! +``` -### Contact me -Seyhun Akyürek - seyhunak [at] gmail com +Glyph: +```erb +<%= glyph(:pencil) %> -### Follow me - - - +<%= glyph(:pencil, {tag: :span}) %> +``` -(Twitter, Facebook, Linkedin, Google+, Github) +### I18n Internationalization Support +The installer creates an English translation file for you and copies it to config/locales/en.bootstrap.yml -http://zerply.com/seyhunak -### Endorse me - - - +NOTE: If you are using Devise in your project, you must have a devise locale file +for handling flash messages, even if those messages are blank. See https://github.com/plataformatec/devise/wiki/I18n -### Klout me - +## Changelog +Please see CHANGELOG.md for more details -Please +K my influence in Ruby on Rails on @klout +## Contributors & Patches & Forks +Please see CONTRIBUTERS.md for contributors list -http://klout.com/#/seyhunak +## About Me +CTO / Senior Developer / Programmer +@useful (Usefulideas) Istanbul / Turkey -### Want to donate? - -[Want to donate for my efforts?. Show your love](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8ZLWQBREFP4U - "Donate") +### Contact me +Seyhun Akyürek - seyhunak [at] gmail com ## Thanks -Twitter Bootstrap and all twitter-bootstrap-rails contributors -http://twitter.github.com/bootstrap +Bootstrap and all twitter-bootstrap-rails contributors +http://twbs.github.io/bootstrap ## License -Copyright (c) 2012 Seyhun Akyürek +Copyright (c) 2014 Seyhun Akyürek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. diff --git a/Rakefile b/Rakefile index a174f36b..e0a37b5f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,7 @@ #!/usr/bin/env rake require 'bundler' +require 'rspec/core/rake_task' + Bundler::GemHelper.install_tasks desc "Bundle the gem" @@ -22,7 +24,7 @@ task :build_static_stylesheets do parser = Less::Parser.new :paths => [toolkit_path] - target_directory = File.expand_path('vendor/assets/stylesheets/twitter-bootstrap-static') + target_directory = File.expand_path('app/assets/stylesheets/twitter-bootstrap-static') sh "rm -rf #{target_directory}" sh "mkdir -p #{target_directory}" @@ -36,3 +38,7 @@ end task(:default).clear task :default => :bundle + +RSpec::Core::RakeTask.new do |task| + task.rspec_opts = ['--color', '--format', 'doc'] +end \ No newline at end of file diff --git a/app/assets/fonts/fontawesome-webfont.eot b/app/assets/fonts/fontawesome-webfont.eot new file mode 100755 index 00000000..84677bc0 Binary files /dev/null and b/app/assets/fonts/fontawesome-webfont.eot differ diff --git a/app/assets/fonts/fontawesome-webfont.svg b/app/assets/fonts/fontawesome-webfont.svg new file mode 100755 index 00000000..d907b25a --- /dev/null +++ b/app/assets/fonts/fontawesome-webfont.svg @@ -0,0 +1,520 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/fonts/fontawesome-webfont.ttf b/app/assets/fonts/fontawesome-webfont.ttf new file mode 100755 index 00000000..96a3639c Binary files /dev/null and b/app/assets/fonts/fontawesome-webfont.ttf differ diff --git a/app/assets/fonts/fontawesome-webfont.woff b/app/assets/fonts/fontawesome-webfont.woff new file mode 100755 index 00000000..628b6a52 Binary files /dev/null and b/app/assets/fonts/fontawesome-webfont.woff differ diff --git a/app/assets/fonts/glyphicons-halflings-regular.eot b/app/assets/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 00000000..423bd5d3 Binary files /dev/null and b/app/assets/fonts/glyphicons-halflings-regular.eot differ diff --git a/app/assets/fonts/glyphicons-halflings-regular.svg b/app/assets/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 00000000..44694887 --- /dev/null +++ b/app/assets/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/fonts/glyphicons-halflings-regular.ttf b/app/assets/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..a498ef4e Binary files /dev/null and b/app/assets/fonts/glyphicons-halflings-regular.ttf differ diff --git a/app/assets/fonts/glyphicons-halflings-regular.woff b/app/assets/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..d83c539b Binary files /dev/null and b/app/assets/fonts/glyphicons-halflings-regular.woff differ diff --git a/app/assets/javascripts/twitter/bootstrap.js b/app/assets/javascripts/twitter/bootstrap.js new file mode 100755 index 00000000..00a7bf29 --- /dev/null +++ b/app/assets/javascripts/twitter/bootstrap.js @@ -0,0 +1,12 @@ +//= require twitter/bootstrap/transition +//= require twitter/bootstrap/alert +//= require twitter/bootstrap/modal +//= require twitter/bootstrap/dropdown +//= require twitter/bootstrap/scrollspy +//= require twitter/bootstrap/tab +//= require twitter/bootstrap/tooltip +//= require twitter/bootstrap/popover +//= require twitter/bootstrap/button +//= require twitter/bootstrap/collapse +//= require twitter/bootstrap/carousel +//= require twitter/bootstrap/affix \ No newline at end of file diff --git a/app/assets/javascripts/twitter/bootstrap/affix.js b/app/assets/javascripts/twitter/bootstrap/affix.js new file mode 100755 index 00000000..7d404ebe --- /dev/null +++ b/app/assets/javascripts/twitter/bootstrap/affix.js @@ -0,0 +1,142 @@ +/* ======================================================================== + * Bootstrap: affix.js v3.2.0 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // AFFIX CLASS DEFINITION + // ====================== + + var Affix = function (element, options) { + this.options = $.extend({}, Affix.DEFAULTS, options) + + this.$target = $(this.options.target) + .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 = + this.pinnedOffset = null + + this.checkPosition() + } + + Affix.VERSION = '3.2.0' + + Affix.RESET = 'affix affix-top affix-bottom' + + Affix.DEFAULTS = { + offset: 0, + target: window + } + + Affix.prototype.getPinnedOffset = function () { + if (this.pinnedOffset) return this.pinnedOffset + this.$element.removeClass(Affix.RESET).addClass('affix') + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + return (this.pinnedOffset = position.top - scrollTop) + } + + 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.$target.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(this.$element) + if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) + + 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 != null) this.$element.css('top', '') + + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger($.Event(affixType.replace('affix', 'affixed'))) + + if (affix == 'bottom') { + this.$element.offset({ + top: scrollHeight - this.$element.height() - offsetBottom + }) + } + } + + + // AFFIX PLUGIN DEFINITION + // ======================= + + function Plugin(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]() + }) + } + + var old = $.fn.affix + + $.fn.affix = Plugin + $.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 + + Plugin.call($spy, data) + }) + }) + +}(jQuery); diff --git a/app/assets/javascripts/twitter/bootstrap/alert.js b/app/assets/javascripts/twitter/bootstrap/alert.js new file mode 100755 index 00000000..0efd92cb --- /dev/null +++ b/app/assets/javascripts/twitter/bootstrap/alert.js @@ -0,0 +1,92 @@ +/* ======================================================================== + * Bootstrap: alert.js v3.2.0 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.2.0' + + 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() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(150) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(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) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.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) + +}(jQuery); diff --git a/app/assets/javascripts/twitter/bootstrap/button.js b/app/assets/javascripts/twitter/bootstrap/button.js new file mode 100755 index 00000000..dc3164f8 --- /dev/null +++ b/app/assets/javascripts/twitter/bootstrap/button.js @@ -0,0 +1,110 @@ +/* ======================================================================== + * Bootstrap: button.js v3.2.0 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.2.0' + + 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 == null) $el.data('resetText', $el[val]()) + + $el[val](data[state] == null ? this.options[state] : data[state]) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked') && this.$element.hasClass('active')) changed = false + else $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(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) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.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') + Plugin.call($btn, 'toggle') + e.preventDefault() + }) + +}(jQuery); diff --git a/app/assets/javascripts/twitter/bootstrap/carousel.js b/app/assets/javascripts/twitter/bootstrap/carousel.js new file mode 100755 index 00000000..b7da1ba5 --- /dev/null +++ b/app/assets/javascripts/twitter/bootstrap/carousel.js @@ -0,0 +1,223 @@ +/* ======================================================================== + * Bootstrap: carousel.js v3.2.0 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this)) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.pause == 'hover' && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.2.0' + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true + } + + Carousel.prototype.keydown = function (e) { + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || $active[type]() + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var fallback = type == 'next' ? 'first' : 'last' + var that = this + + if (!$next.length) { + if (!this.options.wrap) return + $next = this.$element.find('.item')[fallback]() + } + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + }) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); diff --git a/app/assets/javascripts/twitter/bootstrap/collapse.js b/app/assets/javascripts/twitter/bootstrap/collapse.js new file mode 100755 index 00000000..e4e6d793 --- /dev/null +++ b/app/assets/javascripts/twitter/bootstrap/collapse.js @@ -0,0 +1,170 @@ +/* ======================================================================== + * Bootstrap: collapse.js v3.2.0 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.transitioning = null + + if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.2.0' + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var actives = this.$parent && this.$parent.find('> .panel > .in') + + if (actives && actives.length) { + var hasData = actives.data('bs.collapse') + if (hasData && hasData.transitioning) return + Plugin.call(actives, 'hide') + hasData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse') + .removeClass('in') + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .trigger('hidden.bs.collapse') + .removeClass('collapsing') + .addClass('collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(350) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && option == 'show') option = !option + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var href + var $this = $(this) + var target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + var $target = $(target) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + var parent = $this.attr('data-parent') + var $parent = parent && $(parent) + + if (!data || !data.transitioning) { + if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed') + $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + } + + Plugin.call($target, option) + }) + +}(jQuery); diff --git a/app/assets/javascripts/twitter/bootstrap/dropdown.js b/app/assets/javascripts/twitter/bootstrap/dropdown.js new file mode 100755 index 00000000..88f118c2 --- /dev/null +++ b/app/assets/javascripts/twitter/bootstrap/dropdown.js @@ -0,0 +1,151 @@ +/* ======================================================================== + * Bootstrap: dropdown.js v3.2.0 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.2.0' + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('