Commit 853565b87a0ab3b1d59381b5b83d820beb530f85

Authored by Shuky Dvir
2 parents 4a448320 12765b8d
Exists in master and in 1 other branch production

Merge branch 'master' of https://github.com/errbit/errbit

Conflicts:
	Gemfile
	Gemfile.lock
Showing 259 changed files with 4487 additions and 3125 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 259 files displayed.

.gitignore
... ... @@ -4,6 +4,7 @@ log/*.log
4 4 tmp/**/*
5 5 tmp/*
6 6 .rvmrc
  7 +.idea
7 8 *~
8 9 *.rbc
9 10 .DS_Store
... ...
.travis.yml
  1 +language: ruby
1 2 rvm:
  3 + - 1.9.3
2 4 - 1.9.2
3 5 - 1.8.7
4 6  
5   -# Only build master branch
6   -branches:
7   - only:
8   - - master
9   -
10 7 # To stop Travis from running tests for a new commit,
11 8 # add the following to your commit message: [ci skip]
12 9 # You should add this when you edit documentation or comments, etc.
13   -
... ...
Gemfile
1 1 source 'http://rubygems.org'
2 2  
3   -gem 'rails', '3.0.10'
  3 +gem 'rails', '3.2.6'
  4 +
4 5 gem 'nokogiri'
5   -gem 'mongoid', '~> 2.2.2'
  6 +gem 'mongoid', '~> 2.4.10'
6 7  
7 8 gem 'haml'
8 9 gem 'htmlentities', "~> 4.3.0"
9   -gem 'devise', '~> 1.4.0'
  10 +
  11 +gem 'devise', '~> 1.5.3'
  12 +
  13 +gem 'omniauth-github'
  14 +gem 'oa-core'
  15 +
10 16 gem 'lighthouse-api'
11 17 gem 'oruen_redmine_client', :require => 'redmine_client'
12 18 gem 'mongoid_rails_migrations'
13 19 gem 'useragent', '~> 0.3.1'
14 20 gem 'pivotal-tracker'
15 21 gem 'ruby-fogbugz', :require => 'fogbugz'
16   -gem 'octokit', '0.6.4'
  22 +
  23 +gem 'octokit', '~> 1.0.0'
  24 +
17 25 gem 'inherited_resources'
18 26 gem 'SystemTimer', :platform => :ruby_18
19 27 gem 'hoptoad_notifier', "~> 2.4"
20 28 gem 'actionmailer_inline_css', "~> 1.3.0"
21 29 gem 'kaminari'
22 30 gem 'rack-ssl-enforcer'
23   -gem 'heroku'
24   -gem 'newrelic_rpm'
  31 +gem 'fabrication', "~> 1.3.0" # Both for tests, and loading demo data
  32 +gem 'rails_autolink', '~> 1.0.9'
25 33  
26 34 platform :ruby do
27   - gem 'mongo', '= 1.3.1'
28   - gem 'bson', '= 1.3.1'
29   - gem 'bson_ext', '= 1.3.1'
  35 + gem 'mongo', '= 1.6.2'
  36 + gem 'bson', '= 1.6.2'
  37 + gem 'bson_ext', '= 1.6.2'
30 38 end
31 39  
32 40 gem 'ri_cal'
  41 +gem 'yajl-ruby'
33 42  
34 43 group :development, :test do
35 44 gem 'rspec-rails', '~> 2.6'
36 45 gem 'webmock', :require => false
37   - gem 'fabrication'
38   - unless ENV['TRAVIS']
  46 + unless ENV["CI"]
39 47 gem 'ruby-debug', :platform => :mri_18
40   - gem 'ruby-debug19', :platform => :mri_19, :require => 'ruby-debug'
  48 + gem 'debugger', :platform => :mri_19
41 49 end
42   - # gem 'rpm_contrib', :git => "git://github.com/bensymonds/rpm_contrib.git", :branch => "mongo-1.4.0_update"
  50 +# gem 'rpm_contrib'
  51 +# gem 'newrelic_rpm'
  52 + gem 'capistrano'
43 53 end
44 54  
45 55 group :test do
  56 + gem 'capybara'
  57 + gem 'launchy'
46 58 gem 'rspec', '~> 2.6'
47 59 gem 'database_cleaner', '~> 0.6.0'
48 60 gem 'email_spec'
... ... @@ -52,3 +64,13 @@ group :heroku do
52 64 gem 'unicorn'
53 65 end
54 66  
  67 +# Use thin for development
  68 +gem 'thin', :group => :development, :platform => :ruby
  69 +
  70 +# Gems used only for assets and not required
  71 +# in production environments by default.
  72 +group :assets do
  73 + gem 'execjs'
  74 + gem 'therubyracer', :platform => :ruby # C Ruby (MRI) or Rubinius, but NOT Windows
  75 + gem 'uglifier', '>= 1.0.3'
  76 +end
... ...
Gemfile.lock
... ... @@ -2,107 +2,136 @@ GEM
2 2 remote: http://rubygems.org/
3 3 specs:
4 4 SystemTimer (1.2.3)
5   - abstract (1.0.0)
6   - actionmailer (3.0.10)
7   - actionpack (= 3.0.10)
8   - mail (~> 2.2.19)
  5 + actionmailer (3.2.6)
  6 + actionpack (= 3.2.6)
  7 + mail (~> 2.4.4)
9 8 actionmailer_inline_css (1.3.1)
10 9 actionmailer (>= 3.0.0)
11 10 nokogiri (>= 1.4.4)
12 11 premailer (>= 1.7.1)
13   - actionpack (3.0.10)
14   - activemodel (= 3.0.10)
15   - activesupport (= 3.0.10)
16   - builder (~> 2.1.2)
17   - erubis (~> 2.6.6)
18   - i18n (~> 0.5.0)
19   - rack (~> 1.2.1)
20   - rack-mount (~> 0.6.14)
21   - rack-test (~> 0.5.7)
22   - tzinfo (~> 0.3.23)
23   - activemodel (3.0.10)
24   - activesupport (= 3.0.10)
25   - builder (~> 2.1.2)
26   - i18n (~> 0.5.0)
27   - activerecord (3.0.10)
28   - activemodel (= 3.0.10)
29   - activesupport (= 3.0.10)
30   - arel (~> 2.0.10)
31   - tzinfo (~> 0.3.23)
32   - activeresource (3.0.10)
33   - activemodel (= 3.0.10)
34   - activesupport (= 3.0.10)
35   - activesupport (3.0.10)
36   - addressable (2.2.6)
37   - archive-tar-minitar (0.5.2)
38   - arel (2.0.10)
  12 + actionpack (3.2.6)
  13 + activemodel (= 3.2.6)
  14 + activesupport (= 3.2.6)
  15 + builder (~> 3.0.0)
  16 + erubis (~> 2.7.0)
  17 + journey (~> 1.0.1)
  18 + rack (~> 1.4.0)
  19 + rack-cache (~> 1.2)
  20 + rack-test (~> 0.6.1)
  21 + sprockets (~> 2.1.3)
  22 + activemodel (3.2.6)
  23 + activesupport (= 3.2.6)
  24 + builder (~> 3.0.0)
  25 + activerecord (3.2.6)
  26 + activemodel (= 3.2.6)
  27 + activesupport (= 3.2.6)
  28 + arel (~> 3.0.2)
  29 + tzinfo (~> 0.3.29)
  30 + activeresource (3.2.6)
  31 + activemodel (= 3.2.6)
  32 + activesupport (= 3.2.6)
  33 + activesupport (3.2.6)
  34 + i18n (~> 0.6)
  35 + multi_json (~> 1.0)
  36 + addressable (2.3.2)
  37 + arel (3.0.2)
39 38 bcrypt-ruby (3.0.1)
40   - bson (1.3.1)
41   - bson_ext (1.3.1)
42   - builder (2.1.2)
43   - columnize (0.3.4)
  39 + bson (1.6.2)
  40 + bson_ext (1.6.2)
  41 + bson (~> 1.6.2)
  42 + builder (3.0.0)
  43 + capistrano (2.13.3)
  44 + highline
  45 + net-scp (>= 1.0.0)
  46 + net-sftp (>= 2.0.0)
  47 + net-ssh (>= 2.0.14)
  48 + net-ssh-gateway (>= 1.1.0)
  49 + capybara (1.1.2)
  50 + mime-types (>= 1.16)
  51 + nokogiri (>= 1.3.3)
  52 + rack (>= 1.0.0)
  53 + rack-test (>= 0.5.4)
  54 + selenium-webdriver (~> 2.0)
  55 + xpath (~> 0.1.4)
  56 + childprocess (0.3.5)
  57 + ffi (~> 1.0, >= 1.0.6)
  58 + columnize (0.3.6)
44 59 crack (0.3.1)
45   - css_parser (1.2.5)
  60 + css_parser (1.2.6)
46 61 addressable
  62 + rdoc
  63 + daemons (1.1.8)
47 64 database_cleaner (0.6.7)
48   - devise (1.4.7)
  65 + debugger (1.2.0)
  66 + columnize (>= 0.3.1)
  67 + debugger-linecache (~> 1.1.1)
  68 + debugger-ruby_core_source (~> 1.1.3)
  69 + debugger-linecache (1.1.2)
  70 + debugger-ruby_core_source (>= 1.1.1)
  71 + debugger-ruby_core_source (1.1.3)
  72 + devise (1.5.3)
49 73 bcrypt-ruby (~> 3.0)
50 74 orm_adapter (~> 0.0.3)
51   - warden (~> 1.0.3)
  75 + warden (~> 1.1)
52 76 diff-lcs (1.1.3)
53 77 email_spec (1.2.1)
54 78 mail (~> 2.2)
55 79 rspec (~> 2.0)
56   - erubis (2.6.6)
57   - abstract (>= 1.0.0)
58   - fabrication (1.2.0)
59   - faraday (0.7.4)
60   - addressable (~> 2.2.6)
61   - multipart-post (~> 1.1.0)
62   - rack (>= 1.1.0, < 2)
63   - faraday_middleware (0.7.0)
64   - faraday (~> 0.7.3)
65   - haml (3.1.3)
  80 + erubis (2.7.0)
  81 + eventmachine (0.12.10)
  82 + execjs (1.4.0)
  83 + multi_json (~> 1.0)
  84 + fabrication (1.3.2)
  85 + faraday (0.8.1)
  86 + multipart-post (~> 1.1)
  87 + faraday_middleware (0.8.8)
  88 + faraday (>= 0.7.4, < 0.9)
  89 + ffi (1.1.4)
  90 + haml (3.1.6)
66 91 happymapper (0.4.0)
67 92 libxml-ruby (~> 2.0)
68 93 has_scope (0.5.1)
69   - hashie (1.0.0)
70   - heroku (2.19.2)
71   - launchy (>= 0.3.2)
72   - rest-client (~> 1.6.1)
73   - rubyzip
74   - term-ansicolor (~> 1.0.5)
  94 + hashie (1.2.0)
  95 + highline (1.6.13)
  96 + hike (1.2.1)
75 97 hoptoad_notifier (2.4.11)
76 98 activesupport
77 99 builder
78   - htmlentities (4.3.0)
79   - i18n (0.5.0)
80   - inherited_resources (1.3.0)
  100 + htmlentities (4.3.1)
  101 + httpauth (0.1)
  102 + i18n (0.6.0)
  103 + inherited_resources (1.3.1)
81 104 has_scope (~> 0.5.0)
82   - responders (~> 0.6.0)
83   - kaminari (0.12.4)
84   - rails (>= 3.0.0)
85   - kgio (2.6.0)
86   - launchy (2.0.5)
87   - addressable (~> 2.2.6)
88   - libxml-ruby (2.2.2)
  105 + responders (~> 0.6)
  106 + journey (1.0.4)
  107 + json (1.7.4)
  108 + jwt (0.1.5)
  109 + multi_json (>= 1.0)
  110 + kaminari (0.13.0)
  111 + actionpack (>= 3.0.0)
  112 + activesupport (>= 3.0.0)
  113 + railties (>= 3.0.0)
  114 + kgio (2.7.4)
  115 + launchy (2.1.2)
  116 + addressable (~> 2.3)
  117 + libv8 (3.3.10.4)
  118 + libwebsocket (0.1.5)
  119 + addressable
  120 + libxml-ruby (2.3.3)
89 121 lighthouse-api (2.0)
90 122 activeresource (>= 3.0.0)
91 123 activesupport (>= 3.0.0)
92 124 linecache (0.46)
93 125 rbx-require-relative (> 0.0.4)
94   - linecache19 (0.5.12)
95   - ruby_core_source (>= 0.1.4)
96   - mail (2.2.19)
97   - activesupport (>= 2.3.6)
  126 + mail (2.4.4)
98 127 i18n (>= 0.4.0)
99 128 mime-types (~> 1.16)
100 129 treetop (~> 1.4.8)
101   - mime-types (1.16)
102   - mongo (1.3.1)
103   - bson (>= 1.3.1)
104   - mongoid (2.2.4)
105   - activemodel (~> 3.0)
  130 + mime-types (1.19)
  131 + mongo (1.6.2)
  132 + bson (~> 1.6.2)
  133 + mongoid (2.4.10)
  134 + activemodel (~> 3.1)
106 135 mongo (~> 1.3)
107 136 tzinfo (~> 0.3.22)
108 137 mongoid_rails_migrations (0.0.14)
... ... @@ -110,20 +139,42 @@ GEM
110 139 bundler (>= 1.0.0)
111 140 rails (>= 3.0.0)
112 141 railties (>= 3.0.0)
113   - multi_json (1.0.4)
114   - multipart-post (1.1.4)
115   - newrelic_rpm (3.3.1)
116   - nokogiri (1.5.0)
117   - octokit (0.6.4)
118   - addressable (~> 2.2.6)
119   - faraday (~> 0.7.3)
120   - faraday_middleware (~> 0.7.0.rc1)
121   - hashie (~> 1.0.0)
122   - multi_json (~> 1.0.2)
123   - orm_adapter (0.0.5)
  142 + multi_json (1.3.6)
  143 + multipart-post (1.1.5)
  144 + net-scp (1.0.4)
  145 + net-ssh (>= 1.99.1)
  146 + net-sftp (2.0.5)
  147 + net-ssh (>= 2.0.9)
  148 + net-ssh (2.5.2)
  149 + net-ssh-gateway (1.1.0)
  150 + net-ssh (>= 1.99.1)
  151 + nokogiri (1.5.5)
  152 + oa-core (0.3.2)
  153 + oauth2 (0.8.0)
  154 + faraday (~> 0.8)
  155 + httpauth (~> 0.1)
  156 + jwt (~> 0.1.4)
  157 + multi_json (~> 1.0)
  158 + rack (~> 1.2)
  159 + octokit (1.0.7)
  160 + addressable (~> 2.2)
  161 + faraday (~> 0.8)
  162 + faraday_middleware (~> 0.8)
  163 + hashie (~> 1.2)
  164 + multi_json (~> 1.3)
  165 + omniauth (1.1.0)
  166 + hashie (~> 1.2)
  167 + rack
  168 + omniauth-github (1.0.2)
  169 + omniauth (~> 1.0)
  170 + omniauth-oauth2 (~> 1.1)
  171 + omniauth-oauth2 (1.1.0)
  172 + oauth2 (~> 0.8.0)
  173 + omniauth (~> 1.0)
  174 + orm_adapter (0.0.7)
124 175 oruen_redmine_client (0.0.1)
125 176 activeresource (>= 2.3.0)
126   - pivotal-tracker (0.4.1)
  177 + pivotal-tracker (0.5.4)
127 178 builder
128 179 builder
129 180 happymapper (>= 0.3.2)
... ... @@ -132,89 +183,103 @@ GEM
132 183 nokogiri (~> 1.4)
133 184 rest-client (~> 1.6.0)
134 185 rest-client (~> 1.6.0)
135   - polyglot (0.3.2)
  186 + polyglot (0.3.3)
136 187 premailer (1.7.3)
137 188 css_parser (>= 1.1.9)
138 189 htmlentities (>= 4.0.0)
139   - rack (1.2.4)
140   - rack-mount (0.6.14)
141   - rack (>= 1.0.0)
  190 + rack (1.4.1)
  191 + rack-cache (1.2)
  192 + rack (>= 0.4)
  193 + rack-ssl (1.3.2)
  194 + rack
142 195 rack-ssl-enforcer (0.2.4)
143   - rack-test (0.5.7)
  196 + rack-test (0.6.1)
144 197 rack (>= 1.0)
145   - rails (3.0.10)
146   - actionmailer (= 3.0.10)
147   - actionpack (= 3.0.10)
148   - activerecord (= 3.0.10)
149   - activeresource (= 3.0.10)
150   - activesupport (= 3.0.10)
  198 + rails (3.2.6)
  199 + actionmailer (= 3.2.6)
  200 + actionpack (= 3.2.6)
  201 + activerecord (= 3.2.6)
  202 + activeresource (= 3.2.6)
  203 + activesupport (= 3.2.6)
151 204 bundler (~> 1.0)
152   - railties (= 3.0.10)
153   - railties (3.0.10)
154   - actionpack (= 3.0.10)
155   - activesupport (= 3.0.10)
  205 + railties (= 3.2.6)
  206 + rails_autolink (1.0.9)
  207 + rails (~> 3.1)
  208 + railties (3.2.6)
  209 + actionpack (= 3.2.6)
  210 + activesupport (= 3.2.6)
  211 + rack-ssl (~> 1.3.2)
156 212 rake (>= 0.8.7)
157 213 rdoc (~> 3.4)
158   - thor (~> 0.14.4)
159   - raindrops (0.8.0)
160   - rake (0.9.2)
161   - rbx-require-relative (0.0.5)
162   - rdoc (3.9.4)
163   - responders (0.6.4)
  214 + thor (>= 0.14.6, < 2.0)
  215 + raindrops (0.10.0)
  216 + rake (0.9.2.2)
  217 + rbx-require-relative (0.0.9)
  218 + rdoc (3.12)
  219 + json (~> 1.4)
  220 + responders (0.9.2)
  221 + railties (~> 3.1)
164 222 rest-client (1.6.7)
165 223 mime-types (>= 1.16)
166 224 ri_cal (0.8.8)
167   - rspec (2.6.0)
168   - rspec-core (~> 2.6.0)
169   - rspec-expectations (~> 2.6.0)
170   - rspec-mocks (~> 2.6.0)
171   - rspec-core (2.6.4)
172   - rspec-expectations (2.6.0)
173   - diff-lcs (~> 1.1.2)
174   - rspec-mocks (2.6.0)
175   - rspec-rails (2.6.1)
176   - actionpack (~> 3.0)
177   - activesupport (~> 3.0)
178   - railties (~> 3.0)
179   - rspec (~> 2.6.0)
  225 + rspec (2.11.0)
  226 + rspec-core (~> 2.11.0)
  227 + rspec-expectations (~> 2.11.0)
  228 + rspec-mocks (~> 2.11.0)
  229 + rspec-core (2.11.1)
  230 + rspec-expectations (2.11.2)
  231 + diff-lcs (~> 1.1.3)
  232 + rspec-mocks (2.11.1)
  233 + rspec-rails (2.11.0)
  234 + actionpack (>= 3.0)
  235 + activesupport (>= 3.0)
  236 + railties (>= 3.0)
  237 + rspec (~> 2.11.0)
180 238 ruby-debug (0.10.4)
181 239 columnize (>= 0.1)
182 240 ruby-debug-base (~> 0.10.4.0)
183 241 ruby-debug-base (0.10.4)
184 242 linecache (>= 0.3)
185   - ruby-debug-base19 (0.11.25)
186   - columnize (>= 0.3.1)
187   - linecache19 (>= 0.5.11)
188   - ruby_core_source (>= 0.1.4)
189   - ruby-debug19 (0.11.6)
190   - columnize (>= 0.3.1)
191   - linecache19 (>= 0.5.11)
192   - ruby-debug-base19 (>= 0.11.19)
193   - ruby-fogbugz (0.0.4)
  243 + ruby-fogbugz (0.1.1)
194 244 crack
195   - typhoeus
196   - ruby_core_source (0.1.5)
197   - archive-tar-minitar (>= 0.5.2)
198   - rubyzip (0.9.5)
199   - term-ansicolor (1.0.7)
200   - thor (0.14.6)
  245 + rubyzip (0.9.9)
  246 + selenium-webdriver (2.25.0)
  247 + childprocess (>= 0.2.5)
  248 + libwebsocket (~> 0.1.3)
  249 + multi_json (~> 1.0)
  250 + rubyzip
  251 + sprockets (2.1.3)
  252 + hike (~> 1.2)
  253 + rack (~> 1.0)
  254 + tilt (~> 1.1, != 1.3.0)
  255 + therubyracer (0.10.2)
  256 + libv8 (~> 3.3.10)
  257 + thin (1.4.1)
  258 + daemons (>= 1.0.9)
  259 + eventmachine (>= 0.12.6)
  260 + rack (>= 1.0.0)
  261 + thor (0.15.4)
  262 + tilt (1.3.3)
201 263 treetop (1.4.10)
202 264 polyglot
203 265 polyglot (>= 0.3.1)
204   - typhoeus (0.2.4)
205   - mime-types
206   - mime-types
207   - tzinfo (0.3.30)
208   - unicorn (4.1.1)
209   - kgio (~> 2.4)
  266 + tzinfo (0.3.33)
  267 + uglifier (1.2.7)
  268 + execjs (>= 0.3.0)
  269 + multi_json (~> 1.3)
  270 + unicorn (4.3.1)
  271 + kgio (~> 2.6)
210 272 rack
211   - raindrops (~> 0.6)
  273 + raindrops (~> 0.7)
212 274 useragent (0.3.2)
213   - warden (1.0.5)
  275 + warden (1.2.1)
214 276 rack (>= 1.0)
215   - webmock (1.7.6)
216   - addressable (~> 2.2, > 2.2.5)
  277 + webmock (1.8.7)
  278 + addressable (>= 2.2.7)
217 279 crack (>= 0.1.7)
  280 + xpath (0.1.4)
  281 + nokogiri (~> 1.3)
  282 + yajl-ruby (1.1.0)
218 283  
219 284 PLATFORMS
220 285 ruby
... ... @@ -222,35 +287,44 @@ PLATFORMS
222 287 DEPENDENCIES
223 288 SystemTimer
224 289 actionmailer_inline_css (~> 1.3.0)
225   - bson (= 1.3.1)
226   - bson_ext (= 1.3.1)
  290 + bson (= 1.6.2)
  291 + bson_ext (= 1.6.2)
  292 + capistrano
  293 + capybara
227 294 database_cleaner (~> 0.6.0)
228   - devise (~> 1.4.0)
  295 + debugger
  296 + devise (~> 1.5.3)
229 297 email_spec
230   - fabrication
  298 + execjs
  299 + fabrication (~> 1.3.0)
231 300 haml
232   - heroku
233 301 hoptoad_notifier (~> 2.4)
234 302 htmlentities (~> 4.3.0)
235 303 inherited_resources
236 304 kaminari
  305 + launchy
237 306 lighthouse-api
238   - mongo (= 1.3.1)
239   - mongoid (~> 2.2.2)
  307 + mongo (= 1.6.2)
  308 + mongoid (~> 2.4.10)
240 309 mongoid_rails_migrations
241   - newrelic_rpm
242 310 nokogiri
243   - octokit (= 0.6.4)
  311 + oa-core
  312 + octokit (~> 1.0.0)
  313 + omniauth-github
244 314 oruen_redmine_client
245 315 pivotal-tracker
246 316 rack-ssl-enforcer
247   - rails (= 3.0.10)
  317 + rails (= 3.2.6)
  318 + rails_autolink (~> 1.0.9)
248 319 ri_cal
249 320 rspec (~> 2.6)
250 321 rspec-rails (~> 2.6)
251 322 ruby-debug
252   - ruby-debug19
253 323 ruby-fogbugz
  324 + therubyracer
  325 + thin
  326 + uglifier (>= 1.0.3)
254 327 unicorn
255 328 useragent (~> 0.3.1)
256 329 webmock
  330 + yajl-ruby
... ...
README.md
1   -Errbit [![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   -heroku addons:add mongohq:free
  147 +heroku addons:add mongolab:starter
  148 +cp -f config/mongoid.mongolab.yml config/mongoid.yml
  149 +git add -f config/mongoid.yml
  150 +git commit -m "Added mongoid config for Mongolab"
110 151 heroku addons:add sendgrid:starter
111 152 heroku config:add HEROKU=true
112 153 heroku config:add ERRBIT_HOST=some-hostname.example.com
... ... @@ -114,33 +155,129 @@ heroku config:add ERRBIT_EMAIL_FROM=example@example.com
114 155 git push heroku master
115 156 ```
116 157  
117   - 3. Seed the DB (_NOTE_: No bootstrap task is used on Heroku!)
  158 + * Seed the DB (_NOTE_: No bootstrap task is used on Heroku!)
118 159  
119 160 ```bash
120 161 heroku run rake db:seed
121 162 ```
122 163  
123   - 4. If you are using a free database on Heroku, you may want to periodically clear resolved errors to free up space.
  164 + * If you are using a free database on Heroku, you may want to periodically clear resolved errors to free up space.
  165 +
  166 + * With the heroku-scheduler add-on (replacement for cron):
  167 +
  168 + ```bash
  169 + # Install the heroku scheduler add-on
  170 + heroku addons:add scheduler:standard
  171 +
  172 + # Go open the dashboard to schedule the job. You should use
  173 + # 'rake errbit:db:clear_resolved' as the task command, and schedule it
  174 + # at whatever frequency you like (once/day should work great).
  175 + heroku addons:open scheduler
  176 + ```
  177 +
  178 + * With the cron add-on:
  179 +
  180 + ```bash
  181 + # Install the heroku cron addon, to clear resolved errors daily:
  182 + heroku addons:add cron:daily
  183 + ```
  184 +
  185 + * Or clear resolved errors manually:
  186 +
  187 + ```bash
  188 + heroku rake errbit:db:clear_resolved
  189 + ```
  190 +
  191 + * You may want to enable the deployment hook for heroku :
  192 +
  193 +```bash
  194 +heroku addons:add deployhooks:http --url="http://YOUR_ERRBIT_HOST/deploys.txt?api_key=YOUR_API_KEY"
  195 +```
  196 +
  197 + * Enjoy!
  198 +
  199 +
  200 +Authentication
  201 +--------------
  202 +
  203 +### Configuring GitHub authentication:
  204 +
  205 + * In `config/config.yml`, set `github_authentication` to `true`
  206 + * Register your instance of Errbit at: https://github.com/settings/applications
  207 +
  208 +If you hosted Errbit at errbit.example.com, you would fill in:
  209 +
  210 +<table>
  211 + <tr><th>URL:</th><td>http://errbit.example.com/</td></tr>
  212 + <tr><th>Callback URL:</th><td>http://errbit.example.com/users/auth/github</td></tr>
  213 +</table>
  214 +
  215 + * After you have registered your app, set `github_client_id` and `github_secret`
  216 + in `config/config.yml` with your app's Client ID and Secret key.
  217 +
  218 +
  219 +After you have followed these instructions, you will be able to **Sign in with GitHub** on the Login page.
  220 +
  221 +You will also be able to link your GitHub profile to your user account on your **Edit profile** page.
  222 +
  223 +If you have signed in with GitHub, or linked your GitHub profile, and the App has a GitHub repo configured,
  224 +then you will be able to create issues on GitHub.
  225 +You will still be able to create an issue on the App's configured issue tracker.
  226 +
  227 +You can change the requested account permissions by setting `github_access_scope` to:
  228 +
  229 +<table>
  230 + <tr><th>['repo'] </th><td>Allow creating issues for public and private repos.</td></tr>
  231 + <tr><th>['public_repo'] </th><td>Only allow creating issues for public repos.</td></tr>
  232 + <tr><th>[] </th><td>No permission to create issues on any repos.</td></tr>
  233 +</table>
  234 +
  235 +
  236 +### GitHub authentication when served on Heroku
  237 +
  238 +You will need to set up Heroku variables accordingly as described in [Configuring GitHub authentication](#configuring-github-authentication):
  239 +
  240 +* GITHUB_AUTHENTICATION
  241 +
  242 +```bash
  243 +heroku config:add GITHUB_AUTHENTICATION=true
  244 +```
  245 +
  246 +* GITHUB_CLIENT_ID
124 247  
125 248 ```bash
126   -# Install the heroku cron addon, to clear resolved errors daily:
127   -heroku addons:add cron:daily
  249 +heroku config:add GITHUB_CLIENT_ID=the_client_id_provided_by_GitHub
  250 +```
  251 +
  252 +* GITHUB_SECRET
  253 +
  254 +```bash
  255 +heroku config:add GITHUB_SECRET=the_secret_provided_by_GitHub
  256 +```
  257 +
  258 +* GITHUB_ACCESS_SCOPE - set only one scope `repo` or `public_repo`. If you really need to put more than one, separate them with comma.
128 259  
129   -# Or, clear resolved errors manually:
130   -heroku rake errbit:db:clear_resolved
  260 +```bash
  261 +heroku config:add GITHUB_ACCESS_SCOPE=repo,public_repo
131 262 ```
132 263  
133   - 5. Enjoy!
  264 +__Note__: To avoid restarting your Heroku app 4 times you can set Heroku variables in a single command, i.e:
134 265  
  266 +```bash
  267 +heroku config:add GITHUB_AUTHENTICATION=true \
  268 +GITHUB_CLIENT_ID=the_client_id_provided_by_GitHub \
  269 +GITHUB_SECRET=the_secret_provided_by_GitHub \
  270 +GITHUB_ACCESS_SCOPE=repo,public_repo
  271 +```
135 272  
136   -**Configuring LDAP authentication:**
  273 +### Configuring LDAP authentication:
137 274  
138   - 1. In `config/config.yml`, set `user_has_username` to `true`
139   - 2. Follow the instructions at https://github.com/cschiewek/devise_ldap_authenticatable
  275 + * In `config/config.yml`, set `user_has_username` to `true`
  276 + * Follow the instructions at https://github.com/cschiewek/devise_ldap_authenticatable
140 277 to set up the devise_ldap_authenticatable gem.
141 278  
142   - 3. If you are authenticating by `username`, you will need to set the user's email
143   - after authentication. You can do this by adding the following lines to `app/models/user.rb`:
  279 + * If you are authenticating by `username`, you will need to set the user's email manually
  280 + before authentication. You must add the following lines to `app/models/user.rb`:
144 281  
145 282 ```ruby
146 283 before_save :set_ldap_email
... ... @@ -151,7 +288,7 @@ heroku rake errbit:db:clear_resolved
151 288  
152 289 Upgrading
153 290 ---------
154   -*Note*: When upgrading Errbit, please run:
  291 +When upgrading Errbit, please run:
155 292  
156 293 ```bash
157 294 git pull origin master # assuming origin is the github.com/errbit/errbit repo
... ... @@ -161,6 +298,29 @@ rake db:migrate
161 298 If we change the way that data is stored, this will run any migrations to bring your database up to date.
162 299  
163 300  
  301 +User information in error reports
  302 +---------------------------------
  303 +
  304 +Errbit can now display information about the user who experienced an error.
  305 +This gives you the ability to ask the user for more information,
  306 +and let them know when you've fixed the bug.
  307 +
  308 +If you would like to include information about the current user in your error reports,
  309 +you can replace the `airbrake` gem in your Gemfile with `airbrake_user_attributes`,
  310 +which wraps the `airbrake` gem and injects user information.
  311 +It will inject information about the current user into the error report
  312 +if your Rails app's controller responds to a `#current_user` method.
  313 +The user's attributes are filtered to remove authentication fields.
  314 +
  315 +If user information is received with an error report,
  316 +it will be displayed under the *User Details* tab:
  317 +
  318 +
  319 +![User details tab](http://errbit.github.com/errbit/images/error_user_information.png)
  320 +
  321 +(This tab will be hidden if no user information is available.)
  322 +
  323 +
164 324 Issue Trackers
165 325 --------------
166 326  
... ... @@ -186,13 +346,17 @@ Issue Trackers
186 346 * Account is the host of your mingle installation. i.e. **https://mingle.example.com** *note*: You should use SSL if possible.
187 347 * Errbit uses 'sign-in name' & password authentication. You may want to set up an **errbit** user with limited rights.
188 348 * Project id is the identifier of your project, i.e. **awesomeapp** for project at https://mingle.example.com/projects/awesomeapp
189   -* Card properties are comma separated key value pairs. You must specify a 'card_type', but anything else is optional. i.e. card_type = Defect, status = Open, priority = Essential
  349 +* Card properties are comma separated key value pairs. You must specify a 'card_type', but anything else is optional, e.g.:
  350 +
  351 +```
  352 +card_type = Defect, status = Open, priority = Essential
  353 +```
190 354  
191   -**Github Issues Integration**
  355 +**GitHub Issues Integration**
192 356  
193 357 * For 'Account/Repository', the account will either be a username or organization. i.e. **errbit/errbit**
194   -* If you are logged in on [Github](https://github.com), you can find your **API Token** on this page: [https://github.com/account/admin](https://github.com/account/admin).
195   -* You will also need to provide the username that your API Token is connected to.
  358 +* You will also need to provide your username and password for your GitHub account.
  359 + * (We'd really appreciate it if you wanted to help us implement OAuth instead!)
196 360  
197 361  
198 362 What if Errbit has an error?
... ... @@ -201,23 +365,23 @@ What if Errbit has an error?
201 365 Errbit will log it's own errors to an internal app named **Self.Errbit**.
202 366 The **Self.Errbit** app will be automatically created whenever the first error happens.
203 367  
204   -If your Errbit instance has logged an error, we would appreciate a bug report on Github Issues.
  368 +If your Errbit instance has logged an error, we would appreciate a bug report on GitHub Issues.
205 369 You can post this manually at [https://github.com/errbit/errbit/issues](https://github.com/errbit/errbit/issues),
206   -or you can set up the Github Issues tracker for your **Self.Errbit** app:
  370 +or you can set up the GitHub Issues tracker for your **Self.Errbit** app:
207 371  
208   - 1. Go to the **Self.Errbit** app's edit page. If that app does not exist yet, go to the apps page and click **Add a new App** to create it. (You can also create it by running `rake airbrake:test`.)
  372 + * Go to the **Self.Errbit** app's edit page. If that app does not exist yet, go to the apps page and click **Add a new App** to create it. (You can also create it by running `rake airbrake:test`.)
209 373  
210   - 2. In the **Issue Tracker** section, click **Github Issues**.
  374 + * In the **Issue Tracker** section, click **GitHub Issues**.
211 375  
212   - 3. Fill in the **Account/Repository** field with **errbit/errbit**.
  376 + * Fill in the **Account/Repository** field with **errbit/errbit**.
213 377  
214   - 4. Fill in the **Username** field with your github username.
  378 + * Fill in the **Username** field with your github username.
215 379  
216   - 5. If you are logged in on [Github](https://github.com), you can find your **API Token** on this page: [https://github.com/account/admin](https://github.com/account/admin).
  380 + * If you are logged in on [GitHub](https://github.com), you can find your **API Token** on this page: [https://github.com/account/admin](https://github.com/account/admin).
217 381  
218   - 6. Save the settings by clicking **Update App** (or **Add App**)
  382 + * Save the settings by clicking **Update App** (or **Add App**)
219 383  
220   - 7. You can now easily post bug reports to Github Issues by clicking the **Create Issue** button on a **Self.Errbit** error.
  384 + * You can now easily post bug reports to GitHub Issues by clicking the **Create Issue** button on a **Self.Errbit** error.
221 385  
222 386  
223 387 TODO
... ...
Rakefile
... ... @@ -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,139 @@
  1 +// App JS
  2 +
  3 +$(function() {
  4 +
  5 + var currentTab = "summary";
  6 +
  7 + function init() {
  8 +
  9 + activateTabbedPanels();
  10 +
  11 + activateSelectableRows();
  12 +
  13 + toggleProblemsCheckboxes();
  14 +
  15 + bindRequiredPasswordMarks();
  16 +
  17 + $('#watcher_name').live("click", function() {
  18 + $(this).closest('form').find('.show').removeClass('show');
  19 + $('#app_watchers_attributes_0_user_id').addClass('show');
  20 + });
  21 +
  22 + $('#watcher_email').live("click", function() {
  23 + $(this).closest('form').find('.show').removeClass('show');
  24 + $('#app_watchers_attributes_0_email').addClass('show');
  25 + });
  26 +
  27 + $('a.copy_config').live("click", function() {
  28 + $('select.choose_other_app').show().focus();
  29 + });
  30 +
  31 + $('select.choose_other_app').live("change", function() {
  32 + var loc = window.location;
  33 + window.location.href = loc.protocol + "//" + loc.host + loc.pathname +
  34 + "?copy_attributes_from=" + $(this).val();
  35 + });
  36 +
  37 + $('input[type=submit][data-action]').click(function() {
  38 + $(this).closest('form').attr('action', $(this).attr('data-action'));
  39 + });
  40 +
  41 + $('.notice-pagination').each(function() {
  42 + $('.notice-pagination a').pjax('#content', { timeout: 2000});
  43 + $('#content').bind('pjax:start', function() {
  44 + $('.notice-pagination-loader').css("visibility", "visible");
  45 + currentTab = $('.tab-bar ul li a.button.active').attr('rel');
  46 + });
  47 +
  48 + $('#content').bind('pjax:end', function() {
  49 + activateTabbedPanels();
  50 + });
  51 + });
  52 + }
  53 +
  54 + function activateTabbedPanels() {
  55 + $('.tab-bar a').each(function(){
  56 + var tab = $(this);
  57 + var panel = $('#'+tab.attr('rel'));
  58 + panel.addClass('panel');
  59 + panel.find('h3').hide();
  60 + });
  61 +
  62 + $('.tab-bar a').click(function(){
  63 + activateTab($(this));
  64 + return(false);
  65 + });
  66 + activateTab($('.tab-bar ul li a.button[rel=' + currentTab + ']'));
  67 + }
  68 +
  69 + function activateTab(tab) {
  70 + tab = $(tab);
  71 + var panel = $('#'+tab.attr('rel'));
  72 +
  73 + tab.closest('.tab-bar').find('a.active').removeClass('active');
  74 + tab.addClass('active');
  75 +
  76 + // If clicking into 'backtrace' tab, hide external backtrace
  77 + if (tab.attr('rel') == "backtrace") { hide_external_backtrace(); }
  78 +
  79 + $('.panel').hide();
  80 + panel.show();
  81 + }
  82 +
  83 + function toggleProblemsCheckboxes() {
  84 + var checkboxToggler = $('#toggle_problems_checkboxes');
  85 +
  86 + checkboxToggler.live("click", function() {
  87 + $('input[name^="problems"]').each(function() {
  88 + this.checked = checkboxToggler.get(0).checked;
  89 + });
  90 + });
  91 + }
  92 +
  93 + function activateSelectableRows() {
  94 + $('.selectable tr').click(function(event) {
  95 + if(!_.include(['A', 'INPUT', 'BUTTON', 'TEXTAREA'], event.target.nodeName)) {
  96 + var checkbox = $(this).find('input[name="problems[]"]');
  97 + checkbox.attr('checked', !checkbox.is(':checked'));
  98 + }
  99 + });
  100 + }
  101 +
  102 + function bindRequiredPasswordMarks() {
  103 + $('#user_github_login').keyup(function(event) {
  104 + toggleRequiredPasswordMarks(this)
  105 + });
  106 + }
  107 +
  108 + function toggleRequiredPasswordMarks(input) {
  109 + if($(input).val() == "") {
  110 + $('#user_password').parent().attr('class', 'required')
  111 + $('#user_password_confirmation').parent().attr('class', 'required')
  112 + } else {
  113 + $('#user_password').parent().attr('class', '')
  114 + $('#user_password_confirmation').parent().attr('class', '')
  115 + }
  116 + }
  117 +
  118 + toggleRequiredPasswordMarks();
  119 +
  120 + function hide_external_backtrace() {
  121 + $('tr.toggle_external_backtrace').hide();
  122 + $('td.backtrace_separator').show();
  123 + }
  124 + function show_external_backtrace() {
  125 + $('tr.toggle_external_backtrace').show();
  126 + $('td.backtrace_separator').hide();
  127 + }
  128 + // Show external backtrace lines when clicking separator
  129 + $('td.backtrace_separator span').live('click', show_external_backtrace);
  130 + // Hide external backtrace on page load
  131 + hide_external_backtrace();
  132 +
  133 + $('.head a.show_tail').click(function(e) {
  134 + $(this).hide().closest('.head_and_tail').find('.tail').show();
  135 + e.preventDefault();
  136 + });
  137 +
  138 + init();
  139 +});
... ...
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,904 @@
  1 +html {
  2 + margin: 0; padding: 0;
  3 + color: #585858;
  4 + background-color: #e6e6e6;
  5 + font-size: 62.8%; font-family: Helvetica, "Lucida Grande","Lucida Sans",Arial,sans-serif;
  6 +}
  7 +body {
  8 + margin: 0; padding: 0;
  9 + font-size: 1.3em; line-height: 1.4em;
  10 +}
  11 +
  12 +/* Convenience Classes */
  13 +.float-left { float: left; }
  14 +.float-right { float: right; }
  15 +.clear { clear: both; }
  16 +.clear-left { clear: left; }
  17 +.clear-right { clear: right; }
  18 +.nowrap { white-space: nowrap; }
  19 +
  20 +/* Headings */
  21 +h1, h2, h3, h4, h5, h6 { padding: 0.2em 0; margin-bottom: 1em; border-bottom: 1px solid #dedede;}
  22 +h1 { font-size: 2.0em; line-height: 1.2em; text-shadow: 1px 1px 0px #FFF; -webkit-text-shadow: 1px 1px 0px #FFF;}
  23 +h2 { font-size: 1.7em; line-height: 1.2em; }
  24 +h3 { font-size: 1.5em; line-height: 1.2em; }
  25 +h4 { font-size: 1.3em; line-height: 1.2em; }
  26 +h5 { font-size: 1.1em; line-height: 1.2em; }
  27 +h6 { font-size: 0.9em; line-height: 1.2em; }
  28 +
  29 +/* General */
  30 +p { margin-bottom: 1em; }
  31 +
  32 +/* Links */
  33 +a { color: #0069cc; text-decoration: none;}
  34 +a:visited { color: #0069cc;}
  35 +a:hover { color: #0069cc; text-decoration: underline; }
  36 +a.action { float: right; font-size: 0.9em;}
  37 +
  38 +#header > div, #content-wrapper, #footer {
  39 + width: 930px;
  40 + margin: 0 auto;
  41 + position: relative;
  42 +}
  43 +
  44 +/* Header */
  45 +#header {
  46 + margin-bottom: 24px;
  47 + height: 71px;
  48 + border-bottom: 1px solid #fff;
  49 + position:relative;
  50 + background: #000000;
  51 +}
  52 +#header > div {
  53 + height: 71px;
  54 +}
  55 +#header #site-name {
  56 + display: block;
  57 + width: 88px;
  58 + height: 31px;
  59 + position: absolute;
  60 + top: 22px;
  61 + left: 2px;
  62 + background: transparent url(images/logo.png) 0 0 no-repeat;
  63 + text-indent: -5000em;
  64 +}
  65 +
  66 +#header #session-links {
  67 + position: absolute;
  68 + top: 20px;
  69 + right: 0;
  70 + font-size: 0.9em;
  71 +}
  72 +#header #session-links li {
  73 + float: right;
  74 + margin-left: 10px;
  75 + color: #ccc;
  76 + background-color: #1e1e1e;
  77 + border-radius: 30px;
  78 + -moz-border-radius: 30px;
  79 + -webkit-border-radius: 30px;
  80 + border: 1px solid #484B4F;
  81 + font-size: 14px;
  82 +}
  83 +#header #session-links li:hover {
  84 + box-shadow: 0 0 3px #69c;
  85 + -moz-box-shadow: 0 0 3px #69c;
  86 + -webkit-box-shadow: 0 0 3px #69c;
  87 +}
  88 +#header #session-links li:hover a {
  89 + color: white;
  90 +}
  91 +#header #session-links a {
  92 + color: #ccc;
  93 + padding: 0 14px;
  94 + line-height: 30px;
  95 +}
  96 +#header #session-links #sign-out {
  97 + background: transparent url(images/icons/bullet-red-sm.png) 12px 50% no-repeat;
  98 + padding-left: 29px;
  99 +}
  100 +#header #session-links a:hover {
  101 + text-decoration: none;
  102 +}
  103 +
  104 +/* Navigation */
  105 +#nav-bar {
  106 + position: absolute;
  107 + bottom: 0;
  108 + left: 172px;
  109 +}
  110 +#nav-bar li {
  111 + float: left;
  112 + height: 34px;
  113 + margin-right: 12px;
  114 + color: #666;
  115 + background-color: #d0d0d0;
  116 + background-image: none;
  117 + border-top-left-radius: 12px;
  118 + border-top-right-radius: 12px;
  119 + -moz-border-top-left-radius: 12px;
  120 + -moz-border-top-right-radius: 12px;
  121 + -webkit-border-top-left-radius: 12px;
  122 + -webkit-border-top-right-radius: 12px;
  123 + border: 1px solid #bbb;
  124 +}
  125 +#nav-bar li.active {
  126 + border-color: #fff;
  127 + background: #FFF url(images/button-bg.png) 0 -2px repeat-x;
  128 + border-width:1px 1px 0;
  129 + margin-bottom:-2px;
  130 + height: 37px;
  131 +}
  132 +#nav-bar li.active a {
  133 + color: #333;
  134 +}
  135 +
  136 +#nav-bar li a {
  137 + color: #666;
  138 + display: block;
  139 + padding: 0 20px 0 40px;
  140 + font-size: 14px;
  141 + font-weight: bold;
  142 + line-height: 37px;
  143 + text-decoration: none;
  144 + text-shadow: 1px 1px 0px #FFF;
  145 + -webkit-text-shadow: 1px 1px 0px #FFF;
  146 + background: transparent 10px 6px no-repeat;
  147 +}
  148 +#nav-bar li a:hover { color: #666;}
  149 +#nav-bar li.apps a { background-image: url(images/icons/briefcase.png); }
  150 +#nav-bar li.errs a { background-image: url(images/icons/error.png); }
  151 +#nav-bar li.users a { background-image: url(images/icons/user.png); }
  152 +#nav-bar li:not(.active):hover {
  153 + box-shadow: 0 0 3px #69c;
  154 + -moz-box-shadow: 0 0 3px #69c;
  155 + -webkit-box-shadow: 0 0 3px #69c;
  156 +}
  157 +
  158 +/* Content Wrapper */
  159 +#content-wrapper {
  160 + border: 1px solid #C6C6C6;
  161 +}
  162 +
  163 +/* Content Title and Comments */
  164 +#content-title, #content-comments {
  165 + padding: 30px 24px;
  166 + border-top: 1px solid #FFF;
  167 + border-bottom: 1px solid #FFF;
  168 + background-color: #f2f2f2;
  169 +}
  170 +
  171 +/* Make err title bar bigger to fit more buttons */
  172 +#content-title.err_show {
  173 + padding: 43px 24px 37px;
  174 +}
  175 +
  176 +#content-comments {
  177 + background-color: #ffffff;
  178 +}
  179 +#content-title h1, #content-comments h3 {
  180 + padding: 0; margin: 0;
  181 + width: 85%;
  182 + border: none;
  183 + color: #636363;
  184 + font-size: 2em; line-height: 1em; font-weight: bold; font-family: arial, sans-serif;
  185 + word-wrap: break-word;
  186 +}
  187 +#content-comments h3 {
  188 + font-size: 1.5em;
  189 + margin-bottom: 14px;
  190 +}
  191 +
  192 +#content-title .meta { font-size: 0.9em; color: #787878; }
  193 +
  194 +/* Action Bar */
  195 +#action-bar {
  196 + position: absolute;
  197 + text-align: right;
  198 + top: 22px; right: 24px;
  199 +}
  200 +#action-bar span {
  201 + display: inline-block;
  202 + margin-left: 18px;
  203 + margin-bottom: 16px;
  204 + text-decoration: none;
  205 + color: #666;
  206 + background: #FFF url(images/button-bg.png) 0 bottom repeat-x;
  207 + border-radius: 50px;
  208 + -moz-border-radius: 50px;
  209 + -webkit-border-radius: 50px;
  210 + border: 1px solid #bbb;
  211 +}
  212 +#action-bar span a {
  213 + color: #666;
  214 + display: block;
  215 + padding: 0 20px 0 40px;
  216 + font-size: 14px; font-weight: bold; line-height: 39px; text-decoration: none;
  217 + text-shadow: 1px 1px 0px #FFF; -webkit-text-shadow: 1px 1px 0px #FFF;
  218 + background: transparent 10px 8px no-repeat;
  219 +}
  220 +#action-bar a:hover { text-decoration: none;}
  221 +#action-bar span:hover {
  222 + box-shadow: 0 0 3px #69c;
  223 + -moz-box-shadow: 0 0 3px #69c;
  224 + -webkit-box-shadow: 0 0 3px #69c;
  225 +}
  226 +#action-bar a.add {
  227 + background-image: url(images/icons/add.png);
  228 +}
  229 +
  230 +#action-bar .calendar_link {
  231 + background: url(images/icons/ical.png) no-repeat scroll 12px 6px transparent;
  232 + padding-left: 47px;
  233 +}
  234 +
  235 +#action-bar span.github a { background: url(images/icons/github.png) no-repeat 6px 5px; }
  236 +#action-bar span.unlink_github a { background: url(images/icons/unlink_github.png) no-repeat 6px 5px; }
  237 +
  238 +/* Content */
  239 +#content {
  240 + padding: 20px; border-top: 1px solid #C6C6C6;
  241 + background-color: #FFF;
  242 +}
  243 +
  244 +#content a.button {
  245 + float: right;
  246 + display: block;
  247 + margin-bottom: 10px;
  248 +}
  249 +
  250 +/* Footer */
  251 +#footer {
  252 + padding: 20px 0;
  253 + font-size: 0.8em; text-align: center;
  254 + color: #929292;
  255 +}
  256 +
  257 +/* Flash Messages */
  258 +#flash-messages li {
  259 + padding: 13px 45px;
  260 + margin-bottom:25px;
  261 + border: 1px solid #C6C6C6;
  262 + background-color: #F9F9F9;
  263 + line-height: 1em;
  264 +}
  265 +#flash-messages li.notice {
  266 + padding-left: 20px;
  267 + background-color: #b5eeff;
  268 + border: 1px solid #6cf;
  269 +}
  270 +#flash-messages li.success {
  271 + background: #cfc url(images/icons/success.png) 16px 50% no-repeat;
  272 + border: 1px solid #6c3;
  273 +}
  274 +#flash-messages li.error {
  275 + background: #fcc url(images/icons/error.png) 16px 50% no-repeat;
  276 + border: 1px solid #f99;
  277 +}
  278 +#flash-messages .alert {
  279 + background: #ffc url(images/icons/warning.png) 10px 7px no-repeat;
  280 + border-color: #e4bb69;
  281 +}
  282 +
  283 +/* Forms */
  284 +form#new_user,
  285 +form.edit_user,
  286 +form#new_app,
  287 +form.edit_app {
  288 + width: 620px;
  289 +}
  290 +form > div, form fieldset > div { margin: 1em 0;}
  291 +form fieldset {
  292 + padding: 0.8em; margin-bottom: 1em;
  293 + background-color: #F0F0F0; border: 1px solid #C6C6C6; border-left: none; border-right: none;
  294 +}
  295 +form fieldset legend {
  296 + font-size: 1.2em; font-weight: bold; text-transform: uppercase;
  297 + color: #555;
  298 +}
  299 +form label {
  300 + font-weight: bold; text-transform: uppercase; line-height: 1.6em;
  301 + display: inline-block;
  302 +}
  303 +form label.inline { display: inline; }
  304 +form .checkbox label { display: inline; }
  305 +form .required label { padding-right: 20px; background: transparent url(images/icons/required.png) right 50% no-repeat; }
  306 +form .field_with_errors label { color: #900; }
  307 +form input[type=text], form input[type=password] {
  308 + width: 96%; padding: 0.8em;
  309 + font-size: 1em;
  310 + color: #787878; border: 1px solid #C6C6C6;
  311 +}
  312 +form textarea {
  313 + width: 100%; padding: 0.8em;
  314 + font-size: inherit; font-family: inherit;
  315 + color: #787878; border: 1px solid #C6C6C6;
  316 +}
  317 +form textarea.short { height: 8em; }
  318 +form textarea.supershort { height: 4em; }
  319 +form input[type=text]:focus, form input[type=password]:focus, form textarea:focus {
  320 + box-shadow: 0px 0px 4px #69C;
  321 + -moz-box-shadow: 0px 0px 4px #69C;
  322 + -webkit-box-shadow: 0px 0px 4px #69C
  323 +}
  324 +form input[type=checkbox]:focus + label{
  325 + color: #69C;
  326 +}
  327 +form input[type=submit] {
  328 + display:block; width: auto; padding: 0.5em;
  329 + font-size: 1.2em; line-height: 1em; text-transform: uppercase;
  330 + border: none; color: #FFF; background-color: #387fc1;
  331 +}
  332 +form input[type=submit].button {
  333 + font-size: 1em;
  334 + text-transform: none;
  335 +}
  336 +form div.buttons {
  337 + color: #666;
  338 + background: #FFF url(images/button-bg.png) 0 bottom repeat-x;
  339 + border-radius: 50px;
  340 + -moz-border-radius: 50px;
  341 + -webkit-border-radius: 50px;
  342 + border: 1px solid #bbb;
  343 + display: inline-block;
  344 +}
  345 +form div.buttons:hover {
  346 + color: #666;
  347 + box-shadow: 0 0 3px #69c;
  348 + -moz-box-shadow: 0 0 3px #69c;
  349 + -webkit-box-shadow: 0 0 3px #69c;
  350 +}
  351 +form div.buttons input, form div.buttons button {
  352 + padding: 0 20px;
  353 + color: #666;
  354 + background: none;
  355 + display: inline-block;
  356 + height: 36px;
  357 + font-size: 14px; font-weight: bold; line-height: 36px; text-decoration: none;
  358 + text-shadow: 1px 1px 0px #FFF;
  359 + -moz-text-shadow: 1px 1px 0px #FFF;
  360 + -webkit-text-shadow: 1px 1px 0px #FFF;
  361 + border: none;
  362 +}
  363 +form div.buttons button.sign_in {
  364 + padding-left: 40px;
  365 + background: transparent url(images/icons/right-arrow.png) 3px 3px no-repeat;
  366 +}
  367 +form strong.option {
  368 + display: block;
  369 + margin: 0.7em 0;
  370 + color: #999;
  371 +}
  372 +
  373 +form .nested {
  374 + border-top: 1px dotted #BBB;
  375 + margin-top: 1.5em;
  376 + padding-top: 1.5em;
  377 +}
  378 +
  379 +form legend + .nested {
  380 + border: none;
  381 + margin-top: 0;
  382 + padding-top: 0;
  383 +}
  384 +
  385 +form .error-messages {
  386 + padding: 13px;
  387 + background: #fcc;
  388 + border: 1px solid #f99;
  389 +}
  390 +
  391 +form .error-messages h2 {
  392 + font-size: 1.2em;
  393 + border-color: #F99;
  394 +}
  395 +form .error-messages ul {
  396 + margin-left: 2em;
  397 + list-style-type: square;
  398 +}
  399 +
  400 +form .field-helpertext {
  401 + display: inline;
  402 +}
  403 +
  404 +form input#app_email_at_notices {
  405 + width: 130px;
  406 + margin: 0 5px;
  407 +}
  408 +
  409 +
  410 +/* Tables */
  411 +table {
  412 + width: 100%;
  413 + border: 1px solid #C6C6C6;
  414 + margin-bottom: 1.5em;
  415 + border-collapse: separate;
  416 +}
  417 +table thead th {
  418 + border-top: 1px solid #FFF;
  419 + border-bottom: 1px solid #FFF;
  420 +}
  421 +table th, table td {
  422 + border-top: 1px solid #C6C6C6;
  423 + padding: 10px 8px;
  424 + text-align: left;
  425 +}
  426 +table tbody tr:first-child th, table tbody tr:first-child td {
  427 + border-top: none;
  428 +}
  429 +table thead + tbody tr:first-child td {
  430 + border-top: 1px solid #C6C6C6;
  431 +}
  432 +table th { background-color: #ececec; font-weight: bold; text-transform: uppercase; white-space: nowrap; }
  433 +table tbody tr:nth-child(odd) td { background-color: #F9F9F9; }
  434 +table .main { width: 100%; }
  435 +
  436 +table.single_user {
  437 + border-top: none;
  438 +}
  439 +
  440 +.raw_data {
  441 + width: 100%;
  442 + color: #f0f0f0;
  443 + background-color: #222;
  444 + overflow: auto;
  445 +}
  446 +
  447 +/* Code */
  448 +pre {
  449 + padding: 0.8em;
  450 + margin-bottom: 1em;
  451 + font-family: monaco, courier, monospace;
  452 + font-size: 1.1em;
  453 +}
  454 +
  455 +/* HTML Styling */
  456 +.html { padding-left: 1em; border-left: 2px solid #C6C6C6;}
  457 +.html h1, .html h2, .html h3, .html h4, .html h5, .html h6 {
  458 + border: none;
  459 +}
  460 +.html ul, .html ol { margin-left: 2em; margin-bottom: 1em; }
  461 +.html ul li { margin-bottom: 0.5em; list-style: disc; }
  462 +.html ol li { margin-bottom: 0.5em; list-style: decimal; }
  463 +
  464 +/* Pagination */
  465 +.pagination {
  466 + margin: 0 0 25px;
  467 + font-size: 17px;
  468 + text-align: center;
  469 +}
  470 +.pagination em {
  471 + font-style: normal;
  472 + font-weight: bold;
  473 +}
  474 +
  475 +
  476 +/* Buttons */
  477 +input[type="submit"].button,
  478 +a.button {
  479 + display: inline-block;
  480 + padding: 0 0.8em;
  481 + margin-left: 0.5em;
  482 + color: #666;
  483 + background-color: #dadada;
  484 + border: 1px solid #BBB;
  485 + border-radius: 30px;
  486 + -moz-border-radius: 30px;
  487 + -webkit-border-radius: 30px;
  488 + line-height: 30px;
  489 + min-width: 54px;
  490 + text-align: center;
  491 + text-shadow: 0 1px 0 #fff;
  492 +}
  493 +input[type="submit"]:hover.button,
  494 +a:hover.button {
  495 + box-shadow: 0px 0px 4px #bfbfbf;
  496 + -moz-box-shadow: 0px 0px 4px #bfbfbf;
  497 + -webkit-box-shadow: 0px 0px 4px #bfbfbf;
  498 + text-decoration: none;
  499 + background-color: #e5e5e5;
  500 +}
  501 +a.button.active {
  502 + border-color: #fff;
  503 + background-color: #CCC;
  504 + background-image: none;
  505 +}
  506 +
  507 +
  508 +/* Tab Bar */
  509 +.tab-bar {
  510 + margin-top: 12px;
  511 +}
  512 +#content .tab-bar a.button {
  513 + border-bottom:0;
  514 + margin-bottom:0;
  515 + border-top-left-radius:12px;
  516 + border-top-right-radius:12px;
  517 + border-bottom-left-radius:0;
  518 + border-bottom-right-radius:0;
  519 + height:30px;
  520 +}
  521 +#content .tab-bar a.button.active {
  522 + background: #FFF;
  523 + color: #444;
  524 + border-color:#ccc;
  525 + border-style:solid;
  526 + border-width:1px 1px 0;
  527 + margin-bottom:-1px;
  528 + height:31px;
  529 +}
  530 +.tab-bar ul {
  531 + padding: 9px 0 0;
  532 + line-height:0;
  533 +}
  534 +.tab-bar li {
  535 + display: inline-block;
  536 +}
  537 +
  538 +/* Watchers and Issue Tracker Forms */
  539 +div.watcher.nested .watcher_params, div.issue_tracker.nested .tracker_params {
  540 + display: none;
  541 +}
  542 +div.nested .chosen {
  543 + display: block !important;
  544 +}
  545 +div.nested .choose {
  546 + margin-bottom: 0.5em;
  547 +}
  548 +
  549 +div.issue_tracker.nested .choose {
  550 + background-color: #ebebeb;
  551 + border: 1px solid #dddddd;
  552 + margin: 0 0 15px;
  553 + padding: 12px;
  554 +}
  555 +div.issue_tracker.nested img {
  556 + vertical-align: middle;
  557 +}
  558 +
  559 +/* Icons for Issue Tracker Radio Buttons */
  560 +div.issue_tracker.nested label.label_radio {
  561 + color: #929292;
  562 + padding-left: 33px;
  563 + margin-bottom: 6px;
  564 + margin-right: 8px;
  565 + line-height: 30px;
  566 +}
  567 +div.issue_tracker.nested .choose {
  568 + padding-bottom: 6px;
  569 +}
  570 +div.issue_tracker.nested label.label_radio:hover {
  571 + color: #696969;
  572 +}
  573 +div.issue_tracker.nested .label_radio input {
  574 + position: absolute; left: -9999px;
  575 +}
  576 +
  577 +div.issue_tracker.nested label.r_on, div.issue_tracker.nested label.r_on:hover {
  578 + color: #191919;
  579 +}
  580 +
  581 +/* Icons need to be preloaded, otherwise it looks bad */
  582 +.image_preloader { display: none; }
  583 +
  584 +/* Apps Table */
  585 +table.apps tbody tr:hover td ,table.errs tbody tr:hover td { background-color: #F2F2F2;}
  586 +
  587 +table.apps td.name, table.errs td.message { width: 100%; }
  588 +table.apps td { padding: 16px 25px; }
  589 +table.apps th { padding: 10px 25px; }
  590 +
  591 +table.apps td.issue_tracker, table.apps td.count, table.apps td.deploy {
  592 + text-align: center;
  593 +}
  594 +table.apps td.issue_tracker, table.apps td.count {
  595 + padding: 10px 8px;
  596 +}
  597 +table.apps td.issue_tracker img { vertical-align: top; }
  598 +
  599 +td.message .line {
  600 + display:inline-block;
  601 + margin-left:1em;
  602 +}
  603 +td.deploy {
  604 + white-space: nowrap;
  605 +}
  606 +td.latest {
  607 + white-space: nowrap;
  608 +}
  609 +td.count, td.issue_link {
  610 + text-align: center;
  611 +}
  612 +
  613 +.count a {
  614 + display: inline-block;
  615 + padding: 0.1em 0.7em;
  616 + margin-top: 3px;
  617 + color: #fff;
  618 + background: #cc0033 url(images/error-badge-bg.png) 0 bottom repeat-x;
  619 + border: 1px solid #900;
  620 + border-radius: 18px;
  621 + -moz-border-radius: 18px;
  622 + -webkit-border-radius: 18px;
  623 + font-weight: bold;
  624 + opacity: 0.8;
  625 + -moz-opacity: 0.8;
  626 + -webkit-opacity: 0.8
  627 +}
  628 +.count a.resolved {
  629 + background: #05B81d url(images/resolved-badge-bg.png) 0 bottom repeat-x;
  630 + border: 1px solid #080;
  631 +}
  632 +.count a:hover {
  633 + text-decoration: none;
  634 + opacity: 1;
  635 + -moz-opacity: 1;
  636 + -webkit-opacity: 1;
  637 +}
  638 +
  639 +/* Err Tables */
  640 +table.errs td.app {
  641 + padding-right: 2em;
  642 + width: 20%;
  643 +}
  644 +table.errs td.app .environment {
  645 + font-size: 0.8em;
  646 + color: #999;
  647 +}
  648 +table.errs td.message a {
  649 + display: block;
  650 + word-wrap: break-word;
  651 + /* PjpG - configuration in WHAT & WHERE table's columns using ellipsis to avoid oversizing table's width */
  652 + width: 300px;
  653 + overflow: hidden;
  654 + text-overflow: ellipsis;
  655 + -o-text-overflow: ellipsis;
  656 + white-space: nowrap;
  657 + /* ------ */
  658 +}
  659 +table.errs td.message em {
  660 + color: #727272;
  661 + font-size: 0.9em;
  662 +}
  663 +
  664 +table.errs tr.resolved td > * {
  665 + opacity: 0.5;
  666 + -moz-opacity: 0.5;
  667 + -webkit-opacity: 0.5;
  668 +}
  669 +
  670 +/* Tally tables */
  671 +table.tally {
  672 + border:none;
  673 +}
  674 +table.tally td,
  675 +table.tally th {
  676 + border:none !important;
  677 + background:none !important;
  678 + padding:8px 0 0;
  679 +}
  680 +table.tally tbody tr:first-child td,
  681 +table.tally tbody tr:first-child th {
  682 + padding-top:0;
  683 +}
  684 +table.tally td.percent {
  685 + padding-right: 10px;
  686 +}
  687 +table.tally th.value {
  688 + width: 100%;
  689 + text-transform: none;
  690 +}
  691 +
  692 +/* Deploys table */
  693 +table.deploys td.when {
  694 + width: 102px;
  695 +}
  696 +
  697 +/* Resolve Errs */
  698 +#action-bar a.resolve {
  699 + background: transparent url(images/icons/thumbs-up.png) 6px 5px no-repeat;
  700 +}
  701 +
  702 +/* Go Up */
  703 +#action-bar a.up {
  704 + background: transparent url(images/icons/up.png) 6px 5px no-repeat;
  705 +}
  706 +
  707 +/* Notices Pagination */
  708 +.notice-pagination {
  709 + float: left;
  710 + margin-right: 10px;
  711 +}
  712 +
  713 +.notice-pagination-loader {
  714 + visibility: hidden;
  715 + float: left;
  716 + margin-right: 2em;
  717 +}
  718 +.notice-pagination-loader img {
  719 + vertical-align: middle
  720 +}
  721 +
  722 +
  723 +/* Backtrace */
  724 +.window {
  725 + width: 100%;
  726 + margin-bottom: 1em;
  727 + overflow: auto;
  728 + border:1px solid #ccc;
  729 + padding:1px;
  730 +}
  731 +
  732 +.window table {
  733 + margin: 0;
  734 +}
  735 +
  736 +table.backtrace {
  737 + padding: 8px 0;
  738 + background-color: #222;
  739 +}
  740 +
  741 +table.backtrace td {
  742 + width: 100%;
  743 + padding: 0;
  744 + margin: 0;
  745 + color: #C7C7C7;
  746 + background-color: #222;
  747 +}
  748 +
  749 +table.backtrace td, table.backtrace th {
  750 + border-top: none;
  751 +}
  752 +
  753 +/* remove alternating color rules */
  754 +table.backtrace tr:nth-child(2n+1) td { background-color: #222; }
  755 +table.backtrace tr:first-child td { border-top: 0; }
  756 +
  757 +table.backtrace th.line-numbers {
  758 + border-bottom: 1px solid #F0F0F0;
  759 + font-size: 13px;
  760 + text-align: right;
  761 + vertical-align: top;
  762 + padding: 1px 6px 1px 7px;
  763 +}
  764 +
  765 +table.backtrace td.line {
  766 + font-size: 13px;
  767 + padding: 2px 8px;
  768 + vertical-align: top;
  769 + white-space: nowrap;
  770 +}
  771 +table.backtrace td.line .file {
  772 + font-weight: bold;
  773 +}
  774 +table.backtrace td.line .method {
  775 + color: #aaa;
  776 + font-weight: bold;
  777 +}
  778 +
  779 +table.backtrace td.line.in-app {
  780 + color: #2adb2e;
  781 + background-color: #2f2f2f;
  782 +}
  783 +table.backtrace td.line.in-app .path,
  784 +table.backtrace td.line.in-app .number { color: #2ACB2E; }
  785 +table.backtrace td.line.in-app .file { color: #3AFB3E; }
  786 +table.backtrace td.line.in-app .method { color: #2ACB2E; }
  787 +
  788 +table.backtrace td.line.in-app a .path,
  789 +table.backtrace td.line.in-app a .number,
  790 +table.backtrace td.line.in-app a:hover { color: #21B4FF; }
  791 +table.backtrace td.line.in-app a .file { color: #31C4FF; }
  792 +
  793 +/* External backtrace classes and separators */
  794 +table.backtrace tr.hidden_external_backtrace {
  795 + display: none;
  796 +}
  797 +table.backtrace td.backtrace_separator span {
  798 + cursor: pointer;
  799 + display: inline-block;
  800 + font-size: 17px;
  801 + font-weight: bold;
  802 + padding: 0px 11px 5px;
  803 + margin: 4px 0;
  804 + background-color: #444444;
  805 + border: 1px solid #555555;
  806 +}
  807 +table.backtrace td.backtrace_separator span:hover {
  808 + background-color: #666666;
  809 + border: 1px solid #777777;
  810 +}
  811 +
  812 +
  813 +
  814 +/* Extra empty rows at top and bottom of table */
  815 +table.backtrace tr.padding th, table.backtrace tr.padding td {
  816 + height: 10px;
  817 + margin: 0;
  818 + padding: 0;
  819 +}
  820 +
  821 +h3#watchers_toggle, h3#repository_toggle, h3#deploys_toggle {
  822 + cursor: pointer;
  823 +}
  824 +
  825 +span.click_span {
  826 + font-size: 0.7em;
  827 +}
  828 +
  829 +#deploys_div, #repository_div, #watchers_div {
  830 + display: none;
  831 +}
  832 +
  833 +/* Comments */
  834 +#content-comments form p {
  835 + margin: 30px 0 0 0;
  836 + text-transform: uppercase;
  837 +}
  838 +table.comment tbody th {
  839 + text-transform: none;
  840 + font-weight: normal;
  841 + height: 20px;
  842 + line-height: 0.5em;
  843 +}
  844 +table.comment th span, table.comment th img {
  845 + vertical-align: middle;
  846 +}
  847 +table.comment th span.comment-info {
  848 + line-height: 21px;
  849 +}
  850 +table.comment img.gravatar {
  851 + margin-right: 7px;
  852 +}
  853 +
  854 +table.comment tbody td {
  855 + background-color: #F9F9F9;
  856 +}
  857 +#content-comments a.destroy-comment {
  858 + color: #EE0000;
  859 + margin-right: 5px;
  860 + margin-top: 2px;
  861 + font-size: 21px;
  862 + line-height: 1;
  863 + float: right;
  864 +}
  865 +#content-comments a.destroy-comment:hover {
  866 + text-decoration: none;
  867 + color: #AA0000;
  868 +}
  869 +#content-comments #comment_submit {
  870 + margin-top: 15px;
  871 +}
  872 +/* Inline comments in tables */
  873 +table.errs tr td.message .inline_comment {
  874 + display: inline-block;
  875 + padding: 3px 7px;
  876 + margin: 6px 0;
  877 + background-color: #DAE5FF;
  878 + border: 1px solid #E2E2E2;
  879 + text-shadow: 0 1px 0 #FAFAFA;
  880 + font-style: normal;
  881 +}
  882 +table.errs tr:hover td.message .inline_comment {
  883 + background-color: #D5E0FA;
  884 + border-color: #DBDBDB;
  885 + text-shadow: 0 1px 0 #FFFFFF;
  886 +}
  887 +table.errs tr td.message .inline_comment em {
  888 + color: #444;
  889 +}
  890 +table.errs tr td.message .inline_comment em.commenter {
  891 + color: #777;
  892 +}
  893 +
  894 +.current.asc:after { content: ' ↑'; }
  895 +.current.desc:after { content: ' ↓'; }
  896 +
  897 +
  898 +table.users td {
  899 + vertical-align: middle;
  900 +}
  901 +table.users td img.gravatar {
  902 + vertical-align: middle;
  903 + margin-left: 3px;
  904 +}
