Commit 104a62d5b4735badd19092524337c69593ad85a4

Authored by Fletcher Nichol
2 parents 428a011a fc7d6a61
Exists in master and in 1 other branch production

Merge remote-tracking branch 'upstream/master'

Conflicts:
	Gemfile.lock
Showing 253 changed files with 4079 additions and 3105 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 253 files displayed.

.gitignore
... ... @@ -9,6 +9,7 @@ config/deploy
9 9 config/mongoid.yml
10 10 config/newrelic.yml
11 11 .rvmrc
  12 +.idea
12 13 *~
13 14 *.rbc
14 15 .DS_Store
... ...
.travis.yml
1 1 rvm:
  2 + - 1.9.3
2 3 - 1.9.2
3 4 - 1.8.7
4 5  
5   -# Only build master branch
6   -branches:
7   - only:
8   - - master
9 6  
10 7 # To stop Travis from running tests for a new commit,
11 8 # add the following to your commit message: [ci skip]
... ...
Gemfile
1 1 source 'http://rubygems.org'
2 2  
3   -gem 'rails', '3.0.10'
  3 +gem 'rails', '3.2.5'
  4 +
4 5 gem 'nokogiri'
5   -gem 'mongoid', '~> 2.2.2'
  6 +gem 'mongoid', '~> 2.4.10'
6 7  
7 8 # force SSL
8 9 gem 'rack-ssl', :require => 'rack/ssl'
9 10  
10 11 gem 'haml'
11 12 gem 'htmlentities', "~> 4.3.0"
12   -gem 'devise', '~> 1.4.0'
  13 +
  14 +gem 'devise', '~> 1.5.3'
  15 +
  16 +gem 'omniauth-github'
  17 +gem 'oa-core'
  18 +
13 19 gem 'lighthouse-api'
14 20 gem 'oruen_redmine_client', :require => 'redmine_client'
15 21 gem 'mongoid_rails_migrations'
16 22 gem 'useragent', '~> 0.3.1'
17 23 gem 'pivotal-tracker'
18 24 gem 'ruby-fogbugz', :require => 'fogbugz'
19   -gem 'octokit'
  25 +
  26 +gem 'octokit', '~> 1.0.0'
  27 +
20 28 gem 'inherited_resources'
21 29 gem 'SystemTimer', :platform => :ruby_18
22 30 gem 'hoptoad_notifier', "~> 2.4"
23 31 gem 'actionmailer_inline_css', "~> 1.3.0"
24 32 gem 'kaminari'
  33 +gem 'rack-ssl-enforcer'
  34 +gem 'fabrication', "~> 1.3.0" # Both for tests, and loading demo data
  35 +gem 'rails_autolink', '~> 1.0.9'
25 36  
26 37 platform :ruby do
27 38 gem 'mongo', '= 1.3.1'
... ... @@ -30,19 +41,21 @@ platform :ruby do
30 41 end
31 42  
32 43 gem 'ri_cal'
  44 +gem 'yajl-ruby'
33 45  
34 46 group :development, :test do
35 47 gem 'rspec-rails', '~> 2.6'
36 48 gem 'webmock', :require => false
37   - gem 'fabrication'
38   - unless ENV['TRAVIS']
  49 + unless ENV["CI"]
39 50 gem 'ruby-debug', :platform => :mri_18
40   - gem 'ruby-debug19', :platform => :mri_19, :require => 'ruby-debug'
  51 + gem (RUBY_VERSION == "1.9.2" ? 'ruby-debug19' : 'debugger'), :platform => :mri_19
41 52 end
42 53 # gem 'rpm_contrib', :git => "git://github.com/bensymonds/rpm_contrib.git", :branch => "mongo-1.4.0_update"
43 54 end
44 55  
45 56 group :test do
  57 + gem 'capybara'
  58 + gem 'launchy'
46 59 gem 'rspec', '~> 2.6'
47 60 gem 'database_cleaner', '~> 0.6.0'
48 61 gem 'email_spec'
... ... @@ -52,3 +65,13 @@ group :heroku do
52 65 gem 'unicorn'
53 66 end
54 67  
  68 +# Use thin for development
  69 +gem 'thin', :group => :development, :platform => :ruby
  70 +
  71 +# Gems used only for assets and not required
  72 +# in production environments by default.
  73 +group :assets do
  74 + gem 'execjs'
  75 + gem 'therubyracer', :platform => :ruby # C Ruby (MRI) or Rubinius, but NOT Windows
  76 + gem 'uglifier', '>= 1.0.3'
  77 +end
... ...
Gemfile.lock
... ... @@ -2,100 +2,125 @@ 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.5)
  6 + actionpack (= 3.2.5)
  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.5)
  13 + activemodel (= 3.2.5)
  14 + activesupport (= 3.2.5)
  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.5)
  23 + activesupport (= 3.2.5)
  24 + builder (~> 3.0.0)
  25 + activerecord (3.2.5)
  26 + activemodel (= 3.2.5)
  27 + activesupport (= 3.2.5)
  28 + arel (~> 3.0.2)
  29 + tzinfo (~> 0.3.29)
  30 + activeresource (3.2.5)
  31 + activemodel (= 3.2.5)
  32 + activesupport (= 3.2.5)
  33 + activesupport (3.2.5)
  34 + i18n (~> 0.6)
  35 + multi_json (~> 1.0)
  36 + addressable (2.2.8)
  37 + arel (3.0.2)
39 38 bcrypt-ruby (3.0.1)
40 39 bson (1.3.1)
41 40 bson_ext (1.3.1)
42   - builder (2.1.2)
43   - columnize (0.3.4)
  41 + builder (3.0.0)
  42 + capybara (1.1.2)
  43 + mime-types (>= 1.16)
  44 + nokogiri (>= 1.3.3)
  45 + rack (>= 1.0.0)
  46 + rack-test (>= 0.5.4)
  47 + selenium-webdriver (~> 2.0)
  48 + xpath (~> 0.1.4)
  49 + childprocess (0.3.2)
  50 + ffi (~> 1.0.6)
  51 + columnize (0.3.6)
44 52 crack (0.3.1)
45   - css_parser (1.2.5)
  53 + css_parser (1.2.6)
46 54 addressable
  55 + rdoc
  56 + daemons (1.1.8)
