├── .gitignore
├── CONTRIBUTING.md
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── Procfile
├── README.md
├── Rakefile
├── app.rb
├── model.rb
├── public
├── css
│ ├── bootstrap-switch.css
│ ├── bootstrap-theme.css
│ ├── bootstrap.css
│ └── nightlies.css
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ └── glyphicons-halflings-regular.woff2
└── js
│ ├── bootstrap-switch.js
│ ├── bootstrap.js
│ ├── dashboard.js
│ └── jquery.js
├── views
├── dashboard.erb
├── landing.erb
└── layout.erb
└── warden_travis
└── strategy.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/config
2 | .env
3 | nightlies.db
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Dependencies
2 |
3 | - Ruby 2.3.x (2.4.0 currently fails a `json` gem issue)
4 | - Postgresql 9.x
5 |
6 | ## Env Vars
7 |
8 | - `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET`
9 | - `DATABASE_URL=postgres://localhost` or whatever appropriate location your database is at.
10 |
11 | ## Get going
12 |
13 | 1. Install deps: `bundle install`
14 | 1. Init the db schema: `rake schema`
15 | 1. Run the app: `bundle exec ruby app.rb`
16 | 1. Load the website: http://localhost:4567
17 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2015, Noah Kantrowitz
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | source 'https://rubygems.org'
18 |
19 | gem 'sinatra'
20 | gem 'thin'
21 | gem 'travis'
22 | gem 'octokit'
23 | gem 'warden'
24 | gem 'warden-github'
25 | gem 'sinatra_auth_github'
26 | gem 'sequel'
27 | gem 'rake'
28 | gem 'snitcher'
29 | gem 'librato-rack'
30 |
31 | group :production do
32 | gem 'pg'
33 | end
34 |
35 | group :development do
36 | gem 'sqlite3'
37 | end
38 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | activesupport (4.2.1)
5 | i18n (~> 0.7)
6 | json (~> 1.7, >= 1.7.7)
7 | minitest (~> 5.1)
8 | thread_safe (~> 0.3, >= 0.3.4)
9 | tzinfo (~> 1.1)
10 | addressable (2.3.8)
11 | aggregate (0.2.2)
12 | backports (3.6.4)
13 | coderay (1.1.0)
14 | daemons (1.2.2)
15 | ethon (0.7.3)
16 | ffi (>= 1.3.0)
17 | eventmachine (1.0.7)
18 | faraday (0.9.1)
19 | multipart-post (>= 1.2, < 3)
20 | faraday_middleware (0.9.1)
21 | faraday (>= 0.7.4, < 0.10)
22 | ffi (1.9.8)
23 | gh (0.14.0)
24 | addressable
25 | backports
26 | faraday (~> 0.8)
27 | multi_json (~> 1.0)
28 | net-http-persistent (>= 2.7)
29 | net-http-pipeline
30 | highline (1.7.2)
31 | i18n (0.7.0)
32 | json (1.8.2)
33 | launchy (2.4.3)
34 | addressable (~> 2.3)
35 | librato-metrics (1.5.1)
36 | aggregate (~> 0.2.2)
37 | faraday (~> 0.7)
38 | multi_json
39 | librato-rack (0.4.5)
40 | librato-metrics (~> 1.1)
41 | method_source (0.8.2)
42 | minitest (5.6.1)
43 | multi_json (1.11.0)
44 | multipart-post (2.0.0)
45 | net-http-persistent (2.9.4)
46 | net-http-pipeline (1.0.1)
47 | octokit (3.8.0)
48 | sawyer (~> 0.6.0, >= 0.5.3)
49 | pg (0.18.2)
50 | pry (0.9.12.6)
51 | coderay (~> 1.0)
52 | method_source (~> 0.8)
53 | slop (~> 3.4)
54 | pusher-client (0.6.2)
55 | json
56 | websocket (~> 1.0)
57 | rack (1.6.1)
58 | rack-protection (1.5.3)
59 | rack
60 | rake (10.4.2)
61 | sawyer (0.6.0)
62 | addressable (~> 2.3.5)
63 | faraday (~> 0.8, < 0.10)
64 | sequel (4.22.0)
65 | sinatra (1.4.6)
66 | rack (~> 1.4)
67 | rack-protection (~> 1.4)
68 | tilt (>= 1.3, < 3)
69 | sinatra_auth_github (1.2.0)
70 | sinatra (~> 1.0)
71 | warden-github (~> 1.2.0)
72 | slop (3.6.0)
73 | snitcher (0.3.1)
74 | sqlite3 (1.3.10)
75 | thin (1.6.3)
76 | daemons (~> 1.0, >= 1.0.9)
77 | eventmachine (~> 1.0)
78 | rack (~> 1.0)
79 | thread_safe (0.3.5)
80 | tilt (2.0.1)
81 | travis (1.7.6)
82 | addressable (~> 2.3)
83 | backports
84 | faraday (~> 0.9)
85 | faraday_middleware (~> 0.9, >= 0.9.1)
86 | gh (~> 0.13)
87 | highline (~> 1.6)
88 | launchy (~> 2.1)
89 | pry (~> 0.9, < 0.10)
90 | pusher-client (~> 0.4)
91 | typhoeus (~> 0.6, >= 0.6.8)
92 | typhoeus (0.7.1)
93 | ethon (>= 0.7.1)
94 | tzinfo (1.2.2)
95 | thread_safe (~> 0.1)
96 | warden (1.2.3)
97 | rack (>= 1.0)
98 | warden-github (1.2.0)
99 | activesupport (> 3.0)
100 | octokit (> 2.1.0)
101 | warden (> 1.0)
102 | websocket (1.2.2)
103 |
104 | PLATFORMS
105 | ruby
106 |
107 | DEPENDENCIES
108 | librato-rack
109 | octokit
110 | pg
111 | rake
112 | sequel
113 | sinatra
114 | sinatra_auth_github
115 | snitcher
116 | sqlite3
117 | thin
118 | travis
119 | warden
120 | warden-github
121 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec ruby app.rb
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [Nightli.es](https://nightli.es/)
2 |
3 | Nightli.es provides automatic nightly builds for [Travis CI](https://travis-ci.org/).
4 |
5 | ## Why have nightly builds?
6 |
7 | When developing a library, often some components like other libraries or the
8 | language interpreter will use the latest available version. This can lead to
9 | nasty situations when an upstream component releases a new version that breaks
10 | your tests, but you don't find out until your next git push. Nightli.es
11 | ensures each project builds at least once every 24 hours so these failures will
12 | be caught as soon as possible.
13 |
14 | ## Why does Nightli.es require so many GitHub permissions?
15 |
16 | Nightli.es uses the Travis API to get the list of projects you have access to
17 | and to kick off builds. Unfortunately Travis' GitHub authentication integration
18 | requires that I request all the same permissions as Travis itself. Your GitHub
19 | OAuth tokens are never stored server-side, only your Travis API token is stored
20 | for use in starting builds via the API.
21 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2015, Noah Kantrowitz
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | require 'benchmark'
18 |
19 | require 'snitcher'
20 |
21 | require_relative './model'
22 |
23 | task :schema do
24 | Nightlies::Model.schema!
25 | end
26 |
27 | task :run do
28 | time = Benchmark.realtime { Nightlies::Model.run! }
29 | msg = "Finished in #{time} seconds."
30 | puts(msg)
31 | Snitcher.snitch(ENV['SNITCH_ID'], message: msg) if ENV['SNITCH_ID']
32 | end
33 |
--------------------------------------------------------------------------------
/app.rb:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2015, Noah Kantrowitz
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | require 'sinatra'
18 | require 'sinatra_auth_github'
19 | require 'tilt/erb'
20 | require 'librato-rack'
21 |
22 | require_relative './model'
23 | require_relative './warden_travis/strategy'
24 |
25 | module Nightlies
26 | class Application < Sinatra::Application
27 | configure do
28 | # Let's get this party started!
29 | enable :sessions
30 | set :session_secret, ENV['GITHUB_VERIFIER_SECRET']
31 | set :public_folder, 'public'
32 | use Rack::Protection::AuthenticityToken
33 |
34 | # Load librato
35 | use Librato::Rack if ENV['LIBRATO_TOKEN']
36 |
37 | # Load the GithHub authentication stuffs.
38 | set :github_options, {scopes: 'read:org user:email repo:status write:repo_hook repo_deployment'}
39 | register Sinatra::Auth::Github
40 |
41 | # Reconfigure Warden to use our strategy instead.
42 | use Class.new {
43 | def initialize(app)
44 | @app = app
45 | end
46 | def call(env)
47 | env['warden'].config.default_strategies :travis
48 | @app.call(env)
49 | end
50 | }
51 | end
52 |
53 | helpers do
54 | def travis_token
55 | authenticated? && github_user.attribs['travis_token']
56 | end
57 |
58 | def travis_api
59 | raise "No travis token" unless travis_token
60 | Travis::Client.new(access_token: travis_token, agent_info: 'nightli.es')
61 | end
62 |
63 | def no_cache!
64 | cache_control :no_cache, :no_store, :must_revalidate
65 | headers['Expires'] = '0'
66 | headers['Pragma'] = 'no-cache'
67 | end
68 | end
69 |
70 | before do
71 | headers 'Content-Type' => 'text/html; charset=utf8'
72 | end
73 |
74 | get '/' do
75 | no_cache!
76 | if !authenticated?
77 | erb :landing
78 | else
79 | @repos = travis_api.get_raw('/hooks')['hooks'].inject([]) do |memo, data|
80 | if data['admin'] && data['active']
81 | row = Nightlies::Model.by_id(data['id'])
82 | memo << data.merge(
83 | 'last_nightly' => row && row[:last_nightly],
84 | 'enabled' => row && !!row[:travis_token],
85 | )
86 | end
87 | memo
88 | end
89 | @extra_js = 'dashboard.js'
90 | erb :dashboard
91 | end
92 | end
93 |
94 | get '/login' do
95 | no_cache!
96 | authenticate!
97 | redirect '/'
98 | end
99 |
100 | get '/logout' do
101 | no_cache!
102 | logout!
103 | redirect '/'
104 | end
105 |
106 | post '/enable/:id' do
107 | logger.warn "Enabling nightly builds for #{params['slug']} via #{github_user.login}"
108 | Nightlies::Model.enable!(github_user, params['id'].to_i, params['slug'])
109 | content_type :json
110 | {success: true}.to_json
111 | end
112 |
113 | post '/disable/:id' do
114 | logger.warn "Disabling nightly builds for #{params['slug']} via #{github_user.login}"
115 | Nightlies::Model.disable!(github_user, params['id'].to_i)
116 | content_type :json
117 | {success: true}.to_json
118 | end
119 |
120 | run! if app_file == $0
121 | end
122 | end
123 |
--------------------------------------------------------------------------------
/model.rb:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2015, Noah Kantrowitz
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | require 'sequel'
18 | require 'travis'
19 |
20 |
21 | module Nightlies
22 | class Model
23 | def self.db
24 | @db ||= Sequel.connect(ENV['DATABASE_URL'])
25 | end
26 |
27 | def self.schema!
28 | db.create_table(:nightlies) do
29 | primary_key :id
30 | String :username
31 | String :owner
32 | String :name
33 | String :travis_token
34 | Time :last_nightly # Because the API doesn't see API-initiated builds.
35 | unique [:owner, :name]
36 | end
37 | end
38 |
39 | def self.enable!(user, repo_id, slug)
40 | owner, name = slug.split('/', 2)
41 | values = {owner: owner, name: name, username: user.login, travis_token: user.attribs['travis_token']}
42 | if db[:nightlies].filter(id: repo_id).update(values) == 0
43 | db[:nightlies].insert(values.merge(id: repo_id))
44 | end
45 | end
46 |
47 | def self.disable!(user, repo_id)
48 | db[:nightlies].filter(id: repo_id).update(travis_token: nil)
49 | end
50 |
51 | def self.by_id(repo_id)
52 | db[:nightlies].filter(id: repo_id).first
53 | end
54 |
55 | def self.run!
56 | failure = true
57 | db[:nightlies].each do |data|
58 | begin
59 | # Skip disabled repos.
60 | next unless data[:travis_token]
61 | slug = "#{data[:owner]}/#{data[:name]}"
62 | puts "[#{slug}] Checking."
63 | travis = Travis::Client.new(access_token: data[:travis_token])
64 | repo = travis.repo(slug)
65 | # Could speed this up in the future.
66 | last_push_build = repo.builds(event_type: 'push').first
67 | last_api_build = repo.builds(event_type: 'api').first
68 | if (last_push_build && last_push_build.pending?) || (last_api_build && last_api_build.pending?)
69 | # Currently building, we're done here.
70 | puts "[#{slug}] Already building."
71 | next
72 | end
73 | build_times = [data[:last_nightly] || Time.at(0)]
74 | build_times << last_push_build.finished_at if last_push_build
75 | build_times << last_api_build.finished_at if last_api_build
76 | last_build_time = build_times.max
77 | # Check if it has been 23.5 hours since the last build. The 0.5 is so
78 | # that if the last build was kicked off by us slightly more than 24
79 | # hours ago, we don't drift back by an hour each day.
80 | if Time.now - last_build_time > 60*60*23.5
81 | puts "[#{slug}] Requesting a build, last build time #{last_build_time}."
82 | self.run_build!(travis, slug)
83 | db[:nightlies].filter(id: data[:id]).update(last_nightly: Time.now)
84 | end
85 | # If any succeed then we probably aren't globally down.
86 | failure = false
87 | rescue Exception => ex
88 | puts "[#{slug}] ERROR: #{ex}"
89 | end
90 | end
91 | raise "All builds failed" if failure
92 | end
93 |
94 | def self.run_build!(travis, slug)
95 | encoded_slug = slug.gsub(/\//, '%2F')
96 | url = "/repo/#{encoded_slug}/requests"
97 | body = {
98 | request: {
99 | branch: 'master',
100 | message: "Nightly build for #{slug} via nightli.es.",
101 | },
102 | }
103 | headers = {
104 | 'Travis-API-Version' => 3,
105 | }
106 | travis.post_raw(url, body, headers)
107 | end
108 |
109 | end
110 | end
111 |
--------------------------------------------------------------------------------
/public/css/bootstrap-switch.css:
--------------------------------------------------------------------------------
1 | /* ========================================================================
2 | * bootstrap-switch - v3.3.2
3 | * http://www.bootstrap-switch.org
4 | * ========================================================================
5 | * Copyright 2012-2013 Mattia Larentis
6 | *
7 | * ========================================================================
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | * ========================================================================
20 | */
21 |
22 | .bootstrap-switch {
23 | display: inline-block;
24 | direction: ltr;
25 | cursor: pointer;
26 | border-radius: 4px;
27 | border: 1px solid;
28 | border-color: #cccccc;
29 | position: relative;
30 | text-align: left;
31 | overflow: hidden;
32 | line-height: 8px;
33 | z-index: 0;
34 | -webkit-user-select: none;
35 | -moz-user-select: none;
36 | -ms-user-select: none;
37 | user-select: none;
38 | vertical-align: middle;
39 | -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
40 | transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
41 | }
42 | .bootstrap-switch .bootstrap-switch-container {
43 | display: inline-block;
44 | top: 0;
45 | border-radius: 4px;
46 | -webkit-transform: translate3d(0, 0, 0);
47 | transform: translate3d(0, 0, 0);
48 | }
49 | .bootstrap-switch .bootstrap-switch-handle-on,
50 | .bootstrap-switch .bootstrap-switch-handle-off,
51 | .bootstrap-switch .bootstrap-switch-label {
52 | -webkit-box-sizing: border-box;
53 | -moz-box-sizing: border-box;
54 | box-sizing: border-box;
55 | cursor: pointer;
56 | display: inline-block !important;
57 | height: 100%;
58 | padding: 6px 12px;
59 | font-size: 14px;
60 | line-height: 20px;
61 | }
62 | .bootstrap-switch .bootstrap-switch-handle-on,
63 | .bootstrap-switch .bootstrap-switch-handle-off {
64 | text-align: center;
65 | z-index: 1;
66 | }
67 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary,
68 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary {
69 | color: #fff;
70 | background: #428bca;
71 | }
72 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info,
73 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info {
74 | color: #fff;
75 | background: #5bc0de;
76 | }
77 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success,
78 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success {
79 | color: #fff;
80 | background: #5cb85c;
81 | }
82 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning,
83 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning {
84 | background: #f0ad4e;
85 | color: #fff;
86 | }
87 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger,
88 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger {
89 | color: #fff;
90 | background: #d9534f;
91 | }
92 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default,
93 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default {
94 | color: #000;
95 | background: #eeeeee;
96 | }
97 | .bootstrap-switch .bootstrap-switch-label {
98 | text-align: center;
99 | margin-top: -1px;
100 | margin-bottom: -1px;
101 | z-index: 100;
102 | color: #333333;
103 | background: #ffffff;
104 | }
105 | .bootstrap-switch .bootstrap-switch-handle-on {
106 | border-bottom-left-radius: 3px;
107 | border-top-left-radius: 3px;
108 | }
109 | .bootstrap-switch .bootstrap-switch-handle-off {
110 | border-bottom-right-radius: 3px;
111 | border-top-right-radius: 3px;
112 | }
113 | .bootstrap-switch input[type='radio'],
114 | .bootstrap-switch input[type='checkbox'] {
115 | position: absolute !important;
116 | top: 0;
117 | left: 0;
118 | opacity: 0;
119 | filter: alpha(opacity=0);
120 | z-index: -1;
121 | }
122 | .bootstrap-switch input[type='radio'].form-control,
123 | .bootstrap-switch input[type='checkbox'].form-control {
124 | height: auto;
125 | }
126 | .bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,
127 | .bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,
128 | .bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label {
129 | padding: 1px 5px;
130 | font-size: 12px;
131 | line-height: 1.5;
132 | }
133 | .bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,
134 | .bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,
135 | .bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label {
136 | padding: 5px 10px;
137 | font-size: 12px;
138 | line-height: 1.5;
139 | }
140 | .bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,
141 | .bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,
142 | .bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label {
143 | padding: 6px 16px;
144 | font-size: 18px;
145 | line-height: 1.33;
146 | }
147 | .bootstrap-switch.bootstrap-switch-disabled,
148 | .bootstrap-switch.bootstrap-switch-readonly,
149 | .bootstrap-switch.bootstrap-switch-indeterminate {
150 | cursor: default !important;
151 | }
152 | .bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,
153 | .bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,
154 | .bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,
155 | .bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,
156 | .bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,
157 | .bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,
158 | .bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,
159 | .bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label,
160 | .bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label {
161 | opacity: 0.5;
162 | filter: alpha(opacity=50);
163 | cursor: default !important;
164 | }
165 | .bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container {
166 | -webkit-transition: margin-left 0.5s;
167 | transition: margin-left 0.5s;
168 | }
169 | .bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on {
170 | border-bottom-left-radius: 0;
171 | border-top-left-radius: 0;
172 | border-bottom-right-radius: 3px;
173 | border-top-right-radius: 3px;
174 | }
175 | .bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off {
176 | border-bottom-right-radius: 0;
177 | border-top-right-radius: 0;
178 | border-bottom-left-radius: 3px;
179 | border-top-left-radius: 3px;
180 | }
181 | .bootstrap-switch.bootstrap-switch-focused {
182 | border-color: #66afe9;
183 | outline: 0;
184 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
185 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
186 | }
187 | .bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label,
188 | .bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label {
189 | border-bottom-right-radius: 3px;
190 | border-top-right-radius: 3px;
191 | }
192 | .bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label,
193 | .bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label {
194 | border-bottom-left-radius: 3px;
195 | border-top-left-radius: 3px;
196 | }
197 |
--------------------------------------------------------------------------------
/public/css/bootstrap-theme.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.4 (http://getbootstrap.com)
3 | * Copyright 2011-2015 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 |
7 | /*!
8 | * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=9b560b8cf2bcd3a66885)
9 | * Config saved to config.json and https://gist.github.com/9b560b8cf2bcd3a66885
10 | */
11 | .btn-default,
12 | .btn-primary,
13 | .btn-success,
14 | .btn-info,
15 | .btn-warning,
16 | .btn-danger {
17 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
18 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
19 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
20 | }
21 | .btn-default:active,
22 | .btn-primary:active,
23 | .btn-success:active,
24 | .btn-info:active,
25 | .btn-warning:active,
26 | .btn-danger:active,
27 | .btn-default.active,
28 | .btn-primary.active,
29 | .btn-success.active,
30 | .btn-info.active,
31 | .btn-warning.active,
32 | .btn-danger.active {
33 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
34 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
35 | }
36 | .btn-default .badge,
37 | .btn-primary .badge,
38 | .btn-success .badge,
39 | .btn-info .badge,
40 | .btn-warning .badge,
41 | .btn-danger .badge {
42 | text-shadow: none;
43 | }
44 | .btn:active,
45 | .btn.active {
46 | background-image: none;
47 | }
48 | .btn-default {
49 | background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
50 | background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
51 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#e0e0e0));
52 | background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);
53 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
54 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
55 | background-repeat: repeat-x;
56 | border-color: #dbdbdb;
57 | text-shadow: 0 1px 0 #fff;
58 | border-color: #ccc;
59 | }
60 | .btn-default:hover,
61 | .btn-default:focus {
62 | background-color: #e0e0e0;
63 | background-position: 0 -15px;
64 | }
65 | .btn-default:active,
66 | .btn-default.active {
67 | background-color: #e0e0e0;
68 | border-color: #dbdbdb;
69 | }
70 | .btn-default.disabled,
71 | .btn-default:disabled,
72 | .btn-default[disabled] {
73 | background-color: #e0e0e0;
74 | background-image: none;
75 | }
76 | .btn-primary {
77 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
78 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
79 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
80 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
81 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
82 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
83 | background-repeat: repeat-x;
84 | border-color: #245580;
85 | }
86 | .btn-primary:hover,
87 | .btn-primary:focus {
88 | background-color: #265a88;
89 | background-position: 0 -15px;
90 | }
91 | .btn-primary:active,
92 | .btn-primary.active {
93 | background-color: #265a88;
94 | border-color: #245580;
95 | }
96 | .btn-primary.disabled,
97 | .btn-primary:disabled,
98 | .btn-primary[disabled] {
99 | background-color: #265a88;
100 | background-image: none;
101 | }
102 | .btn-success {
103 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
104 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
105 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
106 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
107 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
108 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
109 | background-repeat: repeat-x;
110 | border-color: #3e8f3e;
111 | }
112 | .btn-success:hover,
113 | .btn-success:focus {
114 | background-color: #419641;
115 | background-position: 0 -15px;
116 | }
117 | .btn-success:active,
118 | .btn-success.active {
119 | background-color: #419641;
120 | border-color: #3e8f3e;
121 | }
122 | .btn-success.disabled,
123 | .btn-success:disabled,
124 | .btn-success[disabled] {
125 | background-color: #419641;
126 | background-image: none;
127 | }
128 | .btn-info {
129 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
130 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
131 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
132 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
133 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
134 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
135 | background-repeat: repeat-x;
136 | border-color: #28a4c9;
137 | }
138 | .btn-info:hover,
139 | .btn-info:focus {
140 | background-color: #2aabd2;
141 | background-position: 0 -15px;
142 | }
143 | .btn-info:active,
144 | .btn-info.active {
145 | background-color: #2aabd2;
146 | border-color: #28a4c9;
147 | }
148 | .btn-info.disabled,
149 | .btn-info:disabled,
150 | .btn-info[disabled] {
151 | background-color: #2aabd2;
152 | background-image: none;
153 | }
154 | .btn-warning {
155 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
156 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
157 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
158 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
159 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
160 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
161 | background-repeat: repeat-x;
162 | border-color: #e38d13;
163 | }
164 | .btn-warning:hover,
165 | .btn-warning:focus {
166 | background-color: #eb9316;
167 | background-position: 0 -15px;
168 | }
169 | .btn-warning:active,
170 | .btn-warning.active {
171 | background-color: #eb9316;
172 | border-color: #e38d13;
173 | }
174 | .btn-warning.disabled,
175 | .btn-warning:disabled,
176 | .btn-warning[disabled] {
177 | background-color: #eb9316;
178 | background-image: none;
179 | }
180 | .btn-danger {
181 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
182 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
183 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
184 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
185 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
186 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
187 | background-repeat: repeat-x;
188 | border-color: #b92c28;
189 | }
190 | .btn-danger:hover,
191 | .btn-danger:focus {
192 | background-color: #c12e2a;
193 | background-position: 0 -15px;
194 | }
195 | .btn-danger:active,
196 | .btn-danger.active {
197 | background-color: #c12e2a;
198 | border-color: #b92c28;
199 | }
200 | .btn-danger.disabled,
201 | .btn-danger:disabled,
202 | .btn-danger[disabled] {
203 | background-color: #c12e2a;
204 | background-image: none;
205 | }
206 | .thumbnail,
207 | .img-thumbnail {
208 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
209 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
210 | }
211 | .dropdown-menu > li > a:hover,
212 | .dropdown-menu > li > a:focus {
213 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
214 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
215 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
216 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
217 | background-repeat: repeat-x;
218 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
219 | background-color: #e8e8e8;
220 | }
221 | .dropdown-menu > .active > a,
222 | .dropdown-menu > .active > a:hover,
223 | .dropdown-menu > .active > a:focus {
224 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
225 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
226 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
227 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
228 | background-repeat: repeat-x;
229 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
230 | background-color: #2e6da4;
231 | }
232 | .navbar-default {
233 | background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
234 | background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
235 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
236 | background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
237 | background-repeat: repeat-x;
238 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
239 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
240 | border-radius: 4px;
241 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
242 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
243 | }
244 | .navbar-default .navbar-nav > .open > a,
245 | .navbar-default .navbar-nav > .active > a {
246 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
247 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
248 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
249 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
250 | background-repeat: repeat-x;
251 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
252 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
253 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
254 | }
255 | .navbar-brand,
256 | .navbar-nav > li > a {
257 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
258 | }
259 | .navbar-inverse {
260 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);
261 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);
262 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222222));
263 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);
264 | background-repeat: repeat-x;
265 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
266 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
267 | }
268 | .navbar-inverse .navbar-nav > .open > a,
269 | .navbar-inverse .navbar-nav > .active > a {
270 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
271 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
272 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
273 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
274 | background-repeat: repeat-x;
275 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
276 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
277 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
278 | }
279 | .navbar-inverse .navbar-brand,
280 | .navbar-inverse .navbar-nav > li > a {
281 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
282 | }
283 | .navbar-static-top,
284 | .navbar-fixed-top,
285 | .navbar-fixed-bottom {
286 | border-radius: 0;
287 | }
288 | @media (max-width: 767px) {
289 | .navbar .navbar-nav .open .dropdown-menu > .active > a,
290 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
291 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
292 | color: #fff;
293 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
294 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
295 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
296 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
297 | background-repeat: repeat-x;
298 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
299 | }
300 | }
301 | .alert {
302 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
303 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
304 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
305 | }
306 | .alert-success {
307 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
308 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
309 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
310 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
311 | background-repeat: repeat-x;
312 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
313 | border-color: #b2dba1;
314 | }
315 | .alert-info {
316 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
317 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
318 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
319 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
320 | background-repeat: repeat-x;
321 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
322 | border-color: #9acfea;
323 | }
324 | .alert-warning {
325 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
326 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
327 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
328 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
329 | background-repeat: repeat-x;
330 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
331 | border-color: #f5e79e;
332 | }
333 | .alert-danger {
334 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
335 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
336 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
337 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
338 | background-repeat: repeat-x;
339 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
340 | border-color: #dca7a7;
341 | }
342 | .progress {
343 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
344 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
345 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
346 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
347 | background-repeat: repeat-x;
348 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
349 | }
350 | .progress-bar {
351 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
352 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
353 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
354 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
355 | background-repeat: repeat-x;
356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
357 | }
358 | .progress-bar-success {
359 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
360 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
361 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
362 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
363 | background-repeat: repeat-x;
364 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
365 | }
366 | .progress-bar-info {
367 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
368 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
369 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
370 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
371 | background-repeat: repeat-x;
372 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
373 | }
374 | .progress-bar-warning {
375 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
376 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
377 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
378 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
379 | background-repeat: repeat-x;
380 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
381 | }
382 | .progress-bar-danger {
383 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
384 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
385 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
386 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
387 | background-repeat: repeat-x;
388 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
389 | }
390 | .progress-bar-striped {
391 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
392 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
393 | background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
394 | }
395 | .list-group {
396 | border-radius: 4px;
397 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
398 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
399 | }
400 | .list-group-item.active,
401 | .list-group-item.active:hover,
402 | .list-group-item.active:focus {
403 | text-shadow: 0 -1px 0 #286090;
404 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
405 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
406 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
407 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
408 | background-repeat: repeat-x;
409 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
410 | border-color: #2b669a;
411 | }
412 | .list-group-item.active .badge,
413 | .list-group-item.active:hover .badge,
414 | .list-group-item.active:focus .badge {
415 | text-shadow: none;
416 | }
417 | .panel {
418 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
419 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
420 | }
421 | .panel-default > .panel-heading {
422 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
423 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
424 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
425 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
426 | background-repeat: repeat-x;
427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
428 | }
429 | .panel-primary > .panel-heading {
430 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
431 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
432 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
433 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
434 | background-repeat: repeat-x;
435 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
436 | }
437 | .panel-success > .panel-heading {
438 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
439 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
440 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
441 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
442 | background-repeat: repeat-x;
443 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
444 | }
445 | .panel-info > .panel-heading {
446 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
447 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
448 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
449 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
450 | background-repeat: repeat-x;
451 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
452 | }
453 | .panel-warning > .panel-heading {
454 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
455 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
456 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
457 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
458 | background-repeat: repeat-x;
459 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
460 | }
461 | .panel-danger > .panel-heading {
462 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
463 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
464 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
465 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
466 | background-repeat: repeat-x;
467 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
468 | }
469 | .well {
470 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
471 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
472 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
473 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
474 | background-repeat: repeat-x;
475 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
476 | border-color: #dcdcdc;
477 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
478 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
479 | }
480 |
--------------------------------------------------------------------------------
/public/css/nightlies.css:
--------------------------------------------------------------------------------
1 | html {
2 | height: 100%;
3 | }
4 |
5 | body {
6 | position: relative;
7 | min-height: 100%;
8 | padding-top: 50px;
9 | padding-bottom: 60px;
10 | }
11 |
12 | p.cta {
13 | text-align: center
14 | }
15 |
16 | .dashboard-row.saving {
17 | position: relative;
18 | }
19 |
20 | .dashboard-row.saving::before {
21 | content: "⌛";
22 | position: absolute;
23 | font-size: 30px;
24 | left: -25px;
25 | top: -4px;
26 | }
27 |
28 | .dashboard-slug, .dashboard-description {
29 | line-height: 30px;
30 | }
31 |
32 | .dashboard-slug {
33 | font-weight: bold;
34 | font-size: 130%;
35 | }
36 |
37 | .dashboard-description {
38 | font-style: italic;
39 | padding-left: 2px;
40 | }
41 |
42 | .footer {
43 | position: absolute;
44 | bottom: 0;
45 | width: 100%;
46 | height: 60px;
47 | line-height: 60px;
48 | color: #777;
49 | background-color: #f5f5f5;
50 | }
51 |
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderanger/nightlies/29bdfc76957b78e5539ea5a9d566ddcee556b15c/public/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderanger/nightlies/29bdfc76957b78e5539ea5a9d566ddcee556b15c/public/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderanger/nightlies/29bdfc76957b78e5539ea5a9d566ddcee556b15c/public/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderanger/nightlies/29bdfc76957b78e5539ea5a9d566ddcee556b15c/public/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/public/js/bootstrap-switch.js:
--------------------------------------------------------------------------------
1 | /* ========================================================================
2 | * bootstrap-switch - v3.3.2
3 | * http://www.bootstrap-switch.org
4 | * ========================================================================
5 | * Copyright 2012-2013 Mattia Larentis
6 | *
7 | * ========================================================================
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | * ========================================================================
20 | */
21 |
22 | (function() {
23 | var __slice = [].slice;
24 |
25 | (function($, window) {
26 | "use strict";
27 | var BootstrapSwitch;
28 | BootstrapSwitch = (function() {
29 | function BootstrapSwitch(element, options) {
30 | if (options == null) {
31 | options = {};
32 | }
33 | this.$element = $(element);
34 | this.options = $.extend({}, $.fn.bootstrapSwitch.defaults, {
35 | state: this.$element.is(":checked"),
36 | size: this.$element.data("size"),
37 | animate: this.$element.data("animate"),
38 | disabled: this.$element.is(":disabled"),
39 | readonly: this.$element.is("[readonly]"),
40 | indeterminate: this.$element.data("indeterminate"),
41 | inverse: this.$element.data("inverse"),
42 | radioAllOff: this.$element.data("radio-all-off"),
43 | onColor: this.$element.data("on-color"),
44 | offColor: this.$element.data("off-color"),
45 | onText: this.$element.data("on-text"),
46 | offText: this.$element.data("off-text"),
47 | labelText: this.$element.data("label-text"),
48 | handleWidth: this.$element.data("handle-width"),
49 | labelWidth: this.$element.data("label-width"),
50 | baseClass: this.$element.data("base-class"),
51 | wrapperClass: this.$element.data("wrapper-class")
52 | }, options);
53 | this.$wrapper = $("
", {
54 | "class": (function(_this) {
55 | return function() {
56 | var classes;
57 | classes = ["" + _this.options.baseClass].concat(_this._getClasses(_this.options.wrapperClass));
58 | classes.push(_this.options.state ? "" + _this.options.baseClass + "-on" : "" + _this.options.baseClass + "-off");
59 | if (_this.options.size != null) {
60 | classes.push("" + _this.options.baseClass + "-" + _this.options.size);
61 | }
62 | if (_this.options.disabled) {
63 | classes.push("" + _this.options.baseClass + "-disabled");
64 | }
65 | if (_this.options.readonly) {
66 | classes.push("" + _this.options.baseClass + "-readonly");
67 | }
68 | if (_this.options.indeterminate) {
69 | classes.push("" + _this.options.baseClass + "-indeterminate");
70 | }
71 | if (_this.options.inverse) {
72 | classes.push("" + _this.options.baseClass + "-inverse");
73 | }
74 | if (_this.$element.attr("id")) {
75 | classes.push("" + _this.options.baseClass + "-id-" + (_this.$element.attr("id")));
76 | }
77 | return classes.join(" ");
78 | };
79 | })(this)()
80 | });
81 | this.$container = $("
", {
82 | "class": "" + this.options.baseClass + "-container"
83 | });
84 | this.$on = $("
", {
85 | html: this.options.onText,
86 | "class": "" + this.options.baseClass + "-handle-on " + this.options.baseClass + "-" + this.options.onColor
87 | });
88 | this.$off = $("", {
89 | html: this.options.offText,
90 | "class": "" + this.options.baseClass + "-handle-off " + this.options.baseClass + "-" + this.options.offColor
91 | });
92 | this.$label = $("", {
93 | html: this.options.labelText,
94 | "class": "" + this.options.baseClass + "-label"
95 | });
96 | this.$element.on("init.bootstrapSwitch", (function(_this) {
97 | return function() {
98 | return _this.options.onInit.apply(element, arguments);
99 | };
100 | })(this));
101 | this.$element.on("switchChange.bootstrapSwitch", (function(_this) {
102 | return function() {
103 | return _this.options.onSwitchChange.apply(element, arguments);
104 | };
105 | })(this));
106 | this.$container = this.$element.wrap(this.$container).parent();
107 | this.$wrapper = this.$container.wrap(this.$wrapper).parent();
108 | this.$element.before(this.options.inverse ? this.$off : this.$on).before(this.$label).before(this.options.inverse ? this.$on : this.$off);
109 | if (this.options.indeterminate) {
110 | this.$element.prop("indeterminate", true);
111 | }
112 | this._init();
113 | this._elementHandlers();
114 | this._handleHandlers();
115 | this._labelHandlers();
116 | this._formHandler();
117 | this._externalLabelHandler();
118 | this.$element.trigger("init.bootstrapSwitch");
119 | }
120 |
121 | BootstrapSwitch.prototype._constructor = BootstrapSwitch;
122 |
123 | BootstrapSwitch.prototype.state = function(value, skip) {
124 | if (typeof value === "undefined") {
125 | return this.options.state;
126 | }
127 | if (this.options.disabled || this.options.readonly) {
128 | return this.$element;
129 | }
130 | if (this.options.state && !this.options.radioAllOff && this.$element.is(":radio")) {
131 | return this.$element;
132 | }
133 | if (this.options.indeterminate) {
134 | this.indeterminate(false);
135 | }
136 | value = !!value;
137 | this.$element.prop("checked", value).trigger("change.bootstrapSwitch", skip);
138 | return this.$element;
139 | };
140 |
141 | BootstrapSwitch.prototype.toggleState = function(skip) {
142 | if (this.options.disabled || this.options.readonly) {
143 | return this.$element;
144 | }
145 | if (this.options.indeterminate) {
146 | this.indeterminate(false);
147 | return this.state(true);
148 | } else {
149 | return this.$element.prop("checked", !this.options.state).trigger("change.bootstrapSwitch", skip);
150 | }
151 | };
152 |
153 | BootstrapSwitch.prototype.size = function(value) {
154 | if (typeof value === "undefined") {
155 | return this.options.size;
156 | }
157 | if (this.options.size != null) {
158 | this.$wrapper.removeClass("" + this.options.baseClass + "-" + this.options.size);
159 | }
160 | if (value) {
161 | this.$wrapper.addClass("" + this.options.baseClass + "-" + value);
162 | }
163 | this._width();
164 | this._containerPosition();
165 | this.options.size = value;
166 | return this.$element;
167 | };
168 |
169 | BootstrapSwitch.prototype.animate = function(value) {
170 | if (typeof value === "undefined") {
171 | return this.options.animate;
172 | }
173 | value = !!value;
174 | if (value === this.options.animate) {
175 | return this.$element;
176 | }
177 | return this.toggleAnimate();
178 | };
179 |
180 | BootstrapSwitch.prototype.toggleAnimate = function() {
181 | this.options.animate = !this.options.animate;
182 | this.$wrapper.toggleClass("" + this.options.baseClass + "-animate");
183 | return this.$element;
184 | };
185 |
186 | BootstrapSwitch.prototype.disabled = function(value) {
187 | if (typeof value === "undefined") {
188 | return this.options.disabled;
189 | }
190 | value = !!value;
191 | if (value === this.options.disabled) {
192 | return this.$element;
193 | }
194 | return this.toggleDisabled();
195 | };
196 |
197 | BootstrapSwitch.prototype.toggleDisabled = function() {
198 | this.options.disabled = !this.options.disabled;
199 | this.$element.prop("disabled", this.options.disabled);
200 | this.$wrapper.toggleClass("" + this.options.baseClass + "-disabled");
201 | return this.$element;
202 | };
203 |
204 | BootstrapSwitch.prototype.readonly = function(value) {
205 | if (typeof value === "undefined") {
206 | return this.options.readonly;
207 | }
208 | value = !!value;
209 | if (value === this.options.readonly) {
210 | return this.$element;
211 | }
212 | return this.toggleReadonly();
213 | };
214 |
215 | BootstrapSwitch.prototype.toggleReadonly = function() {
216 | this.options.readonly = !this.options.readonly;
217 | this.$element.prop("readonly", this.options.readonly);
218 | this.$wrapper.toggleClass("" + this.options.baseClass + "-readonly");
219 | return this.$element;
220 | };
221 |
222 | BootstrapSwitch.prototype.indeterminate = function(value) {
223 | if (typeof value === "undefined") {
224 | return this.options.indeterminate;
225 | }
226 | value = !!value;
227 | if (value === this.options.indeterminate) {
228 | return this.$element;
229 | }
230 | return this.toggleIndeterminate();
231 | };
232 |
233 | BootstrapSwitch.prototype.toggleIndeterminate = function() {
234 | this.options.indeterminate = !this.options.indeterminate;
235 | this.$element.prop("indeterminate", this.options.indeterminate);
236 | this.$wrapper.toggleClass("" + this.options.baseClass + "-indeterminate");
237 | this._containerPosition();
238 | return this.$element;
239 | };
240 |
241 | BootstrapSwitch.prototype.inverse = function(value) {
242 | if (typeof value === "undefined") {
243 | return this.options.inverse;
244 | }
245 | value = !!value;
246 | if (value === this.options.inverse) {
247 | return this.$element;
248 | }
249 | return this.toggleInverse();
250 | };
251 |
252 | BootstrapSwitch.prototype.toggleInverse = function() {
253 | var $off, $on;
254 | this.$wrapper.toggleClass("" + this.options.baseClass + "-inverse");
255 | $on = this.$on.clone(true);
256 | $off = this.$off.clone(true);
257 | this.$on.replaceWith($off);
258 | this.$off.replaceWith($on);
259 | this.$on = $off;
260 | this.$off = $on;
261 | this.options.inverse = !this.options.inverse;
262 | return this.$element;
263 | };
264 |
265 | BootstrapSwitch.prototype.onColor = function(value) {
266 | var color;
267 | color = this.options.onColor;
268 | if (typeof value === "undefined") {
269 | return color;
270 | }
271 | if (color != null) {
272 | this.$on.removeClass("" + this.options.baseClass + "-" + color);
273 | }
274 | this.$on.addClass("" + this.options.baseClass + "-" + value);
275 | this.options.onColor = value;
276 | return this.$element;
277 | };
278 |
279 | BootstrapSwitch.prototype.offColor = function(value) {
280 | var color;
281 | color = this.options.offColor;
282 | if (typeof value === "undefined") {
283 | return color;
284 | }
285 | if (color != null) {
286 | this.$off.removeClass("" + this.options.baseClass + "-" + color);
287 | }
288 | this.$off.addClass("" + this.options.baseClass + "-" + value);
289 | this.options.offColor = value;
290 | return this.$element;
291 | };
292 |
293 | BootstrapSwitch.prototype.onText = function(value) {
294 | if (typeof value === "undefined") {
295 | return this.options.onText;
296 | }
297 | this.$on.html(value);
298 | this._width();
299 | this._containerPosition();
300 | this.options.onText = value;
301 | return this.$element;
302 | };
303 |
304 | BootstrapSwitch.prototype.offText = function(value) {
305 | if (typeof value === "undefined") {
306 | return this.options.offText;
307 | }
308 | this.$off.html(value);
309 | this._width();
310 | this._containerPosition();
311 | this.options.offText = value;
312 | return this.$element;
313 | };
314 |
315 | BootstrapSwitch.prototype.labelText = function(value) {
316 | if (typeof value === "undefined") {
317 | return this.options.labelText;
318 | }
319 | this.$label.html(value);
320 | this._width();
321 | this.options.labelText = value;
322 | return this.$element;
323 | };
324 |
325 | BootstrapSwitch.prototype.handleWidth = function(value) {
326 | if (typeof value === "undefined") {
327 | return this.options.handleWidth;
328 | }
329 | this.options.handleWidth = value;
330 | this._width();
331 | this._containerPosition();
332 | return this.$element;
333 | };
334 |
335 | BootstrapSwitch.prototype.labelWidth = function(value) {
336 | if (typeof value === "undefined") {
337 | return this.options.labelWidth;
338 | }
339 | this.options.labelWidth = value;
340 | this._width();
341 | this._containerPosition();
342 | return this.$element;
343 | };
344 |
345 | BootstrapSwitch.prototype.baseClass = function(value) {
346 | return this.options.baseClass;
347 | };
348 |
349 | BootstrapSwitch.prototype.wrapperClass = function(value) {
350 | if (typeof value === "undefined") {
351 | return this.options.wrapperClass;
352 | }
353 | if (!value) {
354 | value = $.fn.bootstrapSwitch.defaults.wrapperClass;
355 | }
356 | this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(" "));
357 | this.$wrapper.addClass(this._getClasses(value).join(" "));
358 | this.options.wrapperClass = value;
359 | return this.$element;
360 | };
361 |
362 | BootstrapSwitch.prototype.radioAllOff = function(value) {
363 | if (typeof value === "undefined") {
364 | return this.options.radioAllOff;
365 | }
366 | value = !!value;
367 | if (value === this.options.radioAllOff) {
368 | return this.$element;
369 | }
370 | this.options.radioAllOff = value;
371 | return this.$element;
372 | };
373 |
374 | BootstrapSwitch.prototype.onInit = function(value) {
375 | if (typeof value === "undefined") {
376 | return this.options.onInit;
377 | }
378 | if (!value) {
379 | value = $.fn.bootstrapSwitch.defaults.onInit;
380 | }
381 | this.options.onInit = value;
382 | return this.$element;
383 | };
384 |
385 | BootstrapSwitch.prototype.onSwitchChange = function(value) {
386 | if (typeof value === "undefined") {
387 | return this.options.onSwitchChange;
388 | }
389 | if (!value) {
390 | value = $.fn.bootstrapSwitch.defaults.onSwitchChange;
391 | }
392 | this.options.onSwitchChange = value;
393 | return this.$element;
394 | };
395 |
396 | BootstrapSwitch.prototype.destroy = function() {
397 | var $form;
398 | $form = this.$element.closest("form");
399 | if ($form.length) {
400 | $form.off("reset.bootstrapSwitch").removeData("bootstrap-switch");
401 | }
402 | this.$container.children().not(this.$element).remove();
403 | this.$element.unwrap().unwrap().off(".bootstrapSwitch").removeData("bootstrap-switch");
404 | return this.$element;
405 | };
406 |
407 | BootstrapSwitch.prototype._width = function() {
408 | var $handles, handleWidth;
409 | $handles = this.$on.add(this.$off);
410 | $handles.add(this.$label).css("width", "");
411 | handleWidth = this.options.handleWidth === "auto" ? Math.max(this.$on.width(), this.$off.width()) : this.options.handleWidth;
412 | $handles.width(handleWidth);
413 | this.$label.width((function(_this) {
414 | return function(index, width) {
415 | if (_this.options.labelWidth !== "auto") {
416 | return _this.options.labelWidth;
417 | }
418 | if (width < handleWidth) {
419 | return handleWidth;
420 | } else {
421 | return width;
422 | }
423 | };
424 | })(this));
425 | this._handleWidth = this.$on.outerWidth();
426 | this._labelWidth = this.$label.outerWidth();
427 | this.$container.width((this._handleWidth * 2) + this._labelWidth);
428 | return this.$wrapper.width(this._handleWidth + this._labelWidth);
429 | };
430 |
431 | BootstrapSwitch.prototype._containerPosition = function(state, callback) {
432 | if (state == null) {
433 | state = this.options.state;
434 | }
435 | this.$container.css("margin-left", (function(_this) {
436 | return function() {
437 | var values;
438 | values = [0, "-" + _this._handleWidth + "px"];
439 | if (_this.options.indeterminate) {
440 | return "-" + (_this._handleWidth / 2) + "px";
441 | }
442 | if (state) {
443 | if (_this.options.inverse) {
444 | return values[1];
445 | } else {
446 | return values[0];
447 | }
448 | } else {
449 | if (_this.options.inverse) {
450 | return values[0];
451 | } else {
452 | return values[1];
453 | }
454 | }
455 | };
456 | })(this));
457 | if (!callback) {
458 | return;
459 | }
460 | return setTimeout(function() {
461 | return callback();
462 | }, 50);
463 | };
464 |
465 | BootstrapSwitch.prototype._init = function() {
466 | var init, initInterval;
467 | init = (function(_this) {
468 | return function() {
469 | _this._width();
470 | return _this._containerPosition(null, function() {
471 | if (_this.options.animate) {
472 | return _this.$wrapper.addClass("" + _this.options.baseClass + "-animate");
473 | }
474 | });
475 | };
476 | })(this);
477 | if (this.$wrapper.is(":visible")) {
478 | return init();
479 | }
480 | return initInterval = window.setInterval((function(_this) {
481 | return function() {
482 | if (_this.$wrapper.is(":visible")) {
483 | init();
484 | return window.clearInterval(initInterval);
485 | }
486 | };
487 | })(this), 50);
488 | };
489 |
490 | BootstrapSwitch.prototype._elementHandlers = function() {
491 | return this.$element.on({
492 | "change.bootstrapSwitch": (function(_this) {
493 | return function(e, skip) {
494 | var state;
495 | e.preventDefault();
496 | e.stopImmediatePropagation();
497 | state = _this.$element.is(":checked");
498 | _this._containerPosition(state);
499 | if (state === _this.options.state) {
500 | return;
501 | }
502 | _this.options.state = state;
503 | _this.$wrapper.toggleClass("" + _this.options.baseClass + "-off").toggleClass("" + _this.options.baseClass + "-on");
504 | if (!skip) {
505 | if (_this.$element.is(":radio")) {
506 | $("[name='" + (_this.$element.attr('name')) + "']").not(_this.$element).prop("checked", false).trigger("change.bootstrapSwitch", true);
507 | }
508 | return _this.$element.trigger("switchChange.bootstrapSwitch", [state]);
509 | }
510 | };
511 | })(this),
512 | "focus.bootstrapSwitch": (function(_this) {
513 | return function(e) {
514 | e.preventDefault();
515 | return _this.$wrapper.addClass("" + _this.options.baseClass + "-focused");
516 | };
517 | })(this),
518 | "blur.bootstrapSwitch": (function(_this) {
519 | return function(e) {
520 | e.preventDefault();
521 | return _this.$wrapper.removeClass("" + _this.options.baseClass + "-focused");
522 | };
523 | })(this),
524 | "keydown.bootstrapSwitch": (function(_this) {
525 | return function(e) {
526 | if (!e.which || _this.options.disabled || _this.options.readonly) {
527 | return;
528 | }
529 | switch (e.which) {
530 | case 37:
531 | e.preventDefault();
532 | e.stopImmediatePropagation();
533 | return _this.state(false);
534 | case 39:
535 | e.preventDefault();
536 | e.stopImmediatePropagation();
537 | return _this.state(true);
538 | }
539 | };
540 | })(this)
541 | });
542 | };
543 |
544 | BootstrapSwitch.prototype._handleHandlers = function() {
545 | this.$on.on("click.bootstrapSwitch", (function(_this) {
546 | return function(event) {
547 | event.preventDefault();
548 | event.stopPropagation();
549 | _this.state(false);
550 | return _this.$element.trigger("focus.bootstrapSwitch");
551 | };
552 | })(this));
553 | return this.$off.on("click.bootstrapSwitch", (function(_this) {
554 | return function(event) {
555 | event.preventDefault();
556 | event.stopPropagation();
557 | _this.state(true);
558 | return _this.$element.trigger("focus.bootstrapSwitch");
559 | };
560 | })(this));
561 | };
562 |
563 | BootstrapSwitch.prototype._labelHandlers = function() {
564 | return this.$label.on({
565 | "mousedown.bootstrapSwitch touchstart.bootstrapSwitch": (function(_this) {
566 | return function(e) {
567 | if (_this._dragStart || _this.options.disabled || _this.options.readonly) {
568 | return;
569 | }
570 | e.preventDefault();
571 | e.stopPropagation();
572 | _this._dragStart = (e.pageX || e.originalEvent.touches[0].pageX) - parseInt(_this.$container.css("margin-left"), 10);
573 | if (_this.options.animate) {
574 | _this.$wrapper.removeClass("" + _this.options.baseClass + "-animate");
575 | }
576 | return _this.$element.trigger("focus.bootstrapSwitch");
577 | };
578 | })(this),
579 | "mousemove.bootstrapSwitch touchmove.bootstrapSwitch": (function(_this) {
580 | return function(e) {
581 | var difference;
582 | if (_this._dragStart == null) {
583 | return;
584 | }
585 | e.preventDefault();
586 | difference = (e.pageX || e.originalEvent.touches[0].pageX) - _this._dragStart;
587 | if (difference < -_this._handleWidth || difference > 0) {
588 | return;
589 | }
590 | _this._dragEnd = difference;
591 | return _this.$container.css("margin-left", "" + _this._dragEnd + "px");
592 | };
593 | })(this),
594 | "mouseup.bootstrapSwitch touchend.bootstrapSwitch": (function(_this) {
595 | return function(e) {
596 | var state;
597 | if (!_this._dragStart) {
598 | return;
599 | }
600 | e.preventDefault();
601 | if (_this.options.animate) {
602 | _this.$wrapper.addClass("" + _this.options.baseClass + "-animate");
603 | }
604 | if (_this._dragEnd) {
605 | state = _this._dragEnd > -(_this._handleWidth / 2);
606 | _this._dragEnd = false;
607 | _this.state(_this.options.inverse ? !state : state);
608 | } else {
609 | _this.state(!_this.options.state);
610 | }
611 | return _this._dragStart = false;
612 | };
613 | })(this),
614 | "mouseleave.bootstrapSwitch": (function(_this) {
615 | return function(e) {
616 | return _this.$label.trigger("mouseup.bootstrapSwitch");
617 | };
618 | })(this)
619 | });
620 | };
621 |
622 | BootstrapSwitch.prototype._externalLabelHandler = function() {
623 | var $externalLabel;
624 | $externalLabel = this.$element.closest("label");
625 | return $externalLabel.on("click", (function(_this) {
626 | return function(event) {
627 | event.preventDefault();
628 | event.stopImmediatePropagation();
629 | if (event.target === $externalLabel[0]) {
630 | return _this.toggleState();
631 | }
632 | };
633 | })(this));
634 | };
635 |
636 | BootstrapSwitch.prototype._formHandler = function() {
637 | var $form;
638 | $form = this.$element.closest("form");
639 | if ($form.data("bootstrap-switch")) {
640 | return;
641 | }
642 | return $form.on("reset.bootstrapSwitch", function() {
643 | return window.setTimeout(function() {
644 | return $form.find("input").filter(function() {
645 | return $(this).data("bootstrap-switch");
646 | }).each(function() {
647 | return $(this).bootstrapSwitch("state", this.checked);
648 | });
649 | }, 1);
650 | }).data("bootstrap-switch", true);
651 | };
652 |
653 | BootstrapSwitch.prototype._getClasses = function(classes) {
654 | var c, cls, _i, _len;
655 | if (!$.isArray(classes)) {
656 | return ["" + this.options.baseClass + "-" + classes];
657 | }
658 | cls = [];
659 | for (_i = 0, _len = classes.length; _i < _len; _i++) {
660 | c = classes[_i];
661 | cls.push("" + this.options.baseClass + "-" + c);
662 | }
663 | return cls;
664 | };
665 |
666 | return BootstrapSwitch;
667 |
668 | })();
669 | $.fn.bootstrapSwitch = function() {
670 | var args, option, ret;
671 | option = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
672 | ret = this;
673 | this.each(function() {
674 | var $this, data;
675 | $this = $(this);
676 | data = $this.data("bootstrap-switch");
677 | if (!data) {
678 | $this.data("bootstrap-switch", data = new BootstrapSwitch(this, option));
679 | }
680 | if (typeof option === "string") {
681 | return ret = data[option].apply(data, args);
682 | }
683 | });
684 | return ret;
685 | };
686 | $.fn.bootstrapSwitch.Constructor = BootstrapSwitch;
687 | return $.fn.bootstrapSwitch.defaults = {
688 | state: true,
689 | size: null,
690 | animate: true,
691 | disabled: false,
692 | readonly: false,
693 | indeterminate: false,
694 | inverse: false,
695 | radioAllOff: false,
696 | onColor: "primary",
697 | offColor: "default",
698 | onText: "ON",
699 | offText: "OFF",
700 | labelText: " ",
701 | handleWidth: "auto",
702 | labelWidth: "auto",
703 | baseClass: "bootstrap-switch",
704 | wrapperClass: "wrapper",
705 | onInit: function() {},
706 | onSwitchChange: function() {}
707 | };
708 | })(window.jQuery, window);
709 |
710 | }).call(this);
711 |
--------------------------------------------------------------------------------
/public/js/bootstrap.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.4 (http://getbootstrap.com)
3 | * Copyright 2011-2015 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 |
7 | /*!
8 | * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=9b560b8cf2bcd3a66885)
9 | * Config saved to config.json and https://gist.github.com/9b560b8cf2bcd3a66885
10 | */
11 | if (typeof jQuery === 'undefined') {
12 | throw new Error('Bootstrap\'s JavaScript requires jQuery')
13 | }
14 | +function ($) {
15 | 'use strict';
16 | var version = $.fn.jquery.split(' ')[0].split('.')
17 | if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) {
18 | throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher')
19 | }
20 | }(jQuery);
21 |
22 | /* ========================================================================
23 | * Bootstrap: alert.js v3.3.4
24 | * http://getbootstrap.com/javascript/#alerts
25 | * ========================================================================
26 | * Copyright 2011-2015 Twitter, Inc.
27 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
28 | * ======================================================================== */
29 |
30 |
31 | +function ($) {
32 | 'use strict';
33 |
34 | // ALERT CLASS DEFINITION
35 | // ======================
36 |
37 | var dismiss = '[data-dismiss="alert"]'
38 | var Alert = function (el) {
39 | $(el).on('click', dismiss, this.close)
40 | }
41 |
42 | Alert.VERSION = '3.3.4'
43 |
44 | Alert.TRANSITION_DURATION = 150
45 |
46 | Alert.prototype.close = function (e) {
47 | var $this = $(this)
48 | var selector = $this.attr('data-target')
49 |
50 | if (!selector) {
51 | selector = $this.attr('href')
52 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
53 | }
54 |
55 | var $parent = $(selector)
56 |
57 | if (e) e.preventDefault()
58 |
59 | if (!$parent.length) {
60 | $parent = $this.closest('.alert')
61 | }
62 |
63 | $parent.trigger(e = $.Event('close.bs.alert'))
64 |
65 | if (e.isDefaultPrevented()) return
66 |
67 | $parent.removeClass('in')
68 |
69 | function removeElement() {
70 | // detach from parent, fire event then clean up data
71 | $parent.detach().trigger('closed.bs.alert').remove()
72 | }
73 |
74 | $.support.transition && $parent.hasClass('fade') ?
75 | $parent
76 | .one('bsTransitionEnd', removeElement)
77 | .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
78 | removeElement()
79 | }
80 |
81 |
82 | // ALERT PLUGIN DEFINITION
83 | // =======================
84 |
85 | function Plugin(option) {
86 | return this.each(function () {
87 | var $this = $(this)
88 | var data = $this.data('bs.alert')
89 |
90 | if (!data) $this.data('bs.alert', (data = new Alert(this)))
91 | if (typeof option == 'string') data[option].call($this)
92 | })
93 | }
94 |
95 | var old = $.fn.alert
96 |
97 | $.fn.alert = Plugin
98 | $.fn.alert.Constructor = Alert
99 |
100 |
101 | // ALERT NO CONFLICT
102 | // =================
103 |
104 | $.fn.alert.noConflict = function () {
105 | $.fn.alert = old
106 | return this
107 | }
108 |
109 |
110 | // ALERT DATA-API
111 | // ==============
112 |
113 | $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
114 |
115 | }(jQuery);
116 |
117 | /* ========================================================================
118 | * Bootstrap: button.js v3.3.4
119 | * http://getbootstrap.com/javascript/#buttons
120 | * ========================================================================
121 | * Copyright 2011-2015 Twitter, Inc.
122 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
123 | * ======================================================================== */
124 |
125 |
126 | +function ($) {
127 | 'use strict';
128 |
129 | // BUTTON PUBLIC CLASS DEFINITION
130 | // ==============================
131 |
132 | var Button = function (element, options) {
133 | this.$element = $(element)
134 | this.options = $.extend({}, Button.DEFAULTS, options)
135 | this.isLoading = false
136 | }
137 |
138 | Button.VERSION = '3.3.4'
139 |
140 | Button.DEFAULTS = {
141 | loadingText: 'loading...'
142 | }
143 |
144 | Button.prototype.setState = function (state) {
145 | var d = 'disabled'
146 | var $el = this.$element
147 | var val = $el.is('input') ? 'val' : 'html'
148 | var data = $el.data()
149 |
150 | state = state + 'Text'
151 |
152 | if (data.resetText == null) $el.data('resetText', $el[val]())
153 |
154 | // push to event loop to allow forms to submit
155 | setTimeout($.proxy(function () {
156 | $el[val](data[state] == null ? this.options[state] : data[state])
157 |
158 | if (state == 'loadingText') {
159 | this.isLoading = true
160 | $el.addClass(d).attr(d, d)
161 | } else if (this.isLoading) {
162 | this.isLoading = false
163 | $el.removeClass(d).removeAttr(d)
164 | }
165 | }, this), 0)
166 | }
167 |
168 | Button.prototype.toggle = function () {
169 | var changed = true
170 | var $parent = this.$element.closest('[data-toggle="buttons"]')
171 |
172 | if ($parent.length) {
173 | var $input = this.$element.find('input')
174 | if ($input.prop('type') == 'radio') {
175 | if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
176 | else $parent.find('.active').removeClass('active')
177 | }
178 | if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
179 | } else {
180 | this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
181 | }
182 |
183 | if (changed) this.$element.toggleClass('active')
184 | }
185 |
186 |
187 | // BUTTON PLUGIN DEFINITION
188 | // ========================
189 |
190 | function Plugin(option) {
191 | return this.each(function () {
192 | var $this = $(this)
193 | var data = $this.data('bs.button')
194 | var options = typeof option == 'object' && option
195 |
196 | if (!data) $this.data('bs.button', (data = new Button(this, options)))
197 |
198 | if (option == 'toggle') data.toggle()
199 | else if (option) data.setState(option)
200 | })
201 | }
202 |
203 | var old = $.fn.button
204 |
205 | $.fn.button = Plugin
206 | $.fn.button.Constructor = Button
207 |
208 |
209 | // BUTTON NO CONFLICT
210 | // ==================
211 |
212 | $.fn.button.noConflict = function () {
213 | $.fn.button = old
214 | return this
215 | }
216 |
217 |
218 | // BUTTON DATA-API
219 | // ===============
220 |
221 | $(document)
222 | .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
223 | var $btn = $(e.target)
224 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
225 | Plugin.call($btn, 'toggle')
226 | e.preventDefault()
227 | })
228 | .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
229 | $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
230 | })
231 |
232 | }(jQuery);
233 |
234 | /* ========================================================================
235 | * Bootstrap: carousel.js v3.3.4
236 | * http://getbootstrap.com/javascript/#carousel
237 | * ========================================================================
238 | * Copyright 2011-2015 Twitter, Inc.
239 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
240 | * ======================================================================== */
241 |
242 |
243 | +function ($) {
244 | 'use strict';
245 |
246 | // CAROUSEL CLASS DEFINITION
247 | // =========================
248 |
249 | var Carousel = function (element, options) {
250 | this.$element = $(element)
251 | this.$indicators = this.$element.find('.carousel-indicators')
252 | this.options = options
253 | this.paused = null
254 | this.sliding = null
255 | this.interval = null
256 | this.$active = null
257 | this.$items = null
258 |
259 | this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
260 |
261 | this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
262 | .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
263 | .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
264 | }
265 |
266 | Carousel.VERSION = '3.3.4'
267 |
268 | Carousel.TRANSITION_DURATION = 600
269 |
270 | Carousel.DEFAULTS = {
271 | interval: 5000,
272 | pause: 'hover',
273 | wrap: true,
274 | keyboard: true
275 | }
276 |
277 | Carousel.prototype.keydown = function (e) {
278 | if (/input|textarea/i.test(e.target.tagName)) return
279 | switch (e.which) {
280 | case 37: this.prev(); break
281 | case 39: this.next(); break
282 | default: return
283 | }
284 |
285 | e.preventDefault()
286 | }
287 |
288 | Carousel.prototype.cycle = function (e) {
289 | e || (this.paused = false)
290 |
291 | this.interval && clearInterval(this.interval)
292 |
293 | this.options.interval
294 | && !this.paused
295 | && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
296 |
297 | return this
298 | }
299 |
300 | Carousel.prototype.getItemIndex = function (item) {
301 | this.$items = item.parent().children('.item')
302 | return this.$items.index(item || this.$active)
303 | }
304 |
305 | Carousel.prototype.getItemForDirection = function (direction, active) {
306 | var activeIndex = this.getItemIndex(active)
307 | var willWrap = (direction == 'prev' && activeIndex === 0)
308 | || (direction == 'next' && activeIndex == (this.$items.length - 1))
309 | if (willWrap && !this.options.wrap) return active
310 | var delta = direction == 'prev' ? -1 : 1
311 | var itemIndex = (activeIndex + delta) % this.$items.length
312 | return this.$items.eq(itemIndex)
313 | }
314 |
315 | Carousel.prototype.to = function (pos) {
316 | var that = this
317 | var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
318 |
319 | if (pos > (this.$items.length - 1) || pos < 0) return
320 |
321 | if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
322 | if (activeIndex == pos) return this.pause().cycle()
323 |
324 | return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
325 | }
326 |
327 | Carousel.prototype.pause = function (e) {
328 | e || (this.paused = true)
329 |
330 | if (this.$element.find('.next, .prev').length && $.support.transition) {
331 | this.$element.trigger($.support.transition.end)
332 | this.cycle(true)
333 | }
334 |
335 | this.interval = clearInterval(this.interval)
336 |
337 | return this
338 | }
339 |
340 | Carousel.prototype.next = function () {
341 | if (this.sliding) return
342 | return this.slide('next')
343 | }
344 |
345 | Carousel.prototype.prev = function () {
346 | if (this.sliding) return
347 | return this.slide('prev')
348 | }
349 |
350 | Carousel.prototype.slide = function (type, next) {
351 | var $active = this.$element.find('.item.active')
352 | var $next = next || this.getItemForDirection(type, $active)
353 | var isCycling = this.interval
354 | var direction = type == 'next' ? 'left' : 'right'
355 | var that = this
356 |
357 | if ($next.hasClass('active')) return (this.sliding = false)
358 |
359 | var relatedTarget = $next[0]
360 | var slideEvent = $.Event('slide.bs.carousel', {
361 | relatedTarget: relatedTarget,
362 | direction: direction
363 | })
364 | this.$element.trigger(slideEvent)
365 | if (slideEvent.isDefaultPrevented()) return
366 |
367 | this.sliding = true
368 |
369 | isCycling && this.pause()
370 |
371 | if (this.$indicators.length) {
372 | this.$indicators.find('.active').removeClass('active')
373 | var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
374 | $nextIndicator && $nextIndicator.addClass('active')
375 | }
376 |
377 | var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
378 | if ($.support.transition && this.$element.hasClass('slide')) {
379 | $next.addClass(type)
380 | $next[0].offsetWidth // force reflow
381 | $active.addClass(direction)
382 | $next.addClass(direction)
383 | $active
384 | .one('bsTransitionEnd', function () {
385 | $next.removeClass([type, direction].join(' ')).addClass('active')
386 | $active.removeClass(['active', direction].join(' '))
387 | that.sliding = false
388 | setTimeout(function () {
389 | that.$element.trigger(slidEvent)
390 | }, 0)
391 | })
392 | .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
393 | } else {
394 | $active.removeClass('active')
395 | $next.addClass('active')
396 | this.sliding = false
397 | this.$element.trigger(slidEvent)
398 | }
399 |
400 | isCycling && this.cycle()
401 |
402 | return this
403 | }
404 |
405 |
406 | // CAROUSEL PLUGIN DEFINITION
407 | // ==========================
408 |
409 | function Plugin(option) {
410 | return this.each(function () {
411 | var $this = $(this)
412 | var data = $this.data('bs.carousel')
413 | var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
414 | var action = typeof option == 'string' ? option : options.slide
415 |
416 | if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
417 | if (typeof option == 'number') data.to(option)
418 | else if (action) data[action]()
419 | else if (options.interval) data.pause().cycle()
420 | })
421 | }
422 |
423 | var old = $.fn.carousel
424 |
425 | $.fn.carousel = Plugin
426 | $.fn.carousel.Constructor = Carousel
427 |
428 |
429 | // CAROUSEL NO CONFLICT
430 | // ====================
431 |
432 | $.fn.carousel.noConflict = function () {
433 | $.fn.carousel = old
434 | return this
435 | }
436 |
437 |
438 | // CAROUSEL DATA-API
439 | // =================
440 |
441 | var clickHandler = function (e) {
442 | var href
443 | var $this = $(this)
444 | var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
445 | if (!$target.hasClass('carousel')) return
446 | var options = $.extend({}, $target.data(), $this.data())
447 | var slideIndex = $this.attr('data-slide-to')
448 | if (slideIndex) options.interval = false
449 |
450 | Plugin.call($target, options)
451 |
452 | if (slideIndex) {
453 | $target.data('bs.carousel').to(slideIndex)
454 | }
455 |
456 | e.preventDefault()
457 | }
458 |
459 | $(document)
460 | .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
461 | .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
462 |
463 | $(window).on('load', function () {
464 | $('[data-ride="carousel"]').each(function () {
465 | var $carousel = $(this)
466 | Plugin.call($carousel, $carousel.data())
467 | })
468 | })
469 |
470 | }(jQuery);
471 |
472 | /* ========================================================================
473 | * Bootstrap: dropdown.js v3.3.4
474 | * http://getbootstrap.com/javascript/#dropdowns
475 | * ========================================================================
476 | * Copyright 2011-2015 Twitter, Inc.
477 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
478 | * ======================================================================== */
479 |
480 |
481 | +function ($) {
482 | 'use strict';
483 |
484 | // DROPDOWN CLASS DEFINITION
485 | // =========================
486 |
487 | var backdrop = '.dropdown-backdrop'
488 | var toggle = '[data-toggle="dropdown"]'
489 | var Dropdown = function (element) {
490 | $(element).on('click.bs.dropdown', this.toggle)
491 | }
492 |
493 | Dropdown.VERSION = '3.3.4'
494 |
495 | Dropdown.prototype.toggle = function (e) {
496 | var $this = $(this)
497 |
498 | if ($this.is('.disabled, :disabled')) return
499 |
500 | var $parent = getParent($this)
501 | var isActive = $parent.hasClass('open')
502 |
503 | clearMenus()
504 |
505 | if (!isActive) {
506 | if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
507 | // if mobile we use a backdrop because click events don't delegate
508 | $('
').insertAfter($(this)).on('click', clearMenus)
509 | }
510 |
511 | var relatedTarget = { relatedTarget: this }
512 | $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
513 |
514 | if (e.isDefaultPrevented()) return
515 |
516 | $this
517 | .trigger('focus')
518 | .attr('aria-expanded', 'true')
519 |
520 | $parent
521 | .toggleClass('open')
522 | .trigger('shown.bs.dropdown', relatedTarget)
523 | }
524 |
525 | return false
526 | }
527 |
528 | Dropdown.prototype.keydown = function (e) {
529 | if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
530 |
531 | var $this = $(this)
532 |
533 | e.preventDefault()
534 | e.stopPropagation()
535 |
536 | if ($this.is('.disabled, :disabled')) return
537 |
538 | var $parent = getParent($this)
539 | var isActive = $parent.hasClass('open')
540 |
541 | if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
542 | if (e.which == 27) $parent.find(toggle).trigger('focus')
543 | return $this.trigger('click')
544 | }
545 |
546 | var desc = ' li:not(.disabled):visible a'
547 | var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
548 |
549 | if (!$items.length) return
550 |
551 | var index = $items.index(e.target)
552 |
553 | if (e.which == 38 && index > 0) index-- // up
554 | if (e.which == 40 && index < $items.length - 1) index++ // down
555 | if (!~index) index = 0
556 |
557 | $items.eq(index).trigger('focus')
558 | }
559 |
560 | function clearMenus(e) {
561 | if (e && e.which === 3) return
562 | $(backdrop).remove()
563 | $(toggle).each(function () {
564 | var $this = $(this)
565 | var $parent = getParent($this)
566 | var relatedTarget = { relatedTarget: this }
567 |
568 | if (!$parent.hasClass('open')) return
569 |
570 | $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
571 |
572 | if (e.isDefaultPrevented()) return
573 |
574 | $this.attr('aria-expanded', 'false')
575 | $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
576 | })
577 | }
578 |
579 | function getParent($this) {
580 | var selector = $this.attr('data-target')
581 |
582 | if (!selector) {
583 | selector = $this.attr('href')
584 | selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
585 | }
586 |
587 | var $parent = selector && $(selector)
588 |
589 | return $parent && $parent.length ? $parent : $this.parent()
590 | }
591 |
592 |
593 | // DROPDOWN PLUGIN DEFINITION
594 | // ==========================
595 |
596 | function Plugin(option) {
597 | return this.each(function () {
598 | var $this = $(this)
599 | var data = $this.data('bs.dropdown')
600 |
601 | if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
602 | if (typeof option == 'string') data[option].call($this)
603 | })
604 | }
605 |
606 | var old = $.fn.dropdown
607 |
608 | $.fn.dropdown = Plugin
609 | $.fn.dropdown.Constructor = Dropdown
610 |
611 |
612 | // DROPDOWN NO CONFLICT
613 | // ====================
614 |
615 | $.fn.dropdown.noConflict = function () {
616 | $.fn.dropdown = old
617 | return this
618 | }
619 |
620 |
621 | // APPLY TO STANDARD DROPDOWN ELEMENTS
622 | // ===================================
623 |
624 | $(document)
625 | .on('click.bs.dropdown.data-api', clearMenus)
626 | .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
627 | .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
628 | .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
629 | .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
630 | .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
631 |
632 | }(jQuery);
633 |
634 | /* ========================================================================
635 | * Bootstrap: modal.js v3.3.4
636 | * http://getbootstrap.com/javascript/#modals
637 | * ========================================================================
638 | * Copyright 2011-2015 Twitter, Inc.
639 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
640 | * ======================================================================== */
641 |
642 |
643 | +function ($) {
644 | 'use strict';
645 |
646 | // MODAL CLASS DEFINITION
647 | // ======================
648 |
649 | var Modal = function (element, options) {
650 | this.options = options
651 | this.$body = $(document.body)
652 | this.$element = $(element)
653 | this.$dialog = this.$element.find('.modal-dialog')
654 | this.$backdrop = null
655 | this.isShown = null
656 | this.originalBodyPad = null
657 | this.scrollbarWidth = 0
658 | this.ignoreBackdropClick = false
659 |
660 | if (this.options.remote) {
661 | this.$element
662 | .find('.modal-content')
663 | .load(this.options.remote, $.proxy(function () {
664 | this.$element.trigger('loaded.bs.modal')
665 | }, this))
666 | }
667 | }
668 |
669 | Modal.VERSION = '3.3.4'
670 |
671 | Modal.TRANSITION_DURATION = 300
672 | Modal.BACKDROP_TRANSITION_DURATION = 150
673 |
674 | Modal.DEFAULTS = {
675 | backdrop: true,
676 | keyboard: true,
677 | show: true
678 | }
679 |
680 | Modal.prototype.toggle = function (_relatedTarget) {
681 | return this.isShown ? this.hide() : this.show(_relatedTarget)
682 | }
683 |
684 | Modal.prototype.show = function (_relatedTarget) {
685 | var that = this
686 | var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
687 |
688 | this.$element.trigger(e)
689 |
690 | if (this.isShown || e.isDefaultPrevented()) return
691 |
692 | this.isShown = true
693 |
694 | this.checkScrollbar()
695 | this.setScrollbar()
696 | this.$body.addClass('modal-open')
697 |
698 | this.escape()
699 | this.resize()
700 |
701 | this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
702 |
703 | this.$dialog.on('mousedown.dismiss.bs.modal', function () {
704 | that.$element.one('mouseup.dismiss.bs.modal', function (e) {
705 | if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
706 | })
707 | })
708 |
709 | this.backdrop(function () {
710 | var transition = $.support.transition && that.$element.hasClass('fade')
711 |
712 | if (!that.$element.parent().length) {
713 | that.$element.appendTo(that.$body) // don't move modals dom position
714 | }
715 |
716 | that.$element
717 | .show()
718 | .scrollTop(0)
719 |
720 | that.adjustDialog()
721 |
722 | if (transition) {
723 | that.$element[0].offsetWidth // force reflow
724 | }
725 |
726 | that.$element
727 | .addClass('in')
728 | .attr('aria-hidden', false)
729 |
730 | that.enforceFocus()
731 |
732 | var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
733 |
734 | transition ?
735 | that.$dialog // wait for modal to slide in
736 | .one('bsTransitionEnd', function () {
737 | that.$element.trigger('focus').trigger(e)
738 | })
739 | .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
740 | that.$element.trigger('focus').trigger(e)
741 | })
742 | }
743 |
744 | Modal.prototype.hide = function (e) {
745 | if (e) e.preventDefault()
746 |
747 | e = $.Event('hide.bs.modal')
748 |
749 | this.$element.trigger(e)
750 |
751 | if (!this.isShown || e.isDefaultPrevented()) return
752 |
753 | this.isShown = false
754 |
755 | this.escape()
756 | this.resize()
757 |
758 | $(document).off('focusin.bs.modal')
759 |
760 | this.$element
761 | .removeClass('in')
762 | .attr('aria-hidden', true)
763 | .off('click.dismiss.bs.modal')
764 | .off('mouseup.dismiss.bs.modal')
765 |
766 | this.$dialog.off('mousedown.dismiss.bs.modal')
767 |
768 | $.support.transition && this.$element.hasClass('fade') ?
769 | this.$element
770 | .one('bsTransitionEnd', $.proxy(this.hideModal, this))
771 | .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
772 | this.hideModal()
773 | }
774 |
775 | Modal.prototype.enforceFocus = function () {
776 | $(document)
777 | .off('focusin.bs.modal') // guard against infinite focus loop
778 | .on('focusin.bs.modal', $.proxy(function (e) {
779 | if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
780 | this.$element.trigger('focus')
781 | }
782 | }, this))
783 | }
784 |
785 | Modal.prototype.escape = function () {
786 | if (this.isShown && this.options.keyboard) {
787 | this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
788 | e.which == 27 && this.hide()
789 | }, this))
790 | } else if (!this.isShown) {
791 | this.$element.off('keydown.dismiss.bs.modal')
792 | }
793 | }
794 |
795 | Modal.prototype.resize = function () {
796 | if (this.isShown) {
797 | $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
798 | } else {
799 | $(window).off('resize.bs.modal')
800 | }
801 | }
802 |
803 | Modal.prototype.hideModal = function () {
804 | var that = this
805 | this.$element.hide()
806 | this.backdrop(function () {
807 | that.$body.removeClass('modal-open')
808 | that.resetAdjustments()
809 | that.resetScrollbar()
810 | that.$element.trigger('hidden.bs.modal')
811 | })
812 | }
813 |
814 | Modal.prototype.removeBackdrop = function () {
815 | this.$backdrop && this.$backdrop.remove()
816 | this.$backdrop = null
817 | }
818 |
819 | Modal.prototype.backdrop = function (callback) {
820 | var that = this
821 | var animate = this.$element.hasClass('fade') ? 'fade' : ''
822 |
823 | if (this.isShown && this.options.backdrop) {
824 | var doAnimate = $.support.transition && animate
825 |
826 | this.$backdrop = $('
')
827 | .appendTo(this.$body)
828 |
829 | this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
830 | if (this.ignoreBackdropClick) {
831 | this.ignoreBackdropClick = false
832 | return
833 | }
834 | if (e.target !== e.currentTarget) return
835 | this.options.backdrop == 'static'
836 | ? this.$element[0].focus()
837 | : this.hide()
838 | }, this))
839 |
840 | if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
841 |
842 | this.$backdrop.addClass('in')
843 |
844 | if (!callback) return
845 |
846 | doAnimate ?
847 | this.$backdrop
848 | .one('bsTransitionEnd', callback)
849 | .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
850 | callback()
851 |
852 | } else if (!this.isShown && this.$backdrop) {
853 | this.$backdrop.removeClass('in')
854 |
855 | var callbackRemove = function () {
856 | that.removeBackdrop()
857 | callback && callback()
858 | }
859 | $.support.transition && this.$element.hasClass('fade') ?
860 | this.$backdrop
861 | .one('bsTransitionEnd', callbackRemove)
862 | .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
863 | callbackRemove()
864 |
865 | } else if (callback) {
866 | callback()
867 | }
868 | }
869 |
870 | // these following methods are used to handle overflowing modals
871 |
872 | Modal.prototype.handleUpdate = function () {
873 | this.adjustDialog()
874 | }
875 |
876 | Modal.prototype.adjustDialog = function () {
877 | var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
878 |
879 | this.$element.css({
880 | paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
881 | paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
882 | })
883 | }
884 |
885 | Modal.prototype.resetAdjustments = function () {
886 | this.$element.css({
887 | paddingLeft: '',
888 | paddingRight: ''
889 | })
890 | }
891 |
892 | Modal.prototype.checkScrollbar = function () {
893 | var fullWindowWidth = window.innerWidth
894 | if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
895 | var documentElementRect = document.documentElement.getBoundingClientRect()
896 | fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
897 | }
898 | this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
899 | this.scrollbarWidth = this.measureScrollbar()
900 | }
901 |
902 | Modal.prototype.setScrollbar = function () {
903 | var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
904 | this.originalBodyPad = document.body.style.paddingRight || ''
905 | if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
906 | }
907 |
908 | Modal.prototype.resetScrollbar = function () {
909 | this.$body.css('padding-right', this.originalBodyPad)
910 | }
911 |
912 | Modal.prototype.measureScrollbar = function () { // thx walsh
913 | var scrollDiv = document.createElement('div')
914 | scrollDiv.className = 'modal-scrollbar-measure'
915 | this.$body.append(scrollDiv)
916 | var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
917 | this.$body[0].removeChild(scrollDiv)
918 | return scrollbarWidth
919 | }
920 |
921 |
922 | // MODAL PLUGIN DEFINITION
923 | // =======================
924 |
925 | function Plugin(option, _relatedTarget) {
926 | return this.each(function () {
927 | var $this = $(this)
928 | var data = $this.data('bs.modal')
929 | var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
930 |
931 | if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
932 | if (typeof option == 'string') data[option](_relatedTarget)
933 | else if (options.show) data.show(_relatedTarget)
934 | })
935 | }
936 |
937 | var old = $.fn.modal
938 |
939 | $.fn.modal = Plugin
940 | $.fn.modal.Constructor = Modal
941 |
942 |
943 | // MODAL NO CONFLICT
944 | // =================
945 |
946 | $.fn.modal.noConflict = function () {
947 | $.fn.modal = old
948 | return this
949 | }
950 |
951 |
952 | // MODAL DATA-API
953 | // ==============
954 |
955 | $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
956 | var $this = $(this)
957 | var href = $this.attr('href')
958 | var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
959 | var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
960 |
961 | if ($this.is('a')) e.preventDefault()
962 |
963 | $target.one('show.bs.modal', function (showEvent) {
964 | if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
965 | $target.one('hidden.bs.modal', function () {
966 | $this.is(':visible') && $this.trigger('focus')
967 | })
968 | })
969 | Plugin.call($target, option, this)
970 | })
971 |
972 | }(jQuery);
973 |
974 | /* ========================================================================
975 | * Bootstrap: tooltip.js v3.3.4
976 | * http://getbootstrap.com/javascript/#tooltip
977 | * Inspired by the original jQuery.tipsy by Jason Frame
978 | * ========================================================================
979 | * Copyright 2011-2015 Twitter, Inc.
980 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
981 | * ======================================================================== */
982 |
983 |
984 | +function ($) {
985 | 'use strict';
986 |
987 | // TOOLTIP PUBLIC CLASS DEFINITION
988 | // ===============================
989 |
990 | var Tooltip = function (element, options) {
991 | this.type = null
992 | this.options = null
993 | this.enabled = null
994 | this.timeout = null
995 | this.hoverState = null
996 | this.$element = null
997 |
998 | this.init('tooltip', element, options)
999 | }
1000 |
1001 | Tooltip.VERSION = '3.3.4'
1002 |
1003 | Tooltip.TRANSITION_DURATION = 150
1004 |
1005 | Tooltip.DEFAULTS = {
1006 | animation: true,
1007 | placement: 'top',
1008 | selector: false,
1009 | template: '',
1010 | trigger: 'hover focus',
1011 | title: '',
1012 | delay: 0,
1013 | html: false,
1014 | container: false,
1015 | viewport: {
1016 | selector: 'body',
1017 | padding: 0
1018 | }
1019 | }
1020 |
1021 | Tooltip.prototype.init = function (type, element, options) {
1022 | this.enabled = true
1023 | this.type = type
1024 | this.$element = $(element)
1025 | this.options = this.getOptions(options)
1026 | this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
1027 |
1028 | if (this.$element[0] instanceof document.constructor && !this.options.selector) {
1029 | throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
1030 | }
1031 |
1032 | var triggers = this.options.trigger.split(' ')
1033 |
1034 | for (var i = triggers.length; i--;) {
1035 | var trigger = triggers[i]
1036 |
1037 | if (trigger == 'click') {
1038 | this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
1039 | } else if (trigger != 'manual') {
1040 | var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
1041 | var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
1042 |
1043 | this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
1044 | this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
1045 | }
1046 | }
1047 |
1048 | this.options.selector ?
1049 | (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
1050 | this.fixTitle()
1051 | }
1052 |
1053 | Tooltip.prototype.getDefaults = function () {
1054 | return Tooltip.DEFAULTS
1055 | }
1056 |
1057 | Tooltip.prototype.getOptions = function (options) {
1058 | options = $.extend({}, this.getDefaults(), this.$element.data(), options)
1059 |
1060 | if (options.delay && typeof options.delay == 'number') {
1061 | options.delay = {
1062 | show: options.delay,
1063 | hide: options.delay
1064 | }
1065 | }
1066 |
1067 | return options
1068 | }
1069 |
1070 | Tooltip.prototype.getDelegateOptions = function () {
1071 | var options = {}
1072 | var defaults = this.getDefaults()
1073 |
1074 | this._options && $.each(this._options, function (key, value) {
1075 | if (defaults[key] != value) options[key] = value
1076 | })
1077 |
1078 | return options
1079 | }
1080 |
1081 | Tooltip.prototype.enter = function (obj) {
1082 | var self = obj instanceof this.constructor ?
1083 | obj : $(obj.currentTarget).data('bs.' + this.type)
1084 |
1085 | if (self && self.$tip && self.$tip.is(':visible')) {
1086 | self.hoverState = 'in'
1087 | return
1088 | }
1089 |
1090 | if (!self) {
1091 | self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
1092 | $(obj.currentTarget).data('bs.' + this.type, self)
1093 | }
1094 |
1095 | clearTimeout(self.timeout)
1096 |
1097 | self.hoverState = 'in'
1098 |
1099 | if (!self.options.delay || !self.options.delay.show) return self.show()
1100 |
1101 | self.timeout = setTimeout(function () {
1102 | if (self.hoverState == 'in') self.show()
1103 | }, self.options.delay.show)
1104 | }
1105 |
1106 | Tooltip.prototype.leave = function (obj) {
1107 | var self = obj instanceof this.constructor ?
1108 | obj : $(obj.currentTarget).data('bs.' + this.type)
1109 |
1110 | if (!self) {
1111 | self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
1112 | $(obj.currentTarget).data('bs.' + this.type, self)
1113 | }
1114 |
1115 | clearTimeout(self.timeout)
1116 |
1117 | self.hoverState = 'out'
1118 |
1119 | if (!self.options.delay || !self.options.delay.hide) return self.hide()
1120 |
1121 | self.timeout = setTimeout(function () {
1122 | if (self.hoverState == 'out') self.hide()
1123 | }, self.options.delay.hide)
1124 | }
1125 |
1126 | Tooltip.prototype.show = function () {
1127 | var e = $.Event('show.bs.' + this.type)
1128 |
1129 | if (this.hasContent() && this.enabled) {
1130 | this.$element.trigger(e)
1131 |
1132 | var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
1133 | if (e.isDefaultPrevented() || !inDom) return
1134 | var that = this
1135 |
1136 | var $tip = this.tip()
1137 |
1138 | var tipId = this.getUID(this.type)
1139 |
1140 | this.setContent()
1141 | $tip.attr('id', tipId)
1142 | this.$element.attr('aria-describedby', tipId)
1143 |
1144 | if (this.options.animation) $tip.addClass('fade')
1145 |
1146 | var placement = typeof this.options.placement == 'function' ?
1147 | this.options.placement.call(this, $tip[0], this.$element[0]) :
1148 | this.options.placement
1149 |
1150 | var autoToken = /\s?auto?\s?/i
1151 | var autoPlace = autoToken.test(placement)
1152 | if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
1153 |
1154 | $tip
1155 | .detach()
1156 | .css({ top: 0, left: 0, display: 'block' })
1157 | .addClass(placement)
1158 | .data('bs.' + this.type, this)
1159 |
1160 | this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
1161 |
1162 | var pos = this.getPosition()
1163 | var actualWidth = $tip[0].offsetWidth
1164 | var actualHeight = $tip[0].offsetHeight
1165 |
1166 | if (autoPlace) {
1167 | var orgPlacement = placement
1168 | var $container = this.options.container ? $(this.options.container) : this.$element.parent()
1169 | var containerDim = this.getPosition($container)
1170 |
1171 | placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
1172 | placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
1173 | placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
1174 | placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
1175 | placement
1176 |
1177 | $tip
1178 | .removeClass(orgPlacement)
1179 | .addClass(placement)
1180 | }
1181 |
1182 | var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
1183 |
1184 | this.applyPlacement(calculatedOffset, placement)
1185 |
1186 | var complete = function () {
1187 | var prevHoverState = that.hoverState
1188 | that.$element.trigger('shown.bs.' + that.type)
1189 | that.hoverState = null
1190 |
1191 | if (prevHoverState == 'out') that.leave(that)
1192 | }
1193 |
1194 | $.support.transition && this.$tip.hasClass('fade') ?
1195 | $tip
1196 | .one('bsTransitionEnd', complete)
1197 | .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
1198 | complete()
1199 | }
1200 | }
1201 |
1202 | Tooltip.prototype.applyPlacement = function (offset, placement) {
1203 | var $tip = this.tip()
1204 | var width = $tip[0].offsetWidth
1205 | var height = $tip[0].offsetHeight
1206 |
1207 | // manually read margins because getBoundingClientRect includes difference
1208 | var marginTop = parseInt($tip.css('margin-top'), 10)
1209 | var marginLeft = parseInt($tip.css('margin-left'), 10)
1210 |
1211 | // we must check for NaN for ie 8/9
1212 | if (isNaN(marginTop)) marginTop = 0
1213 | if (isNaN(marginLeft)) marginLeft = 0
1214 |
1215 | offset.top = offset.top + marginTop
1216 | offset.left = offset.left + marginLeft
1217 |
1218 | // $.fn.offset doesn't round pixel values
1219 | // so we use setOffset directly with our own function B-0
1220 | $.offset.setOffset($tip[0], $.extend({
1221 | using: function (props) {
1222 | $tip.css({
1223 | top: Math.round(props.top),
1224 | left: Math.round(props.left)
1225 | })
1226 | }
1227 | }, offset), 0)
1228 |
1229 | $tip.addClass('in')
1230 |
1231 | // check to see if placing tip in new offset caused the tip to resize itself
1232 | var actualWidth = $tip[0].offsetWidth
1233 | var actualHeight = $tip[0].offsetHeight
1234 |
1235 | if (placement == 'top' && actualHeight != height) {
1236 | offset.top = offset.top + height - actualHeight
1237 | }
1238 |
1239 | var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
1240 |
1241 | if (delta.left) offset.left += delta.left
1242 | else offset.top += delta.top
1243 |
1244 | var isVertical = /top|bottom/.test(placement)
1245 | var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
1246 | var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
1247 |
1248 | $tip.offset(offset)
1249 | this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
1250 | }
1251 |
1252 | Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
1253 | this.arrow()
1254 | .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
1255 | .css(isVertical ? 'top' : 'left', '')
1256 | }
1257 |
1258 | Tooltip.prototype.setContent = function () {
1259 | var $tip = this.tip()
1260 | var title = this.getTitle()
1261 |
1262 | $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
1263 | $tip.removeClass('fade in top bottom left right')
1264 | }
1265 |
1266 | Tooltip.prototype.hide = function (callback) {
1267 | var that = this
1268 | var $tip = $(this.$tip)
1269 | var e = $.Event('hide.bs.' + this.type)
1270 |
1271 | function complete() {
1272 | if (that.hoverState != 'in') $tip.detach()
1273 | that.$element
1274 | .removeAttr('aria-describedby')
1275 | .trigger('hidden.bs.' + that.type)
1276 | callback && callback()
1277 | }
1278 |
1279 | this.$element.trigger(e)
1280 |
1281 | if (e.isDefaultPrevented()) return
1282 |
1283 | $tip.removeClass('in')
1284 |
1285 | $.support.transition && $tip.hasClass('fade') ?
1286 | $tip
1287 | .one('bsTransitionEnd', complete)
1288 | .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
1289 | complete()
1290 |
1291 | this.hoverState = null
1292 |
1293 | return this
1294 | }
1295 |
1296 | Tooltip.prototype.fixTitle = function () {
1297 | var $e = this.$element
1298 | if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
1299 | $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
1300 | }
1301 | }
1302 |
1303 | Tooltip.prototype.hasContent = function () {
1304 | return this.getTitle()
1305 | }
1306 |
1307 | Tooltip.prototype.getPosition = function ($element) {
1308 | $element = $element || this.$element
1309 |
1310 | var el = $element[0]
1311 | var isBody = el.tagName == 'BODY'
1312 |
1313 | var elRect = el.getBoundingClientRect()
1314 | if (elRect.width == null) {
1315 | // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
1316 | elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
1317 | }
1318 | var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
1319 | var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
1320 | var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
1321 |
1322 | return $.extend({}, elRect, scroll, outerDims, elOffset)
1323 | }
1324 |
1325 | Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
1326 | return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
1327 | placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
1328 | placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
1329 | /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
1330 |
1331 | }
1332 |
1333 | Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
1334 | var delta = { top: 0, left: 0 }
1335 | if (!this.$viewport) return delta
1336 |
1337 | var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
1338 | var viewportDimensions = this.getPosition(this.$viewport)
1339 |
1340 | if (/right|left/.test(placement)) {
1341 | var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
1342 | var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
1343 | if (topEdgeOffset < viewportDimensions.top) { // top overflow
1344 | delta.top = viewportDimensions.top - topEdgeOffset
1345 | } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
1346 | delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
1347 | }
1348 | } else {
1349 | var leftEdgeOffset = pos.left - viewportPadding
1350 | var rightEdgeOffset = pos.left + viewportPadding + actualWidth
1351 | if (leftEdgeOffset < viewportDimensions.left) { // left overflow
1352 | delta.left = viewportDimensions.left - leftEdgeOffset
1353 | } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
1354 | delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
1355 | }
1356 | }
1357 |
1358 | return delta
1359 | }
1360 |
1361 | Tooltip.prototype.getTitle = function () {
1362 | var title
1363 | var $e = this.$element
1364 | var o = this.options
1365 |
1366 | title = $e.attr('data-original-title')
1367 | || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
1368 |
1369 | return title
1370 | }
1371 |
1372 | Tooltip.prototype.getUID = function (prefix) {
1373 | do prefix += ~~(Math.random() * 1000000)
1374 | while (document.getElementById(prefix))
1375 | return prefix
1376 | }
1377 |
1378 | Tooltip.prototype.tip = function () {
1379 | return (this.$tip = this.$tip || $(this.options.template))
1380 | }
1381 |
1382 | Tooltip.prototype.arrow = function () {
1383 | return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
1384 | }
1385 |
1386 | Tooltip.prototype.enable = function () {
1387 | this.enabled = true
1388 | }
1389 |
1390 | Tooltip.prototype.disable = function () {
1391 | this.enabled = false
1392 | }
1393 |
1394 | Tooltip.prototype.toggleEnabled = function () {
1395 | this.enabled = !this.enabled
1396 | }
1397 |
1398 | Tooltip.prototype.toggle = function (e) {
1399 | var self = this
1400 | if (e) {
1401 | self = $(e.currentTarget).data('bs.' + this.type)
1402 | if (!self) {
1403 | self = new this.constructor(e.currentTarget, this.getDelegateOptions())
1404 | $(e.currentTarget).data('bs.' + this.type, self)
1405 | }
1406 | }
1407 |
1408 | self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
1409 | }
1410 |
1411 | Tooltip.prototype.destroy = function () {
1412 | var that = this
1413 | clearTimeout(this.timeout)
1414 | this.hide(function () {
1415 | that.$element.off('.' + that.type).removeData('bs.' + that.type)
1416 | })
1417 | }
1418 |
1419 |
1420 | // TOOLTIP PLUGIN DEFINITION
1421 | // =========================
1422 |
1423 | function Plugin(option) {
1424 | return this.each(function () {
1425 | var $this = $(this)
1426 | var data = $this.data('bs.tooltip')
1427 | var options = typeof option == 'object' && option
1428 |
1429 | if (!data && /destroy|hide/.test(option)) return
1430 | if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
1431 | if (typeof option == 'string') data[option]()
1432 | })
1433 | }
1434 |
1435 | var old = $.fn.tooltip
1436 |
1437 | $.fn.tooltip = Plugin
1438 | $.fn.tooltip.Constructor = Tooltip
1439 |
1440 |
1441 | // TOOLTIP NO CONFLICT
1442 | // ===================
1443 |
1444 | $.fn.tooltip.noConflict = function () {
1445 | $.fn.tooltip = old
1446 | return this
1447 | }
1448 |
1449 | }(jQuery);
1450 |
1451 | /* ========================================================================
1452 | * Bootstrap: popover.js v3.3.4
1453 | * http://getbootstrap.com/javascript/#popovers
1454 | * ========================================================================
1455 | * Copyright 2011-2015 Twitter, Inc.
1456 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1457 | * ======================================================================== */
1458 |
1459 |
1460 | +function ($) {
1461 | 'use strict';
1462 |
1463 | // POPOVER PUBLIC CLASS DEFINITION
1464 | // ===============================
1465 |
1466 | var Popover = function (element, options) {
1467 | this.init('popover', element, options)
1468 | }
1469 |
1470 | if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
1471 |
1472 | Popover.VERSION = '3.3.4'
1473 |
1474 | Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
1475 | placement: 'right',
1476 | trigger: 'click',
1477 | content: '',
1478 | template: ''
1479 | })
1480 |
1481 |
1482 | // NOTE: POPOVER EXTENDS tooltip.js
1483 | // ================================
1484 |
1485 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
1486 |
1487 | Popover.prototype.constructor = Popover
1488 |
1489 | Popover.prototype.getDefaults = function () {
1490 | return Popover.DEFAULTS
1491 | }
1492 |
1493 | Popover.prototype.setContent = function () {
1494 | var $tip = this.tip()
1495 | var title = this.getTitle()
1496 | var content = this.getContent()
1497 |
1498 | $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
1499 | $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
1500 | this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
1501 | ](content)
1502 |
1503 | $tip.removeClass('fade top bottom left right in')
1504 |
1505 | // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
1506 | // this manually by checking the contents.
1507 | if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
1508 | }
1509 |
1510 | Popover.prototype.hasContent = function () {
1511 | return this.getTitle() || this.getContent()
1512 | }
1513 |
1514 | Popover.prototype.getContent = function () {
1515 | var $e = this.$element
1516 | var o = this.options
1517 |
1518 | return $e.attr('data-content')
1519 | || (typeof o.content == 'function' ?
1520 | o.content.call($e[0]) :
1521 | o.content)
1522 | }
1523 |
1524 | Popover.prototype.arrow = function () {
1525 | return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
1526 | }
1527 |
1528 |
1529 | // POPOVER PLUGIN DEFINITION
1530 | // =========================
1531 |
1532 | function Plugin(option) {
1533 | return this.each(function () {
1534 | var $this = $(this)
1535 | var data = $this.data('bs.popover')
1536 | var options = typeof option == 'object' && option
1537 |
1538 | if (!data && /destroy|hide/.test(option)) return
1539 | if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
1540 | if (typeof option == 'string') data[option]()
1541 | })
1542 | }
1543 |
1544 | var old = $.fn.popover
1545 |
1546 | $.fn.popover = Plugin
1547 | $.fn.popover.Constructor = Popover
1548 |
1549 |
1550 | // POPOVER NO CONFLICT
1551 | // ===================
1552 |
1553 | $.fn.popover.noConflict = function () {
1554 | $.fn.popover = old
1555 | return this
1556 | }
1557 |
1558 | }(jQuery);
1559 |
1560 | /* ========================================================================
1561 | * Bootstrap: tab.js v3.3.4
1562 | * http://getbootstrap.com/javascript/#tabs
1563 | * ========================================================================
1564 | * Copyright 2011-2015 Twitter, Inc.
1565 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1566 | * ======================================================================== */
1567 |
1568 |
1569 | +function ($) {
1570 | 'use strict';
1571 |
1572 | // TAB CLASS DEFINITION
1573 | // ====================
1574 |
1575 | var Tab = function (element) {
1576 | this.element = $(element)
1577 | }
1578 |
1579 | Tab.VERSION = '3.3.4'
1580 |
1581 | Tab.TRANSITION_DURATION = 150
1582 |
1583 | Tab.prototype.show = function () {
1584 | var $this = this.element
1585 | var $ul = $this.closest('ul:not(.dropdown-menu)')
1586 | var selector = $this.data('target')
1587 |
1588 | if (!selector) {
1589 | selector = $this.attr('href')
1590 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
1591 | }
1592 |
1593 | if ($this.parent('li').hasClass('active')) return
1594 |
1595 | var $previous = $ul.find('.active:last a')
1596 | var hideEvent = $.Event('hide.bs.tab', {
1597 | relatedTarget: $this[0]
1598 | })
1599 | var showEvent = $.Event('show.bs.tab', {
1600 | relatedTarget: $previous[0]
1601 | })
1602 |
1603 | $previous.trigger(hideEvent)
1604 | $this.trigger(showEvent)
1605 |
1606 | if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
1607 |
1608 | var $target = $(selector)
1609 |
1610 | this.activate($this.closest('li'), $ul)
1611 | this.activate($target, $target.parent(), function () {
1612 | $previous.trigger({
1613 | type: 'hidden.bs.tab',
1614 | relatedTarget: $this[0]
1615 | })
1616 | $this.trigger({
1617 | type: 'shown.bs.tab',
1618 | relatedTarget: $previous[0]
1619 | })
1620 | })
1621 | }
1622 |
1623 | Tab.prototype.activate = function (element, container, callback) {
1624 | var $active = container.find('> .active')
1625 | var transition = callback
1626 | && $.support.transition
1627 | && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
1628 |
1629 | function next() {
1630 | $active
1631 | .removeClass('active')
1632 | .find('> .dropdown-menu > .active')
1633 | .removeClass('active')
1634 | .end()
1635 | .find('[data-toggle="tab"]')
1636 | .attr('aria-expanded', false)
1637 |
1638 | element
1639 | .addClass('active')
1640 | .find('[data-toggle="tab"]')
1641 | .attr('aria-expanded', true)
1642 |
1643 | if (transition) {
1644 | element[0].offsetWidth // reflow for transition
1645 | element.addClass('in')
1646 | } else {
1647 | element.removeClass('fade')
1648 | }
1649 |
1650 | if (element.parent('.dropdown-menu').length) {
1651 | element
1652 | .closest('li.dropdown')
1653 | .addClass('active')
1654 | .end()
1655 | .find('[data-toggle="tab"]')
1656 | .attr('aria-expanded', true)
1657 | }
1658 |
1659 | callback && callback()
1660 | }
1661 |
1662 | $active.length && transition ?
1663 | $active
1664 | .one('bsTransitionEnd', next)
1665 | .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
1666 | next()
1667 |
1668 | $active.removeClass('in')
1669 | }
1670 |
1671 |
1672 | // TAB PLUGIN DEFINITION
1673 | // =====================
1674 |
1675 | function Plugin(option) {
1676 | return this.each(function () {
1677 | var $this = $(this)
1678 | var data = $this.data('bs.tab')
1679 |
1680 | if (!data) $this.data('bs.tab', (data = new Tab(this)))
1681 | if (typeof option == 'string') data[option]()
1682 | })
1683 | }
1684 |
1685 | var old = $.fn.tab
1686 |
1687 | $.fn.tab = Plugin
1688 | $.fn.tab.Constructor = Tab
1689 |
1690 |
1691 | // TAB NO CONFLICT
1692 | // ===============
1693 |
1694 | $.fn.tab.noConflict = function () {
1695 | $.fn.tab = old
1696 | return this
1697 | }
1698 |
1699 |
1700 | // TAB DATA-API
1701 | // ============
1702 |
1703 | var clickHandler = function (e) {
1704 | e.preventDefault()
1705 | Plugin.call($(this), 'show')
1706 | }
1707 |
1708 | $(document)
1709 | .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
1710 | .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
1711 |
1712 | }(jQuery);
1713 |
1714 | /* ========================================================================
1715 | * Bootstrap: affix.js v3.3.4
1716 | * http://getbootstrap.com/javascript/#affix
1717 | * ========================================================================
1718 | * Copyright 2011-2015 Twitter, Inc.
1719 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1720 | * ======================================================================== */
1721 |
1722 |
1723 | +function ($) {
1724 | 'use strict';
1725 |
1726 | // AFFIX CLASS DEFINITION
1727 | // ======================
1728 |
1729 | var Affix = function (element, options) {
1730 | this.options = $.extend({}, Affix.DEFAULTS, options)
1731 |
1732 | this.$target = $(this.options.target)
1733 | .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
1734 | .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
1735 |
1736 | this.$element = $(element)
1737 | this.affixed = null
1738 | this.unpin = null
1739 | this.pinnedOffset = null
1740 |
1741 | this.checkPosition()
1742 | }
1743 |
1744 | Affix.VERSION = '3.3.4'
1745 |
1746 | Affix.RESET = 'affix affix-top affix-bottom'
1747 |
1748 | Affix.DEFAULTS = {
1749 | offset: 0,
1750 | target: window
1751 | }
1752 |
1753 | Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
1754 | var scrollTop = this.$target.scrollTop()
1755 | var position = this.$element.offset()
1756 | var targetHeight = this.$target.height()
1757 |
1758 | if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
1759 |
1760 | if (this.affixed == 'bottom') {
1761 | if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
1762 | return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
1763 | }
1764 |
1765 | var initializing = this.affixed == null
1766 | var colliderTop = initializing ? scrollTop : position.top
1767 | var colliderHeight = initializing ? targetHeight : height
1768 |
1769 | if (offsetTop != null && scrollTop <= offsetTop) return 'top'
1770 | if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
1771 |
1772 | return false
1773 | }
1774 |
1775 | Affix.prototype.getPinnedOffset = function () {
1776 | if (this.pinnedOffset) return this.pinnedOffset
1777 | this.$element.removeClass(Affix.RESET).addClass('affix')
1778 | var scrollTop = this.$target.scrollTop()
1779 | var position = this.$element.offset()
1780 | return (this.pinnedOffset = position.top - scrollTop)
1781 | }
1782 |
1783 | Affix.prototype.checkPositionWithEventLoop = function () {
1784 | setTimeout($.proxy(this.checkPosition, this), 1)
1785 | }
1786 |
1787 | Affix.prototype.checkPosition = function () {
1788 | if (!this.$element.is(':visible')) return
1789 |
1790 | var height = this.$element.height()
1791 | var offset = this.options.offset
1792 | var offsetTop = offset.top
1793 | var offsetBottom = offset.bottom
1794 | var scrollHeight = $(document.body).height()
1795 |
1796 | if (typeof offset != 'object') offsetBottom = offsetTop = offset
1797 | if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
1798 | if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
1799 |
1800 | var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
1801 |
1802 | if (this.affixed != affix) {
1803 | if (this.unpin != null) this.$element.css('top', '')
1804 |
1805 | var affixType = 'affix' + (affix ? '-' + affix : '')
1806 | var e = $.Event(affixType + '.bs.affix')
1807 |
1808 | this.$element.trigger(e)
1809 |
1810 | if (e.isDefaultPrevented()) return
1811 |
1812 | this.affixed = affix
1813 | this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
1814 |
1815 | this.$element
1816 | .removeClass(Affix.RESET)
1817 | .addClass(affixType)
1818 | .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
1819 | }
1820 |
1821 | if (affix == 'bottom') {
1822 | this.$element.offset({
1823 | top: scrollHeight - height - offsetBottom
1824 | })
1825 | }
1826 | }
1827 |
1828 |
1829 | // AFFIX PLUGIN DEFINITION
1830 | // =======================
1831 |
1832 | function Plugin(option) {
1833 | return this.each(function () {
1834 | var $this = $(this)
1835 | var data = $this.data('bs.affix')
1836 | var options = typeof option == 'object' && option
1837 |
1838 | if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
1839 | if (typeof option == 'string') data[option]()
1840 | })
1841 | }
1842 |
1843 | var old = $.fn.affix
1844 |
1845 | $.fn.affix = Plugin
1846 | $.fn.affix.Constructor = Affix
1847 |
1848 |
1849 | // AFFIX NO CONFLICT
1850 | // =================
1851 |
1852 | $.fn.affix.noConflict = function () {
1853 | $.fn.affix = old
1854 | return this
1855 | }
1856 |
1857 |
1858 | // AFFIX DATA-API
1859 | // ==============
1860 |
1861 | $(window).on('load', function () {
1862 | $('[data-spy="affix"]').each(function () {
1863 | var $spy = $(this)
1864 | var data = $spy.data()
1865 |
1866 | data.offset = data.offset || {}
1867 |
1868 | if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
1869 | if (data.offsetTop != null) data.offset.top = data.offsetTop
1870 |
1871 | Plugin.call($spy, data)
1872 | })
1873 | })
1874 |
1875 | }(jQuery);
1876 |
1877 | /* ========================================================================
1878 | * Bootstrap: collapse.js v3.3.4
1879 | * http://getbootstrap.com/javascript/#collapse
1880 | * ========================================================================
1881 | * Copyright 2011-2015 Twitter, Inc.
1882 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1883 | * ======================================================================== */
1884 |
1885 |
1886 | +function ($) {
1887 | 'use strict';
1888 |
1889 | // COLLAPSE PUBLIC CLASS DEFINITION
1890 | // ================================
1891 |
1892 | var Collapse = function (element, options) {
1893 | this.$element = $(element)
1894 | this.options = $.extend({}, Collapse.DEFAULTS, options)
1895 | this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
1896 | '[data-toggle="collapse"][data-target="#' + element.id + '"]')
1897 | this.transitioning = null
1898 |
1899 | if (this.options.parent) {
1900 | this.$parent = this.getParent()
1901 | } else {
1902 | this.addAriaAndCollapsedClass(this.$element, this.$trigger)
1903 | }
1904 |
1905 | if (this.options.toggle) this.toggle()
1906 | }
1907 |
1908 | Collapse.VERSION = '3.3.4'
1909 |
1910 | Collapse.TRANSITION_DURATION = 350
1911 |
1912 | Collapse.DEFAULTS = {
1913 | toggle: true
1914 | }
1915 |
1916 | Collapse.prototype.dimension = function () {
1917 | var hasWidth = this.$element.hasClass('width')
1918 | return hasWidth ? 'width' : 'height'
1919 | }
1920 |
1921 | Collapse.prototype.show = function () {
1922 | if (this.transitioning || this.$element.hasClass('in')) return
1923 |
1924 | var activesData
1925 | var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
1926 |
1927 | if (actives && actives.length) {
1928 | activesData = actives.data('bs.collapse')
1929 | if (activesData && activesData.transitioning) return
1930 | }
1931 |
1932 | var startEvent = $.Event('show.bs.collapse')
1933 | this.$element.trigger(startEvent)
1934 | if (startEvent.isDefaultPrevented()) return
1935 |
1936 | if (actives && actives.length) {
1937 | Plugin.call(actives, 'hide')
1938 | activesData || actives.data('bs.collapse', null)
1939 | }
1940 |
1941 | var dimension = this.dimension()
1942 |
1943 | this.$element
1944 | .removeClass('collapse')
1945 | .addClass('collapsing')[dimension](0)
1946 | .attr('aria-expanded', true)
1947 |
1948 | this.$trigger
1949 | .removeClass('collapsed')
1950 | .attr('aria-expanded', true)
1951 |
1952 | this.transitioning = 1
1953 |
1954 | var complete = function () {
1955 | this.$element
1956 | .removeClass('collapsing')
1957 | .addClass('collapse in')[dimension]('')
1958 | this.transitioning = 0
1959 | this.$element
1960 | .trigger('shown.bs.collapse')
1961 | }
1962 |
1963 | if (!$.support.transition) return complete.call(this)
1964 |
1965 | var scrollSize = $.camelCase(['scroll', dimension].join('-'))
1966 |
1967 | this.$element
1968 | .one('bsTransitionEnd', $.proxy(complete, this))
1969 | .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
1970 | }
1971 |
1972 | Collapse.prototype.hide = function () {
1973 | if (this.transitioning || !this.$element.hasClass('in')) return
1974 |
1975 | var startEvent = $.Event('hide.bs.collapse')
1976 | this.$element.trigger(startEvent)
1977 | if (startEvent.isDefaultPrevented()) return
1978 |
1979 | var dimension = this.dimension()
1980 |
1981 | this.$element[dimension](this.$element[dimension]())[0].offsetHeight
1982 |
1983 | this.$element
1984 | .addClass('collapsing')
1985 | .removeClass('collapse in')
1986 | .attr('aria-expanded', false)
1987 |
1988 | this.$trigger
1989 | .addClass('collapsed')
1990 | .attr('aria-expanded', false)
1991 |
1992 | this.transitioning = 1
1993 |
1994 | var complete = function () {
1995 | this.transitioning = 0
1996 | this.$element
1997 | .removeClass('collapsing')
1998 | .addClass('collapse')
1999 | .trigger('hidden.bs.collapse')
2000 | }
2001 |
2002 | if (!$.support.transition) return complete.call(this)
2003 |
2004 | this.$element
2005 | [dimension](0)
2006 | .one('bsTransitionEnd', $.proxy(complete, this))
2007 | .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
2008 | }
2009 |
2010 | Collapse.prototype.toggle = function () {
2011 | this[this.$element.hasClass('in') ? 'hide' : 'show']()
2012 | }
2013 |
2014 | Collapse.prototype.getParent = function () {
2015 | return $(this.options.parent)
2016 | .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
2017 | .each($.proxy(function (i, element) {
2018 | var $element = $(element)
2019 | this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
2020 | }, this))
2021 | .end()
2022 | }
2023 |
2024 | Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
2025 | var isOpen = $element.hasClass('in')
2026 |
2027 | $element.attr('aria-expanded', isOpen)
2028 | $trigger
2029 | .toggleClass('collapsed', !isOpen)
2030 | .attr('aria-expanded', isOpen)
2031 | }
2032 |
2033 | function getTargetFromTrigger($trigger) {
2034 | var href
2035 | var target = $trigger.attr('data-target')
2036 | || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
2037 |
2038 | return $(target)
2039 | }
2040 |
2041 |
2042 | // COLLAPSE PLUGIN DEFINITION
2043 | // ==========================
2044 |
2045 | function Plugin(option) {
2046 | return this.each(function () {
2047 | var $this = $(this)
2048 | var data = $this.data('bs.collapse')
2049 | var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
2050 |
2051 | if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
2052 | if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
2053 | if (typeof option == 'string') data[option]()
2054 | })
2055 | }
2056 |
2057 | var old = $.fn.collapse
2058 |
2059 | $.fn.collapse = Plugin
2060 | $.fn.collapse.Constructor = Collapse
2061 |
2062 |
2063 | // COLLAPSE NO CONFLICT
2064 | // ====================
2065 |
2066 | $.fn.collapse.noConflict = function () {
2067 | $.fn.collapse = old
2068 | return this
2069 | }
2070 |
2071 |
2072 | // COLLAPSE DATA-API
2073 | // =================
2074 |
2075 | $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
2076 | var $this = $(this)
2077 |
2078 | if (!$this.attr('data-target')) e.preventDefault()
2079 |
2080 | var $target = getTargetFromTrigger($this)
2081 | var data = $target.data('bs.collapse')
2082 | var option = data ? 'toggle' : $this.data()
2083 |
2084 | Plugin.call($target, option)
2085 | })
2086 |
2087 | }(jQuery);
2088 |
2089 | /* ========================================================================
2090 | * Bootstrap: scrollspy.js v3.3.4
2091 | * http://getbootstrap.com/javascript/#scrollspy
2092 | * ========================================================================
2093 | * Copyright 2011-2015 Twitter, Inc.
2094 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
2095 | * ======================================================================== */
2096 |
2097 |
2098 | +function ($) {
2099 | 'use strict';
2100 |
2101 | // SCROLLSPY CLASS DEFINITION
2102 | // ==========================
2103 |
2104 | function ScrollSpy(element, options) {
2105 | this.$body = $(document.body)
2106 | this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
2107 | this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
2108 | this.selector = (this.options.target || '') + ' .nav li > a'
2109 | this.offsets = []
2110 | this.targets = []
2111 | this.activeTarget = null
2112 | this.scrollHeight = 0
2113 |
2114 | this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
2115 | this.refresh()
2116 | this.process()
2117 | }
2118 |
2119 | ScrollSpy.VERSION = '3.3.4'
2120 |
2121 | ScrollSpy.DEFAULTS = {
2122 | offset: 10
2123 | }
2124 |
2125 | ScrollSpy.prototype.getScrollHeight = function () {
2126 | return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
2127 | }
2128 |
2129 | ScrollSpy.prototype.refresh = function () {
2130 | var that = this
2131 | var offsetMethod = 'offset'
2132 | var offsetBase = 0
2133 |
2134 | this.offsets = []
2135 | this.targets = []
2136 | this.scrollHeight = this.getScrollHeight()
2137 |
2138 | if (!$.isWindow(this.$scrollElement[0])) {
2139 | offsetMethod = 'position'
2140 | offsetBase = this.$scrollElement.scrollTop()
2141 | }
2142 |
2143 | this.$body
2144 | .find(this.selector)
2145 | .map(function () {
2146 | var $el = $(this)
2147 | var href = $el.data('target') || $el.attr('href')
2148 | var $href = /^#./.test(href) && $(href)
2149 |
2150 | return ($href
2151 | && $href.length
2152 | && $href.is(':visible')
2153 | && [[$href[offsetMethod]().top + offsetBase, href]]) || null
2154 | })
2155 | .sort(function (a, b) { return a[0] - b[0] })
2156 | .each(function () {
2157 | that.offsets.push(this[0])
2158 | that.targets.push(this[1])
2159 | })
2160 | }
2161 |
2162 | ScrollSpy.prototype.process = function () {
2163 | var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
2164 | var scrollHeight = this.getScrollHeight()
2165 | var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
2166 | var offsets = this.offsets
2167 | var targets = this.targets
2168 | var activeTarget = this.activeTarget
2169 | var i
2170 |
2171 | if (this.scrollHeight != scrollHeight) {
2172 | this.refresh()
2173 | }
2174 |
2175 | if (scrollTop >= maxScroll) {
2176 | return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
2177 | }
2178 |
2179 | if (activeTarget && scrollTop < offsets[0]) {
2180 | this.activeTarget = null
2181 | return this.clear()
2182 | }
2183 |
2184 | for (i = offsets.length; i--;) {
2185 | activeTarget != targets[i]
2186 | && scrollTop >= offsets[i]
2187 | && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
2188 | && this.activate(targets[i])
2189 | }
2190 | }
2191 |
2192 | ScrollSpy.prototype.activate = function (target) {
2193 | this.activeTarget = target
2194 |
2195 | this.clear()
2196 |
2197 | var selector = this.selector +
2198 | '[data-target="' + target + '"],' +
2199 | this.selector + '[href="' + target + '"]'
2200 |
2201 | var active = $(selector)
2202 | .parents('li')
2203 | .addClass('active')
2204 |
2205 | if (active.parent('.dropdown-menu').length) {
2206 | active = active
2207 | .closest('li.dropdown')
2208 | .addClass('active')
2209 | }
2210 |
2211 | active.trigger('activate.bs.scrollspy')
2212 | }
2213 |
2214 | ScrollSpy.prototype.clear = function () {
2215 | $(this.selector)
2216 | .parentsUntil(this.options.target, '.active')
2217 | .removeClass('active')
2218 | }
2219 |
2220 |
2221 | // SCROLLSPY PLUGIN DEFINITION
2222 | // ===========================
2223 |
2224 | function Plugin(option) {
2225 | return this.each(function () {
2226 | var $this = $(this)
2227 | var data = $this.data('bs.scrollspy')
2228 | var options = typeof option == 'object' && option
2229 |
2230 | if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
2231 | if (typeof option == 'string') data[option]()
2232 | })
2233 | }
2234 |
2235 | var old = $.fn.scrollspy
2236 |
2237 | $.fn.scrollspy = Plugin
2238 | $.fn.scrollspy.Constructor = ScrollSpy
2239 |
2240 |
2241 | // SCROLLSPY NO CONFLICT
2242 | // =====================
2243 |
2244 | $.fn.scrollspy.noConflict = function () {
2245 | $.fn.scrollspy = old
2246 | return this
2247 | }
2248 |
2249 |
2250 | // SCROLLSPY DATA-API
2251 | // ==================
2252 |
2253 | $(window).on('load.bs.scrollspy.data-api', function () {
2254 | $('[data-spy="scroll"]').each(function () {
2255 | var $spy = $(this)
2256 | Plugin.call($spy, $spy.data())
2257 | })
2258 | })
2259 |
2260 | }(jQuery);
2261 |
2262 | /* ========================================================================
2263 | * Bootstrap: transition.js v3.3.4
2264 | * http://getbootstrap.com/javascript/#transitions
2265 | * ========================================================================
2266 | * Copyright 2011-2015 Twitter, Inc.
2267 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
2268 | * ======================================================================== */
2269 |
2270 |
2271 | +function ($) {
2272 | 'use strict';
2273 |
2274 | // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
2275 | // ============================================================
2276 |
2277 | function transitionEnd() {
2278 | var el = document.createElement('bootstrap')
2279 |
2280 | var transEndEventNames = {
2281 | WebkitTransition : 'webkitTransitionEnd',
2282 | MozTransition : 'transitionend',
2283 | OTransition : 'oTransitionEnd otransitionend',
2284 | transition : 'transitionend'
2285 | }
2286 |
2287 | for (var name in transEndEventNames) {
2288 | if (el.style[name] !== undefined) {
2289 | return { end: transEndEventNames[name] }
2290 | }
2291 | }
2292 |
2293 | return false // explicit for ie8 ( ._.)
2294 | }
2295 |
2296 | // http://blog.alexmaccaw.com/css-transitions
2297 | $.fn.emulateTransitionEnd = function (duration) {
2298 | var called = false
2299 | var $el = this
2300 | $(this).one('bsTransitionEnd', function () { called = true })
2301 | var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
2302 | setTimeout(callback, duration)
2303 | return this
2304 | }
2305 |
2306 | $(function () {
2307 | $.support.transition = transitionEnd()
2308 |
2309 | if (!$.support.transition) return
2310 |
2311 | $.event.special.bsTransitionEnd = {
2312 | bindType: $.support.transition.end,
2313 | delegateType: $.support.transition.end,
2314 | handle: function (e) {
2315 | if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
2316 | }
2317 | }
2318 | })
2319 |
2320 | }(jQuery);
2321 |
--------------------------------------------------------------------------------
/public/js/dashboard.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 | $('input.btn-switch').bootstrapSwitch().on('switchChange.bootstrapSwitch', function(event, state) {
3 | var elm = $(this);
4 | var id = elm.data('repoid');
5 | var url;
6 | if(state)
7 | url = '/enable/' + id;
8 | else
9 | url = '/disable/' + id;
10 | elm.parents('.dashboard-row').addClass('saving');
11 | $.post(url, {authenticity_token: window.CSRF_TOKEN, slug: elm.data('slug')}, function(data, status, xhr) {
12 | elm.parents('.dashboard-row').removeClass('saving');
13 | if(xhr.status != 200)
14 | alert("Failure while saving settings. Fall down go boom.");
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/views/dashboard.erb:
--------------------------------------------------------------------------------
1 |
2 |
Dashboard
3 | <% @repos.each do |repo| %>
4 |
5 | <% repo_slug = "#{repo['owner_name']}/#{repo['name']}" %>
6 | checked="checked"<% end %>>
7 | <%= repo_slug %>
8 | >
9 | <%= repo['description'] %>
10 |
11 | <% end %>
12 |
13 |
16 |
--------------------------------------------------------------------------------
/views/landing.erb:
--------------------------------------------------------------------------------
1 |
2 |
Welcome!
3 |
4 |
5 | Nightli.es provides automatic nightly builds for Travis CI .
6 |
7 |
Why have nightly builds?
8 |
9 | When developing a library, often some components like other libraries or the
10 | language interpreter will use the latest available version. This can lead to
11 | nasty situations when an upstream component releases a new version that
12 | breaks your tests, but you don't find out until your next git push.
13 | Nightli.es ensures each project builds at least once every 24 hours so these
14 | failures will be caught as soon as possible.
15 |
16 |
Why does Nightli.es require so many GitHub permissions?
17 |
18 | Nightli.es uses the Travis API to get the list of projects you have access
19 | to and to kick off builds. Unfortunately Travis' GitHub authentication
20 | integration requires that I request all the same permissions as Travis
21 | itself. Your GitHub OAuth tokens are never stored server-side, only your
22 | Travis API token is stored for use in starting builds via the API.
23 |
24 |
Get Started
25 |
26 | Log in with GitHub
27 |
28 |
29 |
--------------------------------------------------------------------------------
/views/layout.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Nightli.es
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
27 |
28 |
29 | <% if authenticated? %>
30 | <%= github_user.login %>
31 | Log out
32 | <% else %>
33 | Log in with GitHub
34 | <% end %>
35 |
36 |
37 |
38 |
39 |
40 |
41 | <%= yield %>
42 |
43 |
44 |
45 |
55 |
56 |
57 |
58 |
59 | <% if @extra_js %>
60 |
61 | <% end %>
62 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/warden_travis/strategy.rb:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2015, Noah Kantrowitz
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | require 'travis'
18 | require 'warden/github/strategy'
19 |
20 |
21 |
22 | module Warden
23 | module Travis
24 | class Strategy < ::Warden::GitHub::Strategy
25 |
26 | def load_user
27 | ::Warden::GitHub::User.load(oauth.access_token, custom_session['browser_session_id']).tap do |user|
28 | ::Travis.github_auth(user.token)
29 | abort_flow!(e.message) unless ::Travis.access_token
30 | user.attribs['travis_token'] = ::Travis.access_token
31 | end
32 | rescue ::Warden::GitHub::OAuth::BadVerificationCode => e
33 | abort_flow!(e.message)
34 | end
35 |
36 | end
37 | end
38 | end
39 |
40 | Warden::Strategies.add(:travis, Warden::Travis::Strategy)
41 |
--------------------------------------------------------------------------------