... ...
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 = "last_notice_at" unless %w{message app last_deploy_at count}.member?(@sort)
14   - @order = "desc" unless (@order == "asc")
  13 + @sort = "last_notice_at" unless %w{message app last_deploy_at last_notice_at count}.member?(@sort)
  14 + @order = "desc" unless %w{asc desc}.member?(@order)
15 15  
16 16 @problems = resource.problems
17 17 @problems = @problems.unresolved unless @all_errs
... ... @@ -50,13 +50,23 @@ class AppsController &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 + :username => current_user.github_login,
  50 + :oauth_token => current_user.github_oauth_token
  51 + )
  52 + end
45 53  
46   - if @app.issue_tracker
47   - @app.issue_tracker.create_issue @problem
  54 + # Or, create an issue using the App's issue tracker
  55 + elsif @app.issue_tracker_configured?
  56 + @tracker = @app.issue_tracker
  57 +
  58 + # Otherwise, display error about missing tracker configuration.
48 59 else
49 60 flash[:error] = "This app has no issue tracker setup."
50 61 end
51   - redirect_to app_err_path(@app, @problem)
52   - rescue ActiveResource::ConnectionError => e
53   - Rails.logger.error e.to_s
54   - flash[:error] = "There was an error during issue creation. Check your tracker settings or try again later."
  62 +
  63 + if flash[:error].blank? && @tracker
  64 + begin
  65 + @tracker.create_issue @problem, current_user
  66 + rescue Exception => ex
  67 + Rails.logger.error "Error during issue creation: " << ex.message
  68 + flash[:error] = "There was an error during issue creation: #{ex.message}"
  69 + end
  70 + end
  71 +