47 57 database_cleaner (0.6.7)
48   - devise (1.4.7)
  58 + debugger (1.1.3)
  59 + columnize (>= 0.3.1)
  60 + debugger-linecache (~> 1.1.1)
  61 + debugger-ruby_core_source (~> 1.1.2)
  62 + debugger-linecache (1.1.1)
  63 + debugger-ruby_core_source (>= 1.1.1)
  64 + debugger-ruby_core_source (1.1.2)
  65 + devise (1.5.3)
49 66 bcrypt-ruby (~> 3.0)
50 67 orm_adapter (~> 0.0.3)
51   - warden (~> 1.0.3)
  68 + warden (~> 1.1)
52 69 diff-lcs (1.1.3)
53 70 email_spec (1.2.1)
54 71 mail (~> 2.2)
55 72 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)
  73 + erubis (2.7.0)
  74 + eventmachine (0.12.10)
  75 + execjs (1.4.0)
  76 + multi_json (~> 1.0)
  77 + fabrication (1.3.2)
  78 + faraday (0.8.1)
  79 + multipart-post (~> 1.1)
  80 + faraday_middleware (0.8.7)
  81 + faraday (>= 0.7.4, < 0.9)
  82 + ffi (1.0.11)
  83 + haml (3.1.5)
66 84 happymapper (0.4.0)
67 85 libxml-ruby (~> 2.0)
68 86 has_scope (0.5.1)
69   - hashie (1.0.0)
  87 + hashie (1.2.0)
  88 + hike (1.2.1)
70 89 hoptoad_notifier (2.4.11)
71 90 activesupport
72 91 builder
73   - htmlentities (4.3.0)
74   - i18n (0.5.0)
75   - inherited_resources (1.3.0)
  92 + htmlentities (4.3.1)
  93 + i18n (0.6.0)
  94 + inherited_resources (1.3.1)
76 95 has_scope (~> 0.5.0)
77   - responders (~> 0.6.0)
78   - kaminari (0.12.4)
79   - rails (>= 3.0.0)
80   - kgio (2.6.0)
81   - libxml-ruby (2.2.2)
  96 + responders (~> 0.6)
  97 + journey (1.0.3)
  98 + json (1.7.3)
  99 + kaminari (0.13.0)
  100 + actionpack (>= 3.0.0)
  101 + activesupport (>= 3.0.0)
  102 + railties (>= 3.0.0)
  103 + kgio (2.7.4)
  104 + launchy (2.1.0)
  105 + addressable (~> 2.2.6)
  106 + libv8 (3.3.10.4)
  107 + libwebsocket (0.1.3)
  108 + addressable
  109 + libxml-ruby (2.3.2)
82 110 lighthouse-api (2.0)
83 111 activeresource (>= 3.0.0)
84 112 activesupport (>= 3.0.0)
85 113 linecache (0.46)
86 114 rbx-require-relative (> 0.0.4)
87   - linecache19 (0.5.12)
88   - ruby_core_source (>= 0.1.4)
89   - mail (2.2.19)
90   - activesupport (>= 2.3.6)
  115 + mail (2.4.4)
91 116 i18n (>= 0.4.0)
92 117 mime-types (~> 1.16)
93 118 treetop (~> 1.4.8)
94   - mime-types (1.16)
  119 + mime-types (1.18)
95 120 mongo (1.3.1)
96 121 bson (>= 1.3.1)
97   - mongoid (2.2.4)
98   - activemodel (~> 3.0)
  122 + mongoid (2.4.10)
  123 + activemodel (~> 3.1)
99 124 mongo (~> 1.3)
100 125 tzinfo (~> 0.3.22)
101 126 mongoid_rails_migrations (0.0.14)
... ... @@ -103,19 +128,32 @@ GEM
103 128 bundler (>= 1.0.0)
104 129 rails (>= 3.0.0)
105 130 railties (>= 3.0.0)
106   - multi_json (1.0.3)
107   - multipart-post (1.1.3)
  131 + multi_json (1.3.6)
  132 + multipart-post (1.1.5)
108 133 nokogiri (1.5.0)
109   - octokit (0.6.4)
110   - addressable (~> 2.2.6)
111   - faraday (~> 0.7.3)
112   - faraday_middleware (~> 0.7.0.rc1)
113   - hashie (~> 1.0.0)
114   - multi_json (~> 1.0.2)
115   - orm_adapter (0.0.5)
  134 + oa-core (0.3.2)
  135 + oauth2 (0.5.2)
  136 + faraday (~> 0.7)
  137 + multi_json (~> 1.0)
  138 + octokit (1.0.7)
  139 + addressable (~> 2.2)
  140 + faraday (~> 0.8)
  141 + faraday_middleware (~> 0.8)
  142 + hashie (~> 1.2)
  143 + multi_json (~> 1.3)
  144 + omniauth (1.0.3)
  145 + hashie (~> 1.2)
  146 + rack
  147 + omniauth-github (1.0.1)
  148 + omniauth (~> 1.0)
  149 + omniauth-oauth2 (~> 1.0)
  150 + omniauth-oauth2 (1.0.0)
  151 + oauth2 (~> 0.5.0)
  152 + omniauth (~> 1.0)
  153 + orm_adapter (0.0.7)
116 154 oruen_redmine_client (0.0.1)
117 155 activeresource (>= 2.3.0)
118   - pivotal-tracker (0.4.1)
  156 + pivotal-tracker (0.5.4)
119 157 builder
120 158 builder
121 159 happymapper (>= 0.3.2)
... ... @@ -124,88 +162,104 @@ GEM
124 162 nokogiri (~> 1.4)
125 163 rest-client (~> 1.6.0)
126 164 rest-client (~> 1.6.0)
127   - polyglot (0.3.2)
  165 + polyglot (0.3.3)
128 166 premailer (1.7.3)
129 167 css_parser (>= 1.1.9)
130 168 htmlentities (>= 4.0.0)
131   - rack (1.2.4)
132   - rack-mount (0.6.14)
133   - rack (>= 1.0.0)
  169 + rack (1.4.1)
  170 + rack-cache (1.2)
  171 + rack (>= 0.4)
134 172 rack-ssl (1.3.2)
135 173 rack
136   - rack-test (0.5.7)
  174 + rack-ssl-enforcer (0.2.4)
  175 + rack-test (0.6.1)
137 176 rack (>= 1.0)
138   - rails (3.0.10)
139   - actionmailer (= 3.0.10)
140   - actionpack (= 3.0.10)
141   - activerecord (= 3.0.10)
142   - activeresource (= 3.0.10)
143   - activesupport (= 3.0.10)
  177 + rails (3.2.5)
  178 + actionmailer (= 3.2.5)
  179 + actionpack (= 3.2.5)
  180 + activerecord (= 3.2.5)
  181 + activeresource (= 3.2.5)
  182 + activesupport (= 3.2.5)
