qless 0.9.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/install_phantomjs +7 -0
- data/lib/qless.rb +4 -0
- data/lib/qless/job.rb +40 -38
- data/lib/qless/qless-core/cancel.lua +9 -9
- data/lib/qless/qless-core/failed.lua +1 -1
- data/lib/qless/qless-core/peek.lua +22 -12
- data/lib/qless/qless-core/pop.lua +31 -16
- data/lib/qless/qless-core/recur.lua +12 -3
- data/lib/qless/server.rb +96 -66
- data/lib/qless/server/static/css/bootstrap-responsive.css +686 -0
- data/lib/qless/server/static/css/bootstrap-responsive.min.css +12 -0
- data/lib/qless/server/static/css/bootstrap.css +3991 -0
- data/lib/qless/server/static/css/bootstrap.min.css +689 -0
- data/lib/qless/server/static/css/codemirror.css +112 -0
- data/lib/qless/server/static/css/docs.css +819 -0
- data/lib/qless/server/static/css/jquery.noty.css +105 -0
- data/lib/qless/server/static/css/noty_theme_twitter.css +137 -0
- data/lib/qless/server/static/css/style.css +204 -0
- data/lib/qless/server/static/favicon.ico +0 -0
- data/lib/qless/server/static/img/glyphicons-halflings-white.png +0 -0
- data/lib/qless/server/static/img/glyphicons-halflings.png +0 -0
- data/lib/qless/server/static/js/bootstrap-alert.js +94 -0
- data/lib/qless/server/static/js/bootstrap-scrollspy.js +125 -0
- data/lib/qless/server/static/js/bootstrap-tab.js +130 -0
- data/lib/qless/server/static/js/bootstrap-tooltip.js +270 -0
- data/lib/qless/server/static/js/bootstrap-typeahead.js +285 -0
- data/lib/qless/server/static/js/bootstrap.js +1726 -0
- data/lib/qless/server/static/js/bootstrap.min.js +6 -0
- data/lib/qless/server/static/js/codemirror.js +2972 -0
- data/lib/qless/server/static/js/jquery.noty.js +220 -0
- data/lib/qless/server/static/js/mode/javascript.js +360 -0
- data/lib/qless/server/static/js/theme/cobalt.css +18 -0
- data/lib/qless/server/static/js/theme/eclipse.css +25 -0
- data/lib/qless/server/static/js/theme/elegant.css +10 -0
- data/lib/qless/server/static/js/theme/lesser-dark.css +45 -0
- data/lib/qless/server/static/js/theme/monokai.css +28 -0
- data/lib/qless/server/static/js/theme/neat.css +9 -0
- data/lib/qless/server/static/js/theme/night.css +21 -0
- data/lib/qless/server/static/js/theme/rubyblue.css +21 -0
- data/lib/qless/server/static/js/theme/xq-dark.css +46 -0
- data/lib/qless/server/views/_job.erb +219 -0
- data/lib/qless/server/views/_job_list.erb +8 -0
- data/lib/qless/server/views/_pagination.erb +7 -0
- data/lib/qless/server/views/about.erb +130 -0
- data/lib/qless/server/views/config.erb +14 -0
- data/lib/qless/server/views/failed.erb +48 -0
- data/lib/qless/server/views/failed_type.erb +18 -0
- data/lib/qless/server/views/job.erb +17 -0
- data/lib/qless/server/views/layout.erb +341 -0
- data/lib/qless/server/views/overview.erb +90 -0
- data/lib/qless/server/views/queue.erb +122 -0
- data/lib/qless/server/views/queues.erb +26 -0
- data/lib/qless/server/views/tag.erb +6 -0
- data/lib/qless/server/views/track.erb +69 -0
- data/lib/qless/server/views/worker.erb +34 -0
- data/lib/qless/server/views/workers.erb +14 -0
- data/lib/qless/version.rb +1 -1
- data/lib/qless/worker.rb +11 -2
- metadata +72 -6
- data/lib/qless/qless-core/ruby/lib/qless-core.rb +0 -1
- data/lib/qless/qless-core/ruby/lib/qless/core.rb +0 -13
- data/lib/qless/qless-core/ruby/lib/qless/core/version.rb +0 -5
- data/lib/qless/qless-core/ruby/spec/qless_core_spec.rb +0 -13
@@ -34,6 +34,15 @@ if command == 'on' then
|
|
34
34
|
options.tags = assert(cjson.decode(options.tags or {}), 'Recur(): Arg "tags" must be JSON-encoded array of string. Got: ' .. tostring(options.tags))
|
35
35
|
options.priority = assert(tonumber(options.priority or 0) , 'Recur(): Arg "priority" must be a number. Got: ' .. tostring(options.priority))
|
36
36
|
options.retries = assert(tonumber(options.retries or 0) , 'Recur(): Arg "retries" must be a number. Got: ' .. tostring(options.retries))
|
37
|
+
|
38
|
+
local count, old_queue = unpack(redis.call('hmget', 'ql:r:' .. jid, 'count', 'queue'))
|
39
|
+
count = count or 0
|
40
|
+
|
41
|
+
-- If it has previously been in another queue, then we should remove
|
42
|
+
-- some information about it
|
43
|
+
if old_queue then
|
44
|
+
redis.call('zrem', 'ql:q:' .. old_queue .. '-recur', jid)
|
45
|
+
end
|
37
46
|
|
38
47
|
-- Do some insertions
|
39
48
|
redis.call('hmset', 'ql:r:' .. jid,
|
@@ -46,7 +55,7 @@ if command == 'on' then
|
|
46
55
|
'queue' , queue,
|
47
56
|
'type' , 'interval',
|
48
57
|
-- How many jobs we've spawned from this
|
49
|
-
'count' ,
|
58
|
+
'count' , count,
|
50
59
|
'interval', interval,
|
51
60
|
'retries' , options.retries)
|
52
61
|
-- Now, we should schedule the next run of the job
|
@@ -102,9 +111,9 @@ elseif command == 'update' then
|
|
102
111
|
local options = {}
|
103
112
|
|
104
113
|
-- Make sure that the job exists
|
105
|
-
if redis.call('exists', 'ql:r:' .. jid) then
|
114
|
+
if redis.call('exists', 'ql:r:' .. jid) ~= 0 then
|
106
115
|
for i = 3, #ARGV, 2 do
|
107
|
-
local key
|
116
|
+
local key = ARGV[i]
|
108
117
|
local value = ARGV[i+1]
|
109
118
|
if key == 'priority' or key == 'interval' or key == 'retries' then
|
110
119
|
value = assert(tonumber(value), 'Recur(): Arg "' .. key .. '" must be a number: ' .. tostring(value))
|
data/lib/qless/server.rb
CHANGED
@@ -9,13 +9,13 @@ module Qless
|
|
9
9
|
dir = File.dirname(File.expand_path(__FILE__))
|
10
10
|
set :views , "#{dir}/server/views"
|
11
11
|
set :public_folder, "#{dir}/server/static"
|
12
|
-
|
12
|
+
|
13
13
|
# For debugging purposes at least, I want this
|
14
14
|
set :reload_templates, true
|
15
|
-
|
15
|
+
|
16
16
|
# I'm not sure what this option is -- I'll look it up later
|
17
17
|
# set :static, true
|
18
|
-
|
18
|
+
|
19
19
|
def self.client
|
20
20
|
@client ||= Qless::Client.new
|
21
21
|
end
|
@@ -23,7 +23,7 @@ module Qless
|
|
23
23
|
def self.client=(client)
|
24
24
|
@client = client
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
helpers do
|
28
28
|
include Rack::Utils
|
29
29
|
|
@@ -35,7 +35,46 @@ module Qless
|
|
35
35
|
def path_prefix
|
36
36
|
request.env['SCRIPT_NAME']
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
|
+
def url_with_modified_query
|
40
|
+
url = URI(request.url)
|
41
|
+
existing_query = Rack::Utils.parse_query(url.query)
|
42
|
+
url.query = Rack::Utils.build_query(yield existing_query)
|
43
|
+
url.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def page_url(offset)
|
47
|
+
url_with_modified_query do |query|
|
48
|
+
query.merge('page' => current_page + offset)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def next_page_url
|
53
|
+
page_url 1
|
54
|
+
end
|
55
|
+
|
56
|
+
def prev_page_url
|
57
|
+
page_url -1
|
58
|
+
end
|
59
|
+
|
60
|
+
def current_page
|
61
|
+
@current_page ||= begin
|
62
|
+
Integer(params[:page])
|
63
|
+
rescue
|
64
|
+
1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
PAGE_SIZE = 25
|
69
|
+
def pagination_values
|
70
|
+
start = (current_page - 1) * PAGE_SIZE
|
71
|
+
[start, start + PAGE_SIZE]
|
72
|
+
end
|
73
|
+
|
74
|
+
def paginated(qless_object, method, *args)
|
75
|
+
qless_object.send(method, *(args + pagination_values))
|
76
|
+
end
|
77
|
+
|
39
78
|
def tabs
|
40
79
|
return [
|
41
80
|
{:name => 'Queues' , :path => '/queues' },
|
@@ -46,38 +85,38 @@ module Qless
|
|
46
85
|
{:name => 'About' , :path => '/about' }
|
47
86
|
]
|
48
87
|
end
|
49
|
-
|
88
|
+
|
50
89
|
def application_name
|
51
90
|
return Server.client.config['application']
|
52
91
|
end
|
53
|
-
|
92
|
+
|
54
93
|
def queues
|
55
94
|
return Server.client.queues.counts
|
56
95
|
end
|
57
|
-
|
96
|
+
|
58
97
|
def tracked
|
59
98
|
return Server.client.jobs.tracked
|
60
99
|
end
|
61
|
-
|
100
|
+
|
62
101
|
def workers
|
63
102
|
return Server.client.workers.counts
|
64
103
|
end
|
65
|
-
|
104
|
+
|
66
105
|
def failed
|
67
106
|
return Server.client.jobs.failed
|
68
107
|
end
|
69
|
-
|
108
|
+
|
70
109
|
# Return the supplied object back as JSON
|
71
110
|
def json(obj)
|
72
111
|
content_type :json
|
73
112
|
obj.to_json
|
74
113
|
end
|
75
|
-
|
114
|
+
|
76
115
|
# Make the id acceptable as an id / att in HTML
|
77
116
|
def sanitize_attr(attr)
|
78
117
|
return attr.gsub(/[^a-zA-Z\:\_]/, '-')
|
79
118
|
end
|
80
|
-
|
119
|
+
|
81
120
|
# What are the top tags? Since it might go on, say, every
|
82
121
|
# page, then we should probably be caching it
|
83
122
|
def top_tags
|
@@ -93,7 +132,7 @@ module Qless
|
|
93
132
|
end
|
94
133
|
@top_tags[:top]
|
95
134
|
end
|
96
|
-
|
135
|
+
|
97
136
|
def strftime(t)
|
98
137
|
# From http://stackoverflow.com/questions/195740/how-do-you-do-relative-time-in-rails
|
99
138
|
diff_seconds = Time.now - t
|
@@ -104,54 +143,47 @@ module Qless
|
|
104
143
|
"#{(diff_seconds/60).to_i} minutes ago"
|
105
144
|
when 3600 ... 3600*24
|
106
145
|
"#{(diff_seconds/3600).to_i} hours ago"
|
107
|
-
when (3600*24) ... (3600*24*30)
|
146
|
+
when (3600*24) ... (3600*24*30)
|
108
147
|
"#{(diff_seconds/(3600*24)).to_i} days ago"
|
109
148
|
else
|
110
149
|
t.strftime('%b %e, %Y %H:%M:%S %Z (%z)')
|
111
150
|
end
|
112
151
|
end
|
113
152
|
end
|
114
|
-
|
153
|
+
|
115
154
|
get '/?' do
|
116
155
|
erb :overview, :layout => true, :locals => { :title => "Overview" }
|
117
156
|
end
|
118
|
-
|
157
|
+
|
119
158
|
# Returns a JSON blob with the job counts for various queues
|
120
159
|
get '/queues.json' do
|
121
160
|
json(Server.client.queues.counts)
|
122
161
|
end
|
123
|
-
|
162
|
+
|
124
163
|
get '/queues/?' do
|
125
164
|
erb :queues, :layout => true, :locals => {
|
126
165
|
:title => 'Queues'
|
127
166
|
}
|
128
167
|
end
|
129
|
-
|
168
|
+
|
130
169
|
# Return the job counts for a specific queue
|
131
170
|
get '/queues/:name.json' do
|
132
171
|
json(Server.client.queues[params[:name]].counts)
|
133
172
|
end
|
134
|
-
|
173
|
+
|
174
|
+
filtered_tabs = %w[ running scheduled stalled depends recurring ].to_set
|
135
175
|
get '/queues/:name/?:tab?' do
|
136
176
|
queue = Server.client.queues[params[:name]]
|
137
|
-
tab
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
jobs = queue.jobs.stalled
|
146
|
-
when 'depends'
|
147
|
-
jobs = queue.jobs.depends
|
148
|
-
when 'recurring'
|
149
|
-
jobs = queue.jobs.recurring
|
150
|
-
end
|
151
|
-
jobs = jobs.map { |jid| Server.client.jobs[jid] }
|
152
|
-
if tab == 'waiting'
|
153
|
-
jobs = queue.peek(20)
|
177
|
+
tab = params.fetch('tab', 'stats')
|
178
|
+
|
179
|
+
jobs = if tab == 'waiting'
|
180
|
+
queue.peek(20)
|
181
|
+
elsif filtered_tabs.include?(tab)
|
182
|
+
paginated(queue.jobs, tab).map { |jid| Server.client.jobs[jid] }
|
183
|
+
else
|
184
|
+
[]
|
154
185
|
end
|
186
|
+
|
155
187
|
erb :queue, :layout => true, :locals => {
|
156
188
|
:title => "Queue #{params[:name]}",
|
157
189
|
:tab => tab,
|
@@ -160,7 +192,11 @@ module Qless
|
|
160
192
|
:stats => queue.stats
|
161
193
|
}
|
162
194
|
end
|
163
|
-
|
195
|
+
|
196
|
+
get '/failed.json' do
|
197
|
+
json(Server.client.jobs.failed)
|
198
|
+
end
|
199
|
+
|
164
200
|
get '/failed/?' do
|
165
201
|
# qless-core doesn't provide functionality this way, so we'll
|
166
202
|
# do it ourselves. I'm not sure if this is how the core library
|
@@ -170,21 +206,21 @@ module Qless
|
|
170
206
|
:failed => Server.client.jobs.failed.keys.map { |t| Server.client.jobs.failed(t).tap { |f| f['type'] = t } }
|
171
207
|
}
|
172
208
|
end
|
173
|
-
|
209
|
+
|
174
210
|
get '/failed/:type/?' do
|
175
211
|
erb :failed_type, :layout => true, :locals => {
|
176
212
|
:title => 'Failed | ' + params[:type],
|
177
213
|
:type => params[:type],
|
178
|
-
:failed => Server.client.jobs
|
214
|
+
:failed => paginated(Server.client.jobs, :failed, params[:type])
|
179
215
|
}
|
180
216
|
end
|
181
|
-
|
217
|
+
|
182
218
|
get '/track/?' do
|
183
219
|
erb :track, :layout => true, :locals => {
|
184
220
|
:title => 'Track'
|
185
221
|
}
|
186
222
|
end
|
187
|
-
|
223
|
+
|
188
224
|
get '/jobs/:jid' do
|
189
225
|
erb :job, :layout => true, :locals => {
|
190
226
|
:title => "Job | #{params[:jid]}",
|
@@ -192,7 +228,7 @@ module Qless
|
|
192
228
|
:job => Server.client.jobs[params[:jid]]
|
193
229
|
}
|
194
230
|
end
|
195
|
-
|
231
|
+
|
196
232
|
get '/workers/?' do
|
197
233
|
erb :workers, :layout => true, :locals => {
|
198
234
|
:title => 'Workers'
|
@@ -209,9 +245,9 @@ module Qless
|
|
209
245
|
}
|
210
246
|
}
|
211
247
|
end
|
212
|
-
|
248
|
+
|
213
249
|
get '/tag/?' do
|
214
|
-
jobs = Server.client.jobs
|
250
|
+
jobs = paginated(Server.client.jobs, :tagged, params[:tag])
|
215
251
|
erb :tag, :layout => true, :locals => {
|
216
252
|
:title => "Tag | #{params[:tag]}",
|
217
253
|
:tag => params[:tag],
|
@@ -219,26 +255,20 @@ module Qless
|
|
219
255
|
:total => jobs['total']
|
220
256
|
}
|
221
257
|
end
|
222
|
-
|
258
|
+
|
223
259
|
get '/config/?' do
|
224
260
|
erb :config, :layout => true, :locals => {
|
225
261
|
:title => 'Config',
|
226
262
|
:options => Server.client.config.all
|
227
263
|
}
|
228
264
|
end
|
229
|
-
|
265
|
+
|
230
266
|
get '/about/?' do
|
231
267
|
erb :about, :layout => true, :locals => {
|
232
268
|
:title => 'About'
|
233
269
|
}
|
234
270
|
end
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
271
|
+
|
242
272
|
# These are the bits where we accept AJAX requests
|
243
273
|
post "/track/?" do
|
244
274
|
# Expects a JSON-encoded hash with a job id, and optionally some tags
|
@@ -259,7 +289,7 @@ module Qless
|
|
259
289
|
end
|
260
290
|
end
|
261
291
|
end
|
262
|
-
|
292
|
+
|
263
293
|
post "/untrack/?" do
|
264
294
|
# Expects a JSON-encoded array of job ids to stop tracking
|
265
295
|
jobs = JSON.parse(request.body.read).map { |jid| Server.client.jobs[jid] }.select { |j| not j.nil? }
|
@@ -269,7 +299,7 @@ module Qless
|
|
269
299
|
end
|
270
300
|
return json({ :untracked => jobs.map { |job| job.jid } })
|
271
301
|
end
|
272
|
-
|
302
|
+
|
273
303
|
post "/priority/?" do
|
274
304
|
# Expects a JSON-encoded dictionary of jid => priority
|
275
305
|
response = Hash.new
|
@@ -284,7 +314,7 @@ module Qless
|
|
284
314
|
end
|
285
315
|
return json(response)
|
286
316
|
end
|
287
|
-
|
317
|
+
|
288
318
|
post "/tag/?" do
|
289
319
|
# Expects a JSON-encoded dictionary of jid => [tag, tag, tag]
|
290
320
|
response = Hash.new
|
@@ -298,7 +328,7 @@ module Qless
|
|
298
328
|
end
|
299
329
|
return json(response)
|
300
330
|
end
|
301
|
-
|
331
|
+
|
302
332
|
post "/untag/?" do
|
303
333
|
# Expects a JSON-encoded dictionary of jid => [tag, tag, tag]
|
304
334
|
response = Hash.new
|
@@ -312,7 +342,7 @@ module Qless
|
|
312
342
|
end
|
313
343
|
return json(response)
|
314
344
|
end
|
315
|
-
|
345
|
+
|
316
346
|
post "/move/?" do
|
317
347
|
# Expects a JSON-encoded hash of id: jid, and queue: queue_name
|
318
348
|
data = JSON.parse(request.body.read)
|
@@ -328,7 +358,7 @@ module Qless
|
|
328
358
|
end
|
329
359
|
end
|
330
360
|
end
|
331
|
-
|
361
|
+
|
332
362
|
post "/undepend/?" do
|
333
363
|
# Expects a JSON-encoded hash of id: jid, and queue: queue_name
|
334
364
|
data = JSON.parse(request.body.read)
|
@@ -344,7 +374,7 @@ module Qless
|
|
344
374
|
end
|
345
375
|
end
|
346
376
|
end
|
347
|
-
|
377
|
+
|
348
378
|
post "/retry/?" do
|
349
379
|
# Expects a JSON-encoded hash of id: jid, and queue: queue_name
|
350
380
|
data = JSON.parse(request.body.read)
|
@@ -361,7 +391,7 @@ module Qless
|
|
361
391
|
end
|
362
392
|
end
|
363
393
|
end
|
364
|
-
|
394
|
+
|
365
395
|
# Retry all the failures of a particular type
|
366
396
|
post "/retryall/?" do
|
367
397
|
# Expects a JSON-encoded hash of type: failure-type
|
@@ -376,7 +406,7 @@ module Qless
|
|
376
406
|
end)
|
377
407
|
end
|
378
408
|
end
|
379
|
-
|
409
|
+
|
380
410
|
post "/cancel/?" do
|
381
411
|
# Expects a JSON-encoded array of job ids to cancel
|
382
412
|
jobs = JSON.parse(request.body.read).map { |jid| Server.client.jobs[jid] }.select { |j| not j.nil? }
|
@@ -384,14 +414,14 @@ module Qless
|
|
384
414
|
jobs.each do |job|
|
385
415
|
job.cancel()
|
386
416
|
end
|
387
|
-
|
417
|
+
|
388
418
|
if request.xhr?
|
389
419
|
return json({ :canceled => jobs.map { |job| job.jid } })
|
390
420
|
else
|
391
421
|
redirect to(request.referrer)
|
392
422
|
end
|
393
423
|
end
|
394
|
-
|
424
|
+
|
395
425
|
post "/cancelall/?" do
|
396
426
|
# Expects a JSON-encoded hash of type: failure-type
|
397
427
|
data = JSON.parse(request.body.read)
|
@@ -404,7 +434,7 @@ module Qless
|
|
404
434
|
end)
|
405
435
|
end
|
406
436
|
end
|
407
|
-
|
437
|
+
|
408
438
|
# start the server if ruby file executed directly
|
409
439
|
run! if app_file == $0
|
410
440
|
end
|
@@ -0,0 +1,686 @@
|
|
1
|
+
/*!
|
2
|
+
* Bootstrap Responsive v2.0.2
|
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
|
+
.clearfix {
|
11
|
+
*zoom: 1;
|
12
|
+
}
|
13
|
+
.clearfix:before,
|
14
|
+
.clearfix:after {
|
15
|
+
display: table;
|
16
|
+
content: "";
|
17
|
+
}
|
18
|
+
.clearfix:after {
|
19
|
+
clear: both;
|
20
|
+
}
|
21
|
+
.hide-text {
|
22
|
+
overflow: hidden;
|
23
|
+
text-indent: 100%;
|
24
|
+
white-space: nowrap;
|
25
|
+
}
|
26
|
+
.input-block-level {
|
27
|
+
display: block;
|
28
|
+
width: 100%;
|
29
|
+
min-height: 28px;
|
30
|
+
/* Make inputs at least the height of their button counterpart */
|
31
|
+
|
32
|
+
/* Makes inputs behave like true block-level elements */
|
33
|
+
|
34
|
+
-webkit-box-sizing: border-box;
|
35
|
+
-moz-box-sizing: border-box;
|
36
|
+
-ms-box-sizing: border-box;
|
37
|
+
box-sizing: border-box;
|
38
|
+
}
|
39
|
+
.hidden {
|
40
|
+
display: none;
|
41
|
+
visibility: hidden;
|
42
|
+
}
|
43
|
+
.visible-phone {
|
44
|
+
display: none;
|
45
|
+
}
|
46
|
+
.visible-tablet {
|
47
|
+
display: none;
|
48
|
+
}
|
49
|
+
.visible-desktop {
|
50
|
+
display: block;
|
51
|
+
}
|
52
|
+
.hidden-phone {
|
53
|
+
display: block;
|
54
|
+
}
|
55
|
+
.hidden-tablet {
|
56
|
+
display: block;
|
57
|
+
}
|
58
|
+
.hidden-desktop {
|
59
|
+
display: none;
|
60
|
+
}
|
61
|
+
@media (max-width: 767px) {
|
62
|
+
.visible-phone {
|
63
|
+
display: block;
|
64
|
+
}
|
65
|
+
.hidden-phone {
|
66
|
+
display: none;
|
67
|
+
}
|
68
|
+
.hidden-desktop {
|
69
|
+
display: block;
|
70
|
+
}
|
71
|
+
.visible-desktop {
|
72
|
+
display: none;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
@media (min-width: 768px) and (max-width: 979px) {
|
76
|
+
.visible-tablet {
|
77
|
+
display: block;
|
78
|
+
}
|
79
|
+
.hidden-tablet {
|
80
|
+
display: none;
|
81
|
+
}
|
82
|
+
.hidden-desktop {
|
83
|
+
display: block;
|
84
|
+
}
|
85
|
+
.visible-desktop {
|
86
|
+
display: none;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
@media (max-width: 480px) {
|
90
|
+
.nav-collapse {
|
91
|
+
-webkit-transform: translate3d(0, 0, 0);
|
92
|
+
}
|
93
|
+
.page-header h1 small {
|
94
|
+
display: block;
|
95
|
+
line-height: 18px;
|
96
|
+
}
|
97
|
+
input[type="checkbox"],
|
98
|
+
input[type="radio"] {
|
99
|
+
border: 1px solid #ccc;
|
100
|
+
}
|
101
|
+
.form-horizontal .control-group > label {
|
102
|
+
float: none;
|
103
|
+
width: auto;
|
104
|
+
padding-top: 0;
|
105
|
+
text-align: left;
|
106
|
+
}
|
107
|
+
.form-horizontal .controls {
|
108
|
+
margin-left: 0;
|
109
|
+
}
|
110
|
+
.form-horizontal .control-list {
|
111
|
+
padding-top: 0;
|
112
|
+
}
|
113
|
+
.form-horizontal .form-actions {
|
114
|
+
padding-left: 10px;
|
115
|
+
padding-right: 10px;
|
116
|
+
}
|
117
|
+
.modal {
|
118
|
+
position: absolute;
|
119
|
+
top: 10px;
|
120
|
+
left: 10px;
|
121
|
+
right: 10px;
|
122
|
+
width: auto;
|
123
|
+
margin: 0;
|
124
|
+
}
|
125
|
+
.modal.fade.in {
|
126
|
+
top: auto;
|
127
|
+
}
|
128
|
+
.modal-header .close {
|
129
|
+
padding: 10px;
|
130
|
+
margin: -10px;
|
131
|
+
}
|
132
|
+
.carousel-caption {
|
133
|
+
position: static;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
@media (max-width: 767px) {
|
137
|
+
body {
|
138
|
+
padding-left: 20px;
|
139
|
+
padding-right: 20px;
|
140
|
+
}
|
141
|
+
.navbar-fixed-top {
|
142
|
+
margin-left: -20px;
|
143
|
+
margin-right: -20px;
|
144
|
+
}
|
145
|
+
.container {
|
146
|
+
width: auto;
|
147
|
+
}
|
148
|
+
.row-fluid {
|
149
|
+
width: 100%;
|
150
|
+
}
|
151
|
+
.row {
|
152
|
+
margin-left: 0;
|
153
|
+
}
|
154
|
+
.row > [class*="span"],
|
155
|
+
.row-fluid > [class*="span"] {
|
156
|
+
float: none;
|
157
|
+
display: block;
|
158
|
+
width: auto;
|
159
|
+
margin: 0;
|
160
|
+
}
|
161
|
+
.thumbnails [class*="span"] {
|
162
|
+
width: auto;
|
163
|
+
}
|
164
|
+
input[class*="span"],
|
165
|
+
select[class*="span"],
|
166
|
+
textarea[class*="span"],
|
167
|
+
.uneditable-input {
|
168
|
+
display: block;
|
169
|
+
width: 100%;
|
170
|
+
min-height: 28px;
|
171
|
+
/* Make inputs at least the height of their button counterpart */
|
172
|
+
|
173
|
+
/* Makes inputs behave like true block-level elements */
|
174
|
+
|
175
|
+
-webkit-box-sizing: border-box;
|
176
|
+
-moz-box-sizing: border-box;
|
177
|
+
-ms-box-sizing: border-box;
|
178
|
+
box-sizing: border-box;
|
179
|
+
}
|
180
|
+
.input-prepend input[class*="span"],
|
181
|
+
.input-append input[class*="span"] {
|
182
|
+
width: auto;
|
183
|
+
}
|
184
|
+
}
|
185
|
+
@media (min-width: 768px) and (max-width: 979px) {
|
186
|
+
.row {
|
187
|
+
margin-left: -20px;
|
188
|
+
*zoom: 1;
|
189
|
+
}
|
190
|
+
.row:before,
|
191
|
+
.row:after {
|
192
|
+
display: table;
|
193
|
+
content: "";
|
194
|
+
}
|
195
|
+
.row:after {
|
196
|
+
clear: both;
|
197
|
+
}
|
198
|
+
[class*="span"] {
|
199
|
+
float: left;
|
200
|
+
margin-left: 20px;
|
201
|
+
}
|
202
|
+
.container,
|
203
|
+
.navbar-fixed-top .container,
|
204
|
+
.navbar-fixed-bottom .container {
|
205
|
+
width: 724px;
|
206
|
+
}
|
207
|
+
.span12 {
|
208
|
+
width: 724px;
|
209
|
+
}
|
210
|
+
.span11 {
|
211
|
+
width: 662px;
|
212
|
+
}
|
213
|
+
.span10 {
|
214
|
+
width: 600px;
|
215
|
+
}
|
216
|
+
.span9 {
|
217
|
+
width: 538px;
|
218
|
+
}
|
219
|
+
.span8 {
|
220
|
+
width: 476px;
|
221
|
+
}
|
222
|
+
.span7 {
|
223
|
+
width: 414px;
|
224
|
+
}
|
225
|
+
.span6 {
|
226
|
+
width: 352px;
|
227
|
+
}
|
228
|
+
.span5 {
|
229
|
+
width: 290px;
|
230
|
+
}
|
231
|
+
.span4 {
|
232
|
+
width: 228px;
|
233
|
+
}
|
234
|
+
.span3 {
|
235
|
+
width: 166px;
|
236
|
+
}
|
237
|
+
.span2 {
|
238
|
+
width: 104px;
|
239
|
+
}
|
240
|
+
.span1 {
|
241
|
+
width: 42px;
|
242
|
+
}
|
243
|
+
.offset12 {
|
244
|
+
margin-left: 764px;
|
245
|
+
}
|
246
|
+
.offset11 {
|
247
|
+
margin-left: 702px;
|
248
|
+
}
|
249
|
+
.offset10 {
|
250
|
+
margin-left: 640px;
|
251
|
+
}
|
252
|
+
.offset9 {
|
253
|
+
margin-left: 578px;
|
254
|
+
}
|
255
|
+
.offset8 {
|
256
|
+
margin-left: 516px;
|
257
|
+
}
|
258
|
+
.offset7 {
|
259
|
+
margin-left: 454px;
|
260
|
+
}
|
261
|
+
.offset6 {
|
262
|
+
margin-left: 392px;
|
263
|
+
}
|
264
|
+
.offset5 {
|
265
|
+
margin-left: 330px;
|
266
|
+
}
|
267
|
+
.offset4 {
|
268
|
+
margin-left: 268px;
|
269
|
+
}
|
270
|
+
.offset3 {
|
271
|
+
margin-left: 206px;
|
272
|
+
}
|
273
|
+
.offset2 {
|
274
|
+
margin-left: 144px;
|
275
|
+
}
|
276
|
+
.offset1 {
|
277
|
+
margin-left: 82px;
|
278
|
+
}
|
279
|
+
.row-fluid {
|
280
|
+
width: 100%;
|
281
|
+
*zoom: 1;
|
282
|
+
}
|
283
|
+
.row-fluid:before,
|
284
|
+
.row-fluid:after {
|
285
|
+
display: table;
|
286
|
+
content: "";
|
287
|
+
}
|
288
|
+
.row-fluid:after {
|
289
|
+
clear: both;
|
290
|
+
}
|
291
|
+
.row-fluid > [class*="span"] {
|
292
|
+
float: left;
|
293
|
+
margin-left: 2.762430939%;
|
294
|
+
}
|
295
|
+
.row-fluid > [class*="span"]:first-child {
|
296
|
+
margin-left: 0;
|
297
|
+
}
|
298
|
+
.row-fluid > .span12 {
|
299
|
+
width: 99.999999993%;
|
300
|
+
}
|
301
|
+
.row-fluid > .span11 {
|
302
|
+
width: 91.436464082%;
|
303
|
+
}
|
304
|
+
.row-fluid > .span10 {
|
305
|
+
width: 82.87292817100001%;
|
306
|
+
}
|
307
|
+
.row-fluid > .span9 {
|
308
|
+
width: 74.30939226%;
|
309
|
+
}
|
310
|
+
.row-fluid > .span8 {
|
311
|
+
width: 65.74585634900001%;
|
312
|
+
}
|
313
|
+
.row-fluid > .span7 {
|
314
|
+
width: 57.182320438000005%;
|
315
|
+
}
|
316
|
+
.row-fluid > .span6 {
|
317
|
+
width: 48.618784527%;
|
318
|
+
}
|
319
|
+
.row-fluid > .span5 {
|
320
|
+
width: 40.055248616%;
|
321
|
+
}
|
322
|
+
.row-fluid > .span4 {
|
323
|
+
width: 31.491712705%;
|
324
|
+
}
|
325
|
+
.row-fluid > .span3 {
|
326
|
+
width: 22.928176794%;
|
327
|
+
}
|
328
|
+
.row-fluid > .span2 {
|
329
|
+
width: 14.364640883%;
|
330
|
+
}
|
331
|
+
.row-fluid > .span1 {
|
332
|
+
width: 5.801104972%;
|
333
|
+
}
|
334
|
+
input,
|
335
|
+
textarea,
|
336
|
+
.uneditable-input {
|
337
|
+
margin-left: 0;
|
338
|
+
}
|
339
|
+
input.span12, textarea.span12, .uneditable-input.span12 {
|
340
|
+
width: 714px;
|
341
|
+
}
|
342
|
+
input.span11, textarea.span11, .uneditable-input.span11 {
|
343
|
+
width: 652px;
|
344
|
+
}
|
345
|
+
input.span10, textarea.span10, .uneditable-input.span10 {
|
346
|
+
width: 590px;
|
347
|
+
}
|
348
|
+
input.span9, textarea.span9, .uneditable-input.span9 {
|
349
|
+
width: 528px;
|
350
|
+
}
|
351
|
+
input.span8, textarea.span8, .uneditable-input.span8 {
|
352
|
+
width: 466px;
|
353
|
+
}
|
354
|
+
input.span7, textarea.span7, .uneditable-input.span7 {
|
355
|
+
width: 404px;
|
356
|
+
}
|
357
|
+
input.span6, textarea.span6, .uneditable-input.span6 {
|
358
|
+
width: 342px;
|
359
|
+
}
|
360
|
+
input.span5, textarea.span5, .uneditable-input.span5 {
|
361
|
+
width: 280px;
|
362
|
+
}
|
363
|
+
input.span4, textarea.span4, .uneditable-input.span4 {
|
364
|
+
width: 218px;
|
365
|
+
}
|
366
|
+
input.span3, textarea.span3, .uneditable-input.span3 {
|
367
|
+
width: 156px;
|
368
|
+
}
|
369
|
+
input.span2, textarea.span2, .uneditable-input.span2 {
|
370
|
+
width: 94px;
|
371
|
+
}
|
372
|
+
input.span1, textarea.span1, .uneditable-input.span1 {
|
373
|
+
width: 32px;
|
374
|
+
}
|
375
|
+
}
|
376
|
+
@media (max-width: 979px) {
|
377
|
+
body {
|
378
|
+
padding-top: 0;
|
379
|
+
}
|
380
|
+
.navbar-fixed-top {
|
381
|
+
position: static;
|
382
|
+
margin-bottom: 18px;
|
383
|
+
}
|
384
|
+
.navbar-fixed-top .navbar-inner {
|
385
|
+
padding: 5px;
|
386
|
+
}
|
387
|
+
.navbar .container {
|
388
|
+
width: auto;
|
389
|
+
padding: 0;
|
390
|
+
}
|
391
|
+
.navbar .brand {
|
392
|
+
padding-left: 10px;
|
393
|
+
padding-right: 10px;
|
394
|
+
margin: 0 0 0 -5px;
|
395
|
+
}
|
396
|
+
.navbar .nav-collapse {
|
397
|
+
clear: left;
|
398
|
+
}
|
399
|
+
.navbar .nav {
|
400
|
+
float: none;
|
401
|
+
margin: 0 0 9px;
|
402
|
+
}
|
403
|
+
.navbar .nav > li {
|
404
|
+
float: none;
|
405
|
+
}
|
406
|
+
.navbar .nav > li > a {
|
407
|
+
margin-bottom: 2px;
|
408
|
+
}
|
409
|
+
.navbar .nav > .divider-vertical {
|
410
|
+
display: none;
|
411
|
+
}
|
412
|
+
.navbar .nav .nav-header {
|
413
|
+
color: #999999;
|
414
|
+
text-shadow: none;
|
415
|
+
}
|
416
|
+
.navbar .nav > li > a,
|
417
|
+
.navbar .dropdown-menu a {
|
418
|
+
padding: 6px 15px;
|
419
|
+
font-weight: bold;
|
420
|
+
color: #999999;
|
421
|
+
-webkit-border-radius: 3px;
|
422
|
+
-moz-border-radius: 3px;
|
423
|
+
border-radius: 3px;
|
424
|
+
}
|
425
|
+
.navbar .dropdown-menu li + li a {
|
426
|
+
margin-bottom: 2px;
|
427
|
+
}
|
428
|
+
.navbar .nav > li > a:hover,
|
429
|
+
.navbar .dropdown-menu a:hover {
|
430
|
+
background-color: #222222;
|
431
|
+
}
|
432
|
+
.navbar .dropdown-menu {
|
433
|
+
position: static;
|
434
|
+
top: auto;
|
435
|
+
left: auto;
|
436
|
+
float: none;
|
437
|
+
display: block;
|
438
|
+
max-width: none;
|
439
|
+
margin: 0 15px;
|
440
|
+
padding: 0;
|
441
|
+
background-color: transparent;
|
442
|
+
border: none;
|
443
|
+
-webkit-border-radius: 0;
|
444
|
+
-moz-border-radius: 0;
|
445
|
+
border-radius: 0;
|
446
|
+
-webkit-box-shadow: none;
|
447
|
+
-moz-box-shadow: none;
|
448
|
+
box-shadow: none;
|
449
|
+
}
|
450
|
+
.navbar .dropdown-menu:before,
|
451
|
+
.navbar .dropdown-menu:after {
|
452
|
+
display: none;
|
453
|
+
}
|
454
|
+
.navbar .dropdown-menu .divider {
|
455
|
+
display: none;
|
456
|
+
}
|
457
|
+
.navbar-form,
|
458
|
+
.navbar-search {
|
459
|
+
float: none;
|
460
|
+
padding: 9px 15px;
|
461
|
+
margin: 9px 0;
|
462
|
+
border-top: 1px solid #222222;
|
463
|
+
border-bottom: 1px solid #222222;
|
464
|
+
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
465
|
+
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
466
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
467
|
+
}
|
468
|
+
.navbar .nav.pull-right {
|
469
|
+
float: none;
|
470
|
+
margin-left: 0;
|
471
|
+
}
|
472
|
+
.navbar-static .navbar-inner {
|
473
|
+
padding-left: 10px;
|
474
|
+
padding-right: 10px;
|
475
|
+
}
|
476
|
+
.btn-navbar {
|
477
|
+
display: block;
|
478
|
+
}
|
479
|
+
.nav-collapse {
|
480
|
+
overflow: hidden;
|
481
|
+
height: 0;
|
482
|
+
}
|
483
|
+
}
|
484
|
+
@media (min-width: 980px) {
|
485
|
+
.nav-collapse.collapse {
|
486
|
+
height: auto !important;
|
487
|
+
overflow: visible !important;
|
488
|
+
}
|
489
|
+
}
|
490
|
+
@media (min-width: 1200px) {
|
491
|
+
.row {
|
492
|
+
margin-left: -30px;
|
493
|
+
*zoom: 1;
|
494
|
+
}
|
495
|
+
.row:before,
|
496
|
+
.row:after {
|
497
|
+
display: table;
|
498
|
+
content: "";
|
499
|
+
}
|
500
|
+
.row:after {
|
501
|
+
clear: both;
|
502
|
+
}
|
503
|
+
[class*="span"] {
|
504
|
+
float: left;
|
505
|
+
margin-left: 30px;
|
506
|
+
}
|
507
|
+
.container,
|
508
|
+
.navbar-fixed-top .container,
|
509
|
+
.navbar-fixed-bottom .container {
|
510
|
+
width: 1170px;
|
511
|
+
}
|
512
|
+
.span12 {
|
513
|
+
width: 1170px;
|
514
|
+
}
|
515
|
+
.span11 {
|
516
|
+
width: 1070px;
|
517
|
+
}
|
518
|
+
.span10 {
|
519
|
+
width: 970px;
|
520
|
+
}
|
521
|
+
.span9 {
|
522
|
+
width: 870px;
|
523
|
+
}
|
524
|
+
.span8 {
|
525
|
+
width: 770px;
|
526
|
+
}
|
527
|
+
.span7 {
|
528
|
+
width: 670px;
|
529
|
+
}
|
530
|
+
.span6 {
|
531
|
+
width: 570px;
|
532
|
+
}
|
533
|
+
.span5 {
|
534
|
+
width: 470px;
|
535
|
+
}
|
536
|
+
.span4 {
|
537
|
+
width: 370px;
|
538
|
+
}
|
539
|
+
.span3 {
|
540
|
+
width: 270px;
|
541
|
+
}
|
542
|
+
.span2 {
|
543
|
+
width: 170px;
|
544
|
+
}
|
545
|
+
.span1 {
|
546
|
+
width: 70px;
|
547
|
+
}
|
548
|
+
.offset12 {
|
549
|
+
margin-left: 1230px;
|
550
|
+
}
|
551
|
+
.offset11 {
|
552
|
+
margin-left: 1130px;
|
553
|
+
}
|
554
|
+
.offset10 {
|
555
|
+
margin-left: 1030px;
|
556
|
+
}
|
557
|
+
.offset9 {
|
558
|
+
margin-left: 930px;
|
559
|
+
}
|
560
|
+
.offset8 {
|
561
|
+
margin-left: 830px;
|
562
|
+
}
|
563
|
+
.offset7 {
|
564
|
+
margin-left: 730px;
|
565
|
+
}
|
566
|
+
.offset6 {
|
567
|
+
margin-left: 630px;
|
568
|
+
}
|
569
|
+
.offset5 {
|
570
|
+
margin-left: 530px;
|
571
|
+
}
|
572
|
+
.offset4 {
|
573
|
+
margin-left: 430px;
|
574
|
+
}
|
575
|
+
.offset3 {
|
576
|
+
margin-left: 330px;
|
577
|
+
}
|
578
|
+
.offset2 {
|
579
|
+
margin-left: 230px;
|
580
|
+
}
|
581
|
+
.offset1 {
|
582
|
+
margin-left: 130px;
|
583
|
+
}
|
584
|
+
.row-fluid {
|
585
|
+
width: 100%;
|
586
|
+
*zoom: 1;
|
587
|
+
}
|
588
|
+
.row-fluid:before,
|
589
|
+
.row-fluid:after {
|
590
|
+
display: table;
|
591
|
+
content: "";
|
592
|
+
}
|
593
|
+
.row-fluid:after {
|
594
|
+
clear: both;
|
595
|
+
}
|
596
|
+
.row-fluid > [class*="span"] {
|
597
|
+
float: left;
|
598
|
+
margin-left: 2.564102564%;
|
599
|
+
}
|
600
|
+
.row-fluid > [class*="span"]:first-child {
|
601
|
+
margin-left: 0;
|
602
|
+
}
|
603
|
+
.row-fluid > .span12 {
|
604
|
+
width: 100%;
|
605
|
+
}
|
606
|
+
.row-fluid > .span11 {
|
607
|
+
width: 91.45299145300001%;
|
608
|
+
}
|
609
|
+
.row-fluid > .span10 {
|
610
|
+
width: 82.905982906%;
|
611
|
+
}
|
612
|
+
.row-fluid > .span9 {
|
613
|
+
width: 74.358974359%;
|
614
|
+
}
|
615
|
+
.row-fluid > .span8 {
|
616
|
+
width: 65.81196581200001%;
|
617
|
+
}
|
618
|
+
.row-fluid > .span7 {
|
619
|
+
width: 57.264957265%;
|
620
|
+
}
|
621
|
+
.row-fluid > .span6 {
|
622
|
+
width: 48.717948718%;
|
623
|
+
}
|
624
|
+
.row-fluid > .span5 {
|
625
|
+
width: 40.170940171000005%;
|
626
|
+
}
|
627
|
+
.row-fluid > .span4 {
|
628
|
+
width: 31.623931624%;
|
629
|
+
}
|
630
|
+
.row-fluid > .span3 {
|
631
|
+
width: 23.076923077%;
|
632
|
+
}
|
633
|
+
.row-fluid > .span2 {
|
634
|
+
width: 14.529914530000001%;
|
635
|
+
}
|
636
|
+
.row-fluid > .span1 {
|
637
|
+
width: 5.982905983%;
|
638
|
+
}
|
639
|
+
input,
|
640
|
+
textarea,
|
641
|
+
.uneditable-input {
|
642
|
+
margin-left: 0;
|
643
|
+
}
|
644
|
+
input.span12, textarea.span12, .uneditable-input.span12 {
|
645
|
+
width: 1160px;
|
646
|
+
}
|
647
|
+
input.span11, textarea.span11, .uneditable-input.span11 {
|
648
|
+
width: 1060px;
|
649
|
+
}
|
650
|
+
input.span10, textarea.span10, .uneditable-input.span10 {
|
651
|
+
width: 960px;
|
652
|
+
}
|
653
|
+
input.span9, textarea.span9, .uneditable-input.span9 {
|
654
|
+
width: 860px;
|
655
|
+
}
|
656
|
+
input.span8, textarea.span8, .uneditable-input.span8 {
|
657
|
+
width: 760px;
|
658
|
+
}
|
659
|
+
input.span7, textarea.span7, .uneditable-input.span7 {
|
660
|
+
width: 660px;
|
661
|
+
}
|
662
|
+
input.span6, textarea.span6, .uneditable-input.span6 {
|
663
|
+
width: 560px;
|
664
|
+
}
|
665
|
+
input.span5, textarea.span5, .uneditable-input.span5 {
|
666
|
+
width: 460px;
|
667
|
+
}
|
668
|
+
input.span4, textarea.span4, .uneditable-input.span4 {
|
669
|
+
width: 360px;
|
670
|
+
}
|
671
|
+
input.span3, textarea.span3, .uneditable-input.span3 {
|
672
|
+
width: 260px;
|
673
|
+
}
|
674
|
+
input.span2, textarea.span2, .uneditable-input.span2 {
|
675
|
+
width: 160px;
|
676
|
+
}
|
677
|
+
input.span1, textarea.span1, .uneditable-input.span1 {
|
678
|
+
width: 60px;
|
679
|
+
}
|
680
|
+
.thumbnails {
|
681
|
+
margin-left: -30px;
|
682
|
+
}
|
683
|
+
.thumbnails > li {
|
684
|
+
margin-left: 30px;
|
685
|
+
}
|
686
|
+
}
|