55 72 redirect_to app_err_path(@app, @problem)
56 73 end
57 74  
... ... @@ -61,9 +78,6 @@ class ErrsController &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."
... ... @@ -123,8 +114,8 @@ class ErrsController &lt; ApplicationController
123 114 end
124 115  
125 116 def destroy_several
126   - @selected_problems.each(&:destroy)
127   - flash[:notice] = "#{pluralize(@selected_problems.count, 'err has', 'errs have')} been deleted."
  117 + nb_problem_destroy = ProblemDestroy.execute(@selected_problems)
  118 + flash[:notice] = "#{pluralize(nb_problem_destroy, 'err has', 'errs have')} been deleted."
128 119 redirect_to :back
129 120 end
130 121  
... ... @@ -139,9 +130,6 @@ class ErrsController &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/notices_controller.rb
... ... @@ -5,9 +5,17 @@ class NoticesController &lt; ApplicationController
5 5  
6 6 def create
7 7 # params[:data] if the notice came from a GET request, raw_post if it came via POST
8   - @notice = App.report_error!(params[:data] || request.raw_post)
9   - respond_with @notice
  8 + notice = App.report_error!(params[:data] || request.raw_post)
  9 + api_xml = notice.to_xml(:only => false, :methods => [:id]) do |xml|
  10 + xml.url locate_url(notice.id, :host => Errbit::Config.host)
  11 + end
  12 + render :xml => api_xml