144 183 bundler (~> 1.0)
145   - railties (= 3.0.10)
146   - railties (3.0.10)
147   - actionpack (= 3.0.10)
148   - activesupport (= 3.0.10)
  184 + railties (= 3.2.5)
  185 + rails_autolink (1.0.9)
  186 + rails (~> 3.1)
  187 + railties (3.2.5)
  188 + actionpack (= 3.2.5)
  189 + activesupport (= 3.2.5)
  190 + rack-ssl (~> 1.3.2)
149 191 rake (>= 0.8.7)
150 192 rdoc (~> 3.4)
151   - thor (~> 0.14.4)
  193 + thor (>= 0.14.6, < 2.0)
152 194 raindrops (0.8.0)
153   - rake (0.9.2)
154   - rbx-require-relative (0.0.5)
155   - rdoc (3.9.4)
156   - responders (0.6.4)
  195 + rake (0.9.2.2)
  196 + rbx-require-relative (0.0.9)
  197 + rdoc (3.12)
  198 + json (~> 1.4)
  199 + responders (0.9.1)
  200 + railties (~> 3.1)
157 201 rest-client (1.6.7)
158 202 mime-types (>= 1.16)
159 203 ri_cal (0.8.8)
160   - rspec (2.6.0)
161   - rspec-core (~> 2.6.0)
162   - rspec-expectations (~> 2.6.0)
163   - rspec-mocks (~> 2.6.0)
164   - rspec-core (2.6.4)
165   - rspec-expectations (2.6.0)
166   - diff-lcs (~> 1.1.2)
167   - rspec-mocks (2.6.0)
168   - rspec-rails (2.6.1)
169   - actionpack (~> 3.0)
170   - activesupport (~> 3.0)
171   - railties (~> 3.0)
172   - rspec (~> 2.6.0)
  204 + rspec (2.10.0)
  205 + rspec-core (~> 2.10.0)
  206 + rspec-expectations (~> 2.10.0)
  207 + rspec-mocks (~> 2.10.0)
  208 + rspec-core (2.10.0)
  209 + rspec-expectations (2.10.0)
  210 + diff-lcs (~> 1.1.3)
  211 + rspec-mocks (2.10.1)
  212 + rspec-rails (2.10.1)
  213 + actionpack (>= 3.0)
  214 + activesupport (>= 3.0)
  215 + railties (>= 3.0)
  216 + rspec (~> 2.10.0)
173 217 ruby-debug (0.10.4)
174 218 columnize (>= 0.1)
175 219 ruby-debug-base (~> 0.10.4.0)
176 220 ruby-debug-base (0.10.4)
177 221 linecache (>= 0.3)
178   - ruby-debug-base19 (0.11.25)
179   - columnize (>= 0.3.1)
180   - linecache19 (>= 0.5.11)
181   - ruby_core_source (>= 0.1.4)
182   - ruby-debug19 (0.11.6)
183   - columnize (>= 0.3.1)
184   - linecache19 (>= 0.5.11)
185   - ruby-debug-base19 (>= 0.11.19)
186   - ruby-fogbugz (0.0.4)
  222 + ruby-fogbugz (0.1.1)
187 223 crack
188   - typhoeus
189   - ruby_core_source (0.1.5)
190   - archive-tar-minitar (>= 0.5.2)
191   - thor (0.14.6)
  224 + rubyzip (0.9.8)
  225 + selenium-webdriver (2.21.2)
  226 + childprocess (>= 0.2.5)
  227 + ffi (~> 1.0)
  228 + libwebsocket (~> 0.1.3)
  229 + multi_json (~> 1.0)
  230 + rubyzip
  231 + sprockets (2.1.3)
  232 + hike (~> 1.2)
  233 + rack (~> 1.0)
  234 + tilt (~> 1.1, != 1.3.0)
  235 + therubyracer (0.10.1)
  236 + libv8 (~> 3.3.10)
  237 + thin (1.3.1)
  238 + daemons (>= 1.0.9)
  239 + eventmachine (>= 0.12.6)
  240 + rack (>= 1.0.0)
  241 + thor (0.15.2)
  242 + tilt (1.3.3)
192 243 treetop (1.4.10)
193 244 polyglot
194 245 polyglot (>= 0.3.1)
195   - typhoeus (0.2.4)
196   - mime-types
197   - mime-types
198   - tzinfo (0.3.30)
199   - unicorn (4.1.1)
200   - kgio (~> 2.4)
  246 + tzinfo (0.3.33)
  247 + uglifier (1.2.4)
  248 + execjs (>= 0.3.0)
  249 + multi_json (>= 1.0.2)
  250 + unicorn (4.3.1)
  251 + kgio (~> 2.6)
201 252 rack
202   - raindrops (~> 0.6)
  253 + raindrops (~> 0.7)
203 254 useragent (0.3.2)
204   - warden (1.0.5)
  255 + warden (1.2.0)
205 256 rack (>= 1.0)
206   - webmock (1.7.6)
207   - addressable (~> 2.2, > 2.2.5)
  257 + webmock (1.8.6)
  258 + addressable (>= 2.2.7)
208 259 crack (>= 0.1.7)
  260 + xpath (0.1.4)
  261 + nokogiri (~> 1.3)
  262 + yajl-ruby (1.1.0)
209 263  
210 264 PLATFORMS
211 265 ruby
... ... @@ -215,31 +269,42 @@ DEPENDENCIES
215 269 actionmailer_inline_css (~> 1.3.0)
216 270 bson (= 1.3.1)
217 271 bson_ext (= 1.3.1)
  272 + capybara
218 273 database_cleaner (~> 0.6.0)
219   - devise (~> 1.4.0)
  274 + debugger
  275 + devise (~> 1.5.3)
220 276 email_spec
221   - fabrication
  277 + execjs
  278 + fabrication (~> 1.3.0)
222 279 haml
223 280 hoptoad_notifier (~> 2.4)
224 281 htmlentities (~> 4.3.0)
225 282 inherited_resources
226 283 kaminari
  284 + launchy
227 285 lighthouse-api
228 286 mongo (= 1.3.1)
229   - mongoid (~> 2.2.2)
  287 + mongoid (~> 2.4.10)
230 288 mongoid_rails_migrations
231 289 nokogiri
232   - octokit
  290 + oa-core
  291 + octokit (~> 1.0.0)
  292 + omniauth-github
