ass 0.0.11 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -63,6 +63,7 @@ under the current directory, provide single pem file combined with certificate a
63
63
  * start ass server, default port is 4567 (sinatra's default port)
64
64
 
65
65
  ![ass usage](https://raw.github.com/eiffelqiu/ass/master/doc/capture1.png)
66
+ ![ass usage](https://raw.github.com/eiffelqiu/ass/master/doc/capture2.png)
66
67
 
67
68
  Configuration (ass.yml)
68
69
  =======
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.11
1
+ 0.0.15
data/ass.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  port: 4567
2
2
  mode: development
3
+ log: off
3
4
  cron: cron
4
5
  timer: 0
5
6
  apps:
data/cron CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
+ require 'rubygems'
4
5
  require 'yaml'
5
6
  require 'cgi'
6
7
  require 'digest/sha2'
@@ -25,7 +26,7 @@ $apps = config['apps'] || []
25
26
  ## Using curl command to send push notification message
26
27
  ############################################################
27
28
 
28
- @message = CGI::escape("今天你搓一男家的狗怎么样了?".to_uri)
29
+ @message = CGI::escape("This is an push notification message sent by ASS".to_uri)
29
30
 
30
31
  #@pid = Digest::SHA2.hexdigest("#{Time.now.to_i}")
31
32
  @pid = "#{Time.now.to_i}"
data/lib/ass.rb CHANGED
@@ -44,12 +44,14 @@ $port = config['port'] || 4567
44
44
  $mode = config['mode'] || env
45
45
  $VERSION = File.open("#{ROOTDIR}/VERSION", "rb").read
46
46
  $apps = config['apps'] || []
47
+ $log = config['log'] || 'off'
47
48
 
48
49
  ############################################################
49
50
  ## Certificate Key Setup
50
51
  ############################################################
51
52
 
52
53
  $certkey = {}
54
+
53
55
  def check_cert
54
56
  $apps.each { |app|
55
57
  unless File.exist?("#{Dir.pwd}/#{app}_#{$mode}.pem") then
@@ -60,31 +62,41 @@ def check_cert
60
62
  certfile = File.read("#{Dir.pwd}/#{app}_#{$mode}.pem")
61
63
  openSSLContext = OpenSSL::SSL::SSLContext.new
62
64
  openSSLContext.cert = OpenSSL::X509::Certificate.new(certfile)
63
- openSSLContext.key = OpenSSL::PKey::RSA.new(certfile)
64
- $certkey["#{app}"] = openSSLContext
65
+ openSSLContext.key = OpenSSL::PKey::RSA.new(certfile)
66
+ $certkey["#{app}"] = openSSLContext
65
67
  end
66
- }
68
+ }
67
69
  return true
68
70
  end
69
71
 
70
72
  unless check_cert then
71
- puts "1: please provide certificate key pem file under current directory, name should be: appid_dev.pem for development and appid_prod.pem for production"
72
- puts "2: edit your ass.yml under current directory"
73
- puts "3: run ass"
74
- puts "4: iOS Client: in AppDelegate file, didRegisterForRemoteNotificationsWithDeviceToken method should access url below:"
73
+ html = <<-END
74
+ 1: please provide certificate key pem file under current directory, name should be: appid_dev.pem for development and appid_prod.pem for production
75
+ 2: edit your ass.yml under current directory
76
+ 3: run ass
77
+ 4: iOS Client: in AppDelegate file, didRegisterForRemoteNotificationsWithDeviceToken method should access url below:
78
+ END
75
79
  $apps.each { |app|
76
- puts "'#{app}'s registration url: http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken"
80
+ html << "'#{app}'s registration url: http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken"
77
81
  }
78
- puts "5: Server: cron should access 'curl http://localhost:#{$port}/v1/app/push/{messages}/{pid}' to send push message"
82
+ html << "5: Server: cron should access 'curl http://localhost:#{$port}/v1/app/push/{messages}/{pid}' to send push message"
83
+ puts html
79
84
  exit
80
85
  else
81
- puts "*"*80
82
- puts "Apple Service Server(#{$VERSION}) is Running ..."
83
- puts "Push Notification Service: Enabled"
84
- puts "Mode: #{$mode}"
85
- puts "Port: #{$port}"
86
- puts "Cron Job: '#{Dir.pwd}/#{$cron}' script is running every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'} " unless "#{$timer}".to_i == 0
87
- puts "*"*80
86
+ html = <<-END
87
+ #{'*'*80}
88
+ Apple Service Server(#{$VERSION}) is Running ...
89
+ Push Notification Service: Enabled
90
+ Mode: #{$mode}
91
+ Port: #{$port}
92
+ END
93
+ html << "#{'*'*80}"
94
+ html << "Cron Job: '#{Dir.pwd}/#{$cron}' script is running every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'} " unless "#{$timer}".to_i == 0
95
+ html << "\n"
96
+ html << "access http://localhost:#{$port}/ for more information"
97
+ html << "\n"
98
+ html << "#{'*'*80}"
99
+ puts html
88
100
  end
89
101
 
90
102
  ############################################################
@@ -97,7 +109,7 @@ unless File.exist?("#{Dir.pwd}/ass.db") then
97
109
  $DB.create_table :tokens do
98
110
  primary_key :id
99
111
  String :app, :unique => false, :null => false
100
- String :token, :unique => true, :null => false, :size => 100
112
+ String :token, :unique => false, :null => false, :size => 100
101
113
  index [:app, :token]
102
114
  end
103
115
 
@@ -123,25 +135,6 @@ unless $timer == 0 then
123
135
  puts "running job: '#{Dir.pwd}/#{$cron}' every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'}"
124
136
  system "./#{$cron}"
125
137
  end
126
- else
127
- puts "1: How to register notification? (Client Side)"
128
- puts
129
- puts "In AppDelegate file, inside didRegisterForRemoteNotificationsWithDeviceToken method access url below to register device token:"
130
- $apps.each { |app|
131
- puts "'#{app}'s registration url: http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken"
132
- }
133
- puts
134
- puts "2: How to send push notification? (Server Side)"
135
- puts
136
- $apps.each { |app|
137
- puts "curl http://localhost:#{$port}/v1/apps/#{app}/push/{message}/{pid}"
138
- }
139
- puts
140
- puts "Note:"
141
- puts "param1 (message): push notification message you want to send, remember the message should be html escaped"
142
- puts "param2 (pid ): unique string to mark the message, for example current timestamp or md5/sha1 digest"
143
- puts
144
- puts "*"*80
145
138
  end
146
139
 
147
140
  ############################################################
@@ -149,39 +142,34 @@ end
149
142
  ############################################################
150
143
 
151
144
  class App < Sinatra::Base
152
-
153
- set :port, "#{$port}".to_i
154
145
 
146
+ set :root, File.expand_path('../../', __FILE__)
147
+ set :port, "#{$port}".to_i
148
+ set :public_folder, File.dirname(__FILE__) + '/../public'
149
+ set :views, File.dirname(__FILE__) + '/../views'
150
+
151
+ configure :production, :development do
152
+ if "#{$log}".strip == 'on' then
153
+ enable :logging
154
+ end
155
+ end
156
+
155
157
  if "#{$mode}".strip == 'development' then
156
158
  set :show_exceptions, true
157
159
  set :dump_errors, true
158
160
  else
159
161
  set :show_exceptions, false
160
- set :dump_errors, false
162
+ set :dump_errors, false
161
163
  end
162
164
 
163
165
  get '/' do
164
- o = "Apple Service Server #{$VERSION} <br/><br/>" +
165
- "author: Eiffel(Q) <br/>email: eiffelqiu@gmail.com<br/><br/>"
166
- o += "1: How to register notification? (Client Side)<br/><br/>"
167
- o += "In AppDelegate file, inside didRegisterForRemoteNotificationsWithDeviceToken method access url below to register device token:<br/><br/>"
168
- $apps.each { |app|
169
- o += "'#{app}': http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken<br/>"
170
- }
171
- o += "<br/>2: How to send push notification? (Server Side)<br/><br/>"
172
- $apps.each { |app|
173
- o += "curl http://localhost:#{$port}/v1/apps/#{app}/push/{message}/{pid}<br/>"
174
- }
175
- o += "<br/>Note:<br/>"
176
- o += "param1 (message): push notification message you want to send, remember the message should be html escaped<br/>"
177
- o += "param2 (pid ): unique string to mark the message, for example current timestamp or md5/sha1 digest<br/>"
178
- o
166
+ erb :index
179
167
  end
180
168
 
181
169
  $apps.each { |app|
182
170
  get "/v1/apps/#{app}/:token" do
183
- puts "[#{params[:token]}] was added to '#{app}'"
184
- o = Token.first(:token => params[:token])
171
+ puts "[#{params[:token]}] was added to '#{app}'" if "#{$mode}".strip == 'development'
172
+ o = Token.first(:app => app , :token => params[:token])
185
173
  unless o
186
174
  Token.insert(
187
175
  :app => app,
@@ -192,10 +180,10 @@ class App < Sinatra::Base
192
180
 
193
181
  get "/v1/apps/#{app}/push/:message/:pid" do
194
182
  message = CGI::unescape(params[:message])
195
- puts message
183
+ puts message if "#{$mode}".strip == 'development'
196
184
  pid = params[:pid]
197
185
 
198
- puts "'#{message}' was sent to (#{app}) with pid: [#{pid}]"
186
+ puts "'#{message}' was sent to (#{app}) with pid: [#{pid}]" if "#{$mode}".strip == 'development'
199
187
 
200
188
  @push = Token.where(:app => app)
201
189
  @exist = Push.first(:pid => pid)
@@ -220,8 +208,8 @@ class App < Sinatra::Base
220
208
  # pack the token to convert the ascii representation back to binary
221
209
  tokenData = [tokenText].pack('H*')
222
210
  # construct the payload
223
- po = { :aps => { :alert => "#{message}", :badge => 1 }}
224
- payload = ActiveSupport::JSON.encode(po)
211
+ po = {:aps => {:alert => "#{message}", :badge => 1}}
212
+ payload = ActiveSupport::JSON.encode(po)
225
213
  # construct the packet
226
214
  packet = [0, 0, 32, tokenData, 0, payload.length, payload].pack("ccca*cca*")
227
215
  # read our certificate and set up our SSL context
@@ -0,0 +1,1058 @@
1
+ /*!
2
+ * Bootstrap Responsive v2.1.1
3
+ *
4
+ * Copyright 2012 Twitter, Inc
5
+ * Licensed under the Apache License v2.0
6
+ * http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
9
+ */
10
+
11
+ .clearfix {
12
+ *zoom: 1;
13
+ }
14
+
15
+ .clearfix:before,
16
+ .clearfix:after {
17
+ display: table;
18
+ line-height: 0;
19
+ content: "";
20
+ }
21
+
22
+ .clearfix:after {
23
+ clear: both;
24
+ }
25
+
26
+ .hide-text {
27
+ font: 0/0 a;
28
+ color: transparent;
29
+ text-shadow: none;
30
+ background-color: transparent;
31
+ border: 0;
32
+ }
33
+
34
+ .input-block-level {
35
+ display: block;
36
+ width: 100%;
37
+ min-height: 30px;
38
+ -webkit-box-sizing: border-box;
39
+ -moz-box-sizing: border-box;
40
+ box-sizing: border-box;
41
+ }
42
+
43
+ .hidden {
44
+ display: none;
45
+ visibility: hidden;
46
+ }
47
+
48
+ .visible-phone {
49
+ display: none !important;
50
+ }
51
+
52
+ .visible-tablet {
53
+ display: none !important;
54
+ }
55
+
56
+ .hidden-desktop {
57
+ display: none !important;
58
+ }
59
+
60
+ .visible-desktop {
61
+ display: inherit !important;
62
+ }
63
+
64
+ @media (min-width: 768px) and (max-width: 979px) {
65
+ .hidden-desktop {
66
+ display: inherit !important;
67
+ }
68
+ .visible-desktop {
69
+ display: none !important ;
70
+ }
71
+ .visible-tablet {
72
+ display: inherit !important;
73
+ }
74
+ .hidden-tablet {
75
+ display: none !important;
76
+ }
77
+ }
78
+
79
+ @media (max-width: 767px) {
80
+ .hidden-desktop {
81
+ display: inherit !important;
82
+ }
83
+ .visible-desktop {
84
+ display: none !important;
85
+ }
86
+ .visible-phone {
87
+ display: inherit !important;
88
+ }
89
+ .hidden-phone {
90
+ display: none !important;
91
+ }
92
+ }
93
+
94
+ @media (min-width: 1200px) {
95
+ .row {
96
+ margin-left: -30px;
97
+ *zoom: 1;
98
+ }
99
+ .row:before,
100
+ .row:after {
101
+ display: table;
102
+ line-height: 0;
103
+ content: "";
104
+ }
105
+ .row:after {
106
+ clear: both;
107
+ }
108
+ [class*="span"] {
109
+ float: left;
110
+ min-height: 1px;
111
+ margin-left: 30px;
112
+ }
113
+ .container,
114
+ .navbar-static-top .container,
115
+ .navbar-fixed-top .container,
116
+ .navbar-fixed-bottom .container {
117
+ width: 1170px;
118
+ }
119
+ .span12 {
120
+ width: 1170px;
121
+ }
122
+ .span11 {
123
+ width: 1070px;
124
+ }
125
+ .span10 {
126
+ width: 970px;
127
+ }
128
+ .span9 {
129
+ width: 870px;
130
+ }
131
+ .span8 {
132
+ width: 770px;
133
+ }
134
+ .span7 {
135
+ width: 670px;
136
+ }
137
+ .span6 {
138
+ width: 570px;
139
+ }
140
+ .span5 {
141
+ width: 470px;
142
+ }
143
+ .span4 {
144
+ width: 370px;
145
+ }
146
+ .span3 {
147
+ width: 270px;
148
+ }
149
+ .span2 {
150
+ width: 170px;
151
+ }
152
+ .span1 {
153
+ width: 70px;
154
+ }
155
+ .offset12 {
156
+ margin-left: 1230px;
157
+ }
158
+ .offset11 {
159
+ margin-left: 1130px;
160
+ }
161
+ .offset10 {
162
+ margin-left: 1030px;
163
+ }
164
+ .offset9 {
165
+ margin-left: 930px;
166
+ }
167
+ .offset8 {
168
+ margin-left: 830px;
169
+ }
170
+ .offset7 {
171
+ margin-left: 730px;
172
+ }
173
+ .offset6 {
174
+ margin-left: 630px;
175
+ }
176
+ .offset5 {
177
+ margin-left: 530px;
178
+ }
179
+ .offset4 {
180
+ margin-left: 430px;
181
+ }
182
+ .offset3 {
183
+ margin-left: 330px;
184
+ }
185
+ .offset2 {
186
+ margin-left: 230px;
187
+ }
188
+ .offset1 {
189
+ margin-left: 130px;
190
+ }
191
+ .row-fluid {
192
+ width: 100%;
193
+ *zoom: 1;
194
+ }
195
+ .row-fluid:before,
196
+ .row-fluid:after {
197
+ display: table;
198
+ line-height: 0;
199
+ content: "";
200
+ }
201
+ .row-fluid:after {
202
+ clear: both;
203
+ }
204
+ .row-fluid [class*="span"] {
205
+ display: block;
206
+ float: left;
207
+ width: 100%;
208
+ min-height: 30px;
209
+ margin-left: 2.564102564102564%;
210
+ *margin-left: 2.5109110747408616%;
211
+ -webkit-box-sizing: border-box;
212
+ -moz-box-sizing: border-box;
213
+ box-sizing: border-box;
214
+ }
215
+ .row-fluid [class*="span"]:first-child {
216
+ margin-left: 0;
217
+ }
218
+ .row-fluid .span12 {
219
+ width: 100%;
220
+ *width: 99.94680851063829%;
221
+ }
222
+ .row-fluid .span11 {
223
+ width: 91.45299145299145%;
224
+ *width: 91.39979996362975%;
225
+ }
226
+ .row-fluid .span10 {
227
+ width: 82.90598290598291%;
228
+ *width: 82.8527914166212%;
229
+ }
230
+ .row-fluid .span9 {
231
+ width: 74.35897435897436%;
232
+ *width: 74.30578286961266%;
233
+ }
234
+ .row-fluid .span8 {
235
+ width: 65.81196581196582%;
236
+ *width: 65.75877432260411%;
237
+ }
238
+ .row-fluid .span7 {
239
+ width: 57.26495726495726%;
240
+ *width: 57.21176577559556%;
241
+ }
242
+ .row-fluid .span6 {
243
+ width: 48.717948717948715%;
244
+ *width: 48.664757228587014%;
245
+ }
246
+ .row-fluid .span5 {
247
+ width: 40.17094017094017%;
248
+ *width: 40.11774868157847%;
249
+ }
250
+ .row-fluid .span4 {
251
+ width: 31.623931623931625%;
252
+ *width: 31.570740134569924%;
253
+ }
254
+ .row-fluid .span3 {
255
+ width: 23.076923076923077%;
256
+ *width: 23.023731587561375%;
257
+ }
258
+ .row-fluid .span2 {
259
+ width: 14.52991452991453%;
260
+ *width: 14.476723040552828%;
261
+ }
262
+ .row-fluid .span1 {
263
+ width: 5.982905982905983%;
264
+ *width: 5.929714493544281%;
265
+ }
266
+ .row-fluid .offset12 {
267
+ margin-left: 105.12820512820512%;
268
+ *margin-left: 105.02182214948171%;
269
+ }
270
+ .row-fluid .offset12:first-child {
271
+ margin-left: 102.56410256410257%;
272
+ *margin-left: 102.45771958537915%;
273
+ }
274
+ .row-fluid .offset11 {
275
+ margin-left: 96.58119658119658%;
276
+ *margin-left: 96.47481360247316%;
277
+ }
278
+ .row-fluid .offset11:first-child {
279
+ margin-left: 94.01709401709402%;
280
+ *margin-left: 93.91071103837061%;
281
+ }
282
+ .row-fluid .offset10 {
283
+ margin-left: 88.03418803418803%;
284
+ *margin-left: 87.92780505546462%;
285
+ }
286
+ .row-fluid .offset10:first-child {
287
+ margin-left: 85.47008547008548%;
288
+ *margin-left: 85.36370249136206%;
289
+ }
290
+ .row-fluid .offset9 {
291
+ margin-left: 79.48717948717949%;
292
+ *margin-left: 79.38079650845607%;
293
+ }
294
+ .row-fluid .offset9:first-child {
295
+ margin-left: 76.92307692307693%;
296
+ *margin-left: 76.81669394435352%;
297
+ }
298
+ .row-fluid .offset8 {
299
+ margin-left: 70.94017094017094%;
300
+ *margin-left: 70.83378796144753%;
301
+ }
302
+ .row-fluid .offset8:first-child {
303
+ margin-left: 68.37606837606839%;
304
+ *margin-left: 68.26968539734497%;
305
+ }
306
+ .row-fluid .offset7 {
307
+ margin-left: 62.393162393162385%;
308
+ *margin-left: 62.28677941443899%;
309
+ }
310
+ .row-fluid .offset7:first-child {
311
+ margin-left: 59.82905982905982%;
312
+ *margin-left: 59.72267685033642%;
313
+ }
314
+ .row-fluid .offset6 {
315
+ margin-left: 53.84615384615384%;
316
+ *margin-left: 53.739770867430444%;
317
+ }
318
+ .row-fluid .offset6:first-child {
319
+ margin-left: 51.28205128205128%;
320
+ *margin-left: 51.175668303327875%;
321
+ }
322
+ .row-fluid .offset5 {
323
+ margin-left: 45.299145299145295%;
324
+ *margin-left: 45.1927623204219%;
325
+ }
326
+ .row-fluid .offset5:first-child {
327
+ margin-left: 42.73504273504273%;
328
+ *margin-left: 42.62865975631933%;
329
+ }
330
+ .row-fluid .offset4 {
331
+ margin-left: 36.75213675213675%;
332
+ *margin-left: 36.645753773413354%;
333
+ }
334
+ .row-fluid .offset4:first-child {
335
+ margin-left: 34.18803418803419%;
336
+ *margin-left: 34.081651209310785%;
337
+ }
338
+ .row-fluid .offset3 {
339
+ margin-left: 28.205128205128204%;
340
+ *margin-left: 28.0987452264048%;
341
+ }
342
+ .row-fluid .offset3:first-child {
343
+ margin-left: 25.641025641025642%;
344
+ *margin-left: 25.53464266230224%;
345
+ }
346
+ .row-fluid .offset2 {
347
+ margin-left: 19.65811965811966%;
348
+ *margin-left: 19.551736679396257%;
349
+ }
350
+ .row-fluid .offset2:first-child {
351
+ margin-left: 17.094017094017094%;
352
+ *margin-left: 16.98763411529369%;
353
+ }
354
+ .row-fluid .offset1 {
355
+ margin-left: 11.11111111111111%;
356
+ *margin-left: 11.004728132387708%;
357
+ }
358
+ .row-fluid .offset1:first-child {
359
+ margin-left: 8.547008547008547%;
360
+ *margin-left: 8.440625568285142%;
361
+ }
362
+ input,
363
+ textarea,
364
+ .uneditable-input {
365
+ margin-left: 0;
366
+ }
367
+ .controls-row [class*="span"] + [class*="span"] {
368
+ margin-left: 30px;
369
+ }
370
+ input.span12,
371
+ textarea.span12,
372
+ .uneditable-input.span12 {
373
+ width: 1156px;
374
+ }
375
+ input.span11,
376
+ textarea.span11,
377
+ .uneditable-input.span11 {
378
+ width: 1056px;
379
+ }
380
+ input.span10,
381
+ textarea.span10,
382
+ .uneditable-input.span10 {
383
+ width: 956px;
384
+ }
385
+ input.span9,
386
+ textarea.span9,
387
+ .uneditable-input.span9 {
388
+ width: 856px;
389
+ }
390
+ input.span8,
391
+ textarea.span8,
392
+ .uneditable-input.span8 {
393
+ width: 756px;
394
+ }
395
+ input.span7,
396
+ textarea.span7,
397
+ .uneditable-input.span7 {
398
+ width: 656px;
399
+ }
400
+ input.span6,
401
+ textarea.span6,
402
+ .uneditable-input.span6 {
403
+ width: 556px;
404
+ }
405
+ input.span5,
406
+ textarea.span5,
407
+ .uneditable-input.span5 {
408
+ width: 456px;
409
+ }
410
+ input.span4,
411
+ textarea.span4,
412
+ .uneditable-input.span4 {
413
+ width: 356px;
414
+ }
415
+ input.span3,
416
+ textarea.span3,
417
+ .uneditable-input.span3 {
418
+ width: 256px;
419
+ }
420
+ input.span2,
421
+ textarea.span2,
422
+ .uneditable-input.span2 {
423
+ width: 156px;
424
+ }
425
+ input.span1,
426
+ textarea.span1,
427
+ .uneditable-input.span1 {
428
+ width: 56px;
429
+ }
430
+ .thumbnails {
431
+ margin-left: -30px;
432
+ }
433
+ .thumbnails > li {
434
+ margin-left: 30px;
435
+ }
436
+ .row-fluid .thumbnails {
437
+ margin-left: 0;
438
+ }
439
+ }
440
+
441
+ @media (min-width: 768px) and (max-width: 979px) {
442
+ .row {
443
+ margin-left: -20px;
444
+ *zoom: 1;
445
+ }
446
+ .row:before,
447
+ .row:after {
448
+ display: table;
449
+ line-height: 0;
450
+ content: "";
451
+ }
452
+ .row:after {
453
+ clear: both;
454
+ }
455
+ [class*="span"] {
456
+ float: left;
457
+ min-height: 1px;
458
+ margin-left: 20px;
459
+ }
460
+ .container,
461
+ .navbar-static-top .container,
462
+ .navbar-fixed-top .container,
463
+ .navbar-fixed-bottom .container {
464
+ width: 724px;
465
+ }
466
+ .span12 {
467
+ width: 724px;
468
+ }
469
+ .span11 {
470
+ width: 662px;
471
+ }
472
+ .span10 {
473
+ width: 600px;
474
+ }
475
+ .span9 {
476
+ width: 538px;
477
+ }
478
+ .span8 {
479
+ width: 476px;
480
+ }
481
+ .span7 {
482
+ width: 414px;
483
+ }
484
+ .span6 {
485
+ width: 352px;
486
+ }
487
+ .span5 {
488
+ width: 290px;
489
+ }
490
+ .span4 {
491
+ width: 228px;
492
+ }
493
+ .span3 {
494
+ width: 166px;
495
+ }
496
+ .span2 {
497
+ width: 104px;
498
+ }
499
+ .span1 {
500
+ width: 42px;
501
+ }
502
+ .offset12 {
503
+ margin-left: 764px;
504
+ }
505
+ .offset11 {
506
+ margin-left: 702px;
507
+ }
508
+ .offset10 {
509
+ margin-left: 640px;
510
+ }
511
+ .offset9 {
512
+ margin-left: 578px;
513
+ }
514
+ .offset8 {
515
+ margin-left: 516px;
516
+ }
517
+ .offset7 {
518
+ margin-left: 454px;
519
+ }
520
+ .offset6 {
521
+ margin-left: 392px;
522
+ }
523
+ .offset5 {
524
+ margin-left: 330px;
525
+ }
526
+ .offset4 {
527
+ margin-left: 268px;
528
+ }
529
+ .offset3 {
530
+ margin-left: 206px;
531
+ }
532
+ .offset2 {
533
+ margin-left: 144px;
534
+ }
535
+ .offset1 {
536
+ margin-left: 82px;
537
+ }
538
+ .row-fluid {
539
+ width: 100%;
540
+ *zoom: 1;
541
+ }
542
+ .row-fluid:before,
543
+ .row-fluid:after {
544
+ display: table;
545
+ line-height: 0;
546
+ content: "";
547
+ }
548
+ .row-fluid:after {
549
+ clear: both;
550
+ }
551
+ .row-fluid [class*="span"] {
552
+ display: block;
553
+ float: left;
554
+ width: 100%;
555
+ min-height: 30px;
556
+ margin-left: 2.7624309392265194%;
557
+ *margin-left: 2.709239449864817%;
558
+ -webkit-box-sizing: border-box;
559
+ -moz-box-sizing: border-box;
560
+ box-sizing: border-box;
561
+ }
562
+ .row-fluid [class*="span"]:first-child {
563
+ margin-left: 0;
564
+ }
565
+ .row-fluid .span12 {
566
+ width: 100%;
567
+ *width: 99.94680851063829%;
568
+ }
569
+ .row-fluid .span11 {
570
+ width: 91.43646408839778%;
571
+ *width: 91.38327259903608%;
572
+ }
573
+ .row-fluid .span10 {
574
+ width: 82.87292817679558%;
575
+ *width: 82.81973668743387%;
576
+ }
577
+ .row-fluid .span9 {
578
+ width: 74.30939226519337%;
579
+ *width: 74.25620077583166%;
580
+ }
581
+ .row-fluid .span8 {
582
+ width: 65.74585635359117%;
583
+ *width: 65.69266486422946%;
584
+ }
585
+ .row-fluid .span7 {
586
+ width: 57.18232044198895%;
587
+ *width: 57.12912895262725%;
588
+ }
589
+ .row-fluid .span6 {
590
+ width: 48.61878453038674%;
591
+ *width: 48.56559304102504%;
592
+ }
593
+ .row-fluid .span5 {
594
+ width: 40.05524861878453%;
595
+ *width: 40.00205712942283%;
596
+ }
597
+ .row-fluid .span4 {
598
+ width: 31.491712707182323%;
599
+ *width: 31.43852121782062%;
600
+ }
601
+ .row-fluid .span3 {
602
+ width: 22.92817679558011%;
603
+ *width: 22.87498530621841%;
604
+ }
605
+ .row-fluid .span2 {
606
+ width: 14.3646408839779%;
607
+ *width: 14.311449394616199%;
608
+ }
609
+ .row-fluid .span1 {
610
+ width: 5.801104972375691%;
611
+ *width: 5.747913483013988%;
612
+ }
613
+ .row-fluid .offset12 {
614
+ margin-left: 105.52486187845304%;
615
+ *margin-left: 105.41847889972962%;
616
+ }
617
+ .row-fluid .offset12:first-child {
618
+ margin-left: 102.76243093922652%;
619
+ *margin-left: 102.6560479605031%;
620
+ }
621
+ .row-fluid .offset11 {
622
+ margin-left: 96.96132596685082%;
623
+ *margin-left: 96.8549429881274%;
624
+ }
625
+ .row-fluid .offset11:first-child {
626
+ margin-left: 94.1988950276243%;
627
+ *margin-left: 94.09251204890089%;
628
+ }
629
+ .row-fluid .offset10 {
630
+ margin-left: 88.39779005524862%;
631
+ *margin-left: 88.2914070765252%;
632
+ }
633
+ .row-fluid .offset10:first-child {
634
+ margin-left: 85.6353591160221%;
635
+ *margin-left: 85.52897613729868%;
636
+ }
637
+ .row-fluid .offset9 {
638
+ margin-left: 79.8342541436464%;
639
+ *margin-left: 79.72787116492299%;
640
+ }
641
+ .row-fluid .offset9:first-child {
642
+ margin-left: 77.07182320441989%;
643
+ *margin-left: 76.96544022569647%;
644
+ }
645
+ .row-fluid .offset8 {
646
+ margin-left: 71.2707182320442%;
647
+ *margin-left: 71.16433525332079%;
648
+ }
649
+ .row-fluid .offset8:first-child {
650
+ margin-left: 68.50828729281768%;
651
+ *margin-left: 68.40190431409427%;
652
+ }
653
+ .row-fluid .offset7 {
654
+ margin-left: 62.70718232044199%;
655
+ *margin-left: 62.600799341718584%;
656
+ }
657
+ .row-fluid .offset7:first-child {
658
+ margin-left: 59.94475138121547%;
659
+ *margin-left: 59.838368402492065%;
660
+ }
661
+ .row-fluid .offset6 {
662
+ margin-left: 54.14364640883978%;
663
+ *margin-left: 54.037263430116376%;
664
+ }
665
+ .row-fluid .offset6:first-child {
666
+ margin-left: 51.38121546961326%;
667
+ *margin-left: 51.27483249088986%;
668
+ }
669
+ .row-fluid .offset5 {
670
+ margin-left: 45.58011049723757%;
671
+ *margin-left: 45.47372751851417%;
672
+ }
673
+ .row-fluid .offset5:first-child {
674
+ margin-left: 42.81767955801105%;
675
+ *margin-left: 42.71129657928765%;
676
+ }
677
+ .row-fluid .offset4 {
678
+ margin-left: 37.01657458563536%;
679
+ *margin-left: 36.91019160691196%;
680
+ }
681
+ .row-fluid .offset4:first-child {
682
+ margin-left: 34.25414364640884%;
683
+ *margin-left: 34.14776066768544%;
684
+ }
685
+ .row-fluid .offset3 {
686
+ margin-left: 28.45303867403315%;
687
+ *margin-left: 28.346655695309746%;
688
+ }
689
+ .row-fluid .offset3:first-child {
690
+ margin-left: 25.69060773480663%;
691
+ *margin-left: 25.584224756083227%;
692
+ }
693
+ .row-fluid .offset2 {
694
+ margin-left: 19.88950276243094%;
695
+ *margin-left: 19.783119783707537%;
696
+ }
697
+ .row-fluid .offset2:first-child {
698
+ margin-left: 17.12707182320442%;
699
+ *margin-left: 17.02068884448102%;
700
+ }
701
+ .row-fluid .offset1 {
702
+ margin-left: 11.32596685082873%;
703
+ *margin-left: 11.219583872105325%;
704
+ }
705
+ .row-fluid .offset1:first-child {
706
+ margin-left: 8.56353591160221%;
707
+ *margin-left: 8.457152932878806%;
708
+ }
709
+ input,
710
+ textarea,
711
+ .uneditable-input {
712
+ margin-left: 0;
713
+ }
714
+ .controls-row [class*="span"] + [class*="span"] {
715
+ margin-left: 20px;
716
+ }
717
+ input.span12,
718
+ textarea.span12,
719
+ .uneditable-input.span12 {
720
+ width: 710px;
721
+ }
722
+ input.span11,
723
+ textarea.span11,
724
+ .uneditable-input.span11 {
725
+ width: 648px;
726
+ }
727
+ input.span10,
728
+ textarea.span10,
729
+ .uneditable-input.span10 {
730
+ width: 586px;
731
+ }
732
+ input.span9,
733
+ textarea.span9,
734
+ .uneditable-input.span9 {
735
+ width: 524px;
736
+ }
737
+ input.span8,
738
+ textarea.span8,
739
+ .uneditable-input.span8 {
740
+ width: 462px;
741
+ }
742
+ input.span7,
743
+ textarea.span7,
744
+ .uneditable-input.span7 {
745
+ width: 400px;
746
+ }
747
+ input.span6,
748
+ textarea.span6,
749
+ .uneditable-input.span6 {
750
+ width: 338px;
751
+ }
752
+ input.span5,
753
+ textarea.span5,
754
+ .uneditable-input.span5 {
755
+ width: 276px;
756
+ }
757
+ input.span4,
758
+ textarea.span4,
759
+ .uneditable-input.span4 {
760
+ width: 214px;
761
+ }
762
+ input.span3,
763
+ textarea.span3,
764
+ .uneditable-input.span3 {
765
+ width: 152px;
766
+ }
767
+ input.span2,
768
+ textarea.span2,
769
+ .uneditable-input.span2 {
770
+ width: 90px;
771
+ }
772
+ input.span1,
773
+ textarea.span1,
774
+ .uneditable-input.span1 {
775
+ width: 28px;
776
+ }
777
+ }
778
+
779
+ @media (max-width: 767px) {
780
+ body {
781
+ padding-right: 20px;
782
+ padding-left: 20px;
783
+ }
784
+ .navbar-fixed-top,
785
+ .navbar-fixed-bottom,
786
+ .navbar-static-top {
787
+ margin-right: -20px;
788
+ margin-left: -20px;
789
+ }
790
+ .container-fluid {
791
+ padding: 0;
792
+ }
793
+ .dl-horizontal dt {
794
+ float: none;
795
+ width: auto;
796
+ clear: none;
797
+ text-align: left;
798
+ }
799
+ .dl-horizontal dd {
800
+ margin-left: 0;
801
+ }
802
+ .container {
803
+ width: auto;
804
+ }
805
+ .row-fluid {
806
+ width: 100%;
807
+ }
808
+ .row,
809
+ .thumbnails {
810
+ margin-left: 0;
811
+ }
812
+ .thumbnails > li {
813
+ float: none;
814
+ margin-left: 0;
815
+ }
816
+ [class*="span"],
817
+ .row-fluid [class*="span"] {
818
+ display: block;
819
+ float: none;
820
+ width: 100%;
821
+ margin-left: 0;
822
+ -webkit-box-sizing: border-box;
823
+ -moz-box-sizing: border-box;
824
+ box-sizing: border-box;
825
+ }
826
+ .span12,
827
+ .row-fluid .span12 {
828
+ width: 100%;
829
+ -webkit-box-sizing: border-box;
830
+ -moz-box-sizing: border-box;
831
+ box-sizing: border-box;
832
+ }
833
+ .input-large,
834
+ .input-xlarge,
835
+ .input-xxlarge,
836
+ input[class*="span"],
837
+ select[class*="span"],
838
+ textarea[class*="span"],
839
+ .uneditable-input {
840
+ display: block;
841
+ width: 100%;
842
+ min-height: 30px;
843
+ -webkit-box-sizing: border-box;
844
+ -moz-box-sizing: border-box;
845
+ box-sizing: border-box;
846
+ }
847
+ .input-prepend input,
848
+ .input-append input,
849
+ .input-prepend input[class*="span"],
850
+ .input-append input[class*="span"] {
851
+ display: inline-block;
852
+ width: auto;
853
+ }
854
+ .controls-row [class*="span"] + [class*="span"] {
855
+ margin-left: 0;
856
+ }
857
+ .modal {
858
+ position: fixed;
859
+ top: 20px;
860
+ right: 20px;
861
+ left: 20px;
862
+ width: auto;
863
+ margin: 0;
864
+ }
865
+ .modal.fade.in {
866
+ top: auto;
867
+ }
868
+ }
869
+
870
+ @media (max-width: 480px) {
871
+ .nav-collapse {
872
+ -webkit-transform: translate3d(0, 0, 0);
873
+ }
874
+ .page-header h1 small {
875
+ display: block;
876
+ line-height: 20px;
877
+ }
878
+ input[type="checkbox"],
879
+ input[type="radio"] {
880
+ border: 1px solid #ccc;
881
+ }
882
+ .form-horizontal .control-label {
883
+ float: none;
884
+ width: auto;
885
+ padding-top: 0;
886
+ text-align: left;
887
+ }
888
+ .form-horizontal .controls {
889
+ margin-left: 0;
890
+ }
891
+ .form-horizontal .control-list {
892
+ padding-top: 0;
893
+ }
894
+ .form-horizontal .form-actions {
895
+ padding-right: 10px;
896
+ padding-left: 10px;
897
+ }
898
+ .modal {
899
+ top: 10px;
900
+ right: 10px;
901
+ left: 10px;
902
+ }
903
+ .modal-header .close {
904
+ padding: 10px;
905
+ margin: -10px;
906
+ }
907
+ .carousel-caption {
908
+ position: static;
909
+ }
910
+ }
911
+
912
+ @media (max-width: 979px) {
913
+ body {
914
+ padding-top: 0;
915
+ }
916
+ .navbar-fixed-top,
917
+ .navbar-fixed-bottom {
918
+ position: static;
919
+ }
920
+ .navbar-fixed-top {
921
+ margin-bottom: 20px;
922
+ }
923
+ .navbar-fixed-bottom {
924
+ margin-top: 20px;
925
+ }
926
+ .navbar-fixed-top .navbar-inner,
927
+ .navbar-fixed-bottom .navbar-inner {
928
+ padding: 5px;
929
+ }
930
+ .navbar .container {
931
+ width: auto;
932
+ padding: 0;
933
+ }
934
+ .navbar .brand {
935
+ padding-right: 10px;
936
+ padding-left: 10px;
937
+ margin: 0 0 0 -5px;
938
+ }
939
+ .nav-collapse {
940
+ clear: both;
941
+ }
942
+ .nav-collapse .nav {
943
+ float: none;
944
+ margin: 0 0 10px;
945
+ }
946
+ .nav-collapse .nav > li {
947
+ float: none;
948
+ }
949
+ .nav-collapse .nav > li > a {
950
+ margin-bottom: 2px;
951
+ }
952
+ .nav-collapse .nav > .divider-vertical {
953
+ display: none;
954
+ }
955
+ .nav-collapse .nav .nav-header {
956
+ color: #777777;
957
+ text-shadow: none;
958
+ }
959
+ .nav-collapse .nav > li > a,
960
+ .nav-collapse .dropdown-menu a {
961
+ padding: 9px 15px;
962
+ font-weight: bold;
963
+ color: #777777;
964
+ -webkit-border-radius: 3px;
965
+ -moz-border-radius: 3px;
966
+ border-radius: 3px;
967
+ }
968
+ .nav-collapse .btn {
969
+ padding: 4px 10px 4px;
970
+ font-weight: normal;
971
+ -webkit-border-radius: 4px;
972
+ -moz-border-radius: 4px;
973
+ border-radius: 4px;
974
+ }
975
+ .nav-collapse .dropdown-menu li + li a {
976
+ margin-bottom: 2px;
977
+ }
978
+ .nav-collapse .nav > li > a:hover,
979
+ .nav-collapse .dropdown-menu a:hover {
980
+ background-color: #f2f2f2;
981
+ }
982
+ .navbar-inverse .nav-collapse .nav > li > a:hover,
983
+ .navbar-inverse .nav-collapse .dropdown-menu a:hover {
984
+ background-color: #111111;
985
+ }
986
+ .nav-collapse.in .btn-group {
987
+ padding: 0;
988
+ margin-top: 5px;
989
+ }
990
+ .nav-collapse .dropdown-menu {
991
+ position: static;
992
+ top: auto;
993
+ left: auto;
994
+ display: block;
995
+ float: none;
996
+ max-width: none;
997
+ padding: 0;
998
+ margin: 0 15px;
999
+ background-color: transparent;
1000
+ border: none;
1001
+ -webkit-border-radius: 0;
1002
+ -moz-border-radius: 0;
1003
+ border-radius: 0;
1004
+ -webkit-box-shadow: none;
1005
+ -moz-box-shadow: none;
1006
+ box-shadow: none;
1007
+ }
1008
+ .nav-collapse .dropdown-menu:before,
1009
+ .nav-collapse .dropdown-menu:after {
1010
+ display: none;
1011
+ }
1012
+ .nav-collapse .dropdown-menu .divider {
1013
+ display: none;
1014
+ }
1015
+ .nav-collapse .nav > li > .dropdown-menu:before,
1016
+ .nav-collapse .nav > li > .dropdown-menu:after {
1017
+ display: none;
1018
+ }
1019
+ .nav-collapse .navbar-form,
1020
+ .nav-collapse .navbar-search {
1021
+ float: none;
1022
+ padding: 10px 15px;
1023
+ margin: 10px 0;
1024
+ border-top: 1px solid #f2f2f2;
1025
+ border-bottom: 1px solid #f2f2f2;
1026
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1027
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1028
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1029
+ }
1030
+ .navbar-inverse .nav-collapse .navbar-form,
1031
+ .navbar-inverse .nav-collapse .navbar-search {
1032
+ border-top-color: #111111;
1033
+ border-bottom-color: #111111;
1034
+ }
1035
+ .navbar .nav-collapse .nav.pull-right {
1036
+ float: none;
1037
+ margin-left: 0;
1038
+ }
1039
+ .nav-collapse,
1040
+ .nav-collapse.collapse {
1041
+ height: 0;
1042
+ overflow: hidden;
1043
+ }
1044
+ .navbar .btn-navbar {
1045
+ display: block;
1046
+ }
1047
+ .navbar-static .navbar-inner {
1048
+ padding-right: 10px;
1049
+ padding-left: 10px;
1050
+ }
1051
+ }
1052
+
1053
+ @media (min-width: 980px) {
1054
+ .nav-collapse.collapse {
1055
+ height: auto !important;
1056
+ overflow: visible !important;
1057
+ }
1058
+ }