10 13 end
11 14  
  15 + # Redirects a notice to the problem page. Useful when using User Information at Airbrake gem.
  16 + def locate
  17 + problem = Notice.find(params[:id]).problem
  18 + redirect_to app_err_path(problem.app, problem)
  19 + end
12 20 end
13 21  
... ...
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,12 +58,25 @@ module ApplicationHelper
58 58 percent = 100.0 / total.to_f
59 59 rows = tallies.map {|value, count| [(count.to_f * percent), value]} \
60 60 .sort {|a, b| a[0] <=> b[0]}
61   - render :partial => "errs/tally_table", :locals => {:rows => rows}
  61 + render "errs/tally_table", :rows => rows
  62 + end
  63 +
  64 + def head(collection)
  65 + collection.first(head_size)
  66 + end
  67 +
  68 + def tail(collection)
  69 + collection.to_a[head_size..-1].to_a
62 70 end
63 71  
64 72 private
65 73 def total_from_tallies(tallies)
66 74 tallies.values.inject(0) {|sum, n| sum + n}
67 75 end
  76 +
  77 + def head_size
  78 + 4
  79 + end
  80 +
68 81 end
69 82  
... ...
app/helpers/apps_helper.rb
... ... @@ -10,5 +10,31 @@ module AppsHelper
10 10 return html
11 11 end
12 12 end
  13 +
  14 + def any_github_repos?
  15 + detect_any_apps_with_attributes unless @any_github_repos
  16 + @any_github_repos
  17 + end
  18 +
  19 + def any_issue_trackers?
  20 + detect_any_apps_with_attributes unless @any_issue_trackers
  21 + @any_issue_trackers
  22 + end
  23 +
  24 + def any_deploys?
  25 + detect_any_apps_with_attributes unless @any_deploys
  26 + @any_deploys
  27 + end
  28 +
  29 + private
  30 +
  31 + def detect_any_apps_with_attributes
  32 + @any_github_repos = @any_issue_trackers = @any_deploys = false
  33 + @apps.each do |app|
  34 + @any_github_repos ||= app.github_repo?
  35 + @any_issue_trackers ||= app.issue_tracker_configured?
  36 + @any_deploys ||= !!app.last_deploy_at
  37 + end
  38 + end
