diff --git a/.gitignore b/.gitignore index e3c0bdea9..21ad3efcd 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ /log/* !/log/.keep /tmp +.env diff --git a/.rubocop.yml b/.rubocop.yml index ffd0357df..72b6c3e0b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,16 +1,36 @@ +Rails: + Enabled: true + AllCops: - RunRailsCops: true + TargetRailsVersion: 4.0 Exclude: - 'bin/*' - 'db/schema.rb' - 'db/seeds.rb' - 'vendor/bundle/**/*' +Layout/DotPosition: + EnforcedStyle: trailing + +Layout/SpaceInsideHashLiteralBraces: + EnforcedStyle: no_space + +Layout/AccessModifierIndentation: + EnforcedStyle: outdent + Metrics/AbcSize: Exclude: - 'app/controllers/passwords_controller.rb' - 'app/controllers/sessions_controller.rb' - 'app/controllers/users_controller.rb' + - 'lib/thing_importer.rb' + - 'lib/adoption_mover.rb' + +Metrics/BlockLength: + Exclude: + - 'config/initializers/*' + - 'lib/adoption_mover.rb' # removing heredocs would decrease readibility + ExcludedMethods: ['test'] Metrics/BlockNesting: Max: 2 @@ -24,14 +44,13 @@ Metrics/MethodLength: Max: 10 Exclude: - 'db/migrate/*.rb' + - 'lib/thing_importer.rb' # removing heredocs would decrease readibility + - 'lib/adoption_mover.rb' # removing heredocs would decrease readibility Metrics/ParameterLists: Max: 4 CountKeywordArgs: true -Style/AccessModifierIndentation: - EnforcedStyle: outdent - Style/CollectionMethods: PreferredMethods: map: 'collect' @@ -42,16 +61,16 @@ Style/CollectionMethods: Style/Documentation: Enabled: false -Style/DotPosition: - EnforcedStyle: trailing - Style/DoubleNegation: Enabled: false -Style/SpaceInsideHashLiteralBraces: - EnforcedStyle: no_space +Style/TrailingCommaInArguments: + EnforcedStyleForMultiline: 'comma' + +Style/TrailingCommaInArrayLiteral: + EnforcedStyleForMultiline: 'comma' -Style/TrailingComma: +Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: 'comma' Style/WordArray: diff --git a/.ruby-version b/.ruby-version index 585940699..437459cd9 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.2.3 +2.5.0 diff --git a/.travis.yml b/.travis.yml index 72ea1467d..22e8c7e3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,19 @@ -before_script: bundle exec rake db:create db:schema:load +dist: trusty +sudo: false +language: ruby bundler_args: "--without assets:development:production" +addons: + postgresql: "9.6" cache: bundler -language: ruby -rvm: - - 2.2.3 +before_script: bundle exec rake db:create db:schema:load script: - - bundle exec rake - - bundle exec rubocop -sudo: false +- bundle exec rake +- bundle exec rubocop deploy: + provider: heroku api_key: - secure: Ck6BzHMtPmYsBY/PbVnfIE6wnSe9s6fYDkvzYZtT/2qud4j4ElhV9el0ZbDhiTmix0PMwcCnN/Tpw5GbLVaHncDaJkvb6ucEBNyC7xECjNAFsxE6lu3yKATsY2hta7OQ8NwLlvAZpzCLMZQf9lzoSNbh2h/p+CwSNR7vOkw0FNc= - app: adopt-a-hydrant + secure: RYcEa08NYClwvVGXEuxdkTPmWIasRPXIhVFhy3ievSYAlL6kRy44QoTApp26p+gDMImP1UIGcjI37RkPvqiONdmR70n2kVVYpbFYql5aJ3GjVYYYAwXqt6dHnFjt2Bi4mWhVH8ydyqxC7mQ8quW8a+yHkf/YSXhMv5IC1TmPnv4I+vyjAxK/Ru20768E5hn0UlcdYWGpNq81ZZhJzk26KZIEJDVOokJTY4dBDxXYEYsgSpTpAkN5KRipFJNosWPl2ASbEmOXzR23tmoHN4VcmAKhZ4ePwqupWN3G7VhkF5q3FA/1VB5AuNmUuvhr1G5lO9ZoIHStqPrm4G28PwAJF058QHyQOZk309F7bTRpfiY0nu77pnO09Ze/lK98NGygog+9YTQDzF43qwjPdIIn1Az06ng2QWAaur9QGoG3lGKuhadUKdSuFETpH/dVMfV+9DiPbYcIoTigDNLUhC8Pp4atFN1ltz7kLhRkJT8l4gPpWPNOQrQC1ZwVJyM1yCasetGIIcGWXa7pI9qq55P+ERseFTNB5WNmsSQb97eXTGKjaRCcpmLnXxUQ5sG6lxi/ZMJ2L/K4X15PfLHFwMhsc2jWgfvGWqmPYwuNXsqPeUNuNW/GY5dNAqKasa1fmURwJDzxCu/x1XsyktcCzGbY1HOEaYv+5HjV0Ycrj4ca4iY= + app: adoptadrainsf-staging on: - repo: codeforamerica/adopt-a-hydrant - provider: heroku - strategy: git + repo: sfbrigade/adopt-a-drain + branch: production diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..db37966a0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM ruby:2.2.3 +RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs +RUN mkdir /myapp +WORKDIR /myapp +ADD Gemfile /myapp/Gemfile +ADD Gemfile.lock /myapp/Gemfile.lock +RUN bundle install diff --git a/Gemfile b/Gemfile index e38e20aca..1a1b484c9 100644 --- a/Gemfile +++ b/Gemfile @@ -1,22 +1,25 @@ -source 'https://rubygems.org' -ruby '2.2.3' +# frozen_string_literal: true -gem 'rails', '~> 4.2.4' +source 'https://rubygems.org' +ruby '2.5.0' -gem 'arel' -gem 'devise' -gem 'geokit' -gem 'haml' -gem 'http_accept_language' -gem 'nokogiri' +gem 'airbrake', '~> 7.3' +gem 'devise', '~> 4.3' +gem 'geokit', '~> 1.0' +gem 'haml', '~> 5.0' +gem 'http_accept_language', '~> 2.0' +gem 'local_time', '~> 2.0' +gem 'obscenity', '~> 1.0', '>= 1.0.2' gem 'pg' -gem 'rails_12factor' -gem 'rails_admin' -gem 'validates_formatting_of' +gem 'rails', '~> 4.2.10' +gem 'rails_admin', '~> 1.3' +gem 'validates_formatting_of', '~> 0.9.0' -platforms :ruby_18 do - gem 'fastercsv' -end +gem 'paranoia', '~> 2.4' +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw] + +gem 'byebug', groups: %i[development test] +gem 'dotenv-rails', groups: %i[development test] group :assets do gem 'sass-rails', '>= 4.0.3' @@ -29,6 +32,7 @@ end group :production do gem 'puma' + gem 'rails_12factor' gem 'skylight' end @@ -36,6 +40,5 @@ group :test do gem 'coveralls', require: false gem 'rubocop' gem 'simplecov', require: false - gem 'sqlite3' gem 'webmock' end diff --git a/Gemfile.lock b/Gemfile.lock index 876911634..c432f5dc8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,222 +1,247 @@ GEM remote: https://rubygems.org/ specs: - actionmailer (4.2.4) - actionpack (= 4.2.4) - actionview (= 4.2.4) - activejob (= 4.2.4) + actionmailer (4.2.10) + actionpack (= 4.2.10) + actionview (= 4.2.10) + activejob (= 4.2.10) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.4) - actionview (= 4.2.4) - activesupport (= 4.2.4) + actionpack (4.2.10) + actionview (= 4.2.10) + activesupport (= 4.2.10) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.4) - activesupport (= 4.2.4) + actionview (4.2.10) + activesupport (= 4.2.10) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - activejob (4.2.4) - activesupport (= 4.2.4) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (4.2.10) + activesupport (= 4.2.10) globalid (>= 0.3.0) - activemodel (4.2.4) - activesupport (= 4.2.4) + activemodel (4.2.10) + activesupport (= 4.2.10) builder (~> 3.1) - activerecord (4.2.4) - activemodel (= 4.2.4) - activesupport (= 4.2.4) + activerecord (4.2.10) + activemodel (= 4.2.10) + activesupport (= 4.2.10) arel (~> 6.0) - activesupport (4.2.4) + activesupport (4.2.10) i18n (~> 0.7) - json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.3.8) - arel (6.0.3) - ast (2.1.0) - astrolabe (1.3.1) - parser (~> 2.2) - bcrypt (3.1.10) - builder (3.2.2) - coffee-rails (4.1.0) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + airbrake (7.3.0) + airbrake-ruby (~> 2.5) + airbrake-ruby (2.9.0) + arel (6.0.4) + ast (2.4.0) + bcrypt (3.1.11) + builder (3.2.3) + byebug (10.0.2) + coffee-rails (4.2.2) coffee-script (>= 2.2.0) - railties (>= 4.0.0, < 5.0) + railties (>= 4.0.0) coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.9.1.1) - coveralls (0.8.3) - json (~> 1.8) - rest-client (>= 1.6.8, < 2) - simplecov (~> 0.10.0) + coffee-script-source (1.12.2) + concurrent-ruby (1.0.5) + coveralls (0.8.21) + json (>= 1.8, < 3) + simplecov (~> 0.14.1) term-ansicolor (~> 1.3) - thor (~> 0.19.1) - crack (0.4.2) + thor (~> 0.19.4) + tins (~> 1.6) + crack (0.4.3) safe_yaml (~> 1.0.0) - devise (3.5.2) + crass (1.0.4) + devise (4.4.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 3.2.6, < 5) + railties (>= 4.1.0, < 6.0) responders - thread_safe (~> 0.1) warden (~> 1.2.3) docile (1.1.5) - domain_name (0.5.25) - unf (>= 0.0.5, < 1.0.0) + dotenv (2.4.0) + dotenv-rails (2.4.0) + dotenv (= 2.4.0) + railties (>= 3.2, < 6.0) erubis (2.7.0) - execjs (2.6.0) - fastercsv (1.5.5) - font-awesome-rails (4.4.0.0) - railties (>= 3.2, < 5.0) - geokit (1.10.0) - globalid (0.3.6) - activesupport (>= 4.1.0) - haml (4.0.7) + execjs (2.7.0) + ffi (1.9.23) + font-awesome-rails (4.7.0.3) + railties (>= 3.2, < 5.2) + geokit (1.11.0) + globalid (0.4.1) + activesupport (>= 4.2.0) + haml (5.0.4) + temple (>= 0.8.0) tilt - hashdiff (0.2.2) - http-cookie (1.0.2) - domain_name (~> 0.5) - http_accept_language (2.0.5) - i18n (0.7.0) - jquery-rails (4.0.5) - rails-dom-testing (~> 1.0) + hashdiff (0.3.7) + http_accept_language (2.1.1) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jquery-rails (4.3.1) + rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) jquery-ui-rails (5.0.5) railties (>= 3.2.16) - json (1.8.3) - kaminari (0.16.3) - actionpack (>= 3.0.0) - activesupport (>= 3.0.0) - loofah (2.0.3) + json (2.1.0) + kaminari (1.1.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.1.1) + kaminari-activerecord (= 1.1.1) + kaminari-core (= 1.1.1) + kaminari-actionview (1.1.1) + actionview + kaminari-core (= 1.1.1) + kaminari-activerecord (1.1.1) + activerecord + kaminari-core (= 1.1.1) + kaminari-core (1.1.1) + local_time (2.0.0) + loofah (2.2.2) + crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.6.3) - mime-types (>= 1.16, < 3) - mime-types (2.6.2) - mini_portile (0.6.2) - minitest (5.8.1) + mail (2.7.0) + mini_mime (>= 0.1.1) + mini_mime (1.0.0) + mini_portile2 (2.3.0) + minitest (5.11.3) nested_form (0.3.2) - netrc (0.10.3) - nokogiri (1.6.6.2) - mini_portile (~> 0.6.0) + nokogiri (1.8.2) + mini_portile2 (~> 2.3.0) + obscenity (1.0.2) orm_adapter (0.5.0) - parser (2.2.3.0) - ast (>= 1.1, < 3.0) - pg (0.18.3) + parallel (1.12.1) + paranoia (2.4.1) + activerecord (>= 4.0, < 5.3) + parser (2.5.1.0) + ast (~> 2.4.0) + pg (0.21.0) powerpack (0.1.1) - puma (2.14.0) - rack (1.6.4) - rack-pjax (0.8.0) + public_suffix (3.0.2) + puma (3.11.4) + rack (1.6.10) + rack-pjax (1.0.0) nokogiri (~> 1.5) - rack (~> 1.1) + rack (>= 1.1) rack-test (0.6.3) rack (>= 1.0) - rails (4.2.4) - actionmailer (= 4.2.4) - actionpack (= 4.2.4) - actionview (= 4.2.4) - activejob (= 4.2.4) - activemodel (= 4.2.4) - activerecord (= 4.2.4) - activesupport (= 4.2.4) + rails (4.2.10) + actionmailer (= 4.2.10) + actionpack (= 4.2.10) + actionview (= 4.2.10) + activejob (= 4.2.10) + activemodel (= 4.2.10) + activerecord (= 4.2.10) + activesupport (= 4.2.10) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.4) + railties (= 4.2.10) sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.7) - activesupport (>= 4.2.0.beta, < 5.0) - nokogiri (~> 1.6.0) + rails-dom-testing (1.0.9) + activesupport (>= 4.2.0, < 5.0) + nokogiri (~> 1.6) rails-deprecated_sanitizer (>= 1.0.1) - rails-html-sanitizer (1.0.2) - loofah (~> 2.0) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) rails_12factor (0.0.3) rails_serve_static_assets rails_stdout_logging - rails_admin (0.7.0) + rails_admin (1.3.0) builder (~> 3.1) coffee-rails (~> 4.0) font-awesome-rails (>= 3.0, < 5) - haml (~> 4.0) + haml (>= 4.0, < 6) jquery-rails (>= 3.0, < 5) jquery-ui-rails (~> 5.0) - kaminari (~> 0.14) + kaminari (>= 0.14, < 2.0) nested_form (~> 0.3) - rack-pjax (~> 0.7) - rails (~> 4.0) - remotipart (~> 1.0) - safe_yaml (~> 1.0) + rack-pjax (>= 0.7) + rails (>= 4.0, < 6) + remotipart (~> 1.3) sass-rails (>= 4.0, < 6) - rails_serve_static_assets (0.0.4) - rails_stdout_logging (0.0.4) - railties (4.2.4) - actionpack (= 4.2.4) - activesupport (= 4.2.4) + rails_serve_static_assets (0.0.5) + rails_stdout_logging (0.0.5) + railties (4.2.10) + actionpack (= 4.2.10) + activesupport (= 4.2.10) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rainbow (2.0.0) - rake (10.4.2) - remotipart (1.2.1) - responders (2.1.0) - railties (>= 4.2.0, < 5) - rest-client (1.8.0) - http-cookie (>= 1.0.2, < 2.0) - mime-types (>= 1.16, < 3.0) - netrc (~> 0.7) - rubocop (0.34.2) - astrolabe (~> 1.3) - parser (>= 2.2.2.5, < 3.0) + rainbow (3.0.0) + rake (12.3.1) + rb-fsevent (0.10.3) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + remotipart (1.3.1) + responders (2.4.0) + actionpack (>= 4.2.0, < 5.3) + railties (>= 4.2.0, < 5.3) + rubocop (0.55.0) + parallel (~> 1.10) + parser (>= 2.5) powerpack (~> 0.1) - rainbow (>= 1.99.1, < 3.0) - ruby-progressbar (~> 1.4) - ruby-progressbar (1.7.5) + rainbow (>= 2.2.2, < 4.0) + ruby-progressbar (~> 1.7) + unicode-display_width (~> 1.0, >= 1.0.1) + ruby-progressbar (1.9.0) safe_yaml (1.0.4) - sass (3.4.19) - sass-rails (5.0.4) - railties (>= 4.0.0, < 5.0) + sass (3.5.6) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sass-rails (5.0.7) + railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - simplecov (0.10.0) + simplecov (0.14.1) docile (~> 1.1.0) - json (~> 1.8) + json (>= 1.8, < 3) simplecov-html (~> 0.10.0) - simplecov-html (0.10.0) - skylight (0.8.1) - activesupport (>= 3.0.0) - spring (1.4.0) - sprockets (3.4.0) + simplecov-html (0.10.2) + skylight (2.0.0) + skylight-core (= 2.0.0) + skylight-core (2.0.0) + activesupport (>= 4.2.0) + spring (2.0.2) + activesupport (>= 4.2) + sprockets (3.7.1) + concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (2.3.3) - actionpack (>= 3.0) - activesupport (>= 3.0) - sprockets (>= 2.8, < 4.0) - sqlite3 (1.3.11) - term-ansicolor (1.3.2) + sprockets-rails (3.2.1) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + temple (0.8.0) + term-ansicolor (1.6.0) tins (~> 1.0) - thor (0.19.1) - thread_safe (0.3.5) - tilt (2.0.1) - tins (1.6.0) - tzinfo (1.2.2) + thor (0.19.4) + thread_safe (0.3.6) + tilt (2.0.8) + tins (1.15.0) + tzinfo (1.2.5) thread_safe (~> 0.1) - uglifier (2.7.2) - execjs (>= 0.3.0) - json (>= 1.8.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.1) + uglifier (4.1.10) + execjs (>= 0.3.0, < 3) + unicode-display_width (1.3.2) validates_formatting_of (0.9.0) activemodel - warden (1.2.3) + warden (1.2.7) rack (>= 1.0) - webmock (1.22.1) + webmock (3.4.0) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff @@ -225,28 +250,34 @@ PLATFORMS ruby DEPENDENCIES - arel + airbrake (~> 7.3) + byebug coveralls - devise - fastercsv - geokit - haml - http_accept_language - nokogiri + devise (~> 4.3) + dotenv-rails + geokit (~> 1.0) + haml (~> 5.0) + http_accept_language (~> 2.0) + local_time (~> 2.0) + obscenity (~> 1.0, >= 1.0.2) + paranoia (~> 2.4) pg puma - rails (~> 4.2.4) + rails (~> 4.2.10) rails_12factor - rails_admin + rails_admin (~> 1.3) rubocop sass-rails (>= 4.0.3) simplecov skylight spring - sqlite3 + tzinfo-data uglifier - validates_formatting_of + validates_formatting_of (~> 0.9.0) webmock +RUBY VERSION + ruby 2.5.0p0 + BUNDLED WITH - 1.10.6 + 1.16.1 diff --git a/README.md b/README.md index adb15319d..838b85531 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,67 @@ -# Adopt-a-Hydrant +# Adopt-a-Drain -[![Build Status](http://img.shields.io/travis/codeforamerica/adopt-a-hydrant.svg)][travis] -[![Dependency Status](http://img.shields.io/gemnasium/codeforamerica/adopt-a-hydrant.svg)][gemnasium] -[![Coverage Status](http://img.shields.io/coveralls/codeforamerica/adopt-a-hydrant.svg)][coveralls] +[![Join the chat at https://gitter.im/sfbrigade/adopt-a-drain](https://badges.gitter.im/sfbrigade/adopt-a-drain.svg)](https://gitter.im/sfbrigade/adopt-a-drain?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[travis]: http://travis-ci.org/codeforamerica/adopt-a-hydrant -[gemnasium]: https://gemnasium.com/codeforamerica/adopt-a-hydrant -[coveralls]: https://coveralls.io/r/codeforamerica/adopt-a-hydrant +[![Build Status](http://img.shields.io/travis/sfbrigade/adopt-a-drain.svg)][travis] +[![Dependency Status](http://img.shields.io/gemnasium/sfbrigade/adopt-a-drain.svg)][gemnasium] +[![Coverage Status](http://img.shields.io/coveralls/sfbrigade/adopt-a-drain.svg)][coveralls] -Claim responsibility for shoveling out a fire hydrant after it snows. +[travis]: http://travis-ci.org/sfbrigade/adopt-a-drain +[gemnasium]: https://gemnasium.com/sfbrigade/adopt-a-drain +[coveralls]: https://coveralls.io/r/sfbrigade/adopt-a-drain + +Claim responsibility for cleaning out a storm drain after it rains. ## Screenshot -![Adopt-a-Hydrant](https://github.com/codeforamerica/adopt-a-hydrant/raw/master/screenshot.png "Adopt-a-Hydrant") +![Adopt-a-Drain](/adopt.png "Adopt-a-Drain") ## Demo You can see a running version of the application at -[http://adopt-a-hydrant.herokuapp.com/][demo]. +[http://adopt-a-drain.herokuapp.com/][demo]. -[demo]: http://adopt-a-hydrant.herokuapp.com/ +[demo]: http://adopt-a-drain.herokuapp.com/ ## Installation This application requires [Postgres](http://www.postgresql.org/) to be installed - git clone git://github.com/codeforamerica/adopt-a-hydrant.git - cd adopt-a-hydrant + git clone git://github.com/sfbrigade/adopt-a-drain.git + cd adopt-a-drain bundle install bundle exec rake db:create bundle exec rake db:schema:load +See the [wiki](https://github.com/sfbrigade/adopt-a-drain/wiki/Windows-Development-Environment) for a guide on how to install this application on Windows. + +## Docker + +To setup a local development environment with +[Docker](https://docs.docker.com/engine/installation/). + +``` +# Override database settings as the docker host: +echo DB_HOST=db > .env +echo DB_USER=postgres >> .env + +# Setup your docker based postgres database: +docker-compose run --rm web bundle exec rake db:setup + +# Load data: +docker-compose run --rm web bundle exec rake data:load_things +# OR: don't load all that data, and load the seed data: +# docker-compose run --rm web bundle exec rake db:seed + +# Start the web server: +docker-compose up + +# Visit your website http://localhost:3000 (or the IP of your docker-machine) +``` + ## Usage rails server ## Seed Data - bundle exec rake db:seed + bundle exec rake data:load_drains ## Deploying to Heroku A successful deployment to Heroku requires a few setup steps: @@ -67,7 +95,7 @@ A successful deployment to Heroku requires a few setup steps: `heroku run bundle exec rake db:seed` Keep in mind that the Heroku free Postgres plan only allows up to 10,000 rows, -so if your city has more than 10,000 fire hydrants (or other thing to be +so if your city has more than 10,000 fire drains (or other thing to be adopted), you will need to upgrade to the $9/month plan. ### Google Analytics @@ -78,7 +106,7 @@ variables: heroku config:set GOOGLE_ANALYTICS_ID=your_id heroku config:set GOOGLE_ANALYTICS_DOMAIN=your_domain_name -An example ID is `UA-12345678-9`, and an example domain is `adoptahydrant.org`. +An example ID is `UA-12345678-9`, and an example domain is `adoptadrain.org`. ## Contributing In the spirit of [free software][free-sw], **everyone** is encouraged to help @@ -101,9 +129,9 @@ Here are some ways *you* can contribute: * by reviewing patches * [financially][] -[locales]: https://github.com/codeforamerica/adopt-a-hydrant/tree/master/config/locales -[issues]: https://github.com/codeforamerica/adopt-a-hydrant/issues -[financially]: https://secure.codeforamerica.org/page/contribute +[locales]: https://github.com/sfbrigade/adopt-a-drain/tree/master/config/locales +[issues]: https://github.com/sfbrigade/adopt-a-drain/issues +[financially]: https://secure.sfbrigade.org/page/contribute ## Submitting an Issue We use the [GitHub issue tracker][issues] to track bugs and features. Before @@ -128,11 +156,11 @@ Ideally, a bug report should include a pull request with failing specs. 9. [Submit a pull request.][pr] [fork]: http://help.github.com/fork-a-repo/ -[branch]: http://learn.github.com/p/branching.html +[branch]: https://guides.github.com/introduction/flow/ [pr]: http://help.github.com/send-pull-requests/ ## Supported Ruby Version -This library aims to support and is [tested against][travis] Ruby version 2.1.2. +This library aims to support and is [tested against][travis] Ruby version 2.2.2. If something doesn't work on this version, it should be considered a bug. @@ -147,10 +175,6 @@ timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped. ## Copyright -Copyright (c) 2014 Code for America. See [LICENSE][] for details. - -[license]: https://github.com/codeforamerica/adopt-a-hydrant/blob/master/LICENSE.md - -[![Code for America Tracker](http://stats.codeforamerica.org/codeforamerica/adopt-a-hydrant.png)][tracker] +Copyright (c) 2015 Code for San Francisco. See [LICENSE.md](https://github.com/sfbrigade/adopt-a-drain/blob/master/LICENSE.md) for details. -[tracker]: http://stats.codeforamerica.org/projects/adopt-a-hydrant +[license]: https://github.com/sfbrigade/adopt-a-drain/blob/master/LICENSE.md diff --git a/Rakefile b/Rakefile index ba6b733dd..0cfbf42e9 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,17 @@ +# frozen_string_literal: true + # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. -require File.expand_path('../config/application', __FILE__) +require File.expand_path('config/application', __dir__) + +begin + require 'rubocop/rake_task' + RuboCop::RakeTask.new +rescue LoadError + puts 'Could not load rubocop' +end Rails.application.load_tasks + +task default: %i[rubocop test] diff --git a/adopt.png b/adopt.png new file mode 100644 index 000000000..c3f0a87d3 Binary files /dev/null and b/adopt.png differ diff --git a/app.json b/app.json new file mode 100644 index 000000000..95187e045 --- /dev/null +++ b/app.json @@ -0,0 +1,41 @@ +{ + "name": "adopt-a-drain", + "description": "Adopt-a-Drain SF Review App", + "scripts": { + "postdeploy": "bundle exec rake db:schema:load db:seed" + }, + "env": { + "GOOGLE_MAPS_JAVASCRIPT_API_KEY": { + "required": true + }, + "HEROKU_APP_NAME": { + "required": true + }, + "LANG": { + "required": true + }, + "RACK_ENV": { + "required": true + }, + "RAILS_ENV": { + "required": true + }, + "RAILS_SERVE_STATIC_FILES": { + "required": true + }, + "SECRET_KEY_BASE": { + "generator": "secret" + } + }, + "formation": {}, + "addons": [ + "airbrake", + "heroku-postgresql", + "sendgrid" + ], + "buildpacks": [ + { + "url": "heroku/ruby" + } + ] +} diff --git a/app/assets/images/adoptadrain-event.png b/app/assets/images/adoptadrain-event.png new file mode 100644 index 000000000..671060d18 Binary files /dev/null and b/app/assets/images/adoptadrain-event.png differ diff --git a/app/assets/images/icons/facebook.png b/app/assets/images/icons/facebook.png new file mode 100644 index 000000000..22c5d72aa Binary files /dev/null and b/app/assets/images/icons/facebook.png differ diff --git a/app/assets/images/icons/github.png b/app/assets/images/icons/github.png new file mode 100644 index 000000000..ce70d2266 Binary files /dev/null and b/app/assets/images/icons/github.png differ diff --git a/app/assets/images/icons/instagram.png b/app/assets/images/icons/instagram.png new file mode 100644 index 000000000..3606be05a Binary files /dev/null and b/app/assets/images/icons/instagram.png differ diff --git a/app/assets/images/icons/linkedin.png b/app/assets/images/icons/linkedin.png new file mode 100644 index 000000000..60cecb286 Binary files /dev/null and b/app/assets/images/icons/linkedin.png differ diff --git a/app/assets/images/icons/twitter.png b/app/assets/images/icons/twitter.png new file mode 100644 index 000000000..4bd55ee5f Binary files /dev/null and b/app/assets/images/icons/twitter.png differ diff --git a/app/assets/images/icons/youtube.png b/app/assets/images/icons/youtube.png new file mode 100644 index 000000000..6e9194372 Binary files /dev/null and b/app/assets/images/icons/youtube.png differ diff --git a/app/assets/images/logos/90_DPW_color logo.svg b/app/assets/images/logos/90_DPW_color logo.svg new file mode 100644 index 000000000..e0ab249e9 --- /dev/null +++ b/app/assets/images/logos/90_DPW_color logo.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + diff --git a/app/assets/images/logos/DataSFLogo.png b/app/assets/images/logos/DataSFLogo.png new file mode 100644 index 000000000..0d688a1f7 Binary files /dev/null and b/app/assets/images/logos/DataSFLogo.png differ diff --git a/app/assets/images/logos/SFPWlogo.jpg b/app/assets/images/logos/SFPWlogo.jpg new file mode 100644 index 000000000..c71b25ebe Binary files /dev/null and b/app/assets/images/logos/SFPWlogo.jpg differ diff --git a/app/assets/images/logos/SFWPS-Horz-4c.svg b/app/assets/images/logos/SFWPS-Horz-4c.svg new file mode 100644 index 000000000..20c8edb0f --- /dev/null +++ b/app/assets/images/logos/SFWPS-Horz-4c.svg @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/images/logos/adopt-a-drain-horizontal.png b/app/assets/images/logos/adopt-a-drain-horizontal.png new file mode 100644 index 000000000..73822c5cd Binary files /dev/null and b/app/assets/images/logos/adopt-a-drain-horizontal.png differ diff --git a/app/assets/images/logos/adopt-a-drain-sfpuc.png b/app/assets/images/logos/adopt-a-drain-sfpuc.png new file mode 100644 index 000000000..7de2d93c5 Binary files /dev/null and b/app/assets/images/logos/adopt-a-drain-sfpuc.png differ diff --git a/app/assets/images/logos/adopt-a-drain.png b/app/assets/images/logos/adopt-a-drain.png new file mode 100644 index 000000000..0b3402a9c Binary files /dev/null and b/app/assets/images/logos/adopt-a-drain.png differ diff --git a/app/assets/images/logos/adopt-a-drain_large.png b/app/assets/images/logos/adopt-a-drain_large.png new file mode 100644 index 000000000..29b1b5f44 Binary files /dev/null and b/app/assets/images/logos/adopt-a-drain_large.png differ diff --git a/app/assets/images/logos/adopt-a-hydrant.png b/app/assets/images/logos/adopt-a-hydrant.png deleted file mode 100644 index 122a91d67..000000000 Binary files a/app/assets/images/logos/adopt-a-hydrant.png and /dev/null differ diff --git a/app/assets/images/logos/adopt-a-hydrant_large.png b/app/assets/images/logos/adopt-a-hydrant_large.png deleted file mode 100644 index 8ed7b3106..000000000 Binary files a/app/assets/images/logos/adopt-a-hydrant_large.png and /dev/null differ diff --git a/app/assets/images/logos/cfa.jpg b/app/assets/images/logos/cfa.jpg new file mode 100644 index 000000000..61379670b Binary files /dev/null and b/app/assets/images/logos/cfa.jpg differ diff --git a/app/assets/images/logos/cfa.png b/app/assets/images/logos/cfa.png deleted file mode 100644 index 59e1dd30b..000000000 Binary files a/app/assets/images/logos/cfa.png and /dev/null differ diff --git a/app/assets/images/logos/codeforsanfrancisco.png b/app/assets/images/logos/codeforsanfrancisco.png new file mode 100644 index 000000000..88f22d3d2 Binary files /dev/null and b/app/assets/images/logos/codeforsanfrancisco.png differ diff --git a/app/assets/images/logos/dtlogo.png b/app/assets/images/logos/dtlogo.png new file mode 100644 index 000000000..8a39525e7 Binary files /dev/null and b/app/assets/images/logos/dtlogo.png differ diff --git a/app/assets/images/logos/sfgov.png b/app/assets/images/logos/sfgov.png new file mode 100644 index 000000000..fa1c07369 Binary files /dev/null and b/app/assets/images/logos/sfgov.png differ diff --git a/app/assets/images/logos/sfpuc.png b/app/assets/images/logos/sfpuc.png new file mode 100644 index 000000000..549fcf30d Binary files /dev/null and b/app/assets/images/logos/sfpuc.png differ diff --git a/app/assets/images/logos/sfpw.jpg b/app/assets/images/logos/sfpw.jpg new file mode 100644 index 000000000..a6eee994e Binary files /dev/null and b/app/assets/images/logos/sfpw.jpg differ diff --git a/app/assets/images/markers/adopted.png b/app/assets/images/markers/adopted.png new file mode 100755 index 000000000..96e9e2fe6 Binary files /dev/null and b/app/assets/images/markers/adopted.png differ diff --git a/app/assets/images/markers/adoptedbyyou.png b/app/assets/images/markers/adoptedbyyou.png new file mode 100755 index 000000000..cae618ee8 Binary files /dev/null and b/app/assets/images/markers/adoptedbyyou.png differ diff --git a/app/assets/images/markers/tobay.png b/app/assets/images/markers/tobay.png new file mode 100755 index 000000000..fd68ecd31 Binary files /dev/null and b/app/assets/images/markers/tobay.png differ diff --git a/app/assets/images/markers/tosewer.png b/app/assets/images/markers/tosewer.png new file mode 100755 index 000000000..c4262f9f6 Binary files /dev/null and b/app/assets/images/markers/tosewer.png differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index c28e5badc..28bed2d63 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -11,3 +11,4 @@ // about supported directives. // //= require_tree . +//= require local-time diff --git a/app/assets/javascripts/bootstrap.js b/app/assets/javascripts/bootstrap.js index ca868671c..5debfd7de 100644 --- a/app/assets/javascripts/bootstrap.js +++ b/app/assets/javascripts/bootstrap.js @@ -1,1726 +1,2363 @@ -/* =================================================== - * bootstrap-transition.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#transitions - * =================================================== - * 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( $ ) { +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under the MIT license + */ + +if (typeof jQuery === 'undefined') { + throw new Error('Bootstrap\'s JavaScript requires jQuery') +} + ++function ($) { + 'use strict'; + var version = $.fn.jquery.split(' ')[0].split('.') + if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) { + throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher') + } +}(jQuery); - $(function () { +/* ======================================================================== + * Bootstrap: transition.js v3.3.5 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ - "use strict" - - /* CSS TRANSITION SUPPORT (https://gist.github.com/373874) - * ======================================================= */ - - $.support.transition = (function () { - var thisBody = document.body || document.documentElement - , thisStyle = thisBody.style - , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined - - return support && { - end: (function () { - var transitionEnd = "TransitionEnd" - if ( $.browser.webkit ) { - transitionEnd = "webkitTransitionEnd" - } else if ( $.browser.mozilla ) { - transitionEnd = "transitionend" - } else if ( $.browser.opera ) { - transitionEnd = "oTransitionEnd" - } - return transitionEnd - }()) - } - })() - }) ++function ($) { + 'use strict'; -}( window.jQuery );/* ========================================================== - * bootstrap-alert.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#alerts - * ========================================================== - * 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" - - /* ALERT CLASS DEFINITION - * ====================== */ + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ - var dismiss = '[data-dismiss="alert"]' - , Alert = function ( el ) { - $(el).on('click', dismiss, this.close) + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } } + } - Alert.prototype = { + return false // explicit for ie8 ( ._.) + } - constructor: Alert + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false + var $el = this + $(this).one('bsTransitionEnd', function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } - , close: function ( e ) { - var $this = $(this) - , selector = $this.attr('data-target') - , $parent + $(function () { + $.support.transition = transitionEnd() - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } + if (!$.support.transition) return - $parent = $(selector) - $parent.trigger('close') + $.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function (e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) + } + } + }) - e && e.preventDefault() +}(jQuery); - $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) +/* ======================================================================== + * Bootstrap: alert.js v3.3.5 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ - $parent - .trigger('close') - .removeClass('in') - function removeElement() { - $parent - .trigger('closed') - .remove() - } ++function ($) { + 'use strict'; - $.support.transition && $parent.hasClass('fade') ? - $parent.on($.support.transition.end, removeElement) : - removeElement() - } + // ALERT CLASS DEFINITION + // ====================== + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) } + Alert.VERSION = '3.3.5' - /* ALERT PLUGIN DEFINITION - * ======================= */ + Alert.TRANSITION_DURATION = 150 - $.fn.alert = function ( option ) { - return this.each(function () { - var $this = $(this) - , data = $this.data('alert') - if (!data) $this.data('alert', (data = new Alert(this))) - if (typeof option == 'string') data[option].call($this) - }) - } + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') - $.fn.alert.Constructor = Alert + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + var $parent = $(selector) - /* ALERT DATA-API - * ============== */ + if (e) e.preventDefault() - $(function () { - $('body').on('click.alert.data-api', dismiss, Alert.prototype.close) - }) + if (!$parent.length) { + $parent = $this.closest('.alert') + } -}( window.jQuery );/* ============================================================ - * bootstrap-button.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#buttons - * ============================================================ - * 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" - - /* BUTTON PUBLIC CLASS DEFINITION - * ============================== */ - - var Button = function ( element, options ) { - this.$element = $(element) - this.options = $.extend({}, $.fn.button.defaults, options) - } - - Button.prototype = { - - constructor: Button - - , setState: function ( state ) { - var d = 'disabled' - , $el = this.$element - , data = $el.data() - , val = $el.is('input') ? 'val' : 'html' - - state = state + 'Text' - 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) - } + $parent.trigger(e = $.Event('close.bs.alert')) - , toggle: function () { - var $parent = this.$element.parent('[data-toggle="buttons-radio"]') + if (e.isDefaultPrevented()) return - $parent && $parent - .find('.active') - .removeClass('active') + $parent.removeClass('in') - this.$element.toggleClass('active') - } + 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(Alert.TRANSITION_DURATION) : + removeElement() } - /* BUTTON PLUGIN DEFINITION - * ======================== */ + // ALERT PLUGIN DEFINITION + // ======================= - $.fn.button = function ( option ) { + function Plugin(option) { return this.each(function () { var $this = $(this) - , data = $this.data('button') - , options = typeof option == 'object' && option - if (!data) $this.data('button', (data = new Button(this, options))) - if (option == 'toggle') data.toggle() - else if (option) data.setState(option) - }) - } + var data = $this.data('bs.alert') - $.fn.button.defaults = { - loadingText: 'loading...' + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) } - $.fn.button.Constructor = Button - + var old = $.fn.alert - /* BUTTON DATA-API - * =============== */ + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert - $(function () { - $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) { - var $btn = $(e.target) - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') - $btn.button('toggle') - }) - }) -}( window.jQuery );/* ========================================================== - * bootstrap-carousel.js v2.0.2 - * http://twitter.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 - * ========================= */ + // ALERT NO CONFLICT + // ================= - var Carousel = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, $.fn.carousel.defaults, options) - this.options.slide && this.slide(this.options.slide) - this.options.pause == 'hover' && this.$element - .on('mouseenter', $.proxy(this.pause, this)) - .on('mouseleave', $.proxy(this.cycle, this)) + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this } - Carousel.prototype = { - cycle: function () { - this.interval = setInterval($.proxy(this.next, this), this.options.interval) - return this - } + // ALERT DATA-API + // ============== - , to: function (pos) { - var $active = this.$element.find('.active') - , children = $active.parent().children() - , activePos = children.index($active) - , that = this + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) - if (pos > (children.length - 1) || pos < 0) return +}(jQuery); - if (this.sliding) { - return this.$element.one('slid', function () { - that.to(pos) - }) - } +/* ======================================================================== + * Bootstrap: button.js v3.3.5 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ - if (activePos == pos) { - return this.pause().cycle() - } - return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos])) - } ++function ($) { + 'use strict'; - , pause: function () { - clearInterval(this.interval) - this.interval = null - return this - } + // BUTTON PUBLIC CLASS DEFINITION + // ============================== - , next: function () { - if (this.sliding) return - return this.slide('next') - } + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } - , prev: function () { - if (this.sliding) return - return this.slide('prev') - } + Button.VERSION = '3.3.5' - , slide: function (type, next) { - var $active = this.$element.find('.active') - , $next = next || $active[type]() - , isCycling = this.interval - , direction = type == 'next' ? 'left' : 'right' - , fallback = type == 'next' ? 'first' : 'last' - , that = this + Button.DEFAULTS = { + loadingText: 'loading...' + } - this.sliding = true + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() - isCycling && this.pause() + state += 'Text' - $next = $next.length ? $next : this.$element.find('.item')[fallback]() + if (data.resetText == null) $el.data('resetText', $el[val]()) - if ($next.hasClass('active')) return + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + $el[val](data[state] == null ? this.options[state] : data[state]) - if (!$.support.transition && this.$element.hasClass('slide')) { - this.$element.trigger('slide') - $active.removeClass('active') - $next.addClass('active') - this.sliding = false - this.$element.trigger('slid') - } else { - $next.addClass(type) - $next[0].offsetWidth // force reflow - $active.addClass(direction) - $next.addClass(direction) - this.$element.trigger('slide') - this.$element.one($.support.transition.end, function () { - $next.removeClass([type, direction].join(' ')).addClass('active') - $active.removeClass(['active', direction].join(' ')) - that.sliding = false - setTimeout(function () { that.$element.trigger('slid') }, 0) - }) + 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) + } - isCycling && this.cycle() - - return this + 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')) changed = false + $parent.find('.active').removeClass('active') + this.$element.addClass('active') + } else if ($input.prop('type') == 'checkbox') { + if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false + this.$element.toggleClass('active') + } + $input.prop('checked', this.$element.hasClass('active')) + if (changed) $input.trigger('change') + } else { + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) + this.$element.toggleClass('active') } - } - /* CAROUSEL PLUGIN DEFINITION - * ========================== */ + // BUTTON PLUGIN DEFINITION + // ======================== - $.fn.carousel = function ( option ) { + function Plugin(option) { return this.each(function () { - var $this = $(this) - , data = $this.data('carousel') - , options = typeof option == 'object' && option - if (!data) $this.data('carousel', (data = new Carousel(this, options))) - if (typeof option == 'number') data.to(option) - else if (typeof option == 'string' || (option = options.slide)) data[option]() - else data.cycle() - }) - } + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option - $.fn.carousel.defaults = { - interval: 5000 - , pause: 'hover' - } + if (!data) $this.data('bs.button', (data = new Button(this, options))) - $.fn.carousel.Constructor = Carousel + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + var old = $.fn.button - /* CAROUSEL DATA-API - * ================= */ + $.fn.button = Plugin + $.fn.button.Constructor = Button - $(function () { - $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) { - var $this = $(this), href - , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 - , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data()) - $target.carousel(options) - e.preventDefault() - }) - }) -}( window.jQuery );/* ============================================================= - * bootstrap-collapse.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#collapse - * ============================================================= - * 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" - - var Collapse = function ( element, options ) { - this.$element = $(element) - this.options = $.extend({}, $.fn.collapse.defaults, options) - - if (this.options["parent"]) { - this.$parent = $(this.options["parent"]) - } + // BUTTON NO CONFLICT + // ================== - this.options.toggle && this.toggle() + $.fn.button.noConflict = function () { + $.fn.button = old + return this } - Collapse.prototype = { - constructor: Collapse + // BUTTON DATA-API + // =============== - , dimension: function () { - var hasWidth = this.$element.hasClass('width') - return hasWidth ? 'width' : 'height' - } + $(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') + if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault() + }) + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) + }) - , show: function () { - var dimension = this.dimension() - , scroll = $.camelCase(['scroll', dimension].join('-')) - , actives = this.$parent && this.$parent.find('.in') - , hasData +}(jQuery); - if (actives && actives.length) { - hasData = actives.data('collapse') - actives.collapse('hide') - hasData || actives.data('collapse', null) - } +/* ======================================================================== + * Bootstrap: carousel.js v3.3.5 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ - this.$element[dimension](0) - this.transition('addClass', 'show', 'shown') - this.$element[dimension](this.$element[0][scroll]) - } ++function ($) { + 'use strict'; - , hide: function () { - var dimension = this.dimension() - this.reset(this.$element[dimension]()) - this.transition('removeClass', 'hide', 'hidden') - this.$element[dimension](0) - } + // CAROUSEL CLASS DEFINITION + // ========================= - , reset: function ( size ) { - var dimension = this.dimension() + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = null + this.sliding = null + this.interval = null + this.$active = null + this.$items = null + + this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) + + this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } - this.$element - .removeClass('collapse') - [dimension](size || 'auto') - [0].offsetWidth + Carousel.VERSION = '3.3.5' - this.$element[size ? 'addClass' : 'removeClass']('collapse') + Carousel.TRANSITION_DURATION = 600 - return this + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true, + keyboard: true + } + + Carousel.prototype.keydown = function (e) { + if (/input|textarea/i.test(e.target.tagName)) return + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return } - , transition: function ( method, startEvent, completeEvent ) { - var that = this - , complete = function () { - if (startEvent == 'show') that.reset() - that.$element.trigger(completeEvent) - } + e.preventDefault() + } - this.$element - .trigger(startEvent) - [method]('in') + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) - $.support.transition && this.$element.hasClass('collapse') ? - this.$element.one($.support.transition.end, complete) : - complete() - } + this.interval && clearInterval(this.interval) - , toggle: function () { - this[this.$element.hasClass('in') ? 'hide' : 'show']() - } + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + return this } - /* COLLAPSIBLE PLUGIN DEFINITION - * ============================== */ + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } - $.fn.collapse = function ( option ) { - return this.each(function () { - var $this = $(this) - , data = $this.data('collapse') - , options = typeof option == 'object' && option - if (!data) $this.data('collapse', (data = new Collapse(this, options))) - if (typeof option == 'string') data[option]() - }) + Carousel.prototype.getItemForDirection = function (direction, active) { + var activeIndex = this.getItemIndex(active) + var willWrap = (direction == 'prev' && activeIndex === 0) + || (direction == 'next' && activeIndex == (this.$items.length - 1)) + if (willWrap && !this.options.wrap) return active + var delta = direction == 'prev' ? -1 : 1 + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) } - $.fn.collapse.defaults = { - toggle: true + 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.eq(pos)) } - $.fn.collapse.Constructor = Collapse + 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) + } - /* COLLAPSIBLE DATA-API - * ==================== */ + this.interval = clearInterval(this.interval) - $(function () { - $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { - var $this = $(this), href - , target = $this.attr('data-target') - || e.preventDefault() - || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 - , option = $(target).data('collapse') ? 'toggle' : $this.data() - $(target).collapse(option) - }) - }) + return this + } -}( window.jQuery );/* ============================================================ - * bootstrap-dropdown.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#dropdowns - * ============================================================ - * 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" - - /* DROPDOWN CLASS DEFINITION - * ========================= */ - - var toggle = '[data-toggle="dropdown"]' - , Dropdown = function ( element ) { - var $el = $(element).on('click.dropdown.data-api', this.toggle) - $('html').on('click.dropdown.data-api', function () { - $el.parent().removeClass('open') - }) - } + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } - Dropdown.prototype = { + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } - constructor: Dropdown + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || this.getItemForDirection(type, $active) + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var that = this - , toggle: function ( e ) { - var $this = $(this) - , selector = $this.attr('data-target') - , $parent - , isActive + if ($next.hasClass('active')) return (this.sliding = false) - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return - $parent = $(selector) - $parent.length || ($parent = $this.parent()) + this.sliding = true - isActive = $parent.hasClass('open') + isCycling && this.pause() - clearMenus() - !isActive && $parent.toggleClass('open') + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } - return false + 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(Carousel.TRANSITION_DURATION) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) } - } + isCycling && this.cycle() - function clearMenus() { - $(toggle).parent().removeClass('open') + return this } - /* DROPDOWN PLUGIN DEFINITION - * ========================== */ + // CAROUSEL PLUGIN DEFINITION + // ========================== - $.fn.dropdown = function ( option ) { + function Plugin(option) { return this.each(function () { - var $this = $(this) - , data = $this.data('dropdown') - if (!data) $this.data('dropdown', (data = new Dropdown(this))) - if (typeof option == 'string') data[option].call($this) + 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() }) } - $.fn.dropdown.Constructor = Dropdown + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel - /* APPLY TO STANDARD DROPDOWN ELEMENTS - * =================================== */ + // CAROUSEL NO CONFLICT + // ==================== - $(function () { - $('html').on('click.dropdown.data-api', clearMenus) - $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) - }) + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } -}( window.jQuery );/* ========================================================= - * bootstrap-modal.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#modals - * ========================================================= - * 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. - * ========================================================= */ + // CAROUSEL DATA-API + // ================= -!function( $ ){ + var clickHandler = 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 - "use strict" + Plugin.call($target, options) - /* MODAL CLASS DEFINITION - * ====================== */ + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } - var Modal = function ( content, options ) { - this.options = options - this.$element = $(content) - .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) + e.preventDefault() } - Modal.prototype = { + $(document) + .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) + .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) - constructor: Modal + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) - , toggle: function () { - return this[!this.isShown ? 'show' : 'hide']() - } +}(jQuery); - , show: function () { - var that = this +/* ======================================================================== + * Bootstrap: collapse.js v3.3.5 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ - if (this.isShown) return - $('body').addClass('modal-open') ++function ($) { + 'use strict'; - this.isShown = true - this.$element.trigger('show') + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ - escape.call(this) - backdrop.call(this, function () { - var transition = $.support.transition && that.$element.hasClass('fade') + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + + '[data-toggle="collapse"][data-target="#' + element.id + '"]') + this.transitioning = null - !that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position + if (this.options.parent) { + this.$parent = this.getParent() + } else { + this.addAriaAndCollapsedClass(this.$element, this.$trigger) + } - that.$element - .show() + if (this.options.toggle) this.toggle() + } - if (transition) { - that.$element[0].offsetWidth // force reflow - } + Collapse.VERSION = '3.3.5' - that.$element.addClass('in') + Collapse.TRANSITION_DURATION = 350 - transition ? - that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) : - that.$element.trigger('shown') + Collapse.DEFAULTS = { + toggle: true + } - }) - } + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } - , hide: function ( e ) { - e && e.preventDefault() + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return - if (!this.isShown) return + var activesData + var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') - var that = this - this.isShown = false + if (actives && actives.length) { + activesData = actives.data('bs.collapse') + if (activesData && activesData.transitioning) return + } - $('body').removeClass('modal-open') + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return - escape.call(this) + if (actives && actives.length) { + Plugin.call(actives, 'hide') + activesData || actives.data('bs.collapse', null) + } - this.$element - .trigger('hide') - .removeClass('in') + var dimension = this.dimension() - $.support.transition && this.$element.hasClass('fade') ? - hideWithTransition.call(this) : - hideModal.call(this) - } + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + .attr('aria-expanded', true) - } + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', true) + this.transitioning = 1 - /* MODAL PRIVATE METHODS - * ===================== */ + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } - function hideWithTransition() { - var that = this - , timeout = setTimeout(function () { - that.$element.off($.support.transition.end) - hideModal.call(that) - }, 500) - - this.$element.one($.support.transition.end, function () { - clearTimeout(timeout) - hideModal.call(that) - }) - } + if (!$.support.transition) return complete.call(this) - function hideModal( that ) { - this.$element - .hide() - .trigger('hidden') + var scrollSize = $.camelCase(['scroll', dimension].join('-')) - backdrop.call(this) + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } - function backdrop( callback ) { - var that = this - , animate = this.$element.hasClass('fade') ? 'fade' : '' + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return - if (this.isShown && this.options.backdrop) { - var doAnimate = $.support.transition && animate + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return - this.$backdrop = $('