233 293 oruen_redmine_client
234 294 pivotal-tracker
235 295 rack-ssl
236   - rails (= 3.0.10)
  296 + rack-ssl-enforcer
  297 + rails (= 3.2.5)
  298 + rails_autolink (~> 1.0.9)
237 299 ri_cal
238 300 rspec (~> 2.6)
239 301 rspec-rails (~> 2.6)
240 302 ruby-debug
241   - ruby-debug19
242 303 ruby-fogbugz
  304 + therubyracer
  305 + thin
  306 + uglifier (>= 1.0.3)
243 307 unicorn
244 308 useragent (~> 0.3.1)
245 309 webmock
  310 + yajl-ruby
... ...
README.md
1   -Errbit [![TravisCI](http://travis-ci.org/errbit/errbit.png?branch=master)](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 147 heroku addons:add mongohq:free
  148 +cp -f config/mongoid.mongohq.yml config/mongoid.yml
  149 +git add -f config/mongoid.yml
  150 +git commit -m "Added mongoid config for MongoHQ"
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,13 +155,13 @@ 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.
124 165  
125 166 ```bash
126 167 # Install the heroku cron addon, to clear resolved errors daily:
... ... @@ -130,17 +171,59 @@ heroku addons:add cron:daily
130 171 heroku rake errbit:db:clear_resolved
131 172 ```
132 173  
133   - 5. Enjoy!
  174 + * You may want to enable the deployment hook for heroku :
  175 +
  176 +```bash
  177 +heroku addons:add deployhooks:http url="http://YOUR_ERRBIT_HOST/deploys.txt?api_key=YOUR_API_KEY"
  178 +```
  179 +
  180 + * Enjoy!
  181 +
  182 +
  183 +Authentication
  184 +--------------
  185 +
  186 +### Configuring GitHub authentication:
  187 +
  188 + * In `config/config.yml`, set `github_authentication` to `true`
  189 + * Register your instance of Errbit at: https://github.com/settings/applications
  190 +
  191 +If you hosted Errbit at errbit.example.com, you would fill in:
  192 +
  193 +<table>
  194 + <tr><th>URL:</th><td>http://errbit.example.com/</td></tr>
  195 + <tr><th>Callback URL:</th><td>http://errbit.example.com/users/auth/github</td></tr>
  196 +</table>
  197 +
  198 + * After you have registered your app, set `github_client_id` and `github_secret`
  199 + in `config/config.yml` with your app's Client ID and Secret key.
  200 +
134 201  
  202 +After you have followed these instructions, you will be able to **Sign in with GitHub** on the Login page.
135 203  
136   -**Configuring LDAP authentication:**
  204 +You will also be able to link your GitHub profile to your user account on your **Edit profile** page.
137 205  
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
  206 +If you have signed in with GitHub, or linked your GitHub profile, and the App has a GitHub repo configured,
  207 +then you will be able to create issues on GitHub.
  208 +You will still be able to create an issue on the App's configured issue tracker.
  209 +
  210 +You can change the requested account permissions by setting `github_access_scope` to:
  211 +
  212 +<table>
  213 + <tr><th>['repo'] </th><td>Allow creating issues for public and private repos.</td></tr>
  214 + <tr><th>['public_repo'] </th><td>Only allow creating issues for public repos.</td></tr>
  215 + <tr><th>[] </th><td>No permission to create issues on any repos.</td></tr>
  216 +</table>
  217 +
  218 +
  219 +### Configuring LDAP authentication:
  220 +
  221 + * In `config/config.yml`, set `user_has_username` to `true`
  222 + * Follow the instructions at https://github.com/cschiewek/devise_ldap_authenticatable
140 223 to set up the devise_ldap_authenticatable gem.
141 224  
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`:
  225 + * If you are authenticating by `username`, you will need to set the user's email manually
  226 + before authentication. You must add the following lines to `app/models/user.rb`:
144 227  
145 228 ```ruby
146 229 before_save :set_ldap_email
... ... @@ -151,7 +234,7 @@ heroku rake errbit:db:clear_resolved
151 234  
152 235 Upgrading
153 236 ---------
154   -*Note*: When upgrading Errbit, please run:
  237 +When upgrading Errbit, please run:
155 238  
156 239 ```bash
157 240 git pull origin master # assuming origin is the github.com/errbit/errbit repo
... ... @@ -161,6 +244,29 @@ rake db:migrate
161 244 If we change the way that data is stored, this will run any migrations to bring your database up to date.
162 245  
163 246  
  247 +User information in error reports
  248 +---------------------------------
  249 +
  250 +Errbit can now display information about the user who experienced an error.
  251 +This gives you the ability to ask the user for more information,
  252 +and let them know when you've fixed the bug.
  253 +
  254 +If you would like to include information about the current user in your error reports,
  255 +you can replace the `airbrake` gem in your Gemfile with `airbrake_user_attributes`,
  256 +which wraps the `airbrake` gem and injects user information.
  257 +It will inject information about the current user into the error report
  258 +if your Rails app's controller responds to a `#current_user` method.
  259 +The user's attributes are filtered to remove authentication fields.
  260 +
  261 +If user information is received with an error report,
  262 +it will be displayed under the *User Details* tab:
  263 +
  264 +
  265 +![User details tab](http://errbit.github.com/errbit/images/error_user_information.png)
  266 +
  267 +(This tab will be hidden if no user information is available.)
  268 +
  269 +
164 270 Issue Trackers
165 271 --------------
166 272  
... ... @@ -186,13 +292,17 @@ Issue Trackers
186 292 * Account is the host of your mingle installation. i.e. **https://mingle.example.com** *note*: You should use SSL if possible.
187 293 * Errbit uses 'sign-in name' & password authentication. You may want to set up an **errbit** user with limited rights.
188 294 * 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
  295 +* Card properties are comma separated key value pairs. You must specify a 'card_type', but anything else is optional, e.g.:
  296 +
  297 +```
  298 +card_type = Defect, status = Open, priority = Essential
  299 +```
190 300  
191   -**Github Issues Integration**
  301 +**GitHub Issues Integration**
192 302  
193 303 * 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.
  304 +* You will also need to provide your username and password for your GitHub account.
  305 + * (We'd really appreciate it if you wanted to help us implement OAuth instead!)
196 306  
197 307  
198 308 What if Errbit has an error?
... ... @@ -201,23 +311,23 @@ What if Errbit has an error?
201 311 Errbit will log it's own errors to an internal app named **Self.Errbit**.
202 312 The **Self.Errbit** app will be automatically created whenever the first error happens.
203 313  
204   -If your Errbit instance has logged an error, we would appreciate a bug report on Github Issues.
  314 +If your Errbit instance has logged an error, we would appreciate a bug report on GitHub Issues.
205 315 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:
  316 +or you can set up the GitHub Issues tracker for your **Self.Errbit** app:
207 317  
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 hoptoad:test`.)
  318 + * 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 319  
210   - 2. In the **Issue Tracker** section, click **Github Issues**.
  320 + * In the **Issue Tracker** section, click **GitHub Issues**.
211 321  
212   - 3. Fill in the **Account/Repository** field with **errbit/errbit**.
  322 + * Fill in the **Account/Repository** field with **errbit/errbit**.
213 323  
214   - 4. Fill in the **Username** field with your github username.
  324 + * Fill in the **Username** field with your github username.
215 325  
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).
  326 + * 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 327  
218   - 6. Save the settings by clicking **Update App** (or **Add App**)
  328 + * Save the settings by clicking **Update App** (or **Add App**)
219 329  
220   - 7. You can now easily post bug reports to Github Issues by clicking the **Create Issue** button on a **Self.Errbit** error.
  330 + * You can now easily post bug reports to GitHub Issues by clicking the **Create Issue** button on a **Self.Errbit** error.
221 331  
222 332  
223 333 TODO
... ...
Rakefile
... ... @@ -7,7 +7,7 @@ require &#39;bundler&#39;
7 7  
8 8 Errbit::Application.load_tasks
9 9  
10   -Rake::Task[:default].clear
  10 +Rake::Task[:default].clear if Rake::Task.task_defined?(:default)
11 11  
12 12 namespace :spec do
13 13 desc "Preparing test env"
... ...
app/assets/images/alerts/help.gif 0 → 100755

1.54 KB

app/assets/images/alerts/important.gif 0 → 100755

1.46 KB

app/assets/images/alerts/info.gif 0 → 100755

1.45 KB

app/assets/images/alerts/title.gif 0 → 100755

317 Bytes

app/assets/images/fogbugz_create.png 0 → 100644

4.78 KB

app/assets/images/fogbugz_goto.png 0 → 100644

4.78 KB

app/assets/images/fogbugz_inactive.png 0 → 100644

4.58 KB

app/assets/images/github_create.png 0 → 100644

2.3 KB

app/assets/images/github_goto.png 0 → 100644

2.3 KB

app/assets/images/github_inactive.png 0 → 100644

2.03 KB

app/assets/images/lighthouseapp_create.png 0 → 100644

1.57 KB

app/assets/images/lighthouseapp_goto.png 0 → 100644

1.61 KB

app/assets/images/lighthouseapp_inactive.png 0 → 100644

1.57 KB

app/assets/images/loader.gif 0 → 100644

1.7 KB

app/assets/images/mingle_create.png 0 → 100644

2.41 KB

app/assets/images/mingle_goto.png 0 → 100644

2.41 KB

app/assets/images/mingle_inactive.png 0 → 100644

1.96 KB

app/assets/images/none_create.png 0 → 100644

799 Bytes

app/assets/images/none_inactive.png 0 → 100644

818 Bytes

app/assets/images/pivotal_create.png 0 → 100644

1.5 KB

app/assets/images/pivotal_goto.png 0 → 100644

1.52 KB

app/assets/images/pivotal_inactive.png 0 → 100644

1.56 KB

app/assets/images/redmine_create.png 0 → 100644

1.47 KB

app/assets/images/redmine_goto.png 0 → 100644

1.52 KB

app/assets/images/redmine_inactive.png 0 → 100644

1.52 KB

app/assets/images/thumbs-up.png 0 → 100644

1.42 KB

app/assets/javascripts/.gitkeep 0 → 100644
app/assets/javascripts/application.js.erb 0 → 100644
... ... @@ -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 %>
... ...
app/assets/javascripts/apps.show.js 0 → 100644
... ... @@ -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 +});
... ...
app/assets/javascripts/errbit.js 0 → 100644
... ... @@ -0,0 +1,134 @@
  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 + init();
  134 +});
... ...
app/assets/javascripts/form.js 0 → 100644
... ... @@ -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 +
... ...
app/assets/javascripts/jquery.alerts.js 0 → 100644
... ... @@ -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: '&nbsp;OK&nbsp;', // text for the OK button
  42 + cancelButton: '&nbsp;Cancel&nbsp;', // 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
... ...
app/assets/javascripts/jquery.js 0 → 100644
... ... @@ -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
... ...
app/assets/javascripts/jquery.pjax.js 0 → 100644
... ... @@ -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);
... ...
app/assets/javascripts/rails.alerts.js 0 → 100644
... ... @@ -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 +
... ...
app/assets/javascripts/rails.js 0 → 100644
... ... @@ -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 +
... ...
app/assets/javascripts/underscore-1.1.6.js 0 → 100644
... ... @@ -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
... ...
app/assets/stylesheets/.gitkeep 0 → 100644
app/assets/stylesheets/application.css.erb 0 → 100644
... ... @@ -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 %>
... ...
app/assets/stylesheets/errbit.css 0 → 100644
... ... @@ -0,0 +1,873 @@
  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 +}
  652 +table.errs td.message em {
  653 + color: #727272;
  654 + font-size: 0.9em;
  655 +}
  656 +
  657 +table.errs tr.resolved td > * {
  658 + opacity: 0.5;
  659 + -moz-opacity: 0.5;
  660 + -webkit-opacity: 0.5;
  661 +}
  662 +
  663 +/* Tally tables */
  664 +table.tally {
  665 + border:none;
  666 +}
  667 +table.tally td,
  668 +table.tally th {
  669 + border:none !important;
  670 + background:none !important;
  671 + padding:8px 0 0;
  672 +}
  673 +table.tally tbody tr:first-child td,
  674 +table.tally tbody tr:first-child th {
  675 + padding-top:0;
  676 +}
  677 +table.tally td.percent {
  678 + padding-right: 10px;
  679 +}
  680 +table.tally th.value {
  681 + width: 100%;
  682 + text-transform: none;
  683 +}
  684 +
  685 +/* Deploys table */
  686 +table.deploys td.when {
  687 + width: 102px;
  688 +}
  689 +
  690 +/* Resolve Errs */
  691 +#action-bar a.resolve {
  692 + background: transparent url(images/icons/thumbs-up.png) 6px 5px no-repeat;
  693 +}
  694 +
  695 +/* Go Up */
  696 +#action-bar a.up {
  697 + background: transparent url(images/icons/up.png) 6px 5px no-repeat;
  698 +}
  699 +
  700 +/* Notices Pagination */
  701 +.notice-pagination {
  702 + float: left;
  703 + margin-right: 10px;
  704 +}
  705 +
  706 +.notice-pagination-loader {
  707 + visibility: hidden;
  708 + float: left;
  709 + margin-right: 2em;
  710 +}
  711 +.notice-pagination-loader img {
  712 + vertical-align: middle
  713 +}
  714 +
  715 +
  716 +/* Backtrace */
  717 +.window {
  718 + width: 100%;
  719 + margin-bottom: 1em;
  720 + overflow: auto;
  721 + border:1px solid #ccc;
  722 + padding:1px;
  723 +}
  724 +
  725 +.window table {
  726 + margin: 0;
  727 +}
  728 +
  729 +table.backtrace {
  730 + padding: 8px 0;
  731 + background-color: #222;
  732 +}
  733 +
  734 +table.backtrace td {
  735 + width: 100%;
  736 + padding: 0;
  737 + margin: 0;
  738 + color: #C7C7C7;
  739 + background-color: #222;
  740 +}
  741 +
  742 +table.backtrace td, table.backtrace th {
  743 + border-top: none;
  744 +}
  745 +
  746 +/* remove alternating color rules */
  747 +table.backtrace tr:nth-child(2n+1) td { background-color: #222; }
  748 +table.backtrace tr:first-child td { border-top: 0; }
  749 +
  750 +table.backtrace th.line-numbers {
  751 + border-bottom: 1px solid #F0F0F0;
  752 + font-size: 13px;
  753 + text-align: right;
  754 + vertical-align: top;
  755 + padding: 1px 6px 1px 7px;
  756 +}
  757 +
  758 +table.backtrace td.line {
  759 + font-size: 13px;
  760 + padding: 2px 8px;
  761 + vertical-align: top;
  762 + white-space: nowrap;
  763 +}
  764 +table.backtrace td.line .file {
  765 + font-weight: bold;
  766 +}
  767 +table.backtrace td.line .method {
  768 + color: #aaa;
  769 + font-weight: bold;
  770 +}
  771 +
  772 +table.backtrace td.line.in-app {
  773 + color: #2adb2e;
  774 + background-color: #2f2f2f;
  775 +}
  776 +table.backtrace td.line.in-app .path,
  777 +table.backtrace td.line.in-app .number { color: #2ACB2E; }
  778 +table.backtrace td.line.in-app .file { color: #3AFB3E; }
  779 +table.backtrace td.line.in-app .method { color: #2ACB2E; }
  780 +
  781 +table.backtrace td.line.in-app a .path,
  782 +table.backtrace td.line.in-app a .number,
  783 +table.backtrace td.line.in-app a:hover { color: #21B4FF; }
  784 +table.backtrace td.line.in-app a .file { color: #31C4FF; }
  785 +
  786 +/* External backtrace classes and separators */
  787 +table.backtrace tr.hidden_external_backtrace {
  788 + display: none;
  789 +}
  790 +table.backtrace td.backtrace_separator span {
  791 + cursor: pointer;
  792 + display: inline-block;
  793 + font-size: 17px;
  794 + font-weight: bold;
  795 + padding: 0px 11px 5px;
  796 + margin: 4px 0;
  797 + background-color: #444444;
  798 + border: 1px solid #555555;
  799 +}
  800 +table.backtrace td.backtrace_separator span:hover {
  801 + background-color: #666666;
  802 + border: 1px solid #777777;
  803 +}
  804 +
  805 +
  806 +
  807 +/* Extra empty rows at top and bottom of table */
  808 +table.backtrace tr.padding th, table.backtrace tr.padding td {
  809 + height: 10px;
  810 + margin: 0;
  811 + padding: 0;
  812 +}
  813 +
  814 +h3#watchers_toggle, h3#repository_toggle, h3#deploys_toggle {
  815 + cursor: pointer;
  816 +}
  817 +
  818 +span.click_span {
  819 + font-size: 0.7em;
  820 +}
  821 +
  822 +#deploys_div, #repository_div, #watchers_div {
  823 + display: none;
  824 +}
  825 +
  826 +/* Comments */
  827 +#content-comments form p {
  828 + margin: 30px 0 0 0;
  829 + text-transform: uppercase;
  830 +}
  831 +table.comment tbody th {
  832 + text-transform: none;
  833 + font-weight: normal;
  834 + height: 20px;
  835 + line-height: 0.5em;
  836 +}
  837 +table.comment tbody td {
  838 + background-color: #F9F9F9;
  839 +}
  840 +#content-comments a.destroy-comment {
  841 + color: #EE0000;
  842 + margin-right: 5px;
  843 +}
  844 +#content-comments a.destroy-comment:hover {
  845 + text-decoration: none;
  846 +}
  847 +#content-comments #comment_submit {
  848 + margin-top: 15px;
  849 +}
  850 +/* Inline comments in tables */
  851 +table.errs tr td.message .inline_comment {
  852 + display: inline-block;
  853 + padding: 3px 7px;
  854 + margin: 6px 0;
  855 + background-color: #DAE5FF;
  856 + border: 1px solid #E2E2E2;
  857 + text-shadow: 0 1px 0 #FAFAFA;
  858 + font-style: normal;
  859 +}
  860 +table.errs tr:hover td.message .inline_comment {
  861 + background-color: #D5E0FA;
  862 + border-color: #DBDBDB;
  863 + text-shadow: 0 1px 0 #FFFFFF;
  864 +}
  865 +table.errs tr td.message .inline_comment em {
  866 + color: #444;
  867 +}
  868 +table.errs tr td.message .inline_comment em.commenter {
  869 + color: #777;
  870 +}
  871 +
  872 +.current.asc:after { content: ' ↑'; }
  873 +.current.desc:after { content: ' ↓'; }
... ...
app/assets/stylesheets/images/button-bg.png 0 → 100644

148 Bytes

app/assets/stylesheets/images/content-fade.png 0 → 100644

174 Bytes

app/assets/stylesheets/images/error-badge-bg.png 0 → 100644

119 Bytes

app/assets/stylesheets/images/header.png 0 → 100644

196 Bytes

app/assets/stylesheets/images/icons/add.png 0 → 100644

1.04 KB

app/assets/stylesheets/images/icons/briefcase.png 0 → 100644

675 Bytes

app/assets/stylesheets/images/icons/bullet-red-sm.png 0 → 100644

417 Bytes

app/assets/stylesheets/images/icons/cross.png 0 → 100755

473 Bytes

app/assets/stylesheets/images/icons/edit.png 0 → 100644

1.22 KB

app/assets/stylesheets/images/icons/error.png 0 → 100644

1.05 KB

app/assets/stylesheets/images/icons/github.png 0 → 100644

1.94 KB

app/assets/stylesheets/images/icons/ical.png 0 → 100644

1.86 KB

app/assets/stylesheets/images/icons/notice.png 0 → 100644

157 Bytes

app/assets/stylesheets/images/icons/required.png 0 → 100644

250 Bytes

app/assets/stylesheets/images/icons/right-arrow.png 0 → 100644

1.11 KB

app/assets/stylesheets/images/icons/success.png 0 → 100644

1.06 KB

app/assets/stylesheets/images/icons/thumbs-up.png 0 → 100644

1.42 KB

app/assets/stylesheets/images/icons/trash.png 0 → 100644

1.65 KB

app/assets/stylesheets/images/icons/unlink_github.png 0 → 100644

2.02 KB

app/assets/stylesheets/images/icons/up.png 0 → 100644

1.11 KB

app/assets/stylesheets/images/icons/user.png 0 → 100644

877 Bytes

app/assets/stylesheets/images/icons/warning.png 0 → 100644

674 Bytes

app/assets/stylesheets/images/logo.png 0 → 100644

3.21 KB

app/assets/stylesheets/images/notebook.png 0 → 100644

133 Bytes

app/assets/stylesheets/images/resolved-badge-bg.png 0 → 100644

119 Bytes

app/assets/stylesheets/issue_tracker_icons.css.erb 0 → 100644
... ... @@ -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 %>
... ...
app/assets/stylesheets/jquery.alerts.css 0 → 100644
... ... @@ -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
... ...
app/assets/stylesheets/mailers/mailer.css 0 → 100644
... ... @@ -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 +
... ...
app/assets/stylesheets/reset.css 0 → 100644
... ... @@ -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 &lt; 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 &lt; 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 = "app" unless %w{message last_notice_at last_deploy_at count}.member?(@sort)
14   - @order = "asc" unless (@order == "desc")
  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 &lt; 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
... ...
app/controllers/comments_controller.rb 0 → 100644
... ... @@ -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 &lt; 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 &lt; 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 &lt; 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 + :login => 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 &lt; 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 &lt; 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."
... ... @@ -139,9 +130,6 @@ class ErrsController &lt; 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 &lt; 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/users/omniauth_callbacks_controller.rb 0 → 100644
... ... @@ -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 &lt; 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 &lt; 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,7 +58,7 @@ 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 62 end
63 63  
64 64 private
... ...
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/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  
... ...
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 #
... ... @@ -81,7 +74,8 @@ class App
81 74 end
82 75  
83 76 def find_or_create_err!(attrs)
84   - Err.where(attrs).first || problems.create!.errs.create!(attrs)
  77 + Err.any_in(:problem_id => problems.map { |a| a.id }).
  78 + where(attrs).first || problems.create!.errs.create!(attrs)
85 79 end
86 80  
87 81 # Mongoid Bug: find(id) on association proxies returns an Enumerator
... ... @@ -110,20 +104,29 @@ class App
110 104 alias :notify_on_deploys? :notify_on_deploys
111 105  
112 106  
113   - def github_url?
114   - 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?
115 113 end
116 114  
117 115 def github_url_to_file(file)
118   - "#{self.github_url}/blob/master#{file}"
  116 + "#{github_url}/blob/master#{file}"
119 117 end
120 118  
  119 +
121 120 def issue_tracker_configured?
122 121 !!(issue_tracker && issue_tracker.class < IssueTracker && issue_tracker.project_id.present?)
123 122 end
124 123  
125 124 def notification_recipients
126   - notify_all_users ? User.all.map(&:email).reject(&:blank?) : watchers.map(&:address)
  125 + if notify_all_users
  126 + (User.all.map(&:email).reject(&:blank?) + watchers.map(&:address)).uniq
  127 + else
  128 + watchers.map(&:address)
  129 + end
127 130 end
128 131  
129 132 # Copy app attributes from another app.
... ... @@ -149,7 +152,7 @@ class App
149 152 end
150 153  
151 154 def generate_api_key
152   - self.api_key ||= ActiveSupport::SecureRandom.hex
  155 + self.api_key ||= SecureRandom.hex
153 156 end
154 157  
155 158 def check_issue_tracker
... ... @@ -161,11 +164,11 @@ class App
161 164 end
162 165 end
163 166  
164   - def normalize_github_url
165   - return if self.github_url.blank?
166   - self.github_url.gsub!(%r{^http://|git@}, 'https://')
167   - self.github_url.gsub!(/github\.com:/, 'github.com/')
168   - 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$/, '')
169 172 end
170 173 end
171 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/deploy_observer.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class DeployObserver < Mongoid::Observer
  2 + observe :deploy
  3 +
  4 + def after_create deploy
  5 + return unless deploy.app.notify_on_deploys? && deploy.app.notification_recipients.any?
  6 +
  7 + Mailer.deploy_notification(deploy).deliver
  8 + end
  9 +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,10 +14,11 @@ class Err
14 14  
15 15 belongs_to :problem
16 16 index :problem_id
  17 + index :error_class
17 18  
18 19 has_many :notices, :inverse_of => :err, :dependent => :destroy
19 20  
20   - validates_presence_of :klass, :environment
  21 + validates_presence_of :error_class, :environment
21 22  
22 23 delegate :app, :resolved?, :to => :problem
23 24  
... ...
app/models/error_report.rb
... ... @@ -2,7 +2,7 @@ require &#39;digest/md5&#39;
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 &lt; 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 &lt; 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 repository, 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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,23 @@ class IssueTrackers::PivotalLabsTracker &lt; 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 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}"
  21 + problem.update_attributes(
  22 + :issue_link => "https://www.pivotaltracker.com/story/show/#{story.id}",
  23 + :issue_type => Label
  24 + )
22 25 end
23 26  
24 27 def body_template
25 28 @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/pivotal_body.txt.erb"))
26 29 end
  30 +
  31 + def url
  32 + "https://www.pivotaltracker.com/"
  33 + end
27 34 end
28 35  
... ...
app/models/issue_trackers/redmine_tracker.rb
... ... @@ -25,18 +25,22 @@ class IssueTrackers::RedmineTracker &lt; 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 &lt; 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  
... ...
app/models/notice.rb
... ... @@ -10,20 +10,27 @@ class Notice
10 10 field :server_environment, :type => Hash
11 11 field :request, :type => Hash
12 12 field :notifier, :type => Hash
13   - field :klass
  13 + field :user_attributes, :type => Hash
  14 + field :error_class
14 15  
15 16 belongs_to :err
16   - index :err_id
17 17 index :created_at
  18 + index(
  19 + [
  20 + [ :err_id, Mongo::ASCENDING ],
  21 + [ :created_at, Mongo::ASCENDING ],
  22 + [ :_id, Mongo::ASCENDING ]
  23 + ]
  24 + )
18 25  
19 26 after_create :increase_counter_cache, :cache_attributes_on_problem, :unresolve_problem
20   - after_create :deliver_notification, :if => :should_notify?
21 27 before_save :sanitize
22 28 before_destroy :decrease_counter_cache, :remove_cached_attributes_from_problem
23 29  
24 30 validates_presence_of :backtrace, :server_environment, :notifier
25 31  
26 32 scope :ordered, order_by(:created_at.asc)
  33 + scope :reverse_ordered, order_by(:created_at.desc)
27 34 scope :for_errs, lambda {|errs| where(:err_id.in => errs.all.map(&:id))}
28 35  
29 36 delegate :app, :problem, :to => :err
... ... @@ -55,10 +62,6 @@ class Notice
55 62 where
56 63 end
57 64  
58   - def self.in_app_backtrace_line?(line)
59   - !!(line['file'] =~ %r{^\[PROJECT_ROOT\]/(?!(vendor))})
60   - end
61   -
62 65 def request
63 66 read_attribute(:request) || {}
64 67 end
... ... @@ -86,10 +89,6 @@ class Notice
86 89 request['session'] || {}
87 90 end
88 91  
89   - def deliver_notification
90   - Mailer.err_notification(self).deliver
91   - end
92   -
93 92 # Backtrace containing only files from the app itself (ignore gems)
94 93 def app_backtrace
95 94 backtrace.select { |l| l && l['file'] && l['file'].include?("[PROJECT_ROOT]") }
... ... @@ -97,10 +96,6 @@ class Notice
97 96  
98 97 protected
99 98  
100   - def should_notify?
101   - app.notify_on_errs? && (Errbit::Config.per_app_email_at_notices && app.email_at_notices || Errbit::Config.email_at_notices).include?(problem.notices_count) && app.notification_recipients.any?
102   - end
103   -
104 99 def increase_counter_cache
105 100 problem.inc(:notices_count, 1)
106 101 end
... ...
app/models/notice_observer.rb 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +class NoticeObserver < Mongoid::Observer
  2 + observe :notice
  3 +
  4 + def after_create notice
  5 + return unless should_notify? notice
  6 +
  7 + Mailer.err_notification(notice).deliver
  8 + end
  9 +
  10 + private
  11 +
  12 + def should_notify? notice
  13 + app = notice.app
  14 + app.notify_on_errs? &&
  15 + (Errbit::Config.per_app_email_at_notices && app.email_at_notices || Errbit::Config.email_at_notices).include?(notice.problem.notices_count) &&
  16 + app.notification_recipients.any?
  17 + end
  18 +
  19 +end
... ...
app/models/problem.rb
... ... @@ -2,14 +2,6 @@
2 2 # reported as various Errs, but the user has grouped the
3 3 # Errs together as belonging to the same problem.
4 4  
5   -## Add methode nan? in Time because needed by #max(:created_at)
6   -#
7   -# Fix on Mongoid > 2.3.x with commit :
8   -# https://github.com/mongoid/mongoid/commit/5481556e24480f0a1783f85d6b5b343b0cef7192
9   -class Time
10   - def nan?; false ;end
11   -end
12   -
13 5 class Problem
14 6 include Mongoid::Document
15 7 include Mongoid::Timestamps
... ... @@ -18,13 +10,14 @@ class Problem
18 10 field :last_deploy_at, :type => Time
19 11 field :resolved, :type => Boolean, :default => false
20 12 field :issue_link, :type => String
  13 + field :issue_type, :type => String
21 14  
22 15 # Cached fields
23 16 field :app_name, :type => String
24 17 field :notices_count, :type => Integer, :default => 0
25 18 field :message
26 19 field :environment
27   - field :klass
  20 + field :error_class
28 21 field :where
29 22 field :user_agents, :type => Hash, :default => {}
30 23 field :messages, :type => Hash, :default => {}
... ... @@ -122,7 +115,7 @@ class Problem
122 115 if app
123 116 self.app_name = app.name
124 117 self.last_deploy_at = if (last_deploy = app.deploys.where(:environment => self.environment).last)
125   - last_deploy.created_at
  118 + last_deploy.created_at.utc
126 119 end
127 120 collection.update({'_id' => self.id},
128 121 {'$set' => {'app_name' => self.app_name,
... ... @@ -132,11 +125,11 @@ class Problem
132 125  
133 126 def cache_notice_attributes(notice=nil)
134 127 notice ||= notices.first
135   - attrs = {:last_notice_at => notices.max(:created_at)}
  128 + attrs = {:last_notice_at => notices.order_by([:created_at, :asc]).last.try(:created_at)}
136 129 attrs.merge!(
137 130 :message => notice.message,
138 131 :environment => notice.environment_name,
139   - :klass => notice.klass,
  132 + :error_class => notice.error_class,
140 133 :where => notice.where,
141 134 :messages => attribute_count_increase(:messages, notice.message),
142 135 :hosts => attribute_count_increase(:hosts, notice.host),
... ... @@ -153,6 +146,12 @@ class Problem
153 146 )
154 147 end
155 148  
  149 + def issue_type
  150 + # Return issue_type if configured, but fall back to detecting app's issue tracker
  151 + attributes['issue_type'] ||=
  152 + (app.issue_tracker_configured? && app.issue_tracker.label) || nil
  153 + end
  154 +
156 155 private
157 156 def attribute_count_increase(name, value)
158 157 counter, index = send(name), attribute_index(value)
... ... @@ -166,7 +165,7 @@ class Problem
166 165  
167 166 def attribute_count_descrease(name, value)
168 167 counter, index = send(name), attribute_index(value)
169   - if counter[index]['count'] > 1
  168 + if counter[index] && counter[index]['count'] > 1
170 169 counter[index]['count'] -= 1
171 170 else
172 171 counter.delete(index)
... ...