13 39 end
14 40  
... ...
app/helpers/errs_helper.rb
... ... @@ -13,5 +13,19 @@ module ErrsHelper
13 13 truncate(msg, :length => 300).scan(/.{1,5}/).map { |s| h(s) }.join("&#8203;").html_safe
14 14 end
15 15 end
  16 +
  17 + def gravatar_tag(email, options = {})
  18 + image_tag gravatar_url(email, options), :alt => email, :class => 'gravatar'
  19 + end
  20 +
  21 + def gravatar_url(email, options = {})
  22 + default_options = {
  23 + :d => Errbit::Config.gravatar_default,
  24 + }
  25 + options.reverse_merge! default_options
  26 + params = options.extract!(:s, :d).delete_if { |k, v| v.blank? }
  27 + email_hash = Digest::MD5.hexdigest(email)
  28 + "http://www.gravatar.com/avatar/#{email_hash}?#{params.to_query}"
  29 + end
16 30 end
17 31  
... ...
app/helpers/navigation_helper.rb
... ... @@ -34,4 +34,15 @@ module NavigationHelper
34 34 active
35 35 end
36 36  
  37 + # Returns the page number in reverse order.
  38 + # Needed when reverse chronological paginating notices but
  39 + # want to display the actual chronological occurrence number.
  40 + #
  41 + # E.G. - Given 6 notices, page 2 shows the second from last
  42 + # occurrence indexed by 1, but it is diplaying the 5th ever
  43 + # occurence of that error.
  44 + def page_count_from_end(current_page, total_pages)
  45 + (total_pages.to_i - current_page.to_i) + 1
  46 + end
  47 +
