Commit 853565b87a0ab3b1d59381b5b83d820beb530f85
Exists in
master
and in
1 other branch
Merge branch 'master' of https://github.com/errbit/errbit
Conflicts: Gemfile Gemfile.lock
Showing
259 changed files
with
4487 additions
and
3125 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 259 files displayed.
.gitignore
.travis.yml
1 | +language: ruby | |
1 | 2 | rvm: |
3 | + - 1.9.3 | |
2 | 4 | - 1.9.2 |
3 | 5 | - 1.8.7 |
4 | 6 | |
5 | -# Only build master branch | |
6 | -branches: | |
7 | - only: | |
8 | - - master | |
9 | - | |
10 | 7 | # To stop Travis from running tests for a new commit, |
11 | 8 | # add the following to your commit message: [ci skip] |
12 | 9 | # You should add this when you edit documentation or comments, etc. |
13 | - | ... | ... |
Gemfile
1 | 1 | source 'http://rubygems.org' |
2 | 2 | |
3 | -gem 'rails', '3.0.10' | |
3 | +gem 'rails', '3.2.6' | |
4 | + | |
4 | 5 | gem 'nokogiri' |
5 | -gem 'mongoid', '~> 2.2.2' | |
6 | +gem 'mongoid', '~> 2.4.10' | |
6 | 7 | |
7 | 8 | gem 'haml' |
8 | 9 | gem 'htmlentities', "~> 4.3.0" |
9 | -gem 'devise', '~> 1.4.0' | |
10 | + | |
11 | +gem 'devise', '~> 1.5.3' | |
12 | + | |
13 | +gem 'omniauth-github' | |
14 | +gem 'oa-core' | |
15 | + | |
10 | 16 | gem 'lighthouse-api' |
11 | 17 | gem 'oruen_redmine_client', :require => 'redmine_client' |
12 | 18 | gem 'mongoid_rails_migrations' |
13 | 19 | gem 'useragent', '~> 0.3.1' |
14 | 20 | gem 'pivotal-tracker' |
15 | 21 | gem 'ruby-fogbugz', :require => 'fogbugz' |
16 | -gem 'octokit', '0.6.4' | |
22 | + | |
23 | +gem 'octokit', '~> 1.0.0' | |
24 | + | |
17 | 25 | gem 'inherited_resources' |
18 | 26 | gem 'SystemTimer', :platform => :ruby_18 |
19 | 27 | gem 'hoptoad_notifier', "~> 2.4" |
20 | 28 | gem 'actionmailer_inline_css', "~> 1.3.0" |
21 | 29 | gem 'kaminari' |
22 | 30 | gem 'rack-ssl-enforcer' |
23 | -gem 'heroku' | |
24 | -gem 'newrelic_rpm' | |
31 | +gem 'fabrication', "~> 1.3.0" # Both for tests, and loading demo data | |
32 | +gem 'rails_autolink', '~> 1.0.9' | |
25 | 33 | |
26 | 34 | platform :ruby do |
27 | - gem 'mongo', '= 1.3.1' | |
28 | - gem 'bson', '= 1.3.1' | |
29 | - gem 'bson_ext', '= 1.3.1' | |
35 | + gem 'mongo', '= 1.6.2' | |
36 | + gem 'bson', '= 1.6.2' | |
37 | + gem 'bson_ext', '= 1.6.2' | |
30 | 38 | end |
31 | 39 | |
32 | 40 | gem 'ri_cal' |
41 | +gem 'yajl-ruby' | |
33 | 42 | |
34 | 43 | group :development, :test do |
35 | 44 | gem 'rspec-rails', '~> 2.6' |
36 | 45 | gem 'webmock', :require => false |
37 | - gem 'fabrication' | |
38 | - unless ENV['TRAVIS'] | |
46 | + unless ENV["CI"] | |
39 | 47 | gem 'ruby-debug', :platform => :mri_18 |
40 | - gem 'ruby-debug19', :platform => :mri_19, :require => 'ruby-debug' | |
48 | + gem 'debugger', :platform => :mri_19 | |
41 | 49 | end |
42 | - # gem 'rpm_contrib', :git => "git://github.com/bensymonds/rpm_contrib.git", :branch => "mongo-1.4.0_update" | |
50 | +# gem 'rpm_contrib' | |
51 | +# gem 'newrelic_rpm' | |
52 | + gem 'capistrano' | |
43 | 53 | end |
44 | 54 | |
45 | 55 | group :test do |
56 | + gem 'capybara' | |
57 | + gem 'launchy' | |
46 | 58 | gem 'rspec', '~> 2.6' |
47 | 59 | gem 'database_cleaner', '~> 0.6.0' |
48 | 60 | gem 'email_spec' |
... | ... | @@ -52,3 +64,13 @@ group :heroku do |
52 | 64 | gem 'unicorn' |
53 | 65 | end |
54 | 66 | |
67 | +# Use thin for development | |
68 | +gem 'thin', :group => :development, :platform => :ruby | |
69 | + | |
70 | +# Gems used only for assets and not required | |
71 | +# in production environments by default. | |
72 | +group :assets do | |
73 | + gem 'execjs' | |
74 | + gem 'therubyracer', :platform => :ruby # C Ruby (MRI) or Rubinius, but NOT Windows | |
75 | + gem 'uglifier', '>= 1.0.3' | |
76 | +end | ... | ... |
Gemfile.lock
... | ... | @@ -2,107 +2,136 @@ GEM |
2 | 2 | remote: http://rubygems.org/ |
3 | 3 | specs: |
4 | 4 | SystemTimer (1.2.3) |
5 | - abstract (1.0.0) | |
6 | - actionmailer (3.0.10) | |
7 | - actionpack (= 3.0.10) | |
8 | - mail (~> 2.2.19) | |
5 | + actionmailer (3.2.6) | |
6 | + actionpack (= 3.2.6) | |
7 | + mail (~> 2.4.4) | |
9 | 8 | actionmailer_inline_css (1.3.1) |
10 | 9 | actionmailer (>= 3.0.0) |
11 | 10 | nokogiri (>= 1.4.4) |
12 | 11 | premailer (>= 1.7.1) |
13 | - actionpack (3.0.10) | |
14 | - activemodel (= 3.0.10) | |
15 | - activesupport (= 3.0.10) | |
16 | - builder (~> 2.1.2) | |
17 | - erubis (~> 2.6.6) | |
18 | - i18n (~> 0.5.0) | |
19 | - rack (~> 1.2.1) | |
20 | - rack-mount (~> 0.6.14) | |
21 | - rack-test (~> 0.5.7) | |
22 | - tzinfo (~> 0.3.23) | |
23 | - activemodel (3.0.10) | |
24 | - activesupport (= 3.0.10) | |
25 | - builder (~> 2.1.2) | |
26 | - i18n (~> 0.5.0) | |
27 | - activerecord (3.0.10) | |
28 | - activemodel (= 3.0.10) | |
29 | - activesupport (= 3.0.10) | |
30 | - arel (~> 2.0.10) | |
31 | - tzinfo (~> 0.3.23) | |
32 | - activeresource (3.0.10) | |
33 | - activemodel (= 3.0.10) | |
34 | - activesupport (= 3.0.10) | |
35 | - activesupport (3.0.10) | |
36 | - addressable (2.2.6) | |
37 | - archive-tar-minitar (0.5.2) | |
38 | - arel (2.0.10) | |
12 | + actionpack (3.2.6) | |
13 | + activemodel (= 3.2.6) | |
14 | + activesupport (= 3.2.6) | |
15 | + builder (~> 3.0.0) | |
16 | + erubis (~> 2.7.0) | |
17 | + journey (~> 1.0.1) | |
18 | + rack (~> 1.4.0) | |
19 | + rack-cache (~> 1.2) | |
20 | + rack-test (~> 0.6.1) | |
21 | + sprockets (~> 2.1.3) | |
22 | + activemodel (3.2.6) | |
23 | + activesupport (= 3.2.6) | |
24 | + builder (~> 3.0.0) | |
25 | + activerecord (3.2.6) | |
26 | + activemodel (= 3.2.6) | |
27 | + activesupport (= 3.2.6) | |
28 | + arel (~> 3.0.2) | |
29 | + tzinfo (~> 0.3.29) | |
30 | + activeresource (3.2.6) | |
31 | + activemodel (= 3.2.6) | |
32 | + activesupport (= 3.2.6) | |
33 | + activesupport (3.2.6) | |
34 | + i18n (~> 0.6) | |
35 | + multi_json (~> 1.0) | |
36 | + addressable (2.3.2) | |
37 | + arel (3.0.2) | |
39 | 38 | bcrypt-ruby (3.0.1) |
40 | - bson (1.3.1) | |
41 | - bson_ext (1.3.1) | |
42 | - builder (2.1.2) | |
43 | - columnize (0.3.4) | |
39 | + bson (1.6.2) | |
40 | + bson_ext (1.6.2) | |
41 | + bson (~> 1.6.2) | |
42 | + builder (3.0.0) | |
43 | + capistrano (2.13.3) | |
44 | + highline | |
45 | + net-scp (>= 1.0.0) | |
46 | + net-sftp (>= 2.0.0) | |
47 | + net-ssh (>= 2.0.14) | |
48 | + net-ssh-gateway (>= 1.1.0) | |
49 | + capybara (1.1.2) | |
50 | + mime-types (>= 1.16) | |
51 | + nokogiri (>= 1.3.3) | |
52 | + rack (>= 1.0.0) | |
53 | + rack-test (>= 0.5.4) | |
54 | + selenium-webdriver (~> 2.0) | |
55 | + xpath (~> 0.1.4) | |
56 | + childprocess (0.3.5) | |
57 | + ffi (~> 1.0, >= 1.0.6) | |
58 | + columnize (0.3.6) | |
44 | 59 | crack (0.3.1) |
45 | - css_parser (1.2.5) | |
60 | + css_parser (1.2.6) | |
46 | 61 | addressable |
62 | + rdoc | |
63 | + daemons (1.1.8) | |
47 | 64 | database_cleaner (0.6.7) |
48 | - devise (1.4.7) | |
65 | + debugger (1.2.0) | |
66 | + columnize (>= 0.3.1) | |
67 | + debugger-linecache (~> 1.1.1) | |
68 | + debugger-ruby_core_source (~> 1.1.3) | |
69 | + debugger-linecache (1.1.2) | |
70 | + debugger-ruby_core_source (>= 1.1.1) | |
71 | + debugger-ruby_core_source (1.1.3) | |
72 | + devise (1.5.3) | |
49 | 73 | bcrypt-ruby (~> 3.0) |
50 | 74 | orm_adapter (~> 0.0.3) |
51 | - warden (~> 1.0.3) | |
75 | + warden (~> 1.1) | |
52 | 76 | diff-lcs (1.1.3) |
53 | 77 | email_spec (1.2.1) |
54 | 78 | mail (~> 2.2) |
55 | 79 | rspec (~> 2.0) |
56 | - erubis (2.6.6) | |
57 | - abstract (>= 1.0.0) | |
58 | - fabrication (1.2.0) | |
59 | - faraday (0.7.4) | |
60 | - addressable (~> 2.2.6) | |
61 | - multipart-post (~> 1.1.0) | |
62 | - rack (>= 1.1.0, < 2) | |
63 | - faraday_middleware (0.7.0) | |
64 | - faraday (~> 0.7.3) | |
65 | - haml (3.1.3) | |
80 | + erubis (2.7.0) | |
81 | + eventmachine (0.12.10) | |
82 | + execjs (1.4.0) | |
83 | + multi_json (~> 1.0) | |
84 | + fabrication (1.3.2) | |
85 | + faraday (0.8.1) | |
86 | + multipart-post (~> 1.1) | |
87 | + faraday_middleware (0.8.8) | |
88 | + faraday (>= 0.7.4, < 0.9) | |
89 | + ffi (1.1.4) | |
90 | + haml (3.1.6) | |
66 | 91 | happymapper (0.4.0) |
67 | 92 | libxml-ruby (~> 2.0) |
68 | 93 | has_scope (0.5.1) |
69 | - hashie (1.0.0) | |
70 | - heroku (2.19.2) | |
71 | - launchy (>= 0.3.2) | |
72 | - rest-client (~> 1.6.1) | |
73 | - rubyzip | |
74 | - term-ansicolor (~> 1.0.5) | |
94 | + hashie (1.2.0) | |
95 | + highline (1.6.13) | |
96 | + hike (1.2.1) | |
75 | 97 | hoptoad_notifier (2.4.11) |
76 | 98 | activesupport |
77 | 99 | builder |
78 | - htmlentities (4.3.0) | |
79 | - i18n (0.5.0) | |
80 | - inherited_resources (1.3.0) | |
100 | + htmlentities (4.3.1) | |
101 | + httpauth (0.1) | |
102 | + i18n (0.6.0) | |
103 | + inherited_resources (1.3.1) | |
81 | 104 | has_scope (~> 0.5.0) |
82 | - responders (~> 0.6.0) | |
83 | - kaminari (0.12.4) | |
84 | - rails (>= 3.0.0) | |
85 | - kgio (2.6.0) | |
86 | - launchy (2.0.5) | |
87 | - addressable (~> 2.2.6) | |
88 | - libxml-ruby (2.2.2) | |
105 | + responders (~> 0.6) | |
106 | + journey (1.0.4) | |
107 | + json (1.7.4) | |
108 | + jwt (0.1.5) | |
109 | + multi_json (>= 1.0) | |
110 | + kaminari (0.13.0) | |
111 | + actionpack (>= 3.0.0) | |
112 | + activesupport (>= 3.0.0) | |
113 | + railties (>= 3.0.0) | |
114 | + kgio (2.7.4) | |
115 | + launchy (2.1.2) | |
116 | + addressable (~> 2.3) | |
117 | + libv8 (3.3.10.4) | |
118 | + libwebsocket (0.1.5) | |
119 | + addressable | |
120 | + libxml-ruby (2.3.3) | |
89 | 121 | lighthouse-api (2.0) |
90 | 122 | activeresource (>= 3.0.0) |
91 | 123 | activesupport (>= 3.0.0) |
92 | 124 | linecache (0.46) |
93 | 125 | rbx-require-relative (> 0.0.4) |
94 | - linecache19 (0.5.12) | |
95 | - ruby_core_source (>= 0.1.4) | |
96 | - mail (2.2.19) | |
97 | - activesupport (>= 2.3.6) | |
126 | + mail (2.4.4) | |
98 | 127 | i18n (>= 0.4.0) |
99 | 128 | mime-types (~> 1.16) |
100 | 129 | treetop (~> 1.4.8) |
101 | - mime-types (1.16) | |
102 | - mongo (1.3.1) | |
103 | - bson (>= 1.3.1) | |
104 | - mongoid (2.2.4) | |
105 | - activemodel (~> 3.0) | |
130 | + mime-types (1.19) | |
131 | + mongo (1.6.2) | |
132 | + bson (~> 1.6.2) | |
133 | + mongoid (2.4.10) | |
134 | + activemodel (~> 3.1) | |
106 | 135 | mongo (~> 1.3) |
107 | 136 | tzinfo (~> 0.3.22) |
108 | 137 | mongoid_rails_migrations (0.0.14) |
... | ... | @@ -110,20 +139,42 @@ GEM |
110 | 139 | bundler (>= 1.0.0) |
111 | 140 | rails (>= 3.0.0) |
112 | 141 | railties (>= 3.0.0) |
113 | - multi_json (1.0.4) | |
114 | - multipart-post (1.1.4) | |
115 | - newrelic_rpm (3.3.1) | |
116 | - nokogiri (1.5.0) | |
117 | - octokit (0.6.4) | |
118 | - addressable (~> 2.2.6) | |
119 | - faraday (~> 0.7.3) | |
120 | - faraday_middleware (~> 0.7.0.rc1) | |
121 | - hashie (~> 1.0.0) | |
122 | - multi_json (~> 1.0.2) | |
123 | - orm_adapter (0.0.5) | |
142 | + multi_json (1.3.6) | |
143 | + multipart-post (1.1.5) | |
144 | + net-scp (1.0.4) | |
145 | + net-ssh (>= 1.99.1) | |
146 | + net-sftp (2.0.5) | |
147 | + net-ssh (>= 2.0.9) | |
148 | + net-ssh (2.5.2) | |
149 | + net-ssh-gateway (1.1.0) | |
150 | + net-ssh (>= 1.99.1) | |
151 | + nokogiri (1.5.5) | |
152 | + oa-core (0.3.2) | |
153 | + oauth2 (0.8.0) | |
154 | + faraday (~> 0.8) | |
155 | + httpauth (~> 0.1) | |
156 | + jwt (~> 0.1.4) | |
157 | + multi_json (~> 1.0) | |
158 | + rack (~> 1.2) | |
159 | + octokit (1.0.7) | |
160 | + addressable (~> 2.2) | |
161 | + faraday (~> 0.8) | |
162 | + faraday_middleware (~> 0.8) | |
163 | + hashie (~> 1.2) | |
164 | + multi_json (~> 1.3) | |
165 | + omniauth (1.1.0) | |
166 | + hashie (~> 1.2) | |
167 | + rack | |
168 | + omniauth-github (1.0.2) | |
169 | + omniauth (~> 1.0) | |
170 | + omniauth-oauth2 (~> 1.1) | |
171 | + omniauth-oauth2 (1.1.0) | |
172 | + oauth2 (~> 0.8.0) | |
173 | + omniauth (~> 1.0) | |
174 | + orm_adapter (0.0.7) | |
124 | 175 | oruen_redmine_client (0.0.1) |
125 | 176 | activeresource (>= 2.3.0) |
126 | - pivotal-tracker (0.4.1) | |
177 | + pivotal-tracker (0.5.4) | |
127 | 178 | builder |
128 | 179 | builder |
129 | 180 | happymapper (>= 0.3.2) |
... | ... | @@ -132,89 +183,103 @@ GEM |
132 | 183 | nokogiri (~> 1.4) |
133 | 184 | rest-client (~> 1.6.0) |
134 | 185 | rest-client (~> 1.6.0) |
135 | - polyglot (0.3.2) | |
186 | + polyglot (0.3.3) | |
136 | 187 | premailer (1.7.3) |
137 | 188 | css_parser (>= 1.1.9) |
138 | 189 | htmlentities (>= 4.0.0) |
139 | - rack (1.2.4) | |
140 | - rack-mount (0.6.14) | |
141 | - rack (>= 1.0.0) | |
190 | + rack (1.4.1) | |
191 | + rack-cache (1.2) | |
192 | + rack (>= 0.4) | |
193 | + rack-ssl (1.3.2) | |
194 | + rack | |
142 | 195 | rack-ssl-enforcer (0.2.4) |
143 | - rack-test (0.5.7) | |
196 | + rack-test (0.6.1) | |
144 | 197 | rack (>= 1.0) |
145 | - rails (3.0.10) | |
146 | - actionmailer (= 3.0.10) | |
147 | - actionpack (= 3.0.10) | |
148 | - activerecord (= 3.0.10) | |
149 | - activeresource (= 3.0.10) | |
150 | - activesupport (= 3.0.10) | |
198 | + rails (3.2.6) | |
199 | + actionmailer (= 3.2.6) | |
200 | + actionpack (= 3.2.6) | |
201 | + activerecord (= 3.2.6) | |
202 | + activeresource (= 3.2.6) | |
203 | + activesupport (= 3.2.6) | |
151 | 204 | bundler (~> 1.0) |
152 | - railties (= 3.0.10) | |
153 | - railties (3.0.10) | |
154 | - actionpack (= 3.0.10) | |
155 | - activesupport (= 3.0.10) | |
205 | + railties (= 3.2.6) | |
206 | + rails_autolink (1.0.9) | |
207 | + rails (~> 3.1) | |
208 | + railties (3.2.6) | |
209 | + actionpack (= 3.2.6) | |
210 | + activesupport (= 3.2.6) | |
211 | + rack-ssl (~> 1.3.2) | |
156 | 212 | rake (>= 0.8.7) |
157 | 213 | rdoc (~> 3.4) |
158 | - thor (~> 0.14.4) | |
159 | - raindrops (0.8.0) | |
160 | - rake (0.9.2) | |
161 | - rbx-require-relative (0.0.5) | |
162 | - rdoc (3.9.4) | |
163 | - responders (0.6.4) | |
214 | + thor (>= 0.14.6, < 2.0) | |
215 | + raindrops (0.10.0) | |
216 | + rake (0.9.2.2) | |
217 | + rbx-require-relative (0.0.9) | |
218 | + rdoc (3.12) | |
219 | + json (~> 1.4) | |
220 | + responders (0.9.2) | |
221 | + railties (~> 3.1) | |
164 | 222 | rest-client (1.6.7) |
165 | 223 | mime-types (>= 1.16) |
166 | 224 | ri_cal (0.8.8) |
167 | - rspec (2.6.0) | |
168 | - rspec-core (~> 2.6.0) | |
169 | - rspec-expectations (~> 2.6.0) | |
170 | - rspec-mocks (~> 2.6.0) | |
171 | - rspec-core (2.6.4) | |
172 | - rspec-expectations (2.6.0) | |
173 | - diff-lcs (~> 1.1.2) | |
174 | - rspec-mocks (2.6.0) | |
175 | - rspec-rails (2.6.1) | |
176 | - actionpack (~> 3.0) | |
177 | - activesupport (~> 3.0) | |
178 | - railties (~> 3.0) | |
179 | - rspec (~> 2.6.0) | |
225 | + rspec (2.11.0) | |
226 | + rspec-core (~> 2.11.0) | |
227 | + rspec-expectations (~> 2.11.0) | |
228 | + rspec-mocks (~> 2.11.0) | |
229 | + rspec-core (2.11.1) | |
230 | + rspec-expectations (2.11.2) | |
231 | + diff-lcs (~> 1.1.3) | |
232 | + rspec-mocks (2.11.1) | |
233 | + rspec-rails (2.11.0) | |
234 | + actionpack (>= 3.0) | |
235 | + activesupport (>= 3.0) | |
236 | + railties (>= 3.0) | |
237 | + rspec (~> 2.11.0) | |
180 | 238 | ruby-debug (0.10.4) |
181 | 239 | columnize (>= 0.1) |
182 | 240 | ruby-debug-base (~> 0.10.4.0) |
183 | 241 | ruby-debug-base (0.10.4) |
184 | 242 | linecache (>= 0.3) |
185 | - ruby-debug-base19 (0.11.25) | |
186 | - columnize (>= 0.3.1) | |
187 | - linecache19 (>= 0.5.11) | |
188 | - ruby_core_source (>= 0.1.4) | |
189 | - ruby-debug19 (0.11.6) | |
190 | - columnize (>= 0.3.1) | |
191 | - linecache19 (>= 0.5.11) | |
192 | - ruby-debug-base19 (>= 0.11.19) | |
193 | - ruby-fogbugz (0.0.4) | |
243 | + ruby-fogbugz (0.1.1) | |
194 | 244 | crack |
195 | - typhoeus | |
196 | - ruby_core_source (0.1.5) | |
197 | - archive-tar-minitar (>= 0.5.2) | |
198 | - rubyzip (0.9.5) | |
199 | - term-ansicolor (1.0.7) | |
200 | - thor (0.14.6) | |
245 | + rubyzip (0.9.9) | |
246 | + selenium-webdriver (2.25.0) | |
247 | + childprocess (>= 0.2.5) | |
248 | + libwebsocket (~> 0.1.3) | |
249 | + multi_json (~> 1.0) | |
250 | + rubyzip | |
251 | + sprockets (2.1.3) | |
252 | + hike (~> 1.2) | |
253 | + rack (~> 1.0) | |
254 | + tilt (~> 1.1, != 1.3.0) | |
255 | + therubyracer (0.10.2) | |
256 | + libv8 (~> 3.3.10) | |
257 | + thin (1.4.1) | |
258 | + daemons (>= 1.0.9) | |
259 | + eventmachine (>= 0.12.6) | |
260 | + rack (>= 1.0.0) | |
261 | + thor (0.15.4) | |
262 | + tilt (1.3.3) | |
201 | 263 | treetop (1.4.10) |
202 | 264 | polyglot |
203 | 265 | polyglot (>= 0.3.1) |
204 | - typhoeus (0.2.4) | |
205 | - mime-types | |
206 | - mime-types | |
207 | - tzinfo (0.3.30) | |
208 | - unicorn (4.1.1) | |
209 | - kgio (~> 2.4) | |
266 | + tzinfo (0.3.33) | |
267 | + uglifier (1.2.7) | |
268 | + execjs (>= 0.3.0) | |
269 | + multi_json (~> 1.3) | |
270 | + unicorn (4.3.1) | |
271 | + kgio (~> 2.6) | |
210 | 272 | rack |
211 | - raindrops (~> 0.6) | |
273 | + raindrops (~> 0.7) | |
212 | 274 | useragent (0.3.2) |
213 | - warden (1.0.5) | |
275 | + warden (1.2.1) | |
214 | 276 | rack (>= 1.0) |
215 | - webmock (1.7.6) | |
216 | - addressable (~> 2.2, > 2.2.5) | |
277 | + webmock (1.8.7) | |
278 | + addressable (>= 2.2.7) | |
217 | 279 | crack (>= 0.1.7) |
280 | + xpath (0.1.4) | |
281 | + nokogiri (~> 1.3) | |
282 | + yajl-ruby (1.1.0) | |
218 | 283 | |
219 | 284 | PLATFORMS |
220 | 285 | ruby |
... | ... | @@ -222,35 +287,44 @@ PLATFORMS |
222 | 287 | DEPENDENCIES |
223 | 288 | SystemTimer |
224 | 289 | actionmailer_inline_css (~> 1.3.0) |
225 | - bson (= 1.3.1) | |
226 | - bson_ext (= 1.3.1) | |
290 | + bson (= 1.6.2) | |
291 | + bson_ext (= 1.6.2) | |
292 | + capistrano | |
293 | + capybara | |
227 | 294 | database_cleaner (~> 0.6.0) |
228 | - devise (~> 1.4.0) | |
295 | + debugger | |
296 | + devise (~> 1.5.3) | |
229 | 297 | email_spec |
230 | - fabrication | |
298 | + execjs | |
299 | + fabrication (~> 1.3.0) | |
231 | 300 | haml |
232 | - heroku | |
233 | 301 | hoptoad_notifier (~> 2.4) |
234 | 302 | htmlentities (~> 4.3.0) |
235 | 303 | inherited_resources |
236 | 304 | kaminari |
305 | + launchy | |
237 | 306 | lighthouse-api |
238 | - mongo (= 1.3.1) | |
239 | - mongoid (~> 2.2.2) | |
307 | + mongo (= 1.6.2) | |
308 | + mongoid (~> 2.4.10) | |
240 | 309 | mongoid_rails_migrations |
241 | - newrelic_rpm | |
242 | 310 | nokogiri |
243 | - octokit (= 0.6.4) | |
311 | + oa-core | |
312 | + octokit (~> 1.0.0) | |
313 | + omniauth-github | |
244 | 314 | oruen_redmine_client |
245 | 315 | pivotal-tracker |
246 | 316 | rack-ssl-enforcer |
247 | - rails (= 3.0.10) | |
317 | + rails (= 3.2.6) | |
318 | + rails_autolink (~> 1.0.9) | |
248 | 319 | ri_cal |
249 | 320 | rspec (~> 2.6) |
250 | 321 | rspec-rails (~> 2.6) |
251 | 322 | ruby-debug |
252 | - ruby-debug19 | |
253 | 323 | ruby-fogbugz |
324 | + therubyracer | |
325 | + thin | |
326 | + uglifier (>= 1.0.3) | |
254 | 327 | unicorn |
255 | 328 | useragent (~> 0.3.1) |
256 | 329 | webmock |
330 | + yajl-ruby | ... | ... |
README.md
1 | -Errbit [](http://travis-ci.org/errbit/errbit) | |
2 | -====== | |
1 | +# Errbit [![TravisCI][travis-img-url]][travis-ci-url] | |
2 | + | |
3 | +[travis-img-url]: https://secure.travis-ci.org/errbit/errbit.png?branch=master | |
4 | +[travis-ci-url]: http://travis-ci.org/errbit/errbit | |
5 | + | |
6 | +### The open source, self-hosted error catcher | |
3 | 7 | |
4 | -**The open source self-hosted error catcher** | |
5 | 8 | |
6 | 9 | Errbit is a tool for collecting and managing errors from other applications. |
7 | 10 | It is [Airbrake](http://airbrakeapp.com) (formerly known as Hoptoad) API compliant, |
8 | -so if you are already using Airbrake, you can just point hoptoad_notifier at your Errbit server. | |
11 | +so if you are already using Airbrake, you can just point the `airbrake` gem to your Errbit server. | |
12 | + | |
13 | + | |
14 | +<table> | |
15 | + <tr> | |
16 | + <td align="center"> | |
17 | + <a href="http://errbit.github.com/errbit/images/apps.png" target="_blank" title="Apps"> | |
18 | + <img src="http://errbit.github.com/errbit/images/apps_thumb.png" alt="Apps"> | |
19 | + </a> | |
20 | + <br /> | |
21 | + <em>Apps</em> | |
22 | + </td> | |
23 | + <td align="center"> | |
24 | + <a href="http://errbit.github.com/errbit/images/app_errors.png" target="_blank" title="Errors"> | |
25 | + <img src="http://errbit.github.com/errbit/images/app_errors_thumb.png" alt="Errors"> | |
26 | + </a> | |
27 | + <br /> | |
28 | + <em>Errors</em> | |
29 | + </td> | |
30 | + <td align="center"> | |
31 | + <a href="http://errbit.github.com/errbit/images/error_summary.png" target="_blank" title="Error Summary"> | |
32 | + <img src="http://errbit.github.com/errbit/images/error_summary_thumb.png" alt="Error Summary"> | |
33 | + </a> | |
34 | + <br /> | |
35 | + <em>Error Summary</em> | |
36 | + </td> | |
37 | + <td align="center"> | |
38 | + <a href="http://errbit.github.com/errbit/images/error_backtrace.png" target="_blank" title="Error Backtraces"> | |
39 | + <img src="http://errbit.github.com/errbit/images/error_backtrace_thumb.png" alt="Error Backtraces"> | |
40 | + </a> | |
41 | + <br /> | |
42 | + <em>Error Backtraces</em> | |
43 | + </td> | |
44 | + </tr> | |
45 | +</table> | |
46 | + | |
9 | 47 | |
10 | 48 | Errbit may be a good fit for you if: |
11 | 49 | |
... | ... | @@ -37,20 +75,20 @@ for you. Checkout [Airbrake](http://airbrakeapp.com) from the guys over at |
37 | 75 | |
38 | 76 | **Set up your local box or server(Ubuntu):** |
39 | 77 | |
40 | - 1. Install MongoDB. Follow the directions [here](http://www.mongodb.org/display/DOCS/Ubuntu+and+Debian+packages), then: | |
78 | + * Install MongoDB. Follow the directions [here](http://www.mongodb.org/display/DOCS/Ubuntu+and+Debian+packages), then: | |
41 | 79 | |
42 | 80 | ```bash |
43 | 81 | apt-get update |
44 | 82 | apt-get install mongodb |
45 | 83 | ``` |
46 | 84 | |
47 | - 2. Install libxml and libcurl | |
85 | + * Install libxml and libcurl | |
48 | 86 | |
49 | 87 | ```bash |
50 | 88 | apt-get install libxml2 libxml2-dev libxslt-dev libcurl4-openssl-dev |
51 | 89 | ``` |
52 | 90 | |
53 | - 3. Install Bundler | |
91 | + * Install Bundler | |
54 | 92 | |
55 | 93 | ```bash |
56 | 94 | gem install bundler |
... | ... | @@ -58,21 +96,21 @@ gem install bundler |
58 | 96 | |
59 | 97 | **Running Locally:** |
60 | 98 | |
61 | - 1. Install dependencies | |
99 | + * Install dependencies | |
62 | 100 | |
63 | 101 | ```bash |
64 | 102 | bundle install |
65 | 103 | ``` |
66 | 104 | |
67 | - 2. Bootstrap Errbit. This will copy over config.yml and also seed the database. | |
105 | + * Bootstrap Errbit. This will copy over config.yml and also seed the database. | |
68 | 106 | |
69 | 107 | ```bash |
70 | 108 | rake errbit:bootstrap |
71 | 109 | ``` |
72 | 110 | |
73 | - 3. Update the config.yml and mongoid.yml files with information about your environment | |
111 | + * Update the config.yml and mongoid.yml files with information about your environment | |
74 | 112 | |
75 | - 4. Start Server | |
113 | + * Start Server | |
76 | 114 | |
77 | 115 | ```bash |
78 | 116 | script/rails server |
... | ... | @@ -80,14 +118,14 @@ script/rails server |
80 | 118 | |
81 | 119 | **Deploying:** |
82 | 120 | |
83 | - 1. Bootstrap Errbit. This will copy over config.yml and also seed the database. | |
121 | + * Bootstrap Errbit. This will copy over config.yml and also seed the database. | |
84 | 122 | |
85 | 123 | ```bash |
86 | 124 | rake errbit:bootstrap |
87 | 125 | ``` |
88 | 126 | |
89 | - 2. Update the deploy.rb file with information about your server | |
90 | - 3. Setup server and deploy | |
127 | + * Update the deploy.rb file with information about your server | |
128 | + * Setup server and deploy | |
91 | 129 | |
92 | 130 | ```bash |
93 | 131 | cap deploy:setup deploy |
... | ... | @@ -95,18 +133,21 @@ cap deploy:setup deploy |
95 | 133 | |
96 | 134 | **Deploying to Heroku:** |
97 | 135 | |
98 | - 1. Clone the repository | |
136 | + * Clone the repository | |
99 | 137 | |
100 | 138 | ```bash |
101 | 139 | git clone http://github.com/errbit/errbit.git |
102 | 140 | ``` |
103 | 141 | |
104 | - 2. Create & configure for Heroku | |
142 | + * Create & configure for Heroku | |
105 | 143 | |
106 | 144 | ```bash |
107 | 145 | gem install heroku |
108 | 146 | heroku create example-errbit --stack cedar |
109 | -heroku addons:add mongohq:free | |
147 | +heroku addons:add mongolab:starter | |
148 | +cp -f config/mongoid.mongolab.yml config/mongoid.yml | |
149 | +git add -f config/mongoid.yml | |
150 | +git commit -m "Added mongoid config for Mongolab" | |
110 | 151 | heroku addons:add sendgrid:starter |
111 | 152 | heroku config:add HEROKU=true |
112 | 153 | heroku config:add ERRBIT_HOST=some-hostname.example.com |
... | ... | @@ -114,33 +155,129 @@ heroku config:add ERRBIT_EMAIL_FROM=example@example.com |
114 | 155 | git push heroku master |
115 | 156 | ``` |
116 | 157 | |
117 | - 3. Seed the DB (_NOTE_: No bootstrap task is used on Heroku!) | |
158 | + * Seed the DB (_NOTE_: No bootstrap task is used on Heroku!) | |
118 | 159 | |
119 | 160 | ```bash |
120 | 161 | heroku run rake db:seed |
121 | 162 | ``` |
122 | 163 | |
123 | - 4. If you are using a free database on Heroku, you may want to periodically clear resolved errors to free up space. | |
164 | + * If you are using a free database on Heroku, you may want to periodically clear resolved errors to free up space. | |
165 | + | |
166 | + * With the heroku-scheduler add-on (replacement for cron): | |
167 | + | |
168 | + ```bash | |
169 | + # Install the heroku scheduler add-on | |
170 | + heroku addons:add scheduler:standard | |
171 | + | |
172 | + # Go open the dashboard to schedule the job. You should use | |
173 | + # 'rake errbit:db:clear_resolved' as the task command, and schedule it | |
174 | + # at whatever frequency you like (once/day should work great). | |
175 | + heroku addons:open scheduler | |
176 | + ``` | |
177 | + | |
178 | + * With the cron add-on: | |
179 | + | |
180 | + ```bash | |
181 | + # Install the heroku cron addon, to clear resolved errors daily: | |
182 | + heroku addons:add cron:daily | |
183 | + ``` | |
184 | + | |
185 | + * Or clear resolved errors manually: | |
186 | + | |
187 | + ```bash | |
188 | + heroku rake errbit:db:clear_resolved | |
189 | + ``` | |
190 | + | |
191 | + * You may want to enable the deployment hook for heroku : | |
192 | + | |
193 | +```bash | |
194 | +heroku addons:add deployhooks:http --url="http://YOUR_ERRBIT_HOST/deploys.txt?api_key=YOUR_API_KEY" | |
195 | +``` | |
196 | + | |
197 | + * Enjoy! | |
198 | + | |
199 | + | |
200 | +Authentication | |
201 | +-------------- | |
202 | + | |
203 | +### Configuring GitHub authentication: | |
204 | + | |
205 | + * In `config/config.yml`, set `github_authentication` to `true` | |
206 | + * Register your instance of Errbit at: https://github.com/settings/applications | |
207 | + | |
208 | +If you hosted Errbit at errbit.example.com, you would fill in: | |
209 | + | |
210 | +<table> | |
211 | + <tr><th>URL:</th><td>http://errbit.example.com/</td></tr> | |
212 | + <tr><th>Callback URL:</th><td>http://errbit.example.com/users/auth/github</td></tr> | |
213 | +</table> | |
214 | + | |
215 | + * After you have registered your app, set `github_client_id` and `github_secret` | |
216 | + in `config/config.yml` with your app's Client ID and Secret key. | |
217 | + | |
218 | + | |
219 | +After you have followed these instructions, you will be able to **Sign in with GitHub** on the Login page. | |
220 | + | |
221 | +You will also be able to link your GitHub profile to your user account on your **Edit profile** page. | |
222 | + | |
223 | +If you have signed in with GitHub, or linked your GitHub profile, and the App has a GitHub repo configured, | |
224 | +then you will be able to create issues on GitHub. | |
225 | +You will still be able to create an issue on the App's configured issue tracker. | |
226 | + | |
227 | +You can change the requested account permissions by setting `github_access_scope` to: | |
228 | + | |
229 | +<table> | |
230 | + <tr><th>['repo'] </th><td>Allow creating issues for public and private repos.</td></tr> | |
231 | + <tr><th>['public_repo'] </th><td>Only allow creating issues for public repos.</td></tr> | |
232 | + <tr><th>[] </th><td>No permission to create issues on any repos.</td></tr> | |
233 | +</table> | |
234 | + | |
235 | + | |
236 | +### GitHub authentication when served on Heroku | |
237 | + | |
238 | +You will need to set up Heroku variables accordingly as described in [Configuring GitHub authentication](#configuring-github-authentication): | |
239 | + | |
240 | +* GITHUB_AUTHENTICATION | |
241 | + | |
242 | +```bash | |
243 | +heroku config:add GITHUB_AUTHENTICATION=true | |
244 | +``` | |
245 | + | |
246 | +* GITHUB_CLIENT_ID | |
124 | 247 | |
125 | 248 | ```bash |
126 | -# Install the heroku cron addon, to clear resolved errors daily: | |
127 | -heroku addons:add cron:daily | |
249 | +heroku config:add GITHUB_CLIENT_ID=the_client_id_provided_by_GitHub | |
250 | +``` | |
251 | + | |
252 | +* GITHUB_SECRET | |
253 | + | |
254 | +```bash | |
255 | +heroku config:add GITHUB_SECRET=the_secret_provided_by_GitHub | |
256 | +``` | |
257 | + | |
258 | +* GITHUB_ACCESS_SCOPE - set only one scope `repo` or `public_repo`. If you really need to put more than one, separate them with comma. | |
128 | 259 | |
129 | -# Or, clear resolved errors manually: | |
130 | -heroku rake errbit:db:clear_resolved | |
260 | +```bash | |
261 | +heroku config:add GITHUB_ACCESS_SCOPE=repo,public_repo | |
131 | 262 | ``` |
132 | 263 | |
133 | - 5. Enjoy! | |
264 | +__Note__: To avoid restarting your Heroku app 4 times you can set Heroku variables in a single command, i.e: | |
134 | 265 | |
266 | +```bash | |
267 | +heroku config:add GITHUB_AUTHENTICATION=true \ | |
268 | +GITHUB_CLIENT_ID=the_client_id_provided_by_GitHub \ | |
269 | +GITHUB_SECRET=the_secret_provided_by_GitHub \ | |
270 | +GITHUB_ACCESS_SCOPE=repo,public_repo | |
271 | +``` | |
135 | 272 | |
136 | -**Configuring LDAP authentication:** | |
273 | +### Configuring LDAP authentication: | |
137 | 274 | |
138 | - 1. In `config/config.yml`, set `user_has_username` to `true` | |
139 | - 2. Follow the instructions at https://github.com/cschiewek/devise_ldap_authenticatable | |
275 | + * In `config/config.yml`, set `user_has_username` to `true` | |
276 | + * Follow the instructions at https://github.com/cschiewek/devise_ldap_authenticatable | |
140 | 277 | to set up the devise_ldap_authenticatable gem. |
141 | 278 | |
142 | - 3. If you are authenticating by `username`, you will need to set the user's email | |
143 | - after authentication. You can do this by adding the following lines to `app/models/user.rb`: | |
279 | + * If you are authenticating by `username`, you will need to set the user's email manually | |
280 | + before authentication. You must add the following lines to `app/models/user.rb`: | |
144 | 281 | |
145 | 282 | ```ruby |
146 | 283 | before_save :set_ldap_email |
... | ... | @@ -151,7 +288,7 @@ heroku rake errbit:db:clear_resolved |
151 | 288 | |
152 | 289 | Upgrading |
153 | 290 | --------- |
154 | -*Note*: When upgrading Errbit, please run: | |
291 | +When upgrading Errbit, please run: | |
155 | 292 | |
156 | 293 | ```bash |
157 | 294 | git pull origin master # assuming origin is the github.com/errbit/errbit repo |
... | ... | @@ -161,6 +298,29 @@ rake db:migrate |
161 | 298 | If we change the way that data is stored, this will run any migrations to bring your database up to date. |
162 | 299 | |
163 | 300 | |
301 | +User information in error reports | |
302 | +--------------------------------- | |
303 | + | |
304 | +Errbit can now display information about the user who experienced an error. | |
305 | +This gives you the ability to ask the user for more information, | |
306 | +and let them know when you've fixed the bug. | |
307 | + | |
308 | +If you would like to include information about the current user in your error reports, | |
309 | +you can replace the `airbrake` gem in your Gemfile with `airbrake_user_attributes`, | |
310 | +which wraps the `airbrake` gem and injects user information. | |
311 | +It will inject information about the current user into the error report | |
312 | +if your Rails app's controller responds to a `#current_user` method. | |
313 | +The user's attributes are filtered to remove authentication fields. | |
314 | + | |
315 | +If user information is received with an error report, | |
316 | +it will be displayed under the *User Details* tab: | |
317 | + | |
318 | + | |
319 | + | |
320 | + | |
321 | +(This tab will be hidden if no user information is available.) | |
322 | + | |
323 | + | |
164 | 324 | Issue Trackers |
165 | 325 | -------------- |
166 | 326 | |
... | ... | @@ -186,13 +346,17 @@ Issue Trackers |
186 | 346 | * Account is the host of your mingle installation. i.e. **https://mingle.example.com** *note*: You should use SSL if possible. |
187 | 347 | * Errbit uses 'sign-in name' & password authentication. You may want to set up an **errbit** user with limited rights. |
188 | 348 | * Project id is the identifier of your project, i.e. **awesomeapp** for project at https://mingle.example.com/projects/awesomeapp |
189 | -* Card properties are comma separated key value pairs. You must specify a 'card_type', but anything else is optional. i.e. card_type = Defect, status = Open, priority = Essential | |
349 | +* Card properties are comma separated key value pairs. You must specify a 'card_type', but anything else is optional, e.g.: | |
350 | + | |
351 | +``` | |
352 | +card_type = Defect, status = Open, priority = Essential | |
353 | +``` | |
190 | 354 | |
191 | -**Github Issues Integration** | |
355 | +**GitHub Issues Integration** | |
192 | 356 | |
193 | 357 | * For 'Account/Repository', the account will either be a username or organization. i.e. **errbit/errbit** |
194 | -* If you are logged in on [Github](https://github.com), you can find your **API Token** on this page: [https://github.com/account/admin](https://github.com/account/admin). | |
195 | -* You will also need to provide the username that your API Token is connected to. | |
358 | +* You will also need to provide your username and password for your GitHub account. | |
359 | + * (We'd really appreciate it if you wanted to help us implement OAuth instead!) | |
196 | 360 | |
197 | 361 | |
198 | 362 | What if Errbit has an error? |
... | ... | @@ -201,23 +365,23 @@ What if Errbit has an error? |
201 | 365 | Errbit will log it's own errors to an internal app named **Self.Errbit**. |
202 | 366 | The **Self.Errbit** app will be automatically created whenever the first error happens. |
203 | 367 | |
204 | -If your Errbit instance has logged an error, we would appreciate a bug report on Github Issues. | |
368 | +If your Errbit instance has logged an error, we would appreciate a bug report on GitHub Issues. | |
205 | 369 | You can post this manually at [https://github.com/errbit/errbit/issues](https://github.com/errbit/errbit/issues), |
206 | -or you can set up the Github Issues tracker for your **Self.Errbit** app: | |
370 | +or you can set up the GitHub Issues tracker for your **Self.Errbit** app: | |
207 | 371 | |
208 | - 1. Go to the **Self.Errbit** app's edit page. If that app does not exist yet, go to the apps page and click **Add a new App** to create it. (You can also create it by running `rake airbrake:test`.) | |
372 | + * Go to the **Self.Errbit** app's edit page. If that app does not exist yet, go to the apps page and click **Add a new App** to create it. (You can also create it by running `rake airbrake:test`.) | |
209 | 373 | |
210 | - 2. In the **Issue Tracker** section, click **Github Issues**. | |
374 | + * In the **Issue Tracker** section, click **GitHub Issues**. | |
211 | 375 | |
212 | - 3. Fill in the **Account/Repository** field with **errbit/errbit**. | |
376 | + * Fill in the **Account/Repository** field with **errbit/errbit**. | |
213 | 377 | |
214 | - 4. Fill in the **Username** field with your github username. | |
378 | + * Fill in the **Username** field with your github username. | |
215 | 379 | |
216 | - 5. If you are logged in on [Github](https://github.com), you can find your **API Token** on this page: [https://github.com/account/admin](https://github.com/account/admin). | |
380 | + * If you are logged in on [GitHub](https://github.com), you can find your **API Token** on this page: [https://github.com/account/admin](https://github.com/account/admin). | |
217 | 381 | |
218 | - 6. Save the settings by clicking **Update App** (or **Add App**) | |
382 | + * Save the settings by clicking **Update App** (or **Add App**) | |
219 | 383 | |
220 | - 7. You can now easily post bug reports to Github Issues by clicking the **Create Issue** button on a **Self.Errbit** error. | |
384 | + * You can now easily post bug reports to GitHub Issues by clicking the **Create Issue** button on a **Self.Errbit** error. | |
221 | 385 | |
222 | 386 | |
223 | 387 | TODO | ... | ... |
Rakefile
1.54 KB
1.46 KB
1.45 KB
317 Bytes
4.78 KB
4.78 KB
4.58 KB
2.3 KB
2.3 KB
2.03 KB
1.57 KB
1.61 KB
1.57 KB
1.7 KB
2.41 KB
2.41 KB
1.96 KB
799 Bytes
818 Bytes
1.5 KB
1.52 KB
1.56 KB
1.47 KB
1.52 KB
1.52 KB
1.42 KB
... | ... | @@ -0,0 +1,15 @@ |
1 | +//= require jquery | |
2 | +//= require underscore-1.1.6 | |
3 | +//= require rails | |
4 | +//= require form | |
5 | +//= require jquery.pjax | |
6 | +//= require jquery.alerts | |
7 | +//= require rails.alerts | |
8 | +//= require errbit | |
9 | +//= require apps.show | |
10 | +//= require_self | |
11 | + | |
12 | +// Allow any gems named 'errbit_*' to require their own assets | |
13 | +<% Gem.loaded_specs.keys.grep(/^errbit_/).each do |plugin| | |
14 | + require_asset(plugin) rescue Sprockets::FileNotFound | |
15 | +end %> | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +$(function() { | |
2 | + $("#watchers_toggle").click(function() { | |
3 | + $("#watchers_div").slideToggle("slow"); | |
4 | + }); | |
5 | + $("#repository_toggle").click(function() { | |
6 | + $("#repository_div").slideToggle("slow"); | |
7 | + }); | |
8 | + $("#deploys_toggle").click(function() { | |
9 | + $("#deploys_div").slideToggle("slow"); | |
10 | + }); | |
11 | +}); | ... | ... |
... | ... | @@ -0,0 +1,139 @@ |
1 | +// App JS | |
2 | + | |
3 | +$(function() { | |
4 | + | |
5 | + var currentTab = "summary"; | |
6 | + | |
7 | + function init() { | |
8 | + | |
9 | + activateTabbedPanels(); | |
10 | + | |
11 | + activateSelectableRows(); | |
12 | + | |
13 | + toggleProblemsCheckboxes(); | |
14 | + | |
15 | + bindRequiredPasswordMarks(); | |
16 | + | |
17 | + $('#watcher_name').live("click", function() { | |
18 | + $(this).closest('form').find('.show').removeClass('show'); | |
19 | + $('#app_watchers_attributes_0_user_id').addClass('show'); | |
20 | + }); | |
21 | + | |
22 | + $('#watcher_email').live("click", function() { | |
23 | + $(this).closest('form').find('.show').removeClass('show'); | |
24 | + $('#app_watchers_attributes_0_email').addClass('show'); | |
25 | + }); | |
26 | + | |
27 | + $('a.copy_config').live("click", function() { | |
28 | + $('select.choose_other_app').show().focus(); | |
29 | + }); | |
30 | + | |
31 | + $('select.choose_other_app').live("change", function() { | |
32 | + var loc = window.location; | |
33 | + window.location.href = loc.protocol + "//" + loc.host + loc.pathname + | |
34 | + "?copy_attributes_from=" + $(this).val(); | |
35 | + }); | |
36 | + | |
37 | + $('input[type=submit][data-action]').click(function() { | |
38 | + $(this).closest('form').attr('action', $(this).attr('data-action')); | |
39 | + }); | |
40 | + | |
41 | + $('.notice-pagination').each(function() { | |
42 | + $('.notice-pagination a').pjax('#content', { timeout: 2000}); | |
43 | + $('#content').bind('pjax:start', function() { | |
44 | + $('.notice-pagination-loader').css("visibility", "visible"); | |
45 | + currentTab = $('.tab-bar ul li a.button.active').attr('rel'); | |
46 | + }); | |
47 | + | |
48 | + $('#content').bind('pjax:end', function() { | |
49 | + activateTabbedPanels(); | |
50 | + }); | |
51 | + }); | |
52 | + } | |
53 | + | |
54 | + function activateTabbedPanels() { | |
55 | + $('.tab-bar a').each(function(){ | |
56 | + var tab = $(this); | |
57 | + var panel = $('#'+tab.attr('rel')); | |
58 | + panel.addClass('panel'); | |
59 | + panel.find('h3').hide(); | |
60 | + }); | |
61 | + | |
62 | + $('.tab-bar a').click(function(){ | |
63 | + activateTab($(this)); | |
64 | + return(false); | |
65 | + }); | |
66 | + activateTab($('.tab-bar ul li a.button[rel=' + currentTab + ']')); | |
67 | + } | |
68 | + | |
69 | + function activateTab(tab) { | |
70 | + tab = $(tab); | |
71 | + var panel = $('#'+tab.attr('rel')); | |
72 | + | |
73 | + tab.closest('.tab-bar').find('a.active').removeClass('active'); | |
74 | + tab.addClass('active'); | |
75 | + | |
76 | + // If clicking into 'backtrace' tab, hide external backtrace | |
77 | + if (tab.attr('rel') == "backtrace") { hide_external_backtrace(); } | |
78 | + | |
79 | + $('.panel').hide(); | |
80 | + panel.show(); | |
81 | + } | |
82 | + | |
83 | + function toggleProblemsCheckboxes() { | |
84 | + var checkboxToggler = $('#toggle_problems_checkboxes'); | |
85 | + | |
86 | + checkboxToggler.live("click", function() { | |
87 | + $('input[name^="problems"]').each(function() { | |
88 | + this.checked = checkboxToggler.get(0).checked; | |
89 | + }); | |
90 | + }); | |
91 | + } | |
92 | + | |
93 | + function activateSelectableRows() { | |
94 | + $('.selectable tr').click(function(event) { | |
95 | + if(!_.include(['A', 'INPUT', 'BUTTON', 'TEXTAREA'], event.target.nodeName)) { | |
96 | + var checkbox = $(this).find('input[name="problems[]"]'); | |
97 | + checkbox.attr('checked', !checkbox.is(':checked')); | |
98 | + } | |
99 | + }); | |
100 | + } | |
101 | + | |
102 | + function bindRequiredPasswordMarks() { | |
103 | + $('#user_github_login').keyup(function(event) { | |
104 | + toggleRequiredPasswordMarks(this) | |
105 | + }); | |
106 | + } | |
107 | + | |
108 | + function toggleRequiredPasswordMarks(input) { | |
109 | + if($(input).val() == "") { | |
110 | + $('#user_password').parent().attr('class', 'required') | |
111 | + $('#user_password_confirmation').parent().attr('class', 'required') | |
112 | + } else { | |
113 | + $('#user_password').parent().attr('class', '') | |
114 | + $('#user_password_confirmation').parent().attr('class', '') | |
115 | + } | |
116 | + } | |
117 | + | |
118 | + toggleRequiredPasswordMarks(); | |
119 | + | |
120 | + function hide_external_backtrace() { | |
121 | + $('tr.toggle_external_backtrace').hide(); | |
122 | + $('td.backtrace_separator').show(); | |
123 | + } | |
124 | + function show_external_backtrace() { | |
125 | + $('tr.toggle_external_backtrace').show(); | |
126 | + $('td.backtrace_separator').hide(); | |
127 | + } | |
128 | + // Show external backtrace lines when clicking separator | |
129 | + $('td.backtrace_separator span').live('click', show_external_backtrace); | |
130 | + // Hide external backtrace on page load | |
131 | + hide_external_backtrace(); | |
132 | + | |
133 | + $('.head a.show_tail').click(function(e) { | |
134 | + $(this).hide().closest('.head_and_tail').find('.tail').show(); | |
135 | + e.preventDefault(); | |
136 | + }); | |
137 | + | |
138 | + init(); | |
139 | +}); | ... | ... |
... | ... | @@ -0,0 +1,117 @@ |
1 | +$(function(){ | |
2 | + activateNestedForms(); | |
3 | + activateCheckboxHooks(); | |
4 | + | |
5 | + if($('div.watcher.nested').length) | |
6 | + activateTypeSelector('watcher'); | |
7 | + | |
8 | + if($('div.issue_tracker.nested').length) | |
9 | + activateTypeSelector('issue_tracker', 'tracker_params'); | |
10 | + | |
11 | + $('body').addClass('has-js'); | |
12 | + $('.label_radio').click(function(){ | |
13 | + activateLabelIcons(); | |
14 | + }); | |
15 | + activateLabelIcons(); | |
16 | +}); | |
17 | + | |
18 | +function activateNestedForms() { | |
19 | + $('.nested-wrapper').each(function(){ | |
20 | + var wrapper = $(this); | |
21 | + | |
22 | + makeNestedItemsDestroyable(wrapper); | |
23 | + | |
24 | + var addLink = $('<a/>').text('add another').addClass('add-nested'); | |
25 | + addLink.click(appendNestedItem); | |
26 | + wrapper.append(addLink); | |
27 | + }); | |
28 | + $('.nested a.remove-nested').live('click',removeNestedItem); | |
29 | +} | |
30 | + | |
31 | +function makeNestedItemsDestroyable(wrapper) { | |
32 | + wrapper.find('.nested').each(function(){ | |
33 | + var nestedItem = $(this); | |
34 | + var destroyLink = $('<a/>').text('remove').addClass('remove-nested'); | |
35 | + destroyLink.css('float','right'); | |
36 | + nestedItem.find('label').first().before(destroyLink); | |
37 | + }) | |
38 | +} | |
39 | + | |
40 | +function appendNestedItem() { | |
41 | + var addLink = $(this); | |
42 | + var nestedItem = addLink.parent().find('.nested').first().clone().show(); | |
43 | + var timestamp = new Date(); | |
44 | + timestamp = timestamp.valueOf(); | |
45 | + | |
46 | + nestedItem.find('input, select').each(function(){ | |
47 | + var input = $(this); | |
48 | + input.attr('id', input.attr('id').replace(/([_\[])\d+([\]_])/,'$1'+timestamp+'$2')); | |
49 | + input.attr('name', input.attr('name').replace(/([_\[])\d+([\]_])/,'$1'+timestamp+'$2')); | |
50 | + if(input.attr('type') != 'radio') | |
51 | + input.val(''); | |
52 | + }); | |
53 | + nestedItem.find('label').each(function(){ | |
54 | + var label = $(this); | |
55 | + label.attr('for', label.attr('for').replace(/([_\[])\d+([\]_])/,'$1'+timestamp+'$2')); | |
56 | + }); | |
57 | + addLink.before(nestedItem); | |
58 | +} | |
59 | + | |
60 | +function removeNestedItem() { | |
61 | + var destroyLink = $(this); | |
62 | + var nestedItem = destroyLink.closest('.nested'); | |
63 | + var inputNameExample = nestedItem.find('input').first().attr('name'); | |
64 | + var idFieldName = inputNameExample.replace(/\[[^\]]*\]$/,'[id]'); | |
65 | + if($("input[name='"+idFieldName+"']").length) { | |
66 | + var destroyFlagName = inputNameExample.replace(/\[[^\]]*\]$/,'[_destroy]') | |
67 | + var destroyFlag = $('<input/>').attr('name',destroyFlagName).attr('type','hidden').val('true'); | |
68 | + $("input[name='"+idFieldName+"']").after(destroyFlag); | |
69 | + } | |
70 | + nestedItem.hide(); | |
71 | +} | |
72 | + | |
73 | + | |
74 | +function activateTypeSelector(field_class, section_class) { | |
75 | + var section_class = section_class || field_class+"_params"; // section_class can be deduced if not given | |
76 | + // disable all inactive tabs to avoid sending its values on server | |
77 | + $('div.'+field_class+' > div.'+section_class).not('.chosen').find('input') | |
78 | + .attr('disabled','disabled').val(''); | |
79 | + | |
80 | + $('div.'+field_class+' input[name*=type]').live('click', function(){ | |
81 | + // Look for section in 'data-section', and fall back to 'value' | |
82 | + var chosen = $(this).data("section") || $(this).val(); | |
83 | + var wrapper = $(this).closest('.nested'); | |
84 | + wrapper.find('div.chosen.'+section_class).removeClass('chosen').find('input').attr('disabled','disabled'); | |
85 | + wrapper.find('div.'+section_class+'.'+chosen).addClass('chosen').find('input').removeAttr('disabled'); | |
86 | + }); | |
87 | +} | |
88 | + | |
89 | + | |
90 | +function activateCheckboxHooks() { | |
91 | + // Hooks to hide/show content when a checkbox is clicked | |
92 | + $('input[type="checkbox"][data-hide-when-checked]').each(function(){ | |
93 | + $(this).change(function(){ | |
94 | + el = $($(this).data('hide-when-checked')); | |
95 | + $(this).attr('checked') ? el.hide() : el.show(); | |
96 | + }); | |
97 | + }); | |
98 | + $('input[type="checkbox"][data-show-when-checked]').each(function(){ | |
99 | + $(this).change(function(){ | |
100 | + el = $($(this).data('show-when-checked')); | |
101 | + $(this).attr('checked') ? el.show() : el.hide(); | |
102 | + }); | |
103 | + }); | |
104 | +} | |
105 | + | |
106 | + | |
107 | +function activateLabelIcons() { | |
108 | + if ($('.label_radio input').length) { | |
109 | + $('.label_radio').each(function(){ | |
110 | + $(this).removeClass('r_on'); | |
111 | + }); | |
112 | + $('.label_radio input:checked').each(function(){ | |
113 | + $(this).parent('label').addClass('r_on'); | |
114 | + }); | |
115 | + }; | |
116 | +}; | |
117 | + | ... | ... |
... | ... | @@ -0,0 +1,230 @@ |
1 | +// jQuery Alert Dialogs Plugin | |
2 | +// | |
3 | +// Version 1.2 | |
4 | +// | |
5 | +// Cory S.N. LaViska | |
6 | +// A Beautiful Site (http://abeautifulsite.net/) | |
7 | +// 14 May 2009 | |
8 | +// | |
9 | +// Visit http://abeautifulsite.net/notebook/87 for more information | |
10 | +// | |
11 | +// Usage: | |
12 | +// $.jAlert( message, [title, callback] ) | |
13 | +// $.jConfirm( message, [title, callback] ) | |
14 | +// $.jPrompt( message, [value, title, callback] ) | |
15 | +// | |
16 | +// History: | |
17 | +// | |
18 | +// 1.00 - Released (29 December 2008) | |
19 | +// | |
20 | +// 1.01 - Fixed bug where unbinding would destroy all resize events | |
21 | +// | |
22 | +// 1.2 - global methods removed. | |
23 | +// | |
24 | +// License: | |
25 | +// | |
26 | +// This plugin is dual-licensed under the GNU General Public License and the MIT License and | |
27 | +// is copyright 2008 A Beautiful Site, LLC. | |
28 | +// | |
29 | +(function($) { | |
30 | + | |
31 | + $.alerts = { | |
32 | + | |
33 | + // These properties can be read/written by accessing $.alerts.propertyName from your scripts at any time | |
34 | + | |
35 | + verticalOffset: -75, // vertical offset of the dialog from center screen, in pixels | |
36 | + horizontalOffset: 0, // horizontal offset of the dialog from center screen, in pixels/ | |
37 | + repositionOnResize: true, // re-centers the dialog on window resize | |
38 | + overlayOpacity: 0.01, // transparency level of overlay | |
39 | + overlayColor: '#FFF', // base color of overlay | |
40 | + draggable: true, // make the dialogs draggable (requires UI Draggables plugin) | |
41 | + okButton: ' OK ', // text for the OK button | |
42 | + cancelButton: ' Cancel ', // text for the Cancel button | |
43 | + dialogClass: null, // if specified, this class will be applied to all dialogs | |
44 | + titles: { | |
45 | + alert: 'Alert', | |
46 | + confirm: 'Confirm', | |
47 | + prompt: 'Prompt' | |
48 | + }, | |
49 | + | |
50 | + // Public methods | |
51 | + | |
52 | + alert: function(message, title, callback) { | |
53 | + if (! title) title = $.alerts.titles.alert; | |
54 | + $.alerts._show(title, message, null, 'alert', function(result) { | |
55 | + if (callback) callback(result); | |
56 | + }); | |
57 | + }, | |
58 | + | |
59 | + confirm: function(message, title, callback) { | |
60 | + if (! title) title = $.alerts.titles.confirm; | |
61 | + $.alerts._show(title, message, null, 'confirm', function(result) { | |
62 | + if (callback) callback(result); | |
63 | + }); | |
64 | + }, | |
65 | + | |
66 | + prompt: function(message, value, title, callback) { | |
67 | + if (! title) title = $.alerts.titles.prompt; | |
68 | + $.alerts._show(title, message, value, 'prompt', function(result) { | |
69 | + if(callback) callback(result); | |
70 | + }); | |
71 | + }, | |
72 | + | |
73 | + // Private methods | |
74 | + | |
75 | + _show: function(title, msg, value, type, callback) { | |
76 | + | |
77 | + $.alerts._hide(); | |
78 | + $.alerts._overlay('show'); | |
79 | + | |
80 | + $("BODY").append( | |
81 | + '<div id="popup_container">' + | |
82 | + '<h1 id="popup_title"></h1>' + | |
83 | + '<div id="popup_content">' + | |
84 | + '<div id="popup_message"></div>' + | |
85 | + '</div>' + | |
86 | + '</div>'); | |
87 | + | |
88 | + if( $.alerts.dialogClass ) $("#popup_container").addClass($.alerts.dialogClass); | |
89 | + | |
90 | + // IE6 Fix | |
91 | + var pos = ($.browser.msie && parseInt($.browser.version, 10) <= 6 ) ? 'absolute' : 'fixed'; | |
92 | + | |
93 | + $("#popup_container").css({ | |
94 | + position: pos, | |
95 | + zIndex: 99999, | |
96 | + padding: 0, | |
97 | + margin: 0 | |
98 | + }); | |
99 | + | |
100 | + $("#popup_title").text(title); | |
101 | + $("#popup_content").addClass(type); | |
102 | + $("#popup_message").text(msg); | |
103 | + $("#popup_message").html( $("#popup_message").text().replace(/\n/g, '<br />') ); | |
104 | + | |
105 | + $("#popup_container").css({ | |
106 | + minWidth: $("#popup_container").outerWidth(), | |
107 | + maxWidth: $("#popup_container").outerWidth() | |
108 | + }); | |
109 | + | |
110 | + $.alerts._reposition(); | |
111 | + $.alerts._maintainPosition(true); | |
112 | + | |
113 | + switch( type ) { | |
114 | + case 'alert': | |
115 | + $("#popup_message").after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /></div>'); | |
116 | + $("#popup_ok").click( function() { | |
117 | + $.alerts._hide(); | |
118 | + callback(true); | |
119 | + }); | |
120 | + $("#popup_ok").focus().keypress( function(e) { | |
121 | + if( e.keyCode == 13 || e.keyCode == 27 ) $("#popup_ok").trigger('click'); | |
122 | + }); | |
123 | + break; | |
124 | + case 'confirm': | |
125 | + $("#popup_message").after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /> <input type="button" value="' + $.alerts.cancelButton + '" id="popup_cancel" /></div>'); | |
126 | + $("#popup_ok").click( function() { | |
127 | + $.alerts._hide(); | |
128 | + if( callback ) callback(true); | |
129 | + }); | |
130 | + $("#popup_cancel").click( function() { | |
131 | + $.alerts._hide(); | |
132 | + if( callback ) callback(false); | |
133 | + }); | |
134 | + $("#popup_ok").focus(); | |
135 | + $("#popup_ok, #popup_cancel").keypress( function(e) { | |
136 | + if( e.keyCode == 13 ) $("#popup_ok").trigger('click'); | |
137 | + if( e.keyCode == 27 ) $("#popup_cancel").trigger('click'); | |
138 | + }); | |
139 | + break; | |
140 | + case 'prompt': | |
141 | + $("#popup_message").append('<br /><input type="text" size="30" id="popup_prompt" />').after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /> <input type="button" value="' + $.alerts.cancelButton + '" id="popup_cancel" /></div>'); | |
142 | + $("#popup_prompt").width( $("#popup_message").width() ); | |
143 | + $("#popup_ok").click( function() { | |
144 | + var val = $("#popup_prompt").val(); | |
145 | + $.alerts._hide(); | |
146 | + if( callback ) callback( val ); | |
147 | + }); | |
148 | + $("#popup_cancel").click( function() { | |
149 | + $.alerts._hide(); | |
150 | + if( callback ) callback( null ); | |
151 | + }); | |
152 | + $("#popup_prompt, #popup_ok, #popup_cancel").keypress( function(e) { | |
153 | + if( e.keyCode == 13 ) $("#popup_ok").trigger('click'); | |
154 | + if( e.keyCode == 27 ) $("#popup_cancel").trigger('click'); | |
155 | + }); | |
156 | + if( value ) $("#popup_prompt").val(value); | |
157 | + $("#popup_prompt").focus().select(); | |
158 | + break; | |
159 | + default: break; | |
160 | + } | |
161 | + | |
162 | + // Make draggable | |
163 | + if ($.alerts.draggable && $.fn.draggable) { | |
164 | + $("#popup_container").draggable({ handle: $("#popup_title") }); | |
165 | + $("#popup_title").css({ cursor: 'move' }); | |
166 | + } | |
167 | + }, | |
168 | + | |
169 | + _hide: function() { | |
170 | + $("#popup_container").remove(); | |
171 | + $.alerts._overlay('hide'); | |
172 | + $.alerts._maintainPosition(false); | |
173 | + }, | |
174 | + | |
175 | + _overlay: function(status) { | |
176 | + switch( status ) { | |
177 | + case 'show': | |
178 | + $.alerts._overlay('hide'); | |
179 | + $("BODY").append('<div id="popup_overlay"></div>'); | |
180 | + $("#popup_overlay").css({ | |
181 | + position: 'absolute', | |
182 | + zIndex: 99998, | |
183 | + top: '0px', | |
184 | + left: '0px', | |
185 | + width: '100%', | |
186 | + height: $(document).height(), | |
187 | + background: $.alerts.overlayColor, | |
188 | + opacity: $.alerts.overlayOpacity | |
189 | + }); | |
190 | + break; | |
191 | + case 'hide': | |
192 | + $("#popup_overlay").remove(); | |
193 | + break; | |
194 | + default: break; | |
195 | + } | |
196 | + }, | |
197 | + | |
198 | + _reposition: function() { | |
199 | + var top = (($(window).height() / 2) - ($("#popup_container").outerHeight() / 2)) + $.alerts.verticalOffset; | |
200 | + var left = (($(window).width() / 2) - ($("#popup_container").outerWidth() / 2)) + $.alerts.horizontalOffset; | |
201 | + if( top < 0 ) top = 0; | |
202 | + if( left < 0 ) left = 0; | |
203 | + | |
204 | + // IE6 fix | |
205 | + if( $.browser.msie && parseInt($.browser.version, 10) <= 6 ) top = top + $(window).scrollTop(); | |
206 | + | |
207 | + $("#popup_container").css({ | |
208 | + top: top + 'px', | |
209 | + left: left + 'px' | |
210 | + }); | |
211 | + $("#popup_overlay").height( $(document).height() ); | |
212 | + }, | |
213 | + | |
214 | + _maintainPosition: function(status) { | |
215 | + if( $.alerts.repositionOnResize ) { | |
216 | + switch(status) { | |
217 | + case true: | |
218 | + $(window).bind('resize', $.alerts._reposition); | |
219 | + break; | |
220 | + case false: | |
221 | + $(window).unbind('resize', $.alerts._reposition); | |
222 | + break; | |
223 | + default: break; | |
224 | + } | |
225 | + } | |
226 | + } | |
227 | + | |
228 | + }; | |
229 | + | |
230 | +})(jQuery); | |
0 | 231 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +/*! | |
2 | + * jQuery JavaScript Library v1.6.2 | |
3 | + * http://jquery.com/ | |
4 | + * | |
5 | + * Copyright 2011, John Resig | |
6 | + * Dual licensed under the MIT or GPL Version 2 licenses. | |
7 | + * http://jquery.org/license | |
8 | + * | |
9 | + * Includes Sizzle.js | |
10 | + * http://sizzlejs.com/ | |
11 | + * Copyright 2011, The Dojo Foundation | |
12 | + * Released under the MIT, BSD, and GPL Licenses. | |
13 | + * | |
14 | + * Date: Thu Jun 30 14:16:56 2011 -0400 | |
15 | + */ | |
16 | +(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function bZ(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bY(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bC.test(a)?d(a,e):bY(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)bY(a+"["+e+"]",b[e],c,d);else d(a,b)}function bX(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bR,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bX(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bX(a,c,d,e,"*",g));return l}function bW(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bN),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bA(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bv:bw;if(d>0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bg(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function W(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(R.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(x,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b){if(H)return H.call(b,a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g="done fail isResolved isRejected promise then always pipe".split(" "),h=[].slice;f.extend({_Deferred:function(){var a=[],b,c,d,e={done:function(){if(!d){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=f.type(i),j==="array"?e.done.apply(e,i):j==="function"&&a.push(i);k&&e.resolveWith(k[0],k[1])}return this},resolveWith:function(e,f){if(!d&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(e,f)}finally{b=[e,f],c=0}}return this},resolve:function(){e.resolveWith(this,arguments);return this},isResolved:function(){return!!c||!!b},cancel:function(){d=1,a=[];return this}};return e},Deferred:function(a){var b=f._Deferred(),c=f._Deferred(),d;f.extend(b,{then:function(a,c){b.done(a).fail(c);return this},always:function(){return b.done.apply(b,arguments).fail.apply(this,arguments)},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,pipe:function(a,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[c,"reject"]},function(a,c){var e=c[0],g=c[1],h;f.isFunction(e)?b[a](function(){h=e.apply(this,arguments),h&&f.isFunction(h.promise)?h.promise().then(d.resolve,d.reject):d[g](h)}):b[a](d[g])})}).promise()},promise:function(a){if(a==null){if(d)return d;d=a={}}var c=g.length;while(c--)a[g[c]]=b[g[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c<d;c++)b[c]&&f.isFunction(b[c].promise)?b[c].promise().then(i(c),g.reject):--e;e||g.resolveWith(g,b)}else g!==a&&g.resolveWith(g,d?[a]:[]);return g.promise()}}),f.support=function(){var a=c.createElement("div"),b=c.documentElement,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;a.setAttribute("className","t"),a.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h<i;h++)g=e[h].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),k(this[0],g,d[g]))}}return d}if(typeof a=="object")return this.each(function(){f.data(this,a)});var j=a.split(".");j[1]=j[1]?"."+j[1]:"";if(c===b){d=this.triggerHandler("getData"+j[1]+"!",[j[0]]),d===b&&this.length&&(d=f.data(this[0],a),d=k(this[0],a,d));return d===b&&j[1]?this.data(j[0]):d}return this.each(function(){var b=f(this),d=[j[0],c];b.triggerHandler("setData"+j[1]+"!",d),f.data(this,a,c),b.triggerHandler("changeData"+j[1]+"!",d)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,c){a&&(c=(c||"fx")+"mark",f.data(a,c,(f.data(a,c,b,!0)||0)+1,!0))},_unmark:function(a,c,d){a!==!0&&(d=c,c=a,a=!1);if(c){d=d||"fx";var e=d+"mark",g=a?0:(f.data(c,e,b,!0)||1)-1;g?f.data(c,e,g,!0):(f.removeData(c,e,!0),m(c,d,"mark"))}},queue:function(a,c,d){if(a){c=(c||"fx")+"queue";var e=f.data(a,c,b,!0);d&&(!e||f.isArray(d)?e=f.data(a,c,f.makeArray(d),!0):e.push(d));return e||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e;d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),d.call(a,function(){f.dequeue(a,b)})),c.length||(f.removeData(a,b+"queue",!0),m(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){f.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f._Deferred(),!0))h++,l.done(m);m();return d.promise()}});var n=/[\n\t\r]/g,o=/\s+/,p=/\r/g,q=/^(?:button|input)$/i,r=/^(?:button|input|object|select|textarea)$/i,s=/^a(?:rea)?$/i,t=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,u=/\:|^on/,v,w;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(o);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(o);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(n," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(o);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if((" "+this[c].className+" ").replace(n," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h<i;h++){var j=e[h];if(j.selected&&(f.support.optDisabled?!j.disabled:j.getAttribute("disabled")===null)&&(!j.parentNode.disabled||!f.nodeName(j.parentNode,"optgroup"))){b=f(j).val();if(g)return b;d.push(b)}}if(g&&!d.length&&e.length)return f(e[c]).val();return d},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))f.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=f.event.special[h]||{};for(j=e||0;j<p.length;j++){q=p[j];if(d.guid===q.guid){if(l||n.test(q.namespace))e==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(e!=null)break}}if(p.length===0||e!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&f.removeEvent(a,h,s.handle),g=null,delete t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i. | |
17 | +shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,N(a.origType,a.selector),f.extend({},a,{handler:M,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,N(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?E:D):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=E;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=E;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=E,this.stopPropagation()},isDefaultPrevented:D,isPropagationStopped:D,isImmediatePropagationStopped:D};var F=function(a){var b=a.relatedTarget,c=!1,d=a.type;a.type=a.data,b!==this&&(b&&(c=f.contains(this,b)),c||(f.event.handle.apply(this,arguments),a.type=d))},G=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?G:F,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?G:F)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=b.type;(c==="submit"||c==="image")&&f(b).closest("form").length&&K("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=b.type;(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&K("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var H,I=function(a){var b=a.type,c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i<j;i++)f.event.add(this[i],a,g,d);return this}}),f.fn.extend({unbind:function(a,b){if(typeof a=="object"&&!a.preventDefault)for(var c in a)this.unbind(c,a[c]);else for(var d=0,e=this.length;d<e;d++)f.event.remove(this[d],a,b);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f.data(this,"lastToggle"+a.guid)||0)%d;f.data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var L={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};f.each(["live","die"],function(a,c){f.fn[c]=function(a,d,e,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:f(this.context);if(typeof a=="object"&&!a.preventDefault){for(var o in a)n[c](o,d,a[o],m);return this}if(c==="die"&&!a&&g&&g.charAt(0)==="."){n.unbind(g);return this}if(d===!1||f.isFunction(d))e=d||D,d=b;a=(a||"").split(" ");while((h=a[i++])!=null){j=x.exec(h),k="",j&&(k=j[0],h=h.replace(x,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,L[h]?(a.push(L[h]+k),h=h+k):h=(L[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)f.event.add(n[p],"live."+N(h,m),{data:d,selector:m,handler:e,origType:h,origHandler:e,preType:l});else n.unbind("live."+N(h,m),e)}return this}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(!f)g=o=!0;else if(f===!0)continue}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("parentNode",b,f,a,e,c)},"~":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("previousSibling",b,f,a,e,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c<f;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){if(a===b){g=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};f.find=k,f.expr=k.selectors,f.expr[":"]=f.expr.filters,f.unique=k.uniqueSort,f.text=k.getText,f.isXMLDoc=k.isXML,f.contains=k.contains}();var O=/Until$/,P=/^(?:parents|prevUntil|prevAll)/,Q=/,/,R=/^.[^:#\[\.,]*$/,S=Array.prototype.slice,T=f.expr.match.POS,U={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(W(this,a,!1),"not",a)},filter:function(a){return this.pushStack(W(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d<e;d++)i=a[d],j[i]||(j[i]=T.test(i)?f(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(l?l.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/<tbody/i,ba=/<|&#?\w+;/,bb=/<(?:script|object|embed|option|style)/i,bc=/checked\s*(?:[^=]|=\s*.checked.)/i,bd=/\/(java|ecma)script/i,be=/^\s*<!(?:\[CDATA\[|\-\-)/,bf={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bc.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bg(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bm)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i;b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof a[0]=="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!bb.test(a[0])&&(f.support.checkClone||!bc.test(a[0]))&&(g=!0,h=f.fragments[a[0]],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[a[0]]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j | |
18 | +)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1></$2>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bl(k[i]);else bl(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||bd.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bn=/alpha\([^)]*\)/i,bo=/opacity=([^)]*)/,bp=/([A-Z]|^ms)/g,bq=/^-?\d+(?:px)?$/i,br=/^-?\d/,bs=/^[+\-]=/,bt=/[^+\-\.\de]+/g,bu={position:"absolute",visibility:"hidden",display:"block"},bv=["Left","Right"],bw=["Top","Bottom"],bx,by,bz;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bx(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d;if(h==="number"&&isNaN(d)||d==null)return;h==="string"&&bs.test(d)&&(d=+d.replace(bt,"")+parseFloat(f.css(a,c)),h="number"),h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bx)return bx(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bA(a,b,d);f.swap(a,bu,function(){e=bA(a,b,d)});return e}},set:function(a,b){if(!bq.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cs(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cr("hide",3),a,b,c);for(var d=0,e=this.length;d<e;d++)if(this[d].style){var g=f.css(this[d],"display");g!=="none"&&!f._data(this[d],"olddisplay")&&f._data(this[d],"olddisplay",g)}for(d=0;d<e;d++)this[d].style&&(this[d].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cr("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return this[e.queue===!1?"each":"queue"](function(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(f.support.inlineBlockNeedsLayout?(j=cs(this.nodeName),j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)):this.style.display="inline-block"))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)k=new f.fx(this,b,i),h=a[i],cj.test(h)?k[h==="toggle"?d?"show":"hide":h]():(l=ck.exec(h),m=k.cur(),l?(n=parseFloat(l[2]),o=l[3]||(f.cssNumber[i]?"":"px"),o!=="px"&&(f.style(this,i,(n||1)+o),m=(n||1)/k.cur()*m,f.style(this,i,m+o)),l[1]&&(n=(l[1]==="-="?-1:1)*n+m),k.custom(m,n,o)):k.custom(m,h,""));return!0})},stop:function(a,b){a&&this.queue([]),this.each(function(){var a=f.timers,c=a.length;b||f._unmark(!0,this);while(c--)a[c].elem===this&&(b&&a[c](!0),a.splice(c,1))}),b||this.dequeue();return this}}),f.each({slideDown:cr("show",1),slideUp:cr("hide",1),slideToggle:cr("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default,d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue!==!1?f.dequeue(this):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function h(a){return d.step(a)}var d=this,e=f.fx,g;this.startTime=cn||cp(),this.start=a,this.end=b,this.unit=c||this.unit||(f.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,h.elem=this.elem,h()&&f.timers.push(h)&&!cl&&(co?(cl=!0,g=function(){cl&&(co(g),e.tick())},co(g)):cl=setInterval(e.tick,e.interval))},show:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=cn||cp(),c=!0,d=this.elem,e=this.options,g,h;if(a||b>=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b<a.length;++b)a[b]()||a.splice(b--,1);a.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cl),cl=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var ct=/^t(?:able|d|h)$/i,cu=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cv(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);f.offset.initialize();var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.offset.doesNotAddBorder&&(!f.offset.doesAddBorderForTableAndCells||!ct.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={initialize:function(){var a=c.body,b=c.createElement("div"),d,e,g,h,i=parseFloat(f.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); | |
0 | 19 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,264 @@ |
1 | +// jquery.pjax.js | |
2 | +// copyright chris wanstrath | |
3 | +// https://github.com/defunkt/jquery-pjax | |
4 | + | |
5 | +(function($){ | |
6 | + | |
7 | +// When called on a link, fetches the href with ajax into the | |
8 | +// container specified as the first parameter or with the data-pjax | |
9 | +// attribute on the link itself. | |
10 | +// | |
11 | +// Tries to make sure the back button and ctrl+click work the way | |
12 | +// you'd expect. | |
13 | +// | |
14 | +// Accepts a jQuery ajax options object that may include these | |
15 | +// pjax specific options: | |
16 | +// | |
17 | +// container - Where to stick the response body. Usually a String selector. | |
18 | +// $(container).html(xhr.responseBody) | |
19 | +// push - Whether to pushState the URL. Defaults to true (of course). | |
20 | +// replace - Want to use replaceState instead? That's cool. | |
21 | +// | |
22 | +// For convenience the first parameter can be either the container or | |
23 | +// the options object. | |
24 | +// | |
25 | +// Returns the jQuery object | |
26 | +$.fn.pjax = function( container, options ) { | |
27 | + if ( options ) | |
28 | + options.container = container | |
29 | + else | |
30 | + options = $.isPlainObject(container) ? container : {container:container} | |
31 | + | |
32 | + // We can't persist $objects using the history API so we must use | |
33 | + // a String selector. Bail if we got anything else. | |
34 | + if ( options.container && typeof options.container !== 'string' ) { | |
35 | + throw "pjax container must be a string selector!" | |
36 | + return false | |
37 | + } | |
38 | + | |
39 | + return this.live('click', function(event){ | |
40 | + // Middle click, cmd click, and ctrl click should open | |
41 | + // links in a new tab as normal. | |
42 | + if ( event.which > 1 || event.metaKey ) | |
43 | + return true | |
44 | + | |
45 | + var defaults = { | |
46 | + url: this.href, | |
47 | + container: $(this).attr('data-pjax'), | |
48 | + clickedElement: $(this), | |
49 | + fragment: null | |
50 | + } | |
51 | + | |
52 | + $.pjax($.extend({}, defaults, options)) | |
53 | + | |
54 | + event.preventDefault() | |
55 | + }) | |
56 | +} | |
57 | + | |
58 | + | |
59 | +// Loads a URL with ajax, puts the response body inside a container, | |
60 | +// then pushState()'s the loaded URL. | |
61 | +// | |
62 | +// Works just like $.ajax in that it accepts a jQuery ajax | |
63 | +// settings object (with keys like url, type, data, etc). | |
64 | +// | |
65 | +// Accepts these extra keys: | |
66 | +// | |
67 | +// container - Where to stick the response body. Must be a String. | |
68 | +// $(container).html(xhr.responseBody) | |
69 | +// push - Whether to pushState the URL. Defaults to true (of course). | |
70 | +// replace - Want to use replaceState instead? That's cool. | |
71 | +// | |
72 | +// Use it just like $.ajax: | |
73 | +// | |
74 | +// var xhr = $.pjax({ url: this.href, container: '#main' }) | |
75 | +// console.log( xhr.readyState ) | |
76 | +// | |
77 | +// Returns whatever $.ajax returns. | |
78 | +var pjax = $.pjax = function( options ) { | |
79 | + var $container = $(options.container), | |
80 | + success = options.success || $.noop | |
81 | + | |
82 | + // We don't want to let anyone override our success handler. | |
83 | + delete options.success | |
84 | + | |
85 | + // We can't persist $objects using the history API so we must use | |
86 | + // a String selector. Bail if we got anything else. | |
87 | + if ( typeof options.container !== 'string' ) | |
88 | + throw "pjax container must be a string selector!" | |
89 | + | |
90 | + options = $.extend(true, {}, pjax.defaults, options) | |
91 | + | |
92 | + if ( $.isFunction(options.url) ) { | |
93 | + options.url = options.url() | |
94 | + } | |
95 | + | |
96 | + options.context = $container | |
97 | + | |
98 | + options.success = function(data){ | |
99 | + if ( options.fragment ) { | |
100 | + // If they specified a fragment, look for it in the response | |
101 | + // and pull it out. | |
102 | + var $fragment = $(data).find(options.fragment) | |
103 | + if ( $fragment.length ) | |
104 | + data = $fragment.children() | |
105 | + else | |
106 | + return window.location = options.url | |
107 | + } else { | |
108 | + // If we got no data or an entire web page, go directly | |
109 | + // to the page and let normal error handling happen. | |
110 | + if ( !$.trim(data) || /<html/i.test(data) ) | |
111 | + return window.location = options.url | |
112 | + } | |
113 | + | |
114 | + // Make it happen. | |
115 | + this.html(data) | |
116 | + | |
117 | + // If there's a <title> tag in the response, use it as | |
118 | + // the page's title. | |
119 | + var oldTitle = document.title, | |
120 | + title = $.trim( this.find('title').remove().text() ) | |
121 | + if ( title ) document.title = title | |
122 | + | |
123 | + // No <title>? Fragment? Look for data-title and title attributes. | |
124 | + if ( !title && options.fragment ) { | |
125 | + title = $fragment.attr('title') || $fragment.data('title') | |
126 | + } | |
127 | + | |
128 | + var state = { | |
129 | + pjax: options.container, | |
130 | + fragment: options.fragment, | |
131 | + timeout: options.timeout | |
132 | + } | |
133 | + | |
134 | + // If there are extra params, save the complete URL in the state object | |
135 | + var query = $.param(options.data) | |
136 | + if ( query != "_pjax=true" ) | |
137 | + state.url = options.url + (/\?/.test(options.url) ? "&" : "?") + query | |
138 | + | |
139 | + if ( options.replace ) { | |
140 | + window.history.replaceState(state, document.title, options.url) | |
141 | + } else if ( options.push ) { | |
142 | + // this extra replaceState before first push ensures good back | |
143 | + // button behavior | |
144 | + if ( !pjax.active ) { | |
145 | + window.history.replaceState($.extend({}, state, {url:null}), oldTitle) | |
146 | + pjax.active = true | |
147 | + } | |
148 | + | |
149 | + window.history.pushState(state, document.title, options.url) | |
150 | + } | |
151 | + | |
152 | + // Google Analytics support | |
153 | + if ( (options.replace || options.push) && window._gaq ) | |
154 | + _gaq.push(['_trackPageview']) | |
155 | + | |
156 | + // If the URL has a hash in it, make sure the browser | |
157 | + // knows to navigate to the hash. | |
158 | + var hash = window.location.hash.toString() | |
159 | + if ( hash !== '' ) { | |
160 | + window.location.href = hash | |
161 | + } | |
162 | + | |
163 | + // Invoke their success handler if they gave us one. | |
164 | + success.apply(this, arguments) | |
165 | + } | |
166 | + | |
167 | + // Cancel the current request if we're already pjaxing | |
168 | + var xhr = pjax.xhr | |
169 | + if ( xhr && xhr.readyState < 4) { | |
170 | + xhr.onreadystatechange = $.noop | |
171 | + xhr.abort() | |
172 | + } | |
173 | + | |
174 | + pjax.options = options | |
175 | + pjax.xhr = $.ajax(options) | |
176 | + $(document).trigger('pjax', [pjax.xhr, options]) | |
177 | + | |
178 | + return pjax.xhr | |
179 | +} | |
180 | + | |
181 | + | |
182 | +pjax.defaults = { | |
183 | + timeout: 650, | |
184 | + push: true, | |
185 | + replace: false, | |
186 | + // We want the browser to maintain two separate internal caches: one for | |
187 | + // pjax'd partial page loads and one for normal page loads. Without | |
188 | + // adding this secret parameter, some browsers will often confuse the two. | |
189 | + data: { _pjax: true }, | |
190 | + type: 'GET', | |
191 | + dataType: 'html', | |
192 | + beforeSend: function(xhr){ | |
193 | + this.trigger('pjax:start', [xhr, pjax.options]) | |
194 | + // start.pjax is deprecated | |
195 | + this.trigger('start.pjax', [xhr, pjax.options]) | |
196 | + xhr.setRequestHeader('X-PJAX', 'true') | |
197 | + }, | |
198 | + error: function(xhr, textStatus, errorThrown){ | |
199 | + if ( textStatus !== 'abort' ) | |
200 | + window.location = pjax.options.url | |
201 | + }, | |
202 | + complete: function(xhr){ | |
203 | + this.trigger('pjax:end', [xhr, pjax.options]) | |
204 | + // end.pjax is deprecated | |
205 | + this.trigger('end.pjax', [xhr, pjax.options]) | |
206 | + } | |
207 | +} | |
208 | + | |
209 | + | |
210 | +// Used to detect initial (useless) popstate. | |
211 | +// If history.state exists, assume browser isn't going to fire initial popstate. | |
212 | +var popped = ('state' in window.history), initialURL = location.href | |
213 | + | |
214 | + | |
215 | +// popstate handler takes care of the back and forward buttons | |
216 | +// | |
217 | +// You probably shouldn't use pjax on pages with other pushState | |
218 | +// stuff yet. | |
219 | +$(window).bind('popstate', function(event){ | |
220 | + // Ignore inital popstate that some browsers fire on page load | |
221 | + var initialPop = !popped && location.href == initialURL | |
222 | + popped = true | |
223 | + if ( initialPop ) return | |
224 | + | |
225 | + var state = event.state | |
226 | + | |
227 | + if ( state && state.pjax ) { | |
228 | + var container = state.pjax | |
229 | + if ( $(container+'').length ) | |
230 | + $.pjax({ | |
231 | + url: state.url || location.href, | |
232 | + fragment: state.fragment, | |
233 | + container: container, | |
234 | + push: false, | |
235 | + timeout: state.timeout | |
236 | + }) | |
237 | + else | |
238 | + window.location = location.href | |
239 | + } | |
240 | +}) | |
241 | + | |
242 | + | |
243 | +// Add the state property to jQuery's event object so we can use it in | |
244 | +// $(window).bind('popstate') | |
245 | +if ( $.inArray('state', $.event.props) < 0 ) | |
246 | + $.event.props.push('state') | |
247 | + | |
248 | + | |
249 | +// Is pjax supported by this browser? | |
250 | +$.support.pjax = | |
251 | + window.history && window.history.pushState && window.history.replaceState | |
252 | + // pushState isn't reliable on iOS yet. | |
253 | + && !navigator.userAgent.match(/(iPod|iPhone|iPad|WebApps\/.+CFNetwork)/) | |
254 | + | |
255 | + | |
256 | +// Fall back to normalcy for older browsers. | |
257 | +if ( !$.support.pjax ) { | |
258 | + $.pjax = function( options ) { | |
259 | + window.location = $.isFunction(options.url) ? options.url() : options.url | |
260 | + } | |
261 | + $.fn.pjax = function() { return this } | |
262 | +} | |
263 | + | |
264 | +})(jQuery); | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +/* | |
2 | + * Replaces default rails.confirm implementation with $.alerts.confirm. | |
3 | + */ | |
4 | + | |
5 | +(function($) { | |
6 | + $.rails.confirm = function(msg) { | |
7 | + var answer = $.Deferred(); | |
8 | + $.alerts.confirm(msg, 'Confirmation', function(r) { | |
9 | + $.rails.resolveOrReject(answer, r); | |
10 | + }); | |
11 | + return answer.promise(); | |
12 | + }; | |
13 | +})(jQuery); | |
14 | + | ... | ... |
... | ... | @@ -0,0 +1,406 @@ |
1 | +/** | |
2 | + * Unobtrusive scripting adapter for jQuery | |
3 | + * | |
4 | + * Requires jQuery 1.6.0 or later. | |
5 | + * https://github.com/rails/jquery-ujs | |
6 | + | |
7 | + * Uploading file using rails.js | |
8 | + * ============================= | |
9 | + * | |
10 | + * By default, browsers do not allow files to be uploaded via AJAX. As a result, if there are any non-blank file fields | |
11 | + * in the remote form, this adapter aborts the AJAX submission and allows the form to submit through standard means. | |
12 | + * | |
13 | + * The `ajax:aborted:file` event allows you to bind your own handler to process the form submission however you wish. | |
14 | + * | |
15 | + * Ex: | |
16 | + * $('form').live('ajax:aborted:file', function(event, elements){ | |
17 | + * // Implement own remote file-transfer handler here for non-blank file inputs passed in `elements`. | |
18 | + * // Returning false in this handler tells rails.js to disallow standard form submission | |
19 | + * return false; | |
20 | + * }); | |
21 | + * | |
22 | + * The `ajax:aborted:file` event is fired when a file-type input is detected with a non-blank value. | |
23 | + * | |
24 | + * Third-party tools can use this hook to detect when an AJAX file upload is attempted, and then use | |
25 | + * techniques like the iframe method to upload the file instead. | |
26 | + * | |
27 | + * Required fields in rails.js | |
28 | + * =========================== | |
29 | + * | |
30 | + * If any blank required inputs (required="required") are detected in the remote form, the whole form submission | |
31 | + * is canceled. Note that this is unlike file inputs, which still allow standard (non-AJAX) form submission. | |
32 | + * | |
33 | + * The `ajax:aborted:required` event allows you to bind your own handler to inform the user of blank required inputs. | |
34 | + * | |
35 | + * !! Note that Opera does not fire the form's submit event if there are blank required inputs, so this event may never | |
36 | + * get fired in Opera. This event is what causes other browsers to exhibit the same submit-aborting behavior. | |
37 | + * | |
38 | + * Ex: | |
39 | + * $('form').live('ajax:aborted:required', function(event, elements){ | |
40 | + * // Returning false in this handler tells rails.js to submit the form anyway. | |
41 | + * // The blank required inputs are passed to this function in `elements`. | |
42 | + * return ! confirm("Would you like to submit the form with missing info?"); | |
43 | + * }); | |
44 | + */ | |
45 | + | |
46 | +(function($, undefined) { | |
47 | + // Shorthand to make it a little easier to call public rails functions from within rails.js | |
48 | + var rails; | |
49 | + | |
50 | + $.rails = rails = { | |
51 | + // Link elements bound by jquery-ujs | |
52 | + linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]', | |
53 | + | |
54 | + // Select elements bound by jquery-ujs | |
55 | + selectChangeSelector: 'select[data-remote]', | |
56 | + | |
57 | + // Form elements bound by jquery-ujs | |
58 | + formSubmitSelector: 'form', | |
59 | + | |
60 | + // Form input elements bound by jquery-ujs | |
61 | + formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])', | |
62 | + | |
63 | + // Form input elements disabled during form submission | |
64 | + disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]', | |
65 | + | |
66 | + // Form input elements re-enabled after form submission | |
67 | + enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled', | |
68 | + | |
69 | + // Form required input elements | |
70 | + requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])', | |
71 | + | |
72 | + // Form file input elements | |
73 | + fileInputSelector: 'input:file', | |
74 | + | |
75 | + // Make sure that every Ajax request sends the CSRF token | |
76 | + CSRFProtection: function(xhr) { | |
77 | + var token = $('meta[name="csrf-token"]').attr('content'); | |
78 | + if (token) xhr.setRequestHeader('X-CSRF-Token', token); | |
79 | + }, | |
80 | + | |
81 | + // Triggers an event on an element and returns false if the event result is false | |
82 | + fire: function(obj, name, data) { | |
83 | + var event = $.Event(name); | |
84 | + obj.trigger(event, data); | |
85 | + return event.result !== false; | |
86 | + }, | |
87 | + | |
88 | + resolveOrReject: function(deferred, resolved) { | |
89 | + if (resolved) { | |
90 | + deferred.resolve(); | |
91 | + } else { | |
92 | + deferred.reject(); | |
93 | + } | |
94 | + return deferred; | |
95 | + }, | |
96 | + | |
97 | + // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm | |
98 | + confirm: function(message) { | |
99 | + var res = confirm(message), | |
100 | + answer = $.Deferred(); | |
101 | + | |
102 | + rails.resolveOrReject(answer, res); | |
103 | + return answer.promise(); | |
104 | + }, | |
105 | + | |
106 | + // Default ajax function, may be overridden with custom function in $.rails.ajax | |
107 | + ajax: function(options) { | |
108 | + return $.ajax(options); | |
109 | + }, | |
110 | + | |
111 | + // Submits "remote" forms and links with ajax | |
112 | + handleRemote: function(element) { | |
113 | + var method, url, data, button, | |
114 | + crossDomain = element.data('cross-domain') || null, | |
115 | + dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType), | |
116 | + options; | |
117 | + | |
118 | + if (rails.fire(element, 'ajax:before')) { | |
119 | + | |
120 | + if (element.is('form')) { | |
121 | + method = element.attr('method'); | |
122 | + url = element.attr('action'); | |
123 | + data = element.serializeArray(); | |
124 | + // memoized value from clicked submit button | |
125 | + button = element.data('ujs:submit-button'); | |
126 | + if (button) { | |
127 | + data.push(button); | |
128 | + element.data('ujs:submit-button', null); | |
129 | + } | |
130 | + } else if (element.is('select')) { | |
131 | + method = element.data('method'); | |
132 | + url = element.data('url'); | |
133 | + data = element.serialize(); | |
134 | + if (element.data('params')) data = data + "&" + element.data('params'); | |
135 | + } else { | |
136 | + method = element.data('method'); | |
137 | + url = element.attr('href'); | |
138 | + data = element.data('params') || null; | |
139 | + } | |
140 | + | |
141 | + options = { | |
142 | + type: method || 'GET', data: data, dataType: dataType, crossDomain: crossDomain, | |
143 | + // stopping the "ajax:beforeSend" event will cancel the ajax request | |
144 | + beforeSend: function(xhr, settings) { | |
145 | + if (settings.dataType === undefined) { | |
146 | + xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); | |
147 | + } | |
148 | + return rails.fire(element, 'ajax:beforeSend', [xhr, settings]); | |
149 | + }, | |
150 | + success: function(data, status, xhr) { | |
151 | + element.trigger('ajax:success', [data, status, xhr]); | |
152 | + }, | |
153 | + complete: function(xhr, status) { | |
154 | + element.trigger('ajax:complete', [xhr, status]); | |
155 | + }, | |
156 | + error: function(xhr, status, error) { | |
157 | + element.trigger('ajax:error', [xhr, status, error]); | |
158 | + } | |
159 | + }; | |
160 | + // Only pass url to `ajax` options if not blank | |
161 | + if (url) { options.url = url; } | |
162 | + | |
163 | + rails.ajax(options); | |
164 | + } | |
165 | + }, | |
166 | + | |
167 | + // Handles "data-method" on links such as: | |
168 | + // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a> | |
169 | + handleMethod: function(link) { | |
170 | + var href = link.attr('href'), | |
171 | + method = link.data('method') || 'GET', | |
172 | + csrf_token = $('meta[name=csrf-token]').attr('content'), | |
173 | + csrf_param = $('meta[name=csrf-param]').attr('content'), | |
174 | + form = $('<form></form>', { action: href, method: method, 'data-ujs-generated': 'true' }), | |
175 | + metadata_input = ''; | |
176 | + | |
177 | + if (method !== 'GET') { | |
178 | + form.attr('method', 'POST'); | |
179 | + metadata_input += '<input name="_method" value="' + method + '" type="hidden" />'; | |
180 | + | |
181 | + if (csrf_param !== undefined && csrf_token !== undefined) { | |
182 | + metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />'; | |
183 | + } | |
184 | + } | |
185 | + | |
186 | + form.hide().append(metadata_input).appendTo('body'); | |
187 | + form.submit(); | |
188 | + }, | |
189 | + | |
190 | + /* Disables form elements: | |
191 | + - Caches element value in 'ujs:enable-with' data store | |
192 | + - Replaces element text with value of 'data-disable-with' attribute | |
193 | + - Adds disabled=disabled attribute | |
194 | + */ | |
195 | + disableFormElements: function(form) { | |
196 | + form.find(rails.disableSelector).each(function() { | |
197 | + var element = $(this), | |
198 | + method = element.is('button') ? 'html' : 'val'; | |
199 | + | |
200 | + element.data('ujs:enable-with', element[method]()); | |
201 | + element[method](element.data('disable-with')); | |
202 | + element.attr('disabled', 'disabled'); | |
203 | + }); | |
204 | + }, | |
205 | + | |
206 | + /* Re-enables disabled form elements: | |
207 | + - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`) | |
208 | + - Removes disabled attribute | |
209 | + */ | |
210 | + enableFormElements: function(form) { | |
211 | + form.find(rails.enableSelector).each(function() { | |
212 | + var element = $(this), | |
213 | + method = element.is('button') ? 'html' : 'val'; | |
214 | + | |
215 | + if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); | |
216 | + element.removeAttr('disabled'); | |
217 | + }); | |
218 | + }, | |
219 | + | |
220 | + /* For 'data-confirm' attribute: | |
221 | + - Fires `confirm` event | |
222 | + - Shows the confirmation dialog | |
223 | + - Fires the `confirm:complete` event | |
224 | + | |
225 | + Returns `true` if no function stops the chain and user chose yes; `false` otherwise. | |
226 | + Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog. | |
227 | + Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function | |
228 | + return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog. | |
229 | + */ | |
230 | + allowAction: function(element) { | |
231 | + var message = element.data('confirm'), | |
232 | + confirmAnswer, | |
233 | + answer = $.Deferred(); | |
234 | + | |
235 | + if (!message) { return $.when(true); } | |
236 | + | |
237 | + if (rails.fire(element, 'confirm')) { | |
238 | + confirmAnswer = rails.confirm(message); | |
239 | + confirmAnswer.then( | |
240 | + function() { | |
241 | + var callbackOk = rails.fire(element, 'confirm:complete', [ true ]); | |
242 | + rails.resolveOrReject(answer, callbackOk); | |
243 | + }, | |
244 | + function() { | |
245 | + rails.fire(element, 'confirm:complete', [ false ]); | |
246 | + answer.reject(); | |
247 | + } | |
248 | + ); | |
249 | + return answer.promise(); | |
250 | + // If `confirm` event handler returned false... | |
251 | + } else { | |
252 | + answer.reject(); | |
253 | + return answer.promise(); | |
254 | + } | |
255 | + }, | |
256 | + | |
257 | + // Helper function which checks for blank inputs in a form that match the specified CSS selector | |
258 | + blankInputs: function(form, specifiedSelector, nonBlank) { | |
259 | + var inputs = $(), input, | |
260 | + selector = specifiedSelector || 'input,textarea'; | |
261 | + | |
262 | + form.find(selector).each(function() { | |
263 | + input = $(this); | |
264 | + // Collect non-blank inputs if nonBlank option is true, otherwise, collect blank inputs | |
265 | + if (nonBlank ? input.val() : !input.val()) { | |
266 | + inputs = inputs.add(input); | |
267 | + } | |
268 | + }); | |
269 | + return inputs.length ? inputs : false; | |
270 | + }, | |
271 | + | |
272 | + // Helper function which checks for non-blank inputs in a form that match the specified CSS selector | |
273 | + nonBlankInputs: function(form, specifiedSelector) { | |
274 | + return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank | |
275 | + }, | |
276 | + | |
277 | + // Helper function, needed to provide consistent behavior in IE | |
278 | + stopEverything: function(e) { | |
279 | + $(e.target).trigger('ujs:everythingStopped'); | |
280 | + e.stopImmediatePropagation(); | |
281 | + return false; | |
282 | + }, | |
283 | + | |
284 | + // find all the submit events directly bound to the form and | |
285 | + // manually invoke them. If anyone returns false then stop the loop | |
286 | + callFormSubmitBindings: function(form) { | |
287 | + var events = form.data('events'), continuePropagation = true; | |
288 | + | |
289 | + if (events !== undefined && events['submit'] !== undefined) { | |
290 | + $.each(events['submit'], function(i, obj){ | |
291 | + if (typeof obj.handler === 'function') return continuePropagation = obj.handler(obj.data); | |
292 | + }); | |
293 | + } | |
294 | + return continuePropagation; | |
295 | + } | |
296 | + }; | |
297 | + | |
298 | + $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }}); | |
299 | + | |
300 | + $(rails.linkClickSelector).live('click.rails', function(e) { | |
301 | + var link = $(this); | |
302 | + | |
303 | + rails.allowAction(link).then( | |
304 | + function() { | |
305 | + if (link.data('remote') !== undefined) { | |
306 | + rails.handleRemote(link); | |
307 | + } else { | |
308 | + rails.handleMethod(link); | |
309 | + } | |
310 | + }, | |
311 | + function() { | |
312 | + rails.stopEverything(e); | |
313 | + } | |
314 | + ); | |
315 | + | |
316 | + e.preventDefault(); | |
317 | + }); | |
318 | + | |
319 | + $(rails.selectChangeSelector).live('change.rails', function(e) { | |
320 | + var link = $(this); | |
321 | + | |
322 | + rails.allowAction(link).then( | |
323 | + function() { | |
324 | + rails.handleRemote(link); | |
325 | + }, | |
326 | + function() { | |
327 | + rails.stopEverything(e); | |
328 | + } | |
329 | + ); | |
330 | + | |
331 | + e.preventDefault(); | |
332 | + }); | |
333 | + | |
334 | + $(rails.formSubmitSelector).live('submit.rails', function(e) { | |
335 | + var form = $(this), | |
336 | + remote = (form.data('remote') !== undefined), | |
337 | + blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector), | |
338 | + nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); | |
339 | + | |
340 | + rails.allowAction(form).then( | |
341 | + function() { | |
342 | + // skip other logic when required values are missing or file upload is present | |
343 | + if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { | |
344 | + return rails.stopEverything(e); | |
345 | + } | |
346 | + | |
347 | + if (remote) { | |
348 | + if (nonBlankFileInputs) { | |
349 | + return rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]); | |
350 | + } | |
351 | + | |
352 | + // If browser does not support submit bubbling, then this live-binding will be called before direct | |
353 | + // bindings. Therefore, we should directly call any direct bindings before remotely submitting form. | |
354 | + if (!$.support.submitBubbles && rails.callFormSubmitBindings(form) === false) return rails.stopEverything(e); | |
355 | + | |
356 | + rails.handleRemote(form); | |
357 | + } else { | |
358 | + // slight timeout so that the submit button gets properly serialized | |
359 | + setTimeout(function() { | |
360 | + rails.disableFormElements(form); | |
361 | + // Submit the form from dom-level js (i.e. *not* via jquery), | |
362 | + // which will skip all submit bindings (including this live-binding), | |
363 | + // since they have already been called. | |
364 | + form.get(0).submit(); | |
365 | + }, 13); | |
366 | + } | |
367 | + }, | |
368 | + function() { | |
369 | + rails.stopEverything(e); | |
370 | + } | |
371 | + ); | |
372 | + | |
373 | + e.preventDefault(); | |
374 | + }); | |
375 | + | |
376 | + $(rails.formInputClickSelector).live('click.rails', function(event) { | |
377 | + var button = $(this); | |
378 | + | |
379 | + rails.allowAction(button).then( | |
380 | + function() { | |
381 | + // register the pressed submit button | |
382 | + var name = button.attr('name'), form, | |
383 | + data = name ? {name:name, value:button.val()} : null; | |
384 | + | |
385 | + form = button.closest('form'); | |
386 | + form.data('ujs:submit-button', data); | |
387 | + form.submit(); | |
388 | + }, | |
389 | + function() { | |
390 | + rails.stopEverything(event); | |
391 | + } | |
392 | + ); | |
393 | + | |
394 | + event.preventDefault(); | |
395 | + }); | |
396 | + | |
397 | + $(rails.formSubmitSelector).live('ajax:beforeSend.rails', function(event) { | |
398 | + if (this == event.target) rails.disableFormElements($(this)); | |
399 | + }); | |
400 | + | |
401 | + $(rails.formSubmitSelector).live('ajax:complete.rails', function(event) { | |
402 | + if (this == event.target) rails.enableFormElements($(this)); | |
403 | + }); | |
404 | + | |
405 | +})( jQuery ); | |
406 | + | ... | ... |
... | ... | @@ -0,0 +1,26 @@ |
1 | +// Underscore.js 1.1.6 | |
2 | +// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. | |
3 | +// Underscore is freely distributable under the MIT license. | |
4 | +// Portions of Underscore are inspired or borrowed from Prototype, | |
5 | +// Oliver Steele's Functional, and John Resig's Micro-Templating. | |
6 | +// For all details and documentation: | |
7 | +// http://documentcloud.github.com/underscore | |
8 | +(function(){var p=this,C=p._,m={},i=Array.prototype,n=Object.prototype,f=i.slice,D=i.unshift,E=n.toString,l=n.hasOwnProperty,s=i.forEach,t=i.map,u=i.reduce,v=i.reduceRight,w=i.filter,x=i.every,y=i.some,o=i.indexOf,z=i.lastIndexOf;n=Array.isArray;var F=Object.keys,q=Function.prototype.bind,b=function(a){return new j(a)};typeof module!=="undefined"&&module.exports?(module.exports=b,b._=b):p._=b;b.VERSION="1.1.6";var h=b.each=b.forEach=function(a,c,d){if(a!=null)if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e= | |
9 | +0,k=a.length;e<k;e++){if(c.call(d,a[e],e,a)===m)break}else for(e in a)if(l.call(a,e)&&c.call(d,a[e],e,a)===m)break};b.map=function(a,c,b){var e=[];if(a==null)return e;if(t&&a.map===t)return a.map(c,b);h(a,function(a,g,G){e[e.length]=c.call(b,a,g,G)});return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var k=d!==void 0;a==null&&(a=[]);if(u&&a.reduce===u)return e&&(c=b.bind(c,e)),k?a.reduce(c,d):a.reduce(c);h(a,function(a,b,f){!k&&b===0?(d=a,k=!0):d=c.call(e,d,a,b,f)});if(!k)throw new TypeError("Reduce of empty array with no initial value"); | |
10 | +return d};b.reduceRight=b.foldr=function(a,c,d,e){a==null&&(a=[]);if(v&&a.reduceRight===v)return e&&(c=b.bind(c,e)),d!==void 0?a.reduceRight(c,d):a.reduceRight(c);a=(b.isArray(a)?a.slice():b.toArray(a)).reverse();return b.reduce(a,c,d,e)};b.find=b.detect=function(a,c,b){var e;A(a,function(a,g,f){if(c.call(b,a,g,f))return e=a,!0});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(w&&a.filter===w)return a.filter(c,b);h(a,function(a,g,f){c.call(b,a,g,f)&&(e[e.length]=a)});return e}; | |
11 | +b.reject=function(a,c,b){var e=[];if(a==null)return e;h(a,function(a,g,f){c.call(b,a,g,f)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=!0;if(a==null)return e;if(x&&a.every===x)return a.every(c,b);h(a,function(a,g,f){if(!(e=e&&c.call(b,a,g,f)))return m});return e};var A=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=!1;if(a==null)return e;if(y&&a.some===y)return a.some(c,d);h(a,function(a,b,f){if(e=c.call(d,a,b,f))return m});return e};b.include=b.contains=function(a,c){var b= | |
12 | +!1;if(a==null)return b;if(o&&a.indexOf===o)return a.indexOf(c)!=-1;A(a,function(a){if(b=a===c)return!0});return b};b.invoke=function(a,c){var d=f.call(arguments,2);return b.map(a,function(a){return(c.call?c||a:a[c]).apply(a,d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};h(a,function(a,b,f){b=c?c.call(d,a,b,f):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a, | |
13 | +c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};h(a,function(a,b,f){b=c?c.call(d,a,b,f):a;b<e.computed&&(e={value:a,computed:b})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,f){return{value:a,criteria:c.call(d,a,b,f)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray= | |
14 | +function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return f.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?f.call(a,0,b):a[0]};b.rest=b.tail=function(a,b,d){return f.call(a,b==null||d?1:b)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a){return b.reduce(a,function(a,d){if(b.isArray(d))return a.concat(b.flatten(d)); | |
15 | +a[a.length]=d;return a},[])};b.without=function(a){var c=f.call(arguments,1);return b.filter(a,function(a){return!b.include(c,a)})};b.uniq=b.unique=function(a,c){return b.reduce(a,function(a,e,f){if(0==f||(c===!0?b.last(a)!=e:!b.include(a,e)))a[a.length]=e;return a},[])};b.intersect=function(a){var c=f.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.zip=function(){for(var a=f.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c), | |
16 | +e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(o&&a.indexOf===o)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(z&&a.lastIndexOf===z)return a.lastIndexOf(b);for(var d=a.length;d--;)if(a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);d=arguments[2]||1;for(var e=Math.max(Math.ceil((b-a)/ | |
17 | +d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};b.bind=function(a,b){if(a.bind===q&&q)return q.apply(a,f.call(arguments,1));var d=f.call(arguments,2);return function(){return a.apply(b,d.concat(f.call(arguments)))}};b.bindAll=function(a){var c=f.call(arguments,1);c.length==0&&(c=b.functions(a));h(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var b=c.apply(this,arguments);return l.call(d,b)?d[b]:d[b]=a.apply(this,arguments)}};b.delay= | |
18 | +function(a,b){var d=f.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(f.call(arguments,1)))};var B=function(a,b,d){var e;return function(){var f=this,g=arguments,h=function(){e=null;a.apply(f,g)};d&&clearTimeout(e);if(d||!e)e=setTimeout(h,b)}};b.throttle=function(a,b){return B(a,b,!1)};b.debounce=function(a,b){return B(a,b,!0)};b.once=function(a){var b=!1,d;return function(){if(b)return d;b=!0;return d=a.apply(this,arguments)}}; | |
19 | +b.wrap=function(a,b){return function(){var d=[a].concat(f.call(arguments));return b.apply(this,d)}};b.compose=function(){var a=f.call(arguments);return function(){for(var b=f.call(arguments),d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return function(){if(--a<1)return b.apply(this,arguments)}};b.keys=F||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)l.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a, | |
20 | +b.identity)};b.functions=b.methods=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){h(f.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){h(f.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,c){if(a===c)return!0;var d=typeof a;if(d!= | |
21 | +typeof c)return!1;if(a==c)return!0;if(!a&&c||a&&!c)return!1;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return!1;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return!1;if(a.length&&a.length!==c.length)return!1;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return!1; | |
22 | +for(var f in a)if(!(f in c)||!b.isEqual(a[f],c[f]))return!1;return!0};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(l.call(a,c))return!1;return!0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=n||function(a){return E.call(a)==="[object Array]"};b.isArguments=function(a){return!(!a||!l.call(a,"callee"))};b.isFunction=function(a){return!(!a||!a.constructor||!a.call||!a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)}; | |
23 | +b.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===!0||a===!1};b.isDate=function(a){return!(!a||!a.getTimezoneOffset||!a.setUTCFullYear)};b.isRegExp=function(a){return!(!a||!a.test||!a.exec||!(a.ignoreCase||a.ignoreCase===!1))};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){p._=C;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e= | |
24 | +0;e<a;e++)b.call(d,e)};b.mixin=function(a){h(b.functions(a),function(c){H(c,b[c]=a[c])})};var I=0;b.uniqueId=function(a){var b=I++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate|| | |
25 | +null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return c?d(c):d};var j=function(a){this._wrapped=a};b.prototype=j.prototype;var r=function(a,c){return c?b(a).chain():a},H=function(a,c){j.prototype[a]=function(){var a=f.call(arguments);D.call(a,this._wrapped);return r(c.apply(b,a),this._chain)}};b.mixin(b);h(["pop","push","reverse","shift","sort", | |
26 | +"splice","unshift"],function(a){var b=i[a];j.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});h(["concat","join","slice"],function(a){var b=i[a];j.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return this};j.prototype.value=function(){return this._wrapped}})(); | |
0 | 27 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,12 @@ |
1 | +/* | |
2 | + *= require reset | |
3 | + *= require jquery.alerts | |
4 | + *= require errbit | |
5 | + *= require issue_tracker_icons | |
6 | + *= require_self | |
7 | + */ | |
8 | + | |
9 | +// Allow any gems named 'errbit_*' to require their own assets | |
10 | +<% Gem.loaded_specs.keys.grep(/^errbit_/).each do |plugin| | |
11 | + require_asset(plugin) rescue Sprockets::FileNotFound | |
12 | +end %> | ... | ... |
... | ... | @@ -0,0 +1,904 @@ |
1 | +html { | |
2 | + margin: 0; padding: 0; | |
3 | + color: #585858; | |
4 | + background-color: #e6e6e6; | |
5 | + font-size: 62.8%; font-family: Helvetica, "Lucida Grande","Lucida Sans",Arial,sans-serif; | |
6 | +} | |
7 | +body { | |
8 | + margin: 0; padding: 0; | |
9 | + font-size: 1.3em; line-height: 1.4em; | |
10 | +} | |
11 | + | |
12 | +/* Convenience Classes */ | |
13 | +.float-left { float: left; } | |
14 | +.float-right { float: right; } | |
15 | +.clear { clear: both; } | |
16 | +.clear-left { clear: left; } | |
17 | +.clear-right { clear: right; } | |
18 | +.nowrap { white-space: nowrap; } | |
19 | + | |
20 | +/* Headings */ | |
21 | +h1, h2, h3, h4, h5, h6 { padding: 0.2em 0; margin-bottom: 1em; border-bottom: 1px solid #dedede;} | |
22 | +h1 { font-size: 2.0em; line-height: 1.2em; text-shadow: 1px 1px 0px #FFF; -webkit-text-shadow: 1px 1px 0px #FFF;} | |
23 | +h2 { font-size: 1.7em; line-height: 1.2em; } | |
24 | +h3 { font-size: 1.5em; line-height: 1.2em; } | |
25 | +h4 { font-size: 1.3em; line-height: 1.2em; } | |
26 | +h5 { font-size: 1.1em; line-height: 1.2em; } | |
27 | +h6 { font-size: 0.9em; line-height: 1.2em; } | |
28 | + | |
29 | +/* General */ | |
30 | +p { margin-bottom: 1em; } | |
31 | + | |
32 | +/* Links */ | |
33 | +a { color: #0069cc; text-decoration: none;} | |
34 | +a:visited { color: #0069cc;} | |
35 | +a:hover { color: #0069cc; text-decoration: underline; } | |
36 | +a.action { float: right; font-size: 0.9em;} | |
37 | + | |
38 | +#header > div, #content-wrapper, #footer { | |
39 | + width: 930px; | |
40 | + margin: 0 auto; | |
41 | + position: relative; | |
42 | +} | |
43 | + | |
44 | +/* Header */ | |
45 | +#header { | |
46 | + margin-bottom: 24px; | |
47 | + height: 71px; | |
48 | + border-bottom: 1px solid #fff; | |
49 | + position:relative; | |
50 | + background: #000000; | |
51 | +} | |
52 | +#header > div { | |
53 | + height: 71px; | |
54 | +} | |
55 | +#header #site-name { | |
56 | + display: block; | |
57 | + width: 88px; | |
58 | + height: 31px; | |
59 | + position: absolute; | |
60 | + top: 22px; | |
61 | + left: 2px; | |
62 | + background: transparent url(images/logo.png) 0 0 no-repeat; | |
63 | + text-indent: -5000em; | |
64 | +} | |
65 | + | |
66 | +#header #session-links { | |
67 | + position: absolute; | |
68 | + top: 20px; | |
69 | + right: 0; | |
70 | + font-size: 0.9em; | |
71 | +} | |
72 | +#header #session-links li { | |
73 | + float: right; | |
74 | + margin-left: 10px; | |
75 | + color: #ccc; | |
76 | + background-color: #1e1e1e; | |
77 | + border-radius: 30px; | |
78 | + -moz-border-radius: 30px; | |
79 | + -webkit-border-radius: 30px; | |
80 | + border: 1px solid #484B4F; | |
81 | + font-size: 14px; | |
82 | +} | |
83 | +#header #session-links li:hover { | |
84 | + box-shadow: 0 0 3px #69c; | |
85 | + -moz-box-shadow: 0 0 3px #69c; | |
86 | + -webkit-box-shadow: 0 0 3px #69c; | |
87 | +} | |
88 | +#header #session-links li:hover a { | |
89 | + color: white; | |
90 | +} | |
91 | +#header #session-links a { | |
92 | + color: #ccc; | |
93 | + padding: 0 14px; | |
94 | + line-height: 30px; | |
95 | +} | |
96 | +#header #session-links #sign-out { | |
97 | + background: transparent url(images/icons/bullet-red-sm.png) 12px 50% no-repeat; | |
98 | + padding-left: 29px; | |
99 | +} | |
100 | +#header #session-links a:hover { | |
101 | + text-decoration: none; | |
102 | +} | |
103 | + | |
104 | +/* Navigation */ | |
105 | +#nav-bar { | |
106 | + position: absolute; | |
107 | + bottom: 0; | |
108 | + left: 172px; | |
109 | +} | |
110 | +#nav-bar li { | |
111 | + float: left; | |
112 | + height: 34px; | |
113 | + margin-right: 12px; | |
114 | + color: #666; | |
115 | + background-color: #d0d0d0; | |
116 | + background-image: none; | |
117 | + border-top-left-radius: 12px; | |
118 | + border-top-right-radius: 12px; | |
119 | + -moz-border-top-left-radius: 12px; | |
120 | + -moz-border-top-right-radius: 12px; | |
121 | + -webkit-border-top-left-radius: 12px; | |
122 | + -webkit-border-top-right-radius: 12px; | |
123 | + border: 1px solid #bbb; | |
124 | +} | |
125 | +#nav-bar li.active { | |
126 | + border-color: #fff; | |
127 | + background: #FFF url(images/button-bg.png) 0 -2px repeat-x; | |
128 | + border-width:1px 1px 0; | |
129 | + margin-bottom:-2px; | |
130 | + height: 37px; | |
131 | +} | |
132 | +#nav-bar li.active a { | |
133 | + color: #333; | |
134 | +} | |
135 | + | |
136 | +#nav-bar li a { | |
137 | + color: #666; | |
138 | + display: block; | |
139 | + padding: 0 20px 0 40px; | |
140 | + font-size: 14px; | |
141 | + font-weight: bold; | |
142 | + line-height: 37px; | |
143 | + text-decoration: none; | |
144 | + text-shadow: 1px 1px 0px #FFF; | |
145 | + -webkit-text-shadow: 1px 1px 0px #FFF; | |
146 | + background: transparent 10px 6px no-repeat; | |
147 | +} | |
148 | +#nav-bar li a:hover { color: #666;} | |
149 | +#nav-bar li.apps a { background-image: url(images/icons/briefcase.png); } | |
150 | +#nav-bar li.errs a { background-image: url(images/icons/error.png); } | |
151 | +#nav-bar li.users a { background-image: url(images/icons/user.png); } | |
152 | +#nav-bar li:not(.active):hover { | |
153 | + box-shadow: 0 0 3px #69c; | |
154 | + -moz-box-shadow: 0 0 3px #69c; | |
155 | + -webkit-box-shadow: 0 0 3px #69c; | |
156 | +} | |
157 | + | |
158 | +/* Content Wrapper */ | |
159 | +#content-wrapper { | |
160 | + border: 1px solid #C6C6C6; | |
161 | +} | |
162 | + | |
163 | +/* Content Title and Comments */ | |
164 | +#content-title, #content-comments { | |
165 | + padding: 30px 24px; | |
166 | + border-top: 1px solid #FFF; | |
167 | + border-bottom: 1px solid #FFF; | |
168 | + background-color: #f2f2f2; | |
169 | +} | |
170 | + | |
171 | +/* Make err title bar bigger to fit more buttons */ | |
172 | +#content-title.err_show { | |
173 | + padding: 43px 24px 37px; | |
174 | +} | |
175 | + | |
176 | +#content-comments { | |
177 | + background-color: #ffffff; | |
178 | +} | |
179 | +#content-title h1, #content-comments h3 { | |
180 | + padding: 0; margin: 0; | |
181 | + width: 85%; | |
182 | + border: none; | |
183 | + color: #636363; | |
184 | + font-size: 2em; line-height: 1em; font-weight: bold; font-family: arial, sans-serif; | |
185 | + word-wrap: break-word; | |
186 | +} | |
187 | +#content-comments h3 { | |
188 | + font-size: 1.5em; | |
189 | + margin-bottom: 14px; | |
190 | +} | |
191 | + | |
192 | +#content-title .meta { font-size: 0.9em; color: #787878; } | |
193 | + | |
194 | +/* Action Bar */ | |
195 | +#action-bar { | |
196 | + position: absolute; | |
197 | + text-align: right; | |
198 | + top: 22px; right: 24px; | |
199 | +} | |
200 | +#action-bar span { | |
201 | + display: inline-block; | |
202 | + margin-left: 18px; | |
203 | + margin-bottom: 16px; | |
204 | + text-decoration: none; | |
205 | + color: #666; | |
206 | + background: #FFF url(images/button-bg.png) 0 bottom repeat-x; | |
207 | + border-radius: 50px; | |
208 | + -moz-border-radius: 50px; | |
209 | + -webkit-border-radius: 50px; | |
210 | + border: 1px solid #bbb; | |
211 | +} | |
212 | +#action-bar span a { | |
213 | + color: #666; | |
214 | + display: block; | |
215 | + padding: 0 20px 0 40px; | |
216 | + font-size: 14px; font-weight: bold; line-height: 39px; text-decoration: none; | |
217 | + text-shadow: 1px 1px 0px #FFF; -webkit-text-shadow: 1px 1px 0px #FFF; | |
218 | + background: transparent 10px 8px no-repeat; | |
219 | +} | |
220 | +#action-bar a:hover { text-decoration: none;} | |
221 | +#action-bar span:hover { | |
222 | + box-shadow: 0 0 3px #69c; | |
223 | + -moz-box-shadow: 0 0 3px #69c; | |
224 | + -webkit-box-shadow: 0 0 3px #69c; | |
225 | +} | |
226 | +#action-bar a.add { | |
227 | + background-image: url(images/icons/add.png); | |
228 | +} | |
229 | + | |
230 | +#action-bar .calendar_link { | |
231 | + background: url(images/icons/ical.png) no-repeat scroll 12px 6px transparent; | |
232 | + padding-left: 47px; | |
233 | +} | |
234 | + | |
235 | +#action-bar span.github a { background: url(images/icons/github.png) no-repeat 6px 5px; } | |
236 | +#action-bar span.unlink_github a { background: url(images/icons/unlink_github.png) no-repeat 6px 5px; } | |
237 | + | |
238 | +/* Content */ | |
239 | +#content { | |
240 | + padding: 20px; border-top: 1px solid #C6C6C6; | |
241 | + background-color: #FFF; | |
242 | +} | |
243 | + | |
244 | +#content a.button { | |
245 | + float: right; | |
246 | + display: block; | |
247 | + margin-bottom: 10px; | |
248 | +} | |
249 | + | |
250 | +/* Footer */ | |
251 | +#footer { | |
252 | + padding: 20px 0; | |
253 | + font-size: 0.8em; text-align: center; | |
254 | + color: #929292; | |
255 | +} | |
256 | + | |
257 | +/* Flash Messages */ | |
258 | +#flash-messages li { | |
259 | + padding: 13px 45px; | |
260 | + margin-bottom:25px; | |
261 | + border: 1px solid #C6C6C6; | |
262 | + background-color: #F9F9F9; | |
263 | + line-height: 1em; | |
264 | +} | |
265 | +#flash-messages li.notice { | |
266 | + padding-left: 20px; | |
267 | + background-color: #b5eeff; | |
268 | + border: 1px solid #6cf; | |
269 | +} | |
270 | +#flash-messages li.success { | |
271 | + background: #cfc url(images/icons/success.png) 16px 50% no-repeat; | |
272 | + border: 1px solid #6c3; | |
273 | +} | |
274 | +#flash-messages li.error { | |
275 | + background: #fcc url(images/icons/error.png) 16px 50% no-repeat; | |
276 | + border: 1px solid #f99; | |
277 | +} | |
278 | +#flash-messages .alert { | |
279 | + background: #ffc url(images/icons/warning.png) 10px 7px no-repeat; | |
280 | + border-color: #e4bb69; | |
281 | +} | |
282 | + | |
283 | +/* Forms */ | |
284 | +form#new_user, | |
285 | +form.edit_user, | |
286 | +form#new_app, | |
287 | +form.edit_app { | |
288 | + width: 620px; | |
289 | +} | |
290 | +form > div, form fieldset > div { margin: 1em 0;} | |
291 | +form fieldset { | |
292 | + padding: 0.8em; margin-bottom: 1em; | |
293 | + background-color: #F0F0F0; border: 1px solid #C6C6C6; border-left: none; border-right: none; | |
294 | +} | |
295 | +form fieldset legend { | |
296 | + font-size: 1.2em; font-weight: bold; text-transform: uppercase; | |
297 | + color: #555; | |
298 | +} | |
299 | +form label { | |
300 | + font-weight: bold; text-transform: uppercase; line-height: 1.6em; | |
301 | + display: inline-block; | |
302 | +} | |
303 | +form label.inline { display: inline; } | |
304 | +form .checkbox label { display: inline; } | |
305 | +form .required label { padding-right: 20px; background: transparent url(images/icons/required.png) right 50% no-repeat; } | |
306 | +form .field_with_errors label { color: #900; } | |
307 | +form input[type=text], form input[type=password] { | |
308 | + width: 96%; padding: 0.8em; | |
309 | + font-size: 1em; | |
310 | + color: #787878; border: 1px solid #C6C6C6; | |
311 | +} | |
312 | +form textarea { | |
313 | + width: 100%; padding: 0.8em; | |
314 | + font-size: inherit; font-family: inherit; | |
315 | + color: #787878; border: 1px solid #C6C6C6; | |
316 | +} | |
317 | +form textarea.short { height: 8em; } | |
318 | +form textarea.supershort { height: 4em; } | |
319 | +form input[type=text]:focus, form input[type=password]:focus, form textarea:focus { | |
320 | + box-shadow: 0px 0px 4px #69C; | |
321 | + -moz-box-shadow: 0px 0px 4px #69C; | |
322 | + -webkit-box-shadow: 0px 0px 4px #69C | |
323 | +} | |
324 | +form input[type=checkbox]:focus + label{ | |
325 | + color: #69C; | |
326 | +} | |
327 | +form input[type=submit] { | |
328 | + display:block; width: auto; padding: 0.5em; | |
329 | + font-size: 1.2em; line-height: 1em; text-transform: uppercase; | |
330 | + border: none; color: #FFF; background-color: #387fc1; | |
331 | +} | |
332 | +form input[type=submit].button { | |
333 | + font-size: 1em; | |
334 | + text-transform: none; | |
335 | +} | |
336 | +form div.buttons { | |
337 | + color: #666; | |
338 | + background: #FFF url(images/button-bg.png) 0 bottom repeat-x; | |
339 | + border-radius: 50px; | |
340 | + -moz-border-radius: 50px; | |
341 | + -webkit-border-radius: 50px; | |
342 | + border: 1px solid #bbb; | |
343 | + display: inline-block; | |
344 | +} | |
345 | +form div.buttons:hover { | |
346 | + color: #666; | |
347 | + box-shadow: 0 0 3px #69c; | |
348 | + -moz-box-shadow: 0 0 3px #69c; | |
349 | + -webkit-box-shadow: 0 0 3px #69c; | |
350 | +} | |
351 | +form div.buttons input, form div.buttons button { | |
352 | + padding: 0 20px; | |
353 | + color: #666; | |
354 | + background: none; | |
355 | + display: inline-block; | |
356 | + height: 36px; | |
357 | + font-size: 14px; font-weight: bold; line-height: 36px; text-decoration: none; | |
358 | + text-shadow: 1px 1px 0px #FFF; | |
359 | + -moz-text-shadow: 1px 1px 0px #FFF; | |
360 | + -webkit-text-shadow: 1px 1px 0px #FFF; | |
361 | + border: none; | |
362 | +} | |
363 | +form div.buttons button.sign_in { | |
364 | + padding-left: 40px; | |
365 | + background: transparent url(images/icons/right-arrow.png) 3px 3px no-repeat; | |
366 | +} | |
367 | +form strong.option { | |
368 | + display: block; | |
369 | + margin: 0.7em 0; | |
370 | + color: #999; | |
371 | +} | |
372 | + | |
373 | +form .nested { | |
374 | + border-top: 1px dotted #BBB; | |
375 | + margin-top: 1.5em; | |
376 | + padding-top: 1.5em; | |
377 | +} | |
378 | + | |
379 | +form legend + .nested { | |
380 | + border: none; | |
381 | + margin-top: 0; | |
382 | + padding-top: 0; | |
383 | +} | |
384 | + | |
385 | +form .error-messages { | |
386 | + padding: 13px; | |
387 | + background: #fcc; | |
388 | + border: 1px solid #f99; | |
389 | +} | |
390 | + | |
391 | +form .error-messages h2 { | |
392 | + font-size: 1.2em; | |
393 | + border-color: #F99; | |
394 | +} | |
395 | +form .error-messages ul { | |
396 | + margin-left: 2em; | |
397 | + list-style-type: square; | |
398 | +} | |
399 | + | |
400 | +form .field-helpertext { | |
401 | + display: inline; | |
402 | +} | |
403 | + | |
404 | +form input#app_email_at_notices { | |
405 | + width: 130px; | |
406 | + margin: 0 5px; | |
407 | +} | |
408 | + | |
409 | + | |
410 | +/* Tables */ | |
411 | +table { | |
412 | + width: 100%; | |
413 | + border: 1px solid #C6C6C6; | |
414 | + margin-bottom: 1.5em; | |
415 | + border-collapse: separate; | |
416 | +} | |
417 | +table thead th { | |
418 | + border-top: 1px solid #FFF; | |
419 | + border-bottom: 1px solid #FFF; | |
420 | +} | |
421 | +table th, table td { | |
422 | + border-top: 1px solid #C6C6C6; | |
423 | + padding: 10px 8px; | |
424 | + text-align: left; | |
425 | +} | |
426 | +table tbody tr:first-child th, table tbody tr:first-child td { | |
427 | + border-top: none; | |
428 | +} | |
429 | +table thead + tbody tr:first-child td { | |
430 | + border-top: 1px solid #C6C6C6; | |
431 | +} | |
432 | +table th { background-color: #ececec; font-weight: bold; text-transform: uppercase; white-space: nowrap; } | |
433 | +table tbody tr:nth-child(odd) td { background-color: #F9F9F9; } | |
434 | +table .main { width: 100%; } | |
435 | + | |
436 | +table.single_user { | |
437 | + border-top: none; | |
438 | +} | |
439 | + | |
440 | +.raw_data { | |
441 | + width: 100%; | |
442 | + color: #f0f0f0; | |
443 | + background-color: #222; | |
444 | + overflow: auto; | |
445 | +} | |
446 | + | |
447 | +/* Code */ | |
448 | +pre { | |
449 | + padding: 0.8em; | |
450 | + margin-bottom: 1em; | |
451 | + font-family: monaco, courier, monospace; | |
452 | + font-size: 1.1em; | |
453 | +} | |
454 | + | |
455 | +/* HTML Styling */ | |
456 | +.html { padding-left: 1em; border-left: 2px solid #C6C6C6;} | |
457 | +.html h1, .html h2, .html h3, .html h4, .html h5, .html h6 { | |
458 | + border: none; | |
459 | +} | |
460 | +.html ul, .html ol { margin-left: 2em; margin-bottom: 1em; } | |
461 | +.html ul li { margin-bottom: 0.5em; list-style: disc; } | |
462 | +.html ol li { margin-bottom: 0.5em; list-style: decimal; } | |
463 | + | |
464 | +/* Pagination */ | |
465 | +.pagination { | |
466 | + margin: 0 0 25px; | |
467 | + font-size: 17px; | |
468 | + text-align: center; | |
469 | +} | |
470 | +.pagination em { | |
471 | + font-style: normal; | |
472 | + font-weight: bold; | |
473 | +} | |
474 | + | |
475 | + | |
476 | +/* Buttons */ | |
477 | +input[type="submit"].button, | |
478 | +a.button { | |
479 | + display: inline-block; | |
480 | + padding: 0 0.8em; | |
481 | + margin-left: 0.5em; | |
482 | + color: #666; | |
483 | + background-color: #dadada; | |
484 | + border: 1px solid #BBB; | |
485 | + border-radius: 30px; | |
486 | + -moz-border-radius: 30px; | |
487 | + -webkit-border-radius: 30px; | |
488 | + line-height: 30px; | |
489 | + min-width: 54px; | |
490 | + text-align: center; | |
491 | + text-shadow: 0 1px 0 #fff; | |
492 | +} | |
493 | +input[type="submit"]:hover.button, | |
494 | +a:hover.button { | |
495 | + box-shadow: 0px 0px 4px #bfbfbf; | |
496 | + -moz-box-shadow: 0px 0px 4px #bfbfbf; | |
497 | + -webkit-box-shadow: 0px 0px 4px #bfbfbf; | |
498 | + text-decoration: none; | |
499 | + background-color: #e5e5e5; | |
500 | +} | |
501 | +a.button.active { | |
502 | + border-color: #fff; | |
503 | + background-color: #CCC; | |
504 | + background-image: none; | |
505 | +} | |
506 | + | |
507 | + | |
508 | +/* Tab Bar */ | |
509 | +.tab-bar { | |
510 | + margin-top: 12px; | |
511 | +} | |
512 | +#content .tab-bar a.button { | |
513 | + border-bottom:0; | |
514 | + margin-bottom:0; | |
515 | + border-top-left-radius:12px; | |
516 | + border-top-right-radius:12px; | |
517 | + border-bottom-left-radius:0; | |
518 | + border-bottom-right-radius:0; | |
519 | + height:30px; | |
520 | +} | |
521 | +#content .tab-bar a.button.active { | |
522 | + background: #FFF; | |
523 | + color: #444; | |
524 | + border-color:#ccc; | |
525 | + border-style:solid; | |
526 | + border-width:1px 1px 0; | |
527 | + margin-bottom:-1px; | |
528 | + height:31px; | |
529 | +} | |
530 | +.tab-bar ul { | |
531 | + padding: 9px 0 0; | |
532 | + line-height:0; | |
533 | +} | |
534 | +.tab-bar li { | |
535 | + display: inline-block; | |
536 | +} | |
537 | + | |
538 | +/* Watchers and Issue Tracker Forms */ | |
539 | +div.watcher.nested .watcher_params, div.issue_tracker.nested .tracker_params { | |
540 | + display: none; | |
541 | +} | |
542 | +div.nested .chosen { | |
543 | + display: block !important; | |
544 | +} | |
545 | +div.nested .choose { | |
546 | + margin-bottom: 0.5em; | |
547 | +} | |
548 | + | |
549 | +div.issue_tracker.nested .choose { | |
550 | + background-color: #ebebeb; | |
551 | + border: 1px solid #dddddd; | |
552 | + margin: 0 0 15px; | |
553 | + padding: 12px; | |
554 | +} | |
555 | +div.issue_tracker.nested img { | |
556 | + vertical-align: middle; | |
557 | +} | |
558 | + | |
559 | +/* Icons for Issue Tracker Radio Buttons */ | |
560 | +div.issue_tracker.nested label.label_radio { | |
561 | + color: #929292; | |
562 | + padding-left: 33px; | |
563 | + margin-bottom: 6px; | |
564 | + margin-right: 8px; | |
565 | + line-height: 30px; | |
566 | +} | |
567 | +div.issue_tracker.nested .choose { | |
568 | + padding-bottom: 6px; | |
569 | +} | |
570 | +div.issue_tracker.nested label.label_radio:hover { | |
571 | + color: #696969; | |
572 | +} | |
573 | +div.issue_tracker.nested .label_radio input { | |
574 | + position: absolute; left: -9999px; | |
575 | +} | |
576 | + | |
577 | +div.issue_tracker.nested label.r_on, div.issue_tracker.nested label.r_on:hover { | |
578 | + color: #191919; | |
579 | +} | |
580 | + | |
581 | +/* Icons need to be preloaded, otherwise it looks bad */ | |
582 | +.image_preloader { display: none; } | |
583 | + | |
584 | +/* Apps Table */ | |
585 | +table.apps tbody tr:hover td ,table.errs tbody tr:hover td { background-color: #F2F2F2;} | |
586 | + | |
587 | +table.apps td.name, table.errs td.message { width: 100%; } | |
588 | +table.apps td { padding: 16px 25px; } | |
589 | +table.apps th { padding: 10px 25px; } | |
590 | + | |
591 | +table.apps td.issue_tracker, table.apps td.count, table.apps td.deploy { | |
592 | + text-align: center; | |
593 | +} | |
594 | +table.apps td.issue_tracker, table.apps td.count { | |
595 | + padding: 10px 8px; | |
596 | +} | |
597 | +table.apps td.issue_tracker img { vertical-align: top; } | |
598 | + | |
599 | +td.message .line { | |
600 | + display:inline-block; | |
601 | + margin-left:1em; | |
602 | +} | |
603 | +td.deploy { | |
604 | + white-space: nowrap; | |
605 | +} | |
606 | +td.latest { | |
607 | + white-space: nowrap; | |
608 | +} | |
609 | +td.count, td.issue_link { | |
610 | + text-align: center; | |
611 | +} | |
612 | + | |
613 | +.count a { | |
614 | + display: inline-block; | |
615 | + padding: 0.1em 0.7em; | |
616 | + margin-top: 3px; | |
617 | + color: #fff; | |
618 | + background: #cc0033 url(images/error-badge-bg.png) 0 bottom repeat-x; | |
619 | + border: 1px solid #900; | |
620 | + border-radius: 18px; | |
621 | + -moz-border-radius: 18px; | |
622 | + -webkit-border-radius: 18px; | |
623 | + font-weight: bold; | |
624 | + opacity: 0.8; | |
625 | + -moz-opacity: 0.8; | |
626 | + -webkit-opacity: 0.8 | |
627 | +} | |
628 | +.count a.resolved { | |
629 | + background: #05B81d url(images/resolved-badge-bg.png) 0 bottom repeat-x; | |
630 | + border: 1px solid #080; | |
631 | +} | |
632 | +.count a:hover { | |
633 | + text-decoration: none; | |
634 | + opacity: 1; | |
635 | + -moz-opacity: 1; | |
636 | + -webkit-opacity: 1; | |
637 | +} | |
638 | + | |
639 | +/* Err Tables */ | |
640 | +table.errs td.app { | |
641 | + padding-right: 2em; | |
642 | + width: 20%; | |
643 | +} | |
644 | +table.errs td.app .environment { | |
645 | + font-size: 0.8em; | |
646 | + color: #999; | |
647 | +} | |
648 | +table.errs td.message a { | |
649 | + display: block; | |
650 | + word-wrap: break-word; | |
651 | + /* PjpG - configuration in WHAT & WHERE table's columns using ellipsis to avoid oversizing table's width */ | |
652 | + width: 300px; | |
653 | + overflow: hidden; | |
654 | + text-overflow: ellipsis; | |
655 | + -o-text-overflow: ellipsis; | |
656 | + white-space: nowrap; | |
657 | + /* ------ */ | |
658 | +} | |
659 | +table.errs td.message em { | |
660 | + color: #727272; | |
661 | + font-size: 0.9em; | |
662 | +} | |
663 | + | |
664 | +table.errs tr.resolved td > * { | |
665 | + opacity: 0.5; | |
666 | + -moz-opacity: 0.5; | |
667 | + -webkit-opacity: 0.5; | |
668 | +} | |
669 | + | |
670 | +/* Tally tables */ | |
671 | +table.tally { | |
672 | + border:none; | |
673 | +} | |
674 | +table.tally td, | |
675 | +table.tally th { | |
676 | + border:none !important; | |
677 | + background:none !important; | |
678 | + padding:8px 0 0; | |
679 | +} | |
680 | +table.tally tbody tr:first-child td, | |
681 | +table.tally tbody tr:first-child th { | |
682 | + padding-top:0; | |
683 | +} | |
684 | +table.tally td.percent { | |
685 | + padding-right: 10px; | |
686 | +} | |
687 | +table.tally th.value { | |
688 | + width: 100%; | |
689 | + text-transform: none; | |
690 | +} | |
691 | + | |
692 | +/* Deploys table */ | |
693 | +table.deploys td.when { | |
694 | + width: 102px; | |
695 | +} | |
696 | + | |
697 | +/* Resolve Errs */ | |
698 | +#action-bar a.resolve { | |
699 | + background: transparent url(images/icons/thumbs-up.png) 6px 5px no-repeat; | |
700 | +} | |
701 | + | |
702 | +/* Go Up */ | |
703 | +#action-bar a.up { | |
704 | + background: transparent url(images/icons/up.png) 6px 5px no-repeat; | |
705 | +} | |
706 | + | |
707 | +/* Notices Pagination */ | |
708 | +.notice-pagination { | |
709 | + float: left; | |
710 | + margin-right: 10px; | |
711 | +} | |
712 | + | |
713 | +.notice-pagination-loader { | |
714 | + visibility: hidden; | |
715 | + float: left; | |
716 | + margin-right: 2em; | |
717 | +} | |
718 | +.notice-pagination-loader img { | |
719 | + vertical-align: middle | |
720 | +} | |
721 | + | |
722 | + | |
723 | +/* Backtrace */ | |
724 | +.window { | |
725 | + width: 100%; | |
726 | + margin-bottom: 1em; | |
727 | + overflow: auto; | |
728 | + border:1px solid #ccc; | |
729 | + padding:1px; | |
730 | +} | |
731 | + | |
732 | +.window table { | |
733 | + margin: 0; | |
734 | +} | |
735 | + | |
736 | +table.backtrace { | |
737 | + padding: 8px 0; | |
738 | + background-color: #222; | |
739 | +} | |
740 | + | |
741 | +table.backtrace td { | |
742 | + width: 100%; | |
743 | + padding: 0; | |
744 | + margin: 0; | |
745 | + color: #C7C7C7; | |
746 | + background-color: #222; | |
747 | +} | |
748 | + | |
749 | +table.backtrace td, table.backtrace th { | |
750 | + border-top: none; | |
751 | +} | |
752 | + | |
753 | +/* remove alternating color rules */ | |
754 | +table.backtrace tr:nth-child(2n+1) td { background-color: #222; } | |
755 | +table.backtrace tr:first-child td { border-top: 0; } | |
756 | + | |
757 | +table.backtrace th.line-numbers { | |
758 | + border-bottom: 1px solid #F0F0F0; | |
759 | + font-size: 13px; | |
760 | + text-align: right; | |
761 | + vertical-align: top; | |
762 | + padding: 1px 6px 1px 7px; | |
763 | +} | |
764 | + | |
765 | +table.backtrace td.line { | |
766 | + font-size: 13px; | |
767 | + padding: 2px 8px; | |
768 | + vertical-align: top; | |
769 | + white-space: nowrap; | |
770 | +} | |
771 | +table.backtrace td.line .file { | |
772 | + font-weight: bold; | |
773 | +} | |
774 | +table.backtrace td.line .method { | |
775 | + color: #aaa; | |
776 | + font-weight: bold; | |
777 | +} | |
778 | + | |
779 | +table.backtrace td.line.in-app { | |
780 | + color: #2adb2e; | |
781 | + background-color: #2f2f2f; | |
782 | +} | |
783 | +table.backtrace td.line.in-app .path, | |
784 | +table.backtrace td.line.in-app .number { color: #2ACB2E; } | |
785 | +table.backtrace td.line.in-app .file { color: #3AFB3E; } | |
786 | +table.backtrace td.line.in-app .method { color: #2ACB2E; } | |
787 | + | |
788 | +table.backtrace td.line.in-app a .path, | |
789 | +table.backtrace td.line.in-app a .number, | |
790 | +table.backtrace td.line.in-app a:hover { color: #21B4FF; } | |
791 | +table.backtrace td.line.in-app a .file { color: #31C4FF; } | |
792 | + | |
793 | +/* External backtrace classes and separators */ | |
794 | +table.backtrace tr.hidden_external_backtrace { | |
795 | + display: none; | |
796 | +} | |
797 | +table.backtrace td.backtrace_separator span { | |
798 | + cursor: pointer; | |
799 | + display: inline-block; | |
800 | + font-size: 17px; | |
801 | + font-weight: bold; | |
802 | + padding: 0px 11px 5px; | |
803 | + margin: 4px 0; | |
804 | + background-color: #444444; | |
805 | + border: 1px solid #555555; | |
806 | +} | |
807 | +table.backtrace td.backtrace_separator span:hover { | |
808 | + background-color: #666666; | |
809 | + border: 1px solid #777777; | |
810 | +} | |
811 | + | |
812 | + | |
813 | + | |
814 | +/* Extra empty rows at top and bottom of table */ | |
815 | +table.backtrace tr.padding th, table.backtrace tr.padding td { | |
816 | + height: 10px; | |
817 | + margin: 0; | |
818 | + padding: 0; | |
819 | +} | |
820 | + | |
821 | +h3#watchers_toggle, h3#repository_toggle, h3#deploys_toggle { | |
822 | + cursor: pointer; | |
823 | +} | |
824 | + | |
825 | +span.click_span { | |
826 | + font-size: 0.7em; | |
827 | +} | |
828 | + | |
829 | +#deploys_div, #repository_div, #watchers_div { | |
830 | + display: none; | |
831 | +} | |
832 | + | |
833 | +/* Comments */ | |
834 | +#content-comments form p { | |
835 | + margin: 30px 0 0 0; | |
836 | + text-transform: uppercase; | |
837 | +} | |
838 | +table.comment tbody th { | |
839 | + text-transform: none; | |
840 | + font-weight: normal; | |
841 | + height: 20px; | |
842 | + line-height: 0.5em; | |
843 | +} | |
844 | +table.comment th span, table.comment th img { | |
845 | + vertical-align: middle; | |
846 | +} | |
847 | +table.comment th span.comment-info { | |
848 | + line-height: 21px; | |
849 | +} | |
850 | +table.comment img.gravatar { | |
851 | + margin-right: 7px; | |
852 | +} | |
853 | + | |
854 | +table.comment tbody td { | |
855 | + background-color: #F9F9F9; | |
856 | +} | |
857 | +#content-comments a.destroy-comment { | |
858 | + color: #EE0000; | |
859 | + margin-right: 5px; | |
860 | + margin-top: 2px; | |
861 | + font-size: 21px; | |
862 | + line-height: 1; | |
863 | + float: right; | |
864 | +} | |
865 | +#content-comments a.destroy-comment:hover { | |
866 | + text-decoration: none; | |
867 | + color: #AA0000; | |
868 | +} | |
869 | +#content-comments #comment_submit { | |
870 | + margin-top: 15px; | |
871 | +} | |
872 | +/* Inline comments in tables */ | |
873 | +table.errs tr td.message .inline_comment { | |
874 | + display: inline-block; | |
875 | + padding: 3px 7px; | |
876 | + margin: 6px 0; | |
877 | + background-color: #DAE5FF; | |
878 | + border: 1px solid #E2E2E2; | |
879 | + text-shadow: 0 1px 0 #FAFAFA; | |
880 | + font-style: normal; | |
881 | +} | |
882 | +table.errs tr:hover td.message .inline_comment { | |
883 | + background-color: #D5E0FA; | |
884 | + border-color: #DBDBDB; | |
885 | + text-shadow: 0 1px 0 #FFFFFF; | |
886 | +} | |
887 | +table.errs tr td.message .inline_comment em { | |
888 | + color: #444; | |
889 | +} | |
890 | +table.errs tr td.message .inline_comment em.commenter { | |
891 | + color: #777; | |
892 | +} | |
893 | + | |
894 | +.current.asc:after { content: ' ↑'; } | |
895 | +.current.desc:after { content: ' ↓'; } | |
896 | + | |
897 | + | |
898 | +table.users td { | |
899 | + vertical-align: middle; | |
900 | +} | |
901 | +table.users td img.gravatar { | |
902 | + vertical-align: middle; | |
903 | + margin-left: 3px; | |
904 | +} | ... | ... |
148 Bytes
174 Bytes
119 Bytes
196 Bytes
1.04 KB
675 Bytes
417 Bytes
473 Bytes
1.22 KB
1.05 KB
1.94 KB
1.86 KB
157 Bytes
250 Bytes
1.11 KB
1.06 KB
1.42 KB
1.65 KB
2.02 KB
1.11 KB
877 Bytes
674 Bytes
3.21 KB
133 Bytes
119 Bytes
... | ... | @@ -0,0 +1,16 @@ |
1 | +/* Issue Tracker inactive, select, create and goto icons */ | |
2 | +<% trackers = IssueTracker.subclasses.map{|t| t.label } << 'none' %> | |
3 | +<% trackers.each do |tracker| %> | |
4 | +div.issue_tracker.nested label.<%= tracker %> { | |
5 | + background: url(/assets/<%= tracker %>_inactive.png) no-repeat; | |
6 | +} | |
7 | +div.issue_tracker.nested label.r_on.<%= tracker %> { | |
8 | + background: url(/assets/<%= tracker %>_create.png) no-repeat; | |
9 | +} | |
10 | +#action-bar a.<%= tracker %>_create { | |
11 | + background: transparent url(/assets/<%= tracker %>_create.png) 6px 5px no-repeat; | |
12 | +} | |
13 | +#action-bar a.<%= tracker %>_goto { | |
14 | + background: transparent url(/assets/<%= tracker %>_goto.png) 6px 5px no-repeat; | |
15 | +} | |
16 | +<% end %> | ... | ... |
... | ... | @@ -0,0 +1,57 @@ |
1 | +#popup_container { | |
2 | + font-family: Arial, sans-serif; | |
3 | + font-size: 12px; | |
4 | + min-width: 300px; /* Dialog will be no smaller than this */ | |
5 | + max-width: 600px; /* Dialog will wrap after this width */ | |
6 | + background: #FFF; | |
7 | + border: solid 5px #999; | |
8 | + color: #000; | |
9 | + -moz-border-radius: 5px; | |
10 | + -webkit-border-radius: 5px; | |
11 | + border-radius: 5px; | |
12 | +} | |
13 | + | |
14 | +#popup_title { | |
15 | + font-size: 14px; | |
16 | + font-weight: bold; | |
17 | + text-align: center; | |
18 | + line-height: 1.75em; | |
19 | + color: #666; | |
20 | + background: #CCC url(/images/alerts/title.gif) top repeat-x; | |
21 | + border: solid 1px #FFF; | |
22 | + border-bottom: solid 1px #999; | |
23 | + cursor: default; | |
24 | + padding: 0em; | |
25 | + margin: 0em; | |
26 | +} | |
27 | + | |
28 | +#popup_content { | |
29 | + background: 16px 16px no-repeat url(/images/alerts/info.gif); | |
30 | + padding: 1em 1.75em; | |
31 | + margin: 0em; | |
32 | +} | |
33 | + | |
34 | +#popup_content.alert { | |
35 | + background-image: url(/images/alerts/info.gif); | |
36 | +} | |
37 | + | |
38 | +#popup_content.confirm { | |
39 | + background-image: url(/images/alerts/important.gif); | |
40 | +} | |
41 | + | |
42 | +#popup_content.prompt { | |
43 | + background-image: url(/images/alerts/help.gif); | |
44 | +} | |
45 | + | |
46 | +#popup_message { | |
47 | + padding-left: 48px; | |
48 | +} | |
49 | + | |
50 | +#popup_panel { | |
51 | + text-align: center; | |
52 | + margin: 1em 0em 0em 1em; | |
53 | +} | |
54 | + | |
55 | +#popup_prompt { | |
56 | + margin: .5em 0em; | |
57 | +} | |
0 | 58 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,55 @@ |
1 | +td.header { | |
2 | + padding: 10px 20px 8px 20px; | |
3 | + height: 70px; | |
4 | + background-color: #11112f; | |
5 | + text-align: left; | |
6 | + border-bottom: 1px solid #ccccee; | |
7 | +} | |
8 | + td.header a { | |
9 | + display: block; | |
10 | + height: 31px; | |
11 | + width: 88px; | |
12 | + margin-top: 4px; | |
13 | + } | |
14 | + /* Style 'Errbit' logo alt text if image cannot be loaded. */ | |
15 | + td.header a img { | |
16 | + border: none; | |
17 | + color: #E3E3E3; | |
18 | + font-family: helvetica; | |
19 | + font-size: 30px; | |
20 | + font-weight: bold; | |
21 | + min-height: 31px; | |
22 | + text-shadow: 0 1px 0 #EEEEFF; | |
23 | + } | |
24 | + | |
25 | +td.section, td.content, td.footer { | |
26 | + font-family: Helvetica,Arial,sans-serif; | |
27 | + font-size: 14px; | |
28 | + background-color: #ffffff; | |
29 | + text-align: left; | |
30 | +} | |
31 | +td.section { | |
32 | + padding: 0; | |
33 | + border-bottom: 1px solid #dddddd; | |
34 | +} | |
35 | +td.content { | |
36 | + padding: 20px 20px 10px 20px; | |
37 | + line-height: 1.3em; | |
38 | +} | |
39 | +td.footer { | |
40 | + padding: 10px 20px 20px 20px; | |
41 | + font-size: 11px; | |
42 | + font-weight: bold; | |
43 | + color: #666666; | |
44 | +} | |
45 | + | |
46 | +a.bold, span.bold { font-weight: bold; } | |
47 | + | |
48 | +p { margin: 0 0 15px 0; } | |
49 | +p.heading { | |
50 | + color: #6a6a6a; | |
51 | + margin-bottom: 4px; | |
52 | +} | |
53 | +p.monospace, p.backtrace { font-family: monospace; } | |
54 | +p.backtrace { margin-bottom: 2px; } | |
55 | + | ... | ... |
... | ... | @@ -0,0 +1,74 @@ |
1 | +/* | |
2 | + * Reset.css - by Eric Meyer | |
3 | + * Modified for NewsStand | |
4 | + * By Jared Pace | |
5 | + * Codeword: Studios | |
6 | + */ | |
7 | + | |
8 | + html, body, div, span, applet, object, iframe, | |
9 | + h1, h2, h3, h4, h5, h6, p, blockquote, pre, | |
10 | + a, abbr, acronym, address, big, cite, code, | |
11 | + del, dfn, em, font, img, ins, kbd, q, s, samp, | |
12 | + small, strike, strong, sub, sup, tt, var, | |
13 | + b, u, i, center, | |
14 | + dl, dt, dd, ol, ul, li, | |
15 | + fieldset, form, label, legend, | |
16 | + table, caption, tbody, tfoot, thead, tr, th, td { | |
17 | + margin: 0; | |
18 | + padding: 0; | |
19 | + border: 0; | |
20 | + outline: 0; | |
21 | + font-size: 100%; | |
22 | + vertical-align: baseline; | |
23 | + background: transparent; | |
24 | + } | |
25 | + body { | |
26 | + line-height: 1; | |
27 | + } | |
28 | + ol, ul { | |
29 | + list-style: none; | |
30 | + } | |
31 | + blockquote, q { | |
32 | + quotes: none; | |
33 | + } | |
34 | + td { | |
35 | + vertical-align: top; | |
36 | +} | |
37 | + | |
38 | + /* remember to define focus styles! */ | |
39 | + :focus { | |
40 | + outline: 0; | |
41 | + } | |
42 | + | |
43 | + /* remember to highlight inserts somehow! */ | |
44 | + ins { | |
45 | + text-decoration: none; | |
46 | + } | |
47 | + del { | |
48 | + text-decoration: line-through; | |
49 | + } | |
50 | + | |
51 | + /* tables still need 'cellspacing="0"' in the markup */ | |
52 | + table { | |
53 | + border-collapse: collapse; | |
54 | + border-spacing: 0; | |
55 | + } | |
56 | + | |
57 | + sup, sub { | |
58 | + height: 0; | |
59 | + line-height: 1; | |
60 | + vertical-align: baseline; | |
61 | + _vertical-align: bottom; | |
62 | + position: relative; | |
63 | + font-size: 0.8em; | |
64 | + } | |
65 | + | |
66 | + sup { | |
67 | + bottom: 1ex; | |
68 | + } | |
69 | + | |
70 | + sub { | |
71 | + top: .5ex; | |
72 | + } | |
73 | + | |
74 | +p { margin-bottom: 1em; } | |
0 | 75 | \ No newline at end of file | ... | ... |
app/controllers/application_controller.rb
... | ... | @@ -8,7 +8,7 @@ class ApplicationController < ActionController::Base |
8 | 8 | # redirect to that app's path instead of the root path (apps#index). |
9 | 9 | def stored_location_for(resource) |
10 | 10 | location = super || root_path |
11 | - (location == root_path && App.count == 1) ? app_path(App.first) : location | |
11 | + (location == root_path && current_user.apps.count == 1) ? app_path(current_user.apps.first) : location | |
12 | 12 | end |
13 | 13 | |
14 | 14 | rescue_from ActionController::RedirectBackError, :with => :redirect_to_root |
... | ... | @@ -24,7 +24,7 @@ protected |
24 | 24 | def redirect_to_root |
25 | 25 | redirect_to(root_path) |
26 | 26 | end |
27 | - | |
27 | + | |
28 | 28 | def set_time_zone |
29 | 29 | Time.zone = current_user.time_zone if user_signed_in? |
30 | 30 | end | ... | ... |
app/controllers/apps_controller.rb
... | ... | @@ -8,10 +8,10 @@ class AppsController < InheritedResources::Base |
8 | 8 | format.html do |
9 | 9 | @all_errs = !!params[:all_errs] |
10 | 10 | |
11 | - @sort = params[:sort] | |
11 | + @sort = params[:sort] | |
12 | 12 | @order = params[:order] |
13 | - @sort = "last_notice_at" unless %w{message app last_deploy_at count}.member?(@sort) | |
14 | - @order = "desc" unless (@order == "asc") | |
13 | + @sort = "last_notice_at" unless %w{message app last_deploy_at last_notice_at count}.member?(@sort) | |
14 | + @order = "desc" unless %w{asc desc}.member?(@order) | |
15 | 15 | |
16 | 16 | @problems = resource.problems |
17 | 17 | @problems = @problems.unresolved unless @all_errs |
... | ... | @@ -50,13 +50,23 @@ class AppsController < InheritedResources::Base |
50 | 50 | |
51 | 51 | protected |
52 | 52 | def collection |
53 | - # Sort apps by number of unresolved errs, descending. | |
54 | - # Caches the unresolved err counts while performing the sort. | |
55 | - @unresolved_counts = {} | |
56 | - @apps ||= end_of_association_chain.all.sort{|a,b| | |
57 | - [a,b].each{|app| @unresolved_counts[app.id] ||= app.problems.unresolved.count } | |
58 | - @unresolved_counts[b.id] <=> @unresolved_counts[a.id] | |
59 | - } | |
53 | + @unresolved_counts, @problem_counts = {}, {} | |
54 | + @apps ||= begin | |
55 | + apps = end_of_association_chain.all | |
56 | + | |
57 | + # Cache counts for unresolved errs and problems | |
58 | + apps.each do |app| | |
59 | + @unresolved_counts[app.id] ||= app.problems.unresolved.count | |
60 | + @problem_counts[app.id] ||= app.problems.count | |
61 | + end | |
62 | + | |
63 | + # Sort apps by number of unresolved errs, then problem counts. | |
64 | + apps.sort do |a,b| | |
65 | + (@unresolved_counts[b.id] <=> @unresolved_counts[a.id]).nonzero? || | |
66 | + (@problem_counts[b.id] <=> @problem_counts[a.id]).nonzero? || | |
67 | + a.name <=> b.name | |
68 | + end | |
69 | + end | |
60 | 70 | end |
61 | 71 | |
62 | 72 | def initialize_subclassed_issue_tracker | ... | ... |
... | ... | @@ -0,0 +1,40 @@ |
1 | +class CommentsController < ApplicationController | |
2 | + before_filter :find_app | |
3 | + before_filter :find_problem | |
4 | + | |
5 | + def create | |
6 | + @comment = Comment.new(params[:comment].merge(:user_id => current_user.id)) | |
7 | + if @comment.valid? | |
8 | + @problem.comments << @comment | |
9 | + @problem.save | |
10 | + flash[:success] = "Comment saved!" | |
11 | + else | |
12 | + flash[:error] = "I'm sorry, your comment was blank! Try again?" | |
13 | + end | |
14 | + redirect_to app_err_path(@app, @problem) | |
15 | + end | |
16 | + | |
17 | + def destroy | |
18 | + @comment = Comment.find(params[:id]) | |
19 | + if @comment.destroy | |
20 | + flash[:success] = "Comment deleted!" | |
21 | + else | |
22 | + flash[:error] = "Sorry, I couldn't delete your comment for some reason. I hope you don't have any sensitive information in there!" | |
23 | + end | |
24 | + redirect_to app_err_path(@app, @problem) | |
25 | + end | |
26 | + | |
27 | + protected | |
28 | + def find_app | |
29 | + @app = App.find(params[:app_id]) | |
30 | + | |
31 | + # Mongoid Bug: could not chain: current_user.apps.find_by_id! | |
32 | + # apparently finding by 'watchers.email' and 'id' is broken | |
33 | + raise(Mongoid::Errors::DocumentNotFound.new(App,@app.id)) unless current_user.admin? || current_user.watching?(@app) | |
34 | + end | |
35 | + | |
36 | + def find_problem | |
37 | + @problem = @app.problems.find(params[:err_id]) | |
38 | + end | |
39 | +end | |
40 | + | ... | ... |
app/controllers/errs_controller.rb
... | ... | @@ -4,14 +4,12 @@ class ErrsController < ApplicationController |
4 | 4 | before_filter :find_app, :except => [:index, :all, :destroy_several, :resolve_several, :unresolve_several, :merge_several, :unmerge_several] |
5 | 5 | before_filter :find_problem, :except => [:index, :all, :destroy_several, :resolve_several, :unresolve_several, :merge_several, :unmerge_several] |
6 | 6 | before_filter :find_selected_problems, :only => [:destroy_several, :resolve_several, :unresolve_several, :merge_several, :unmerge_several] |
7 | + before_filter :set_sorting_params, :only => [:index, :all] | |
8 | + before_filter :set_tracker_params, :only => [:create_issue] | |
7 | 9 | |
8 | 10 | def index |
9 | 11 | app_scope = current_user.admin? ? App.all : current_user.apps |
10 | 12 | |
11 | - @sort = params[:sort] | |
12 | - @sort = "last_notice_at" unless %w{app message last_notice_at last_deploy_at count}.member?(@sort) | |
13 | - @order = params[:order] || "desc" | |
14 | - | |
15 | 13 | @problems = Problem.for_apps(app_scope).in_env(params[:environment]).unresolved.ordered_by(@sort, @order) |
16 | 14 | @selected_problems = params[:problems] || [] |
17 | 15 | respond_to do |format| |
... | ... | @@ -24,14 +22,12 @@ class ErrsController < ApplicationController |
24 | 22 | |
25 | 23 | def all |
26 | 24 | app_scope = current_user.admin? ? App.all : current_user.apps |
27 | - @problems = Problem.for_apps(app_scope).ordered.page(params[:page]).per(current_user.per_page) | |
25 | + @problems = Problem.for_apps(app_scope).ordered_by(@sort, @order).page(params[:page]).per(current_user.per_page) | |
28 | 26 | @selected_problems = params[:problems] || [] |
29 | 27 | end |
30 | 28 | |
31 | 29 | def show |
32 | - page = (params[:notice] || @problem.notices_count) | |
33 | - page = 1 if page.to_i.zero? | |
34 | - @notices = @problem.notices.page(page.to_i).per(1) | |
30 | + @notices = @problem.notices.reverse_ordered.page(params[:notice]).per(1) | |
35 | 31 | @notice = @notices.first |
36 | 32 | @comment = Comment.new |
37 | 33 | if request.headers['X-PJAX'] |
... | ... | @@ -41,17 +37,38 @@ class ErrsController < ApplicationController |
41 | 37 | end |
42 | 38 | |
43 | 39 | def create_issue |
44 | - set_tracker_params | |
40 | + # Create an issue on GitHub using user's github token | |
41 | + if params[:tracker] == 'user_github' | |
42 | + if !@app.github_repo? | |
43 | + flash[:error] = "This app doesn't have a GitHub repo set up." | |
44 | + elsif !current_user.github_account? | |
45 | + flash[:error] = "You haven't linked your Github account." | |
46 | + else | |
47 | + @tracker = GithubIssuesTracker.new( | |
48 | + :app => @app, | |
49 | + :username => current_user.github_login, | |
50 | + :oauth_token => current_user.github_oauth_token | |
51 | + ) | |
52 | + end | |
45 | 53 | |
46 | - if @app.issue_tracker | |
47 | - @app.issue_tracker.create_issue @problem | |
54 | + # Or, create an issue using the App's issue tracker | |
55 | + elsif @app.issue_tracker_configured? | |
56 | + @tracker = @app.issue_tracker | |
57 | + | |
58 | + # Otherwise, display error about missing tracker configuration. | |
48 | 59 | else |
49 | 60 | flash[:error] = "This app has no issue tracker setup." |
50 | 61 | end |
51 | - redirect_to app_err_path(@app, @problem) | |
52 | - rescue ActiveResource::ConnectionError => e | |
53 | - Rails.logger.error e.to_s | |
54 | - flash[:error] = "There was an error during issue creation. Check your tracker settings or try again later." | |
62 | + | |
63 | + if flash[:error].blank? && @tracker | |
64 | + begin | |
65 | + @tracker.create_issue @problem, current_user | |
66 | + rescue Exception => ex | |
67 | + Rails.logger.error "Error during issue creation: " << ex.message | |
68 | + flash[:error] = "There was an error during issue creation: #{ex.message}" | |
69 | + end | |
70 | + end | |
71 | + | |
55 | 72 | redirect_to app_err_path(@app, @problem) |
56 | 73 | end |
57 | 74 | |
... | ... | @@ -61,9 +78,6 @@ class ErrsController < ApplicationController |
61 | 78 | end |
62 | 79 | |
63 | 80 | def resolve |
64 | - # Deal with bug in mongoid where find is returning an Enumberable obj | |
65 | - @problem = @problem.first if @problem.respond_to?(:first) | |
66 | - | |
67 | 81 | @problem.resolve! |
68 | 82 | flash[:success] = 'Great news everyone! The err has been resolved.' |
69 | 83 | redirect_to :back |
... | ... | @@ -71,29 +85,6 @@ class ErrsController < ApplicationController |
71 | 85 | redirect_to app_path(@app) |
72 | 86 | end |
73 | 87 | |
74 | - def create_comment | |
75 | - @comment = Comment.new(params[:comment].merge(:user_id => current_user.id)) | |
76 | - if @comment.valid? | |
77 | - @problem.comments << @comment | |
78 | - @problem.save | |
79 | - flash[:success] = "Comment saved!" | |
80 | - else | |
81 | - flash[:error] = "I'm sorry, your comment was blank! Try again?" | |
82 | - end | |
83 | - redirect_to app_err_path(@app, @problem) | |
84 | - end | |
85 | - | |
86 | - def destroy_comment | |
87 | - @comment = Comment.find(params[:comment_id]) | |
88 | - if @comment.destroy | |
89 | - flash[:success] = "Comment deleted!" | |
90 | - else | |
91 | - flash[:error] = "Sorry, I couldn't delete your comment for some reason. I hope you don't have any sensitive information in there!" | |
92 | - end | |
93 | - redirect_to app_err_path(@app, @problem) | |
94 | - end | |
95 | - | |
96 | - | |
97 | 88 | def resolve_several |
98 | 89 | @selected_problems.each(&:resolve!) |
99 | 90 | flash[:success] = "Great news everyone! #{pluralize(@selected_problems.count, 'err has', 'errs have')} been resolved." |
... | ... | @@ -123,8 +114,8 @@ class ErrsController < ApplicationController |
123 | 114 | end |
124 | 115 | |
125 | 116 | def destroy_several |
126 | - @selected_problems.each(&:destroy) | |
127 | - flash[:notice] = "#{pluralize(@selected_problems.count, 'err has', 'errs have')} been deleted." | |
117 | + nb_problem_destroy = ProblemDestroy.execute(@selected_problems) | |
118 | + flash[:notice] = "#{pluralize(nb_problem_destroy, 'err has', 'errs have')} been deleted." | |
128 | 119 | redirect_to :back |
129 | 120 | end |
130 | 121 | |
... | ... | @@ -139,9 +130,6 @@ class ErrsController < ApplicationController |
139 | 130 | |
140 | 131 | def find_problem |
141 | 132 | @problem = @app.problems.find(params[:id]) |
142 | - | |
143 | - # Deal with bug in mogoid where find is returning an Enumberable obj | |
144 | - @problem = @problem.first if @problem.respond_to?(:first) | |
145 | 133 | end |
146 | 134 | |
147 | 135 | def set_tracker_params |
... | ... | @@ -159,5 +147,11 @@ class ErrsController < ApplicationController |
159 | 147 | @selected_problems = Array(Problem.find(err_ids)) |
160 | 148 | end |
161 | 149 | end |
150 | + | |
151 | + def set_sorting_params | |
152 | + @sort = params[:sort] | |
153 | + @sort = "last_notice_at" unless %w{app message last_notice_at last_deploy_at count}.member?(@sort) | |
154 | + @order = params[:order] || "desc" | |
155 | + end | |
162 | 156 | end |
163 | 157 | ... | ... |
app/controllers/notices_controller.rb
... | ... | @@ -5,9 +5,17 @@ class NoticesController < ApplicationController |
5 | 5 | |
6 | 6 | def create |
7 | 7 | # params[:data] if the notice came from a GET request, raw_post if it came via POST |
8 | - @notice = App.report_error!(params[:data] || request.raw_post) | |
9 | - respond_with @notice | |
8 | + notice = App.report_error!(params[:data] || request.raw_post) | |
9 | + api_xml = notice.to_xml(:only => false, :methods => [:id]) do |xml| | |
10 | + xml.url locate_url(notice.id, :host => Errbit::Config.host) | |
11 | + end | |
12 | + render :xml => api_xml | |
10 | 13 | end |
11 | 14 | |
15 | + # Redirects a notice to the problem page. Useful when using User Information at Airbrake gem. | |
16 | + def locate | |
17 | + problem = Notice.find(params[:id]).problem | |
18 | + redirect_to app_err_path(problem.app, problem) | |
19 | + end | |
12 | 20 | end |
13 | 21 | ... | ... |
... | ... | @@ -0,0 +1,38 @@ |
1 | +class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController | |
2 | + def github | |
3 | + github_login = env["omniauth.auth"].extra.raw_info.login | |
4 | + github_token = env["omniauth.auth"].credentials.token | |
5 | + github_user = User.where(:github_login => github_login).first | |
6 | + | |
7 | + # If user is already signed in, link github details to their account | |
8 | + if current_user | |
9 | + # ... unless a user is already registered with same github login | |
10 | + if github_user && github_user != current_user | |
11 | + flash[:error] = "User already registered with GitHub login '#{github_login}'!" | |
12 | + else | |
13 | + # Add github details to current user | |
14 | + update_user_with_github_attributes(current_user, github_login, github_token) | |
15 | + flash[:success] = "Successfully linked GitHub account!" | |
16 | + end | |
17 | + # User must have clicked 'link account' from their user page, so redirect there. | |
18 | + redirect_to user_path(current_user) | |
19 | + elsif github_user | |
20 | + # Store OAuth token | |
21 | + update_user_with_github_attributes(github_user, github_login, github_token) | |
22 | + flash[:success] = I18n.t "devise.omniauth_callbacks.success", :kind => "GitHub" | |
23 | + sign_in_and_redirect github_user, :event => :authentication | |
24 | + else | |
25 | + flash[:error] = "There are no authorized users with GitHub login '#{github_login}'. Please ask an administrator to register your user account." | |
26 | + redirect_to new_user_session_path | |
27 | + end | |
28 | + end | |
29 | + | |
30 | + private | |
31 | + | |
32 | + def update_user_with_github_attributes(user, login, token) | |
33 | + user.update_attributes( | |
34 | + :github_login => login, | |
35 | + :github_oauth_token => token | |
36 | + ) | |
37 | + end | |
38 | +end | ... | ... |
app/controllers/users_controller.rb
... | ... | @@ -2,24 +2,17 @@ class UsersController < ApplicationController |
2 | 2 | respond_to :html |
3 | 3 | |
4 | 4 | before_filter :require_admin!, :except => [:edit, :update] |
5 | - before_filter :find_user, :only => [:show, :edit, :update, :destroy] | |
5 | + before_filter :find_user, :only => [:show, :edit, :update, :destroy, :unlink_github] | |
6 | 6 | before_filter :require_user_edit_priviledges, :only => [:edit, :update] |
7 | 7 | |
8 | 8 | def index |
9 | 9 | @users = User.all.page(params[:page]).per(current_user.per_page) |
10 | 10 | end |
11 | 11 | |
12 | - def show | |
13 | - @user = User.find(params[:id]) | |
14 | - end | |
15 | - | |
16 | 12 | def new |
17 | 13 | @user = User.new |
18 | 14 | end |
19 | 15 | |
20 | - def edit | |
21 | - end | |
22 | - | |
23 | 16 | def create |
24 | 17 | @user = User.new(params[:user]) |
25 | 18 | |
... | ... | @@ -59,6 +52,11 @@ class UsersController < ApplicationController |
59 | 52 | redirect_to users_path |
60 | 53 | end |
61 | 54 | |
55 | + def unlink_github | |
56 | + @user.update_attributes :github_login => nil, :github_oauth_token => nil | |
57 | + redirect_to user_path(@user) | |
58 | + end | |
59 | + | |
62 | 60 | protected |
63 | 61 | |
64 | 62 | def find_user | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -58,12 +58,25 @@ module ApplicationHelper |
58 | 58 | percent = 100.0 / total.to_f |
59 | 59 | rows = tallies.map {|value, count| [(count.to_f * percent), value]} \ |
60 | 60 | .sort {|a, b| a[0] <=> b[0]} |
61 | - render :partial => "errs/tally_table", :locals => {:rows => rows} | |
61 | + render "errs/tally_table", :rows => rows | |
62 | + end | |
63 | + | |
64 | + def head(collection) | |
65 | + collection.first(head_size) | |
66 | + end | |
67 | + | |
68 | + def tail(collection) | |
69 | + collection.to_a[head_size..-1].to_a | |
62 | 70 | end |
63 | 71 | |
64 | 72 | private |
65 | 73 | def total_from_tallies(tallies) |
66 | 74 | tallies.values.inject(0) {|sum, n| sum + n} |
67 | 75 | end |
76 | + | |
77 | + def head_size | |
78 | + 4 | |
79 | + end | |
80 | + | |
68 | 81 | end |
69 | 82 | ... | ... |
app/helpers/apps_helper.rb
... | ... | @@ -10,5 +10,31 @@ module AppsHelper |
10 | 10 | return html |
11 | 11 | end |
12 | 12 | end |
13 | + | |
14 | + def any_github_repos? | |
15 | + detect_any_apps_with_attributes unless @any_github_repos | |
16 | + @any_github_repos | |
17 | + end | |
18 | + | |
19 | + def any_issue_trackers? | |
20 | + detect_any_apps_with_attributes unless @any_issue_trackers | |
21 | + @any_issue_trackers | |
22 | + end | |
23 | + | |
24 | + def any_deploys? | |
25 | + detect_any_apps_with_attributes unless @any_deploys | |
26 | + @any_deploys | |
27 | + end | |
28 | + | |
29 | + private | |
30 | + | |
31 | + def detect_any_apps_with_attributes | |
32 | + @any_github_repos = @any_issue_trackers = @any_deploys = false | |
33 | + @apps.each do |app| | |
34 | + @any_github_repos ||= app.github_repo? | |
35 | + @any_issue_trackers ||= app.issue_tracker_configured? | |
36 | + @any_deploys ||= !!app.last_deploy_at | |
37 | + end | |
38 | + end | |
13 | 39 | end |
14 | 40 | ... | ... |
app/helpers/errs_helper.rb
... | ... | @@ -13,5 +13,19 @@ module ErrsHelper |
13 | 13 | truncate(msg, :length => 300).scan(/.{1,5}/).map { |s| h(s) }.join("​").html_safe |
14 | 14 | end |
15 | 15 | end |
16 | + | |
17 | + def gravatar_tag(email, options = {}) | |
18 | + image_tag gravatar_url(email, options), :alt => email, :class => 'gravatar' | |
19 | + end | |
20 | + | |
21 | + def gravatar_url(email, options = {}) | |
22 | + default_options = { | |
23 | + :d => Errbit::Config.gravatar_default, | |
24 | + } | |
25 | + options.reverse_merge! default_options | |
26 | + params = options.extract!(:s, :d).delete_if { |k, v| v.blank? } | |
27 | + email_hash = Digest::MD5.hexdigest(email) | |
28 | + "http://www.gravatar.com/avatar/#{email_hash}?#{params.to_query}" | |
29 | + end | |
16 | 30 | end |
17 | 31 | ... | ... |
app/helpers/navigation_helper.rb
... | ... | @@ -34,4 +34,15 @@ module NavigationHelper |
34 | 34 | active |
35 | 35 | end |
36 | 36 | |
37 | + # Returns the page number in reverse order. | |
38 | + # Needed when reverse chronological paginating notices but | |
39 | + # want to display the actual chronological occurrence number. | |
40 | + # | |
41 | + # E.G. - Given 6 notices, page 2 shows the second from last | |
42 | + # occurrence indexed by 1, but it is diplaying the 5th ever | |
43 | + # occurence of that error. | |
44 | + def page_count_from_end(current_page, total_pages) | |
45 | + (total_pages.to_i - current_page.to_i) + 1 | |
46 | + end | |
47 | + | |
37 | 48 | end | ... | ... |
app/helpers/notices_helper.rb
1 | 1 | # encoding: utf-8 |
2 | 2 | module NoticesHelper |
3 | - def notice_atom_summary notice | |
4 | - render :partial => "notices/atom_entry.html.haml", :locals => {:notice => notice} | |
3 | + def in_app_backtrace_line?(line) | |
4 | + !!(line['file'] =~ %r{^\[PROJECT_ROOT\]/(?!(vendor))}) | |
5 | 5 | end |
6 | 6 | |
7 | - def link_to_source_file(app, line, text) | |
8 | - if Notice.in_app_backtrace_line?(line) | |
9 | - return link_to_github(app, line, text) if app.github_url? | |
7 | + def notice_atom_summary(notice) | |
8 | + render "notices/atom_entry.html.haml", :notice => notice | |
9 | + end | |
10 | + | |
11 | + def link_to_source_file(app, line, &block) | |
12 | + text = capture_haml(&block) | |
13 | + if in_app_backtrace_line?(line) | |
14 | + return link_to_github(app, line, text) if app.github_repo? | |
10 | 15 | if app.issue_tracker && app.issue_tracker.respond_to?(:url_to_file) |
11 | 16 | # Return link to file on tracker if issue tracker supports this |
12 | 17 | return link_to_issue_tracker_file(app, line, text) |
... | ... | @@ -30,5 +35,34 @@ module NoticesHelper |
30 | 35 | href = app.issue_tracker.url_to_file(file_path, line['number']) |
31 | 36 | link_to(text || file_name, href, :target => '_blank') |
32 | 37 | end |
38 | + | |
39 | + # Group lines into sections of in-app files and external files | |
40 | + # (An implementation of Enumerable#chunk so we don't break 1.8.7 support.) | |
41 | + def grouped_lines(lines) | |
42 | + line_groups = [] | |
43 | + lines.each do |line| | |
44 | + in_app = in_app_backtrace_line?(line) | |
45 | + if line_groups.last && line_groups.last[0] == in_app | |
46 | + line_groups.last[1] << line | |
47 | + else | |
48 | + line_groups << [in_app, [line]] | |
49 | + end | |
50 | + end | |
51 | + line_groups | |
52 | + end | |
53 | + | |
54 | + def path_for_backtrace_line(line) | |
55 | + path = File.dirname(line['file']) | |
56 | + return '' if path == '.' | |
57 | + # Remove [PROJECT_ROOT] | |
58 | + path.gsub!('[PROJECT_ROOT]/', '') | |
59 | + # Make gem name bold if starts with [GEM_ROOT]/gems | |
60 | + path.gsub!(/\[GEM_ROOT\]\/gems\/([^\/]+)/, "<strong>\\1</strong>") | |
61 | + (path << '/').html_safe | |
62 | + end | |
63 | + | |
64 | + def file_for_backtrace_line(line) | |
65 | + file = File.basename(line['file']) | |
66 | + end | |
33 | 67 | end |
34 | 68 | ... | ... |
... | ... | @@ -0,0 +1,48 @@ |
1 | +class ProblemDestroy | |
2 | + | |
3 | + attr_reader :problem | |
4 | + | |
5 | + def initialize(problem) | |
6 | + @problem = problem | |
7 | + end | |
8 | + | |
9 | + def execute | |
10 | + delete_errs | |
11 | + delete_comments | |
12 | + problem.delete | |
13 | + end | |
14 | + | |
15 | + ## | |
16 | + # Destroy all problem pass in args | |
17 | + # | |
18 | + # @params [ Array[Problem] ] problems the list of problem need to be delete | |
19 | + # can be a single Problem | |
20 | + # @return [ Integer ] | |
21 | + # the number of problem destroy | |
22 | + # | |
23 | + def self.execute(problems) | |
24 | + Array(problems).each{ |problem| | |
25 | + ProblemDestroy.new(problem).execute | |
26 | + }.count | |
27 | + end | |
28 | + | |
29 | + private | |
30 | + | |
31 | + def errs_id | |
32 | + problem.errs.only(:id).map(&:id) | |
33 | + end | |
34 | + | |
35 | + def comments_id | |
36 | + problem.comments.only(:id).map(&:id) | |
37 | + end | |
38 | + | |
39 | + def delete_errs | |
40 | + Err.collection.remove(:_id => { '$in' => errs_id }) | |
41 | + Notice.collection.remove(:err_id => { '$in' => errs_id }) | |
42 | + end | |
43 | + | |
44 | + def delete_comments | |
45 | + Comment.collection.remove(:_id => { '$in' => comments_id }) | |
46 | + end | |
47 | + | |
48 | +end | ... | ... |
app/models/app.rb
... | ... | @@ -4,7 +4,7 @@ class App |
4 | 4 | |
5 | 5 | field :name, :type => String |
6 | 6 | field :api_key |
7 | - field :github_url | |
7 | + field :github_repo | |
8 | 8 | field :resolve_errs_on_deploy, :type => Boolean, :default => false |
9 | 9 | field :notify_all_users, :type => Boolean, :default => false |
10 | 10 | field :notify_on_errs, :type => Boolean, :default => true |
... | ... | @@ -14,20 +14,13 @@ class App |
14 | 14 | # Some legacy apps may have string as key instead of BSON::ObjectID |
15 | 15 | identity :type => String |
16 | 16 | |
17 | - # There seems to be a Mongoid bug making it impossible to use String identity with references_many feature: | |
18 | - # https://github.com/mongoid/mongoid/issues/703 | |
19 | - # Using 32 character string as a workaround. | |
20 | - before_create do |r| | |
21 | - r.id = ActiveSupport::SecureRandom.hex | |
22 | - end | |
23 | - | |
24 | 17 | embeds_many :watchers |
25 | 18 | embeds_many :deploys |
26 | 19 | embeds_one :issue_tracker |
27 | 20 | has_many :problems, :inverse_of => :app, :dependent => :destroy |
28 | 21 | |
29 | 22 | before_validation :generate_api_key, :on => :create |
30 | - before_save :normalize_github_url | |
23 | + before_save :normalize_github_repo | |
31 | 24 | after_update :store_cached_attributes_on_problems |
32 | 25 | |
33 | 26 | validates_presence_of :name, :api_key |
... | ... | @@ -46,7 +39,7 @@ class App |
46 | 39 | # |
47 | 40 | # Accepts either XML or a hash with the following attributes: |
48 | 41 | # |
49 | - # * <tt>:klass</tt> - the class of error | |
42 | + # * <tt>:error_class</tt> - the class of error | |
50 | 43 | # * <tt>:message</tt> - the error message |
51 | 44 | # * <tt>:backtrace</tt> - an array of stack trace lines |
52 | 45 | # |
... | ... | @@ -66,7 +59,7 @@ class App |
66 | 59 | # |
67 | 60 | # Accepts a hash with the following attributes: |
68 | 61 | # |
69 | - # * <tt>:klass</tt> - the class of error | |
62 | + # * <tt>:error_class</tt> - the class of error | |
70 | 63 | # * <tt>:message</tt> - the error message |
71 | 64 | # * <tt>:backtrace</tt> - an array of stack trace lines |
72 | 65 | # |
... | ... | @@ -111,14 +104,19 @@ class App |
111 | 104 | alias :notify_on_deploys? :notify_on_deploys |
112 | 105 | |
113 | 106 | |
114 | - def github_url? | |
115 | - self.github_url.present? | |
107 | + def github_repo? | |
108 | + self.github_repo.present? | |
109 | + end | |
110 | + | |
111 | + def github_url | |
112 | + "https://github.com/#{github_repo}" if github_repo? | |
116 | 113 | end |
117 | 114 | |
118 | 115 | def github_url_to_file(file) |
119 | - "#{self.github_url}/blob/master#{file}" | |
116 | + "#{github_url}/blob/master#{file}" | |
120 | 117 | end |
121 | 118 | |
119 | + | |
122 | 120 | def issue_tracker_configured? |
123 | 121 | !!(issue_tracker && issue_tracker.class < IssueTracker && issue_tracker.project_id.present?) |
124 | 122 | end |
... | ... | @@ -154,7 +152,7 @@ class App |
154 | 152 | end |
155 | 153 | |
156 | 154 | def generate_api_key |
157 | - self.api_key ||= ActiveSupport::SecureRandom.hex | |
155 | + self.api_key ||= SecureRandom.hex | |
158 | 156 | end |
159 | 157 | |
160 | 158 | def check_issue_tracker |
... | ... | @@ -166,11 +164,11 @@ class App |
166 | 164 | end |
167 | 165 | end |
168 | 166 | |
169 | - def normalize_github_url | |
170 | - return if self.github_url.blank? | |
171 | - self.github_url.gsub!(%r{^http://|git@}, 'https://') | |
172 | - self.github_url.gsub!(/github\.com:/, 'github.com/') | |
173 | - self.github_url.gsub!(/\.git$/, '') | |
167 | + def normalize_github_repo | |
168 | + return if github_repo.blank? | |
169 | + github_repo.strip! | |
170 | + github_repo.sub!(/(git@|https?:\/\/)github\.com(\/|:)/, '') | |
171 | + github_repo.sub!(/\.git$/, '') | |
174 | 172 | end |
175 | 173 | end |
176 | 174 | ... | ... |
app/models/deploy.rb
... | ... | @@ -12,16 +12,11 @@ class Deploy |
12 | 12 | |
13 | 13 | embedded_in :app, :inverse_of => :deploys |
14 | 14 | |
15 | - after_create :deliver_notification, :if => :should_notify? | |
16 | 15 | after_create :resolve_app_errs, :if => :should_resolve_app_errs? |
17 | 16 | after_create :store_cached_attributes_on_problems |
18 | 17 | |
19 | 18 | validates_presence_of :username, :environment |
20 | 19 | |
21 | - def deliver_notification | |
22 | - Mailer.deploy_notification(self).deliver | |
23 | - end | |
24 | - | |
25 | 20 | def resolve_app_errs |
26 | 21 | app.problems.unresolved.in_env(environment).each {|problem| problem.resolve!} |
27 | 22 | end |
... | ... | @@ -32,10 +27,6 @@ class Deploy |
32 | 27 | |
33 | 28 | protected |
34 | 29 | |
35 | - def should_notify? | |
36 | - app.notify_on_deploys? && app.notification_recipients.any? | |
37 | - end | |
38 | - | |
39 | 30 | def should_resolve_app_errs? |
40 | 31 | app.resolve_errs_on_deploy? |
41 | 32 | end | ... | ... |
app/models/err.rb
... | ... | @@ -6,7 +6,7 @@ class Err |
6 | 6 | include Mongoid::Document |
7 | 7 | include Mongoid::Timestamps |
8 | 8 | |
9 | - field :klass | |
9 | + field :error_class | |
10 | 10 | field :component |
11 | 11 | field :action |
12 | 12 | field :environment |
... | ... | @@ -14,11 +14,11 @@ class Err |
14 | 14 | |
15 | 15 | belongs_to :problem |
16 | 16 | index :problem_id |
17 | - index :klass | |
17 | + index :error_class | |
18 | 18 | |
19 | 19 | has_many :notices, :inverse_of => :err, :dependent => :destroy |
20 | 20 | |
21 | - validates_presence_of :klass, :environment | |
21 | + validates_presence_of :error_class, :environment | |
22 | 22 | |
23 | 23 | delegate :app, :resolved?, :to => :problem |
24 | 24 | ... | ... |
app/models/error_report.rb
... | ... | @@ -2,7 +2,7 @@ require 'digest/md5' |
2 | 2 | require 'hoptoad_notifier' |
3 | 3 | |
4 | 4 | class ErrorReport |
5 | - attr_reader :klass, :message, :backtrace, :request, :server_environment, :api_key, :notifier | |
5 | + attr_reader :error_class, :message, :backtrace, :request, :server_environment, :api_key, :notifier, :user_attributes | |
6 | 6 | |
7 | 7 | def initialize(xml_or_attributes) |
8 | 8 | @attributes = (xml_or_attributes.is_a?(String) ? Hoptoad.parse_xml!(xml_or_attributes) : xml_or_attributes).with_indifferent_access |
... | ... | @@ -10,7 +10,11 @@ class ErrorReport |
10 | 10 | end |
11 | 11 | |
12 | 12 | def fingerprint |
13 | - @fingerprint ||= Digest::MD5.hexdigest(backtrace[0].to_s) | |
13 | + normalized_backtrace = backtrace[0...3].map do |trace| | |
14 | + trace.merge 'method' => trace['method'].gsub(/[0-9_]{10,}+/, "__FRAGMENT__") | |
15 | + end | |
16 | + | |
17 | + @fingerprint ||= Digest::MD5.hexdigest(normalized_backtrace.to_s) | |
14 | 18 | end |
15 | 19 | |
16 | 20 | def rails_env |
... | ... | @@ -32,13 +36,15 @@ class ErrorReport |
32 | 36 | def generate_notice! |
33 | 37 | notice = Notice.new( |
34 | 38 | :message => message, |
39 | + :error_class => error_class, | |
35 | 40 | :backtrace => backtrace, |
36 | 41 | :request => request, |
37 | 42 | :server_environment => server_environment, |
38 | - :notifier => notifier) | |
43 | + :notifier => notifier, | |
44 | + :user_attributes => user_attributes) | |
39 | 45 | |
40 | 46 | err = app.find_or_create_err!( |
41 | - :klass => klass, | |
47 | + :error_class => error_class, | |
42 | 48 | :component => component, |
43 | 49 | :action => action, |
44 | 50 | :environment => rails_env, | ... | ... |
app/models/issue_tracker.rb
... | ... | @@ -27,5 +27,12 @@ class IssueTracker |
27 | 27 | # Allows us to set the issue tracker class from a single form. |
28 | 28 | def type; self._type; end |
29 | 29 | def type=(t); self._type=t; end |
30 | + | |
31 | + def url; nil; end | |
32 | + | |
33 | + # Retrieve tracker label from either class or instance. | |
34 | + Label = '' | |
35 | + def self.label; self::Label; end | |
36 | + def label; self.class.label; end | |
30 | 37 | end |
31 | 38 | ... | ... |
app/models/issue_trackers/fogbugz_tracker.rb
... | ... | @@ -22,7 +22,7 @@ class IssueTrackers::FogbugzTracker < IssueTracker |
22 | 22 | end |
23 | 23 | end |
24 | 24 | |
25 | - def create_issue(problem) | |
25 | + def create_issue(problem, reported_by = nil) | |
26 | 26 | fogbugz = Fogbugz::Interface.new(:email => username, :password => password, :uri => "https://#{account}.fogbugz.com") |
27 | 27 | fogbugz.authenticate |
28 | 28 | |
... | ... | @@ -34,11 +34,19 @@ class IssueTrackers::FogbugzTracker < IssueTracker |
34 | 34 | issue['cols'] = ['ixBug'].join(',') |
35 | 35 | |
36 | 36 | fb_resp = fogbugz.command(:new, issue) |
37 | - problem.update_attribute :issue_link, "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}" | |
37 | + problem.update_attributes( | |
38 | + :issue_link => "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}", | |
39 | + :issue_type => Label | |
40 | + ) | |
41 | + | |
38 | 42 | end |
39 | 43 | |
40 | 44 | def body_template |
41 | 45 | @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/fogbugz_body.txt.erb")) |
42 | 46 | end |
47 | + | |
48 | + def url | |
49 | + "http://#{account}.fogbugz.com/" | |
50 | + end | |
43 | 51 | end |
44 | 52 | ... | ... |
app/models/issue_trackers/github_issues_tracker.rb
1 | 1 | class IssueTrackers::GithubIssuesTracker < IssueTracker |
2 | 2 | Label = "github" |
3 | + Note = 'Please configure your github repository in the <strong>GITHUB REPO</strong> field above.<br/>' << | |
4 | + 'Instead of providing your username & password, you can link your Github account ' << | |
5 | + 'to your user profile, and allow Errbit to create issues using your OAuth token.' | |
6 | + | |
3 | 7 | Fields = [ |
4 | - [:project_id, { | |
5 | - :label => "Account/Repository", | |
6 | - :placeholder => "errbit/errbit from https://github.com/errbit/errbit" | |
7 | - }], | |
8 | 8 | [:username, { |
9 | - :placeholder => "Your username on Github" | |
9 | + :placeholder => "Your username on GitHub" | |
10 | 10 | }], |
11 | - [:api_token, { | |
12 | - :placeholder => "Your Github API Token" | |
11 | + [:password, { | |
12 | + :placeholder => "Password for your account" | |
13 | 13 | }] |
14 | 14 | ] |
15 | 15 | |
16 | + attr_accessor :oauth_token | |
17 | + | |
18 | + def project_id | |
19 | + app.github_repo | |
20 | + end | |
21 | + | |
16 | 22 | def check_params |
17 | 23 | if Fields.detect {|f| self[f[0]].blank? } |
18 | - errors.add :base, 'You must specify your Github repository, username and API Token' | |
24 | + errors.add :base, 'You must specify your GitHub username and password' | |
19 | 25 | end |
20 | 26 | end |
21 | 27 | |
22 | - def create_issue(err) | |
23 | - client = Octokit::Client.new(:login => username, :token => api_token) | |
24 | - issue = client.create_issue(project_id, issue_title(err), body_template.result(binding).unpack('C*').pack('U*'), options = {}) | |
25 | - err.update_attribute :issue_link, issue.html_url | |
28 | + def create_issue(problem, reported_by = nil) | |
29 | + # Login using OAuth token, if given. | |
30 | + if oauth_token | |
31 | + client = Octokit::Client.new(:login => username, :oauth_token => oauth_token) | |
32 | + else | |
33 | + client = Octokit::Client.new(:login => username, :password => password) | |
34 | + end | |
35 | + | |
36 | + begin | |
37 | + issue = client.create_issue(project_id, issue_title(problem), body_template.result(binding).unpack('C*').pack('U*'), options = {}) | |
38 | + problem.update_attributes( | |
39 | + :issue_link => issue.html_url, | |
40 | + :issue_type => Label | |
41 | + ) | |
42 | + | |
43 | + rescue Octokit::Unauthorized | |
44 | + raise IssueTrackers::AuthenticationError, "Could not authenticate with GitHub. Please check your username and password." | |
45 | + end | |
26 | 46 | end |
27 | 47 | |
28 | 48 | def body_template |
29 | 49 | @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/github_issues_body.txt.erb").gsub(/^\s*/, '')) |
30 | 50 | end |
31 | -end | |
32 | 51 | |
52 | + def url | |
53 | + "https://github.com/#{project_id}/issues" | |
54 | + end | |
55 | +end | ... | ... |
app/models/issue_trackers/lighthouse_tracker.rb
... | ... | @@ -2,7 +2,7 @@ class IssueTrackers::LighthouseTracker < IssueTracker |
2 | 2 | Label = "lighthouseapp" |
3 | 3 | Fields = [ |
4 | 4 | [:account, { |
5 | - :placeholder => "abc from abc.lighthouseapp.com" | |
5 | + :placeholder => "abc from http://abc.lighthouseapp.com" | |
6 | 6 | }], |
7 | 7 | [:api_token, { |
8 | 8 | :placeholder => "API Token for your account" |
... | ... | @@ -18,12 +18,12 @@ class IssueTrackers::LighthouseTracker < IssueTracker |
18 | 18 | end |
19 | 19 | end |
20 | 20 | |
21 | - def create_issue(problem) | |
21 | + def create_issue(problem, reported_by = nil) | |
22 | 22 | Lighthouse.account = account |
23 | 23 | Lighthouse.token = api_token |
24 | 24 | # updating lighthouse account |
25 | 25 | Lighthouse::Ticket.site |
26 | - | |
26 | + Lighthouse::Ticket.format = :xml | |
27 | 27 | ticket = Lighthouse::Ticket.new(:project_id => project_id) |
28 | 28 | ticket.title = issue_title problem |
29 | 29 | |
... | ... | @@ -31,11 +31,19 @@ class IssueTrackers::LighthouseTracker < IssueTracker |
31 | 31 | |
32 | 32 | ticket.tags << "errbit" |
33 | 33 | ticket.save! |
34 | - problem.update_attribute :issue_link, "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, '') | |
34 | + problem.update_attributes( | |
35 | + :issue_link => "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, ''), | |
36 | + :issue_type => Label | |
37 | + ) | |
38 | + | |
35 | 39 | end |
36 | 40 | |
37 | 41 | def body_template |
38 | 42 | @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/lighthouseapp_body.txt.erb").gsub(/^\s*/, '')) |
39 | 43 | end |
44 | + | |
45 | + def url | |
46 | + "http://#{account}.lighthouseapp.com" | |
47 | + end | |
40 | 48 | end |
41 | 49 | ... | ... |
app/models/issue_trackers/mingle_tracker.rb
1 | 1 | class IssueTrackers::MingleTracker < IssueTracker |
2 | 2 | Label = "mingle" |
3 | + Note = "Note: <strong>CARD PROPERTIES</strong> must be comma separated <em>key = value</em> pairs" | |
4 | + | |
3 | 5 | Fields = [ |
4 | 6 | [:account, { |
5 | 7 | :label => "Mingle URL", |
6 | - :placeholder => "abc from http://abc.fogbugz.com/" | |
8 | + :placeholder => "http://mingle.example.com/" | |
7 | 9 | }], |
8 | 10 | [:project_id, { |
9 | 11 | :placeholder => "Mingle project" |
10 | 12 | }], |
11 | 13 | [:ticket_properties, { |
12 | - :label => "Card Properties (comma separated key=value pairs)", | |
14 | + :label => "Card Properties", | |
13 | 15 | :placeholder => "card_type = Defect, defect_status = Open, priority = Essential" |
14 | 16 | }], |
15 | 17 | [:username, { |
... | ... | @@ -27,7 +29,7 @@ class IssueTrackers::MingleTracker < IssueTracker |
27 | 29 | end |
28 | 30 | end |
29 | 31 | |
30 | - def create_issue(problem) | |
32 | + def create_issue(problem, reported_by = nil) | |
31 | 33 | properties = ticket_properties_hash |
32 | 34 | basic_auth = account.gsub(/https?:\/\//, "https://#{username}:#{password}@") |
33 | 35 | Mingle.set_site "#{basic_auth}/api/v1/projects/#{project_id}/" |
... | ... | @@ -41,7 +43,10 @@ class IssueTrackers::MingleTracker < IssueTracker |
41 | 43 | end |
42 | 44 | |
43 | 45 | card.save! |
44 | - problem.update_attribute :issue_link, URI.parse("#{account}/projects/#{project_id}/cards/#{card.id}").to_s | |
46 | + problem.update_attributes( | |
47 | + :issue_link => URI.parse("#{account}/projects/#{project_id}/cards/#{card.id}").to_s, | |
48 | + :issue_type => Label | |
49 | + ) | |
45 | 50 | end |
46 | 51 | |
47 | 52 | def body_template |
... | ... | @@ -56,5 +61,11 @@ class IssueTrackers::MingleTracker < IssueTracker |
56 | 61 | hash |
57 | 62 | end |
58 | 63 | end |
64 | + | |
65 | + def url | |
66 | + acc_url = account.start_with?('http') ? account : "http://#{account}" | |
67 | + URI.parse("#{acc_url}/projects/#{project_id}").to_s | |
68 | + rescue URI::InvalidURIError | |
69 | + end | |
59 | 70 | end |
60 | 71 | ... | ... |
app/models/issue_trackers/pivotal_labs_tracker.rb
... | ... | @@ -13,16 +13,30 @@ class IssueTrackers::PivotalLabsTracker < IssueTracker |
13 | 13 | end |
14 | 14 | end |
15 | 15 | |
16 | - def create_issue(problem) | |
16 | + def create_issue(problem, reported_by = nil) | |
17 | 17 | PivotalTracker::Client.token = api_token |
18 | 18 | PivotalTracker::Client.use_ssl = true |
19 | 19 | project = PivotalTracker::Project.find project_id.to_i |
20 | - story = project.stories.create :name => issue_title(problem), :story_type => 'bug', :description => body_template.result(binding) | |
21 | - problem.update_attribute :issue_link, "https://www.pivotaltracker.com/story/show/#{story.id}" | |
20 | + story = project.stories.create :name => issue_title(problem), | |
21 | + :story_type => 'bug', :description => body_template.result(binding), | |
22 | + :requested_by => reported_by.name | |
23 | + | |
24 | + if story.errors.present? | |
25 | + raise IssueTrackers::IssueTrackerError, story.errors.first | |
26 | + else | |
27 | + problem.update_attributes( | |
28 | + :issue_link => "https://www.pivotaltracker.com/story/show/#{story.id}", | |
29 | + :issue_type => Label | |
30 | + ) | |
31 | + end | |
22 | 32 | end |
23 | 33 | |
24 | 34 | def body_template |
25 | 35 | @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/pivotal_body.txt.erb")) |
26 | 36 | end |
37 | + | |
38 | + def url | |
39 | + "https://www.pivotaltracker.com/" | |
40 | + end | |
27 | 41 | end |
28 | 42 | ... | ... |
app/models/issue_trackers/redmine_tracker.rb
... | ... | @@ -25,18 +25,22 @@ class IssueTrackers::RedmineTracker < IssueTracker |
25 | 25 | end |
26 | 26 | end |
27 | 27 | |
28 | - def create_issue(problem) | |
28 | + def create_issue(problem, reported_by = nil) | |
29 | 29 | token = api_token |
30 | 30 | acc = account |
31 | 31 | RedmineClient::Base.configure do |
32 | 32 | self.token = token |
33 | 33 | self.site = acc |
34 | + self.format = :xml | |
34 | 35 | end |
35 | 36 | issue = RedmineClient::Issue.new(:project_id => project_id) |
36 | 37 | issue.subject = issue_title problem |
37 | 38 | issue.description = body_template.result(binding) |
38 | 39 | issue.save! |
39 | - problem.update_attribute :issue_link, "#{RedmineClient::Issue.site.to_s.sub(/#{RedmineClient::Issue.site.path}$/, '')}#{RedmineClient::Issue.element_path(issue.id, :project_id => project_id)}".sub(/\.xml\?project_id=#{project_id}$/, "\?project_id=#{project_id}") | |
40 | + problem.update_attributes( | |
41 | + :issue_link => "#{RedmineClient::Issue.site.to_s.sub(/#{RedmineClient::Issue.site.path}$/, '')}#{RedmineClient::Issue.element_path(issue.id, :project_id => project_id)}".sub(/\.xml\?project_id=#{project_id}$/, "\?project_id=#{project_id}"), | |
42 | + :issue_type => Label | |
43 | + ) | |
40 | 44 | end |
41 | 45 | |
42 | 46 | def url_to_file(file_path, line_number = nil) |
... | ... | @@ -49,5 +53,11 @@ class IssueTrackers::RedmineTracker < IssueTracker |
49 | 53 | def body_template |
50 | 54 | @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/textile_body.txt.erb")) |
51 | 55 | end |
56 | + | |
57 | + def url | |
58 | + acc_url = account.start_with?('http') ? account : "http://#{account}" | |
59 | + URI.parse("#{acc_url}?project_id=#{project_id}").to_s | |
60 | + rescue URI::InvalidURIError | |
61 | + end | |
52 | 62 | end |
53 | 63 | ... | ... |