37 48 end
... ...
app/helpers/notices_helper.rb
1 1 # encoding: utf-8
2 2 module NoticesHelper
3   - def notice_atom_summary notice
4   - render :partial => "notices/atom_entry.html.haml", :locals => {:notice => notice}
  3 + def in_app_backtrace_line?(line)
  4 + !!(line['file'] =~ %r{^\[PROJECT_ROOT\]/(?!(vendor))})
5 5 end
6 6  
7   - def link_to_source_file(app, line, text)
8   - if Notice.in_app_backtrace_line?(line)
9   - return link_to_github(app, line, text) if app.github_url?
  7 + def notice_atom_summary(notice)
  8 + render "notices/atom_entry.html.haml", :notice => notice
  9 + end
  10 +
  11 + def link_to_source_file(app, line, &block)
  12 + text = capture_haml(&block)
  13 + if in_app_backtrace_line?(line)
  14 + return link_to_github(app, line, text) if app.github_repo?
10 15 if app.issue_tracker && app.issue_tracker.respond_to?(:url_to_file)
11 16 # Return link to file on tracker if issue tracker supports this
12 17 return link_to_issue_tracker_file(app, line, text)
... ... @@ -30,5 +35,34 @@ module NoticesHelper
30 35 href = app.issue_tracker.url_to_file(file_path, line['number'])
31 36 link_to(text || file_name, href, :target => '_blank')
32 37 end
  38 +
  39 + # Group lines into sections of in-app files and external files
  40 + # (An implementation of Enumerable#chunk so we don't break 1.8.7 support.)
  41 + def grouped_lines(lines)
  42 + line_groups = []
  43 + lines.each do |line|
  44 + in_app = in_app_backtrace_line?(line)
  45 + if line_groups.last && line_groups.last[0] == in_app
  46 + line_groups.last[1] << line
  47 + else
  48 + line_groups << [in_app, [line]]
  49 + end
  50 + end
  51 + line_groups
  52 + end
  53 +
  54 + def path_for_backtrace_line(line)
  55 + path = File.dirname(line['file'])
  56 + return '' if path == '.'
  57 + # Remove [PROJECT_ROOT]
  58 + path.gsub!('[PROJECT_ROOT]/', '')
  59 + # Make gem name bold if starts with [GEM_ROOT]/gems
  60 + path.gsub!(/\[GEM_ROOT\]\/gems\/([^\/]+)/, "<strong>\\1</strong>")
  61 + (path << '/').html_safe
  62 + end
  63 +
  64 + def file_for_backtrace_line(line)
  65 + file = File.basename(line['file'])
  66 + end
33 67 end
34 68  
... ...
app/interactors/problem_destroy.rb 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +class ProblemDestroy
  2 +
  3 + attr_reader :problem
  4 +
  5 + def initialize(problem)
  6 + @problem = problem
  7 + end
  8 +
  9 + def execute
  10 + delete_errs
  11 + delete_comments
  12 + problem.delete
  13 + end
  14 +
  15 + ##
  16 + # Destroy all problem pass in args
  17 + #
  18 + # @params [ Array[Problem] ] problems the list of problem need to be delete
  19 + # can be a single Problem
  20 + # @return [ Integer ]
  21 + # the number of problem destroy
  22 + #
  23 + def self.execute(problems)
  24 + Array(problems).each{ |problem|
  25 + ProblemDestroy.new(problem).execute
  26 + }.count
  27 + end
  28 +
  29 + private
  30 +
  31 + def errs_id
  32 + problem.errs.only(:id).map(&:id)
  33 + end
  34 +
  35 + def comments_id
  36 + problem.comments.only(:id).map(&:id)
  37 + end
  38 +
  39 + def delete_errs
  40 + Err.collection.remove(:_id => { '$in' => errs_id })
  41 + Notice.collection.remove(:err_id => { '$in' => errs_id })
  42 + end
  43 +
  44 + def delete_comments
  45 + Comment.collection.remove(:_id => { '$in' => comments_id })
  46 + end
  47 +
  48 +end
... ...
app/models/app.rb
... ... @@ -4,7 +4,7 @@ class App
4 4  
5 5 field :name, :type => String
6 6 field :api_key
7   - field :github_url
  7 + field :github_repo
8 8 field :resolve_errs_on_deploy, :type => Boolean, :default => false
9 9 field :notify_all_users, :type => Boolean, :default => false
10 10 field :notify_on_errs, :type => Boolean, :default => true
... ... @@ -14,20 +14,13 @@ class App
14 14 # Some legacy apps may have string as key instead of BSON::ObjectID
15 15 identity :type => String
16 16  
17   - # There seems to be a Mongoid bug making it impossible to use String identity with references_many feature:
18   - # https://github.com/mongoid/mongoid/issues/703
19   - # Using 32 character string as a workaround.
20   - before_create do |r|
21   - r.id = ActiveSupport::SecureRandom.hex
22   - end
23   -
24 17 embeds_many :watchers
25 18 embeds_many :deploys
26 19 embeds_one :issue_tracker
27 20 has_many :problems, :inverse_of => :app, :dependent => :destroy
28 21  
29 22 before_validation :generate_api_key, :on => :create
30   - before_save :normalize_github_url
  23 + before_save :normalize_github_repo
31 24 after_update :store_cached_attributes_on_problems
32 25  
33 26 validates_presence_of :name, :api_key
... ... @@ -46,7 +39,7 @@ class App
46 39 #
47 40 # Accepts either XML or a hash with the following attributes:
48 41 #
49   - # * <tt>:klass</tt> - the class of error
  42 + # * <tt>:error_class</tt> - the class of error
50 43 # * <tt>:message</tt> - the error message
51 44 # * <tt>:backtrace</tt> - an array of stack trace lines
52 45 #
... ... @@ -66,7 +59,7 @@ class App
66 59 #
67 60 # Accepts a hash with the following attributes:
68 61 #
69   - # * <tt>:klass</tt> - the class of error
  62 + # * <tt>:error_class</tt> - the class of error
70 63 # * <tt>:message</tt> - the error message
71 64 # * <tt>:backtrace</tt> - an array of stack trace lines
72 65 #
... ... @@ -111,14 +104,19 @@ class App
111 104 alias :notify_on_deploys? :notify_on_deploys
112 105  
113 106  
114   - def github_url?
115   - self.github_url.present?
  107 + def github_repo?
  108 + self.github_repo.present?
  109 + end
  110 +
  111 + def github_url
  112 + "https://github.com/#{github_repo}" if github_repo?
116 113 end
117 114  
118 115 def github_url_to_file(file)
119   - "#{self.github_url}/blob/master#{file}"
  116 + "#{github_url}/blob/master#{file}"
120 117 end
121 118  
  119 +
122 120 def issue_tracker_configured?
123 121 !!(issue_tracker && issue_tracker.class < IssueTracker && issue_tracker.project_id.present?)
124 122 end
... ... @@ -154,7 +152,7 @@ class App
154 152 end
155 153  
156 154 def generate_api_key
157   - self.api_key ||= ActiveSupport::SecureRandom.hex
  155 + self.api_key ||= SecureRandom.hex
158 156 end
159 157  
160 158 def check_issue_tracker
... ... @@ -166,11 +164,11 @@ class App
166 164 end
167 165 end
168 166  
169   - def normalize_github_url
170   - return if self.github_url.blank?
171   - self.github_url.gsub!(%r{^http://|git@}, 'https://')
172   - self.github_url.gsub!(/github\.com:/, 'github.com/')
173   - self.github_url.gsub!(/\.git$/, '')
  167 + def normalize_github_repo
  168 + return if github_repo.blank?
  169 + github_repo.strip!
  170 + github_repo.sub!(/(git@|https?:\/\/)github\.com(\/|:)/, '')
  171 + github_repo.sub!(/\.git$/, '')
174 172 end
175 173 end
176 174  
... ...
app/models/deploy.rb
... ... @@ -12,16 +12,11 @@ class Deploy
12 12  
13 13 embedded_in :app, :inverse_of => :deploys
14 14  
15   - after_create :deliver_notification, :if => :should_notify?
16 15 after_create :resolve_app_errs, :if => :should_resolve_app_errs?
17 16 after_create :store_cached_attributes_on_problems
18 17  
19 18 validates_presence_of :username, :environment
20 19  
21   - def deliver_notification
22   - Mailer.deploy_notification(self).deliver
23   - end
24   -
25 20 def resolve_app_errs
26 21 app.problems.unresolved.in_env(environment).each {|problem| problem.resolve!}
27 22 end
... ... @@ -32,10 +27,6 @@ class Deploy
32 27  
33 28 protected
34 29  
35   - def should_notify?
36   - app.notify_on_deploys? && app.notification_recipients.any?
37   - end
38   -
39 30 def should_resolve_app_errs?
40 31 app.resolve_errs_on_deploy?
41 32 end
... ...
app/models/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,11 +14,11 @@ class Err
14 14  
15 15 belongs_to :problem
16 16 index :problem_id
17   - index :klass
  17 + index :error_class
18 18  
19 19 has_many :notices, :inverse_of => :err, :dependent => :destroy
20 20  
21   - validates_presence_of :klass, :environment
  21 + validates_presence_of :error_class, :environment
22 22  
23 23 delegate :app, :resolved?, :to => :problem
24 24  
... ...
app/models/error_report.rb
... ... @@ -2,7 +2,7 @@ require &#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 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,30 @@ 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   - story = project.stories.create :name => issue_title(problem), :story_type => 'bug', :description => body_template.result(binding)
21   - problem.update_attribute :issue_link, "https://www.pivotaltracker.com/story/show/#{story.id}"
  20 + story = project.stories.create :name => issue_title(problem),
  21 + :story_type => 'bug', :description => body_template.result(binding),
  22 + :requested_by => reported_by.name
  23 +
  24 + if story.errors.present?
  25 + raise IssueTrackers::IssueTrackerError, story.errors.first
  26 + else
  27 + problem.update_attributes(
  28 + :issue_link => "https://www.pivotaltracker.com/story/show/#{story.id}",
  29 + :issue_type => Label
  30 + )
  31 + end
22 32 end
23 33  
24 34 def body_template
25 35 @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/pivotal_body.txt.erb"))
26 36 end
  37 +
  38 + def url
  39 + "https://www.pivotaltracker.com/"
  40 + end
27 41 end
28 42  
... ...
app/models/issue_trackers/redmine_tracker.rb
... ... @@ -25,18 +25,22 @@ class IssueTrackers::RedmineTracker &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  
... ...