nofxx-god_web 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. data/.gitignore +1 -0
  2. data/Rakefile +50 -0
  3. data/Readme.textile +82 -0
  4. data/VERSION +1 -0
  5. data/bin/god_web +11 -0
  6. data/bugs/issue-28789de6ef59a981f26031fdf68030a1474251e8.yaml +18 -0
  7. data/bugs/issue-949b87d1535bd55950daf2ec197a25ce2a0de13f.yaml +18 -0
  8. data/bugs/issue-9579e138eec0aee906895eefa1cc546bd7d54b0d.yaml +18 -0
  9. data/bugs/issue-b13b98759d3cf194cfee1bc00f0245f4b19f6cd2.yaml +18 -0
  10. data/bugs/issue-cf27fa73c85d2b7a389cbdcfe669b9703380667c.yaml +18 -0
  11. data/bugs/project.yaml +16 -0
  12. data/god_web.gemspec +109 -0
  13. data/lib/app.rb +56 -0
  14. data/lib/environment.rb +28 -0
  15. data/lib/god_web.rb +197 -0
  16. data/lib/sinatra_http_auth.rb +53 -0
  17. data/public/.DS_Store +0 -0
  18. data/public/app.css +30 -0
  19. data/public/icons/databases.png +0 -0
  20. data/public/icons/gear.png +0 -0
  21. data/public/icons/groups.png +0 -0
  22. data/public/icons/key.png +0 -0
  23. data/public/icons/monitor.png +0 -0
  24. data/public/icons/restart.png +0 -0
  25. data/public/icons/ruby.png +0 -0
  26. data/public/icons/server.png +0 -0
  27. data/public/icons/start.png +0 -0
  28. data/public/icons/stop.png +0 -0
  29. data/public/icons/terminal.png +0 -0
  30. data/public/icons/unmonitor.png +0 -0
  31. data/public/icons/unmonitored.png +0 -0
  32. data/public/icons/unmonitored_old.png +0 -0
  33. data/public/icons/up.png +0 -0
  34. data/public/icons/warn.png +0 -0
  35. data/public/icons/wrench.png +0 -0
  36. data/public/iui/backButton.png +0 -0
  37. data/public/iui/blueButton.png +0 -0
  38. data/public/iui/cancel.png +0 -0
  39. data/public/iui/grayButton.png +0 -0
  40. data/public/iui/iui-logo-touch-icon.png +0 -0
  41. data/public/iui/iui.css +396 -0
  42. data/public/iui/iui.js +510 -0
  43. data/public/iui/iuix.css +1 -0
  44. data/public/iui/iuix.js +1 -0
  45. data/public/iui/listArrow.png +0 -0
  46. data/public/iui/listArrowSel.png +0 -0
  47. data/public/iui/listGroup.png +0 -0
  48. data/public/iui/loading.gif +0 -0
  49. data/public/iui/pinstripes.png +0 -0
  50. data/public/iui/redButton.png +0 -0
  51. data/public/iui/selection.png +0 -0
  52. data/public/iui/thumb.png +0 -0
  53. data/public/iui/toggle.png +0 -0
  54. data/public/iui/toggleOn.png +0 -0
  55. data/public/iui/toolButton.png +0 -0
  56. data/public/iui/toolbar.png +0 -0
  57. data/public/iui/whiteButton.png +0 -0
  58. data/spec/god_web_spec.rb +46 -0
  59. data/spec/spec_helper.rb +11 -0
  60. data/views/command.erb +4 -0
  61. data/views/status.erb +30 -0
  62. data/views/watch.erb +10 -0
  63. metadata +146 -0
data/lib/god_web.rb ADDED
@@ -0,0 +1,197 @@
1
+ class GodWeb
2
+ def initialize(config)
3
+ @config = config
4
+ setup
5
+ ping
6
+ end
7
+
8
+ def self.watch(options = {})
9
+ options[:port] ||= 8888
10
+ options[:environment] ||= 'production'
11
+ start_string = "god_web #{options[:config]} -e #{options[:environment]} -p #{options[:port]}"
12
+ God.watch do |w|
13
+ w.name = "god_web"
14
+ w.interval = 1.minute
15
+ w.start = start_string
16
+ w.start_grace = 10.seconds
17
+ w.restart_grace = 10.seconds
18
+
19
+ w.start_if do |start|
20
+ start.condition(:process_running) do |c|
21
+ c.running = false
22
+ end
23
+ end
24
+
25
+ w.restart_if do |restart|
26
+ restart.condition(:memory_usage) do |c|
27
+ c.above = 20.megabytes
28
+ c.times = [3, 5]
29
+ end
30
+
31
+ restart.condition(:cpu_usage) do |c|
32
+ c.above = 25.percent
33
+ c.times = 5
34
+ end
35
+ end
36
+
37
+ w.lifecycle do |on|
38
+ on.condition(:flapping) do |c|
39
+ c.to_state = [:start, :restart]
40
+ c.times = 5
41
+ c.within = 5.minute
42
+ c.transition = :unmonitored
43
+ c.retry_in = 10.minutes
44
+ c.retry_times = 5
45
+ c.retry_within = 2.hours
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def setup
52
+ DRb.start_service
53
+ @server = DRbObject.new(nil, God::Socket.socket(@config['god_port']))
54
+ end
55
+
56
+ # ping server to ensure that it is responsive
57
+ def ping
58
+ tries = 3
59
+ begin
60
+ @server.ping
61
+ rescue Exception => e
62
+ if (tries -= 1) > 0
63
+ retry
64
+ end
65
+ raise e, "The server is not available (or you do not have permissions to access it)"
66
+ end
67
+ end
68
+
69
+ def last_log(watch)
70
+ format_log(log_command(watch, true))
71
+ end
72
+
73
+ def self.possible_statuses(status)
74
+ case status
75
+ when :up
76
+ return %w{stop restart unmonitor}
77
+ when :unmonitored
78
+ return %w{start monitor}
79
+ else
80
+ return %w{start stop restart}
81
+ end
82
+ end
83
+
84
+
85
+ private
86
+
87
+ def method_missing(meth,*args)
88
+ if %w{groups status log quit terminate}.include?(meth.to_s)
89
+ ping
90
+ send("#{meth}_command")
91
+ elsif %w{start stop restart unmonitor monitor}.include?(meth.to_s)
92
+ ping
93
+ lifecycle_command(args.first, meth.to_s)
94
+ else
95
+ raise NoMethodError
96
+ end
97
+ end
98
+
99
+ def groups_command
100
+ groups = []
101
+ @server.groups.each do |key, value|
102
+ groups << key
103
+ end
104
+ groups.sort
105
+ end
106
+
107
+ def status_command
108
+ @server.status
109
+ end
110
+
111
+ #
112
+ # To make it look good (no horiz scrollbars) in the iphone
113
+ #
114
+ def format_log(raw)
115
+ raw.split("\n").map do |l|
116
+ # clean stuff we don't need
117
+ l.gsub!(/I\s+|\(\w*\)|within bounds/, "") # gsub(/\(\w*\)/, """)
118
+ # if ok, span is green
119
+ ok = l =~ /\[ok\]/
120
+ if l =~ /\[\w*\]/
121
+ # get some data we want...
122
+ l.gsub(/\[\S*\s(\S*)\]\W+INFO: (\w*-?\w*|.*)?\s\[(\w*)?\]/, "<span class='gray'>\\1</span> | <span class='#{ok ? 'green' : 'red'}'>\\3</span> |").
123
+ # take only the integer from cpu
124
+ gsub(/cpu/, "cpu %").gsub(/(\d{1,3})\.\d*%/, "\\1").
125
+ # show mem usage in mb
126
+ gsub(/memory/, "memory mb").gsub(/(\d*kb)/) { ($1.to_i / 1000).to_s }
127
+ else
128
+ l.gsub(/\[\S*\s(\S*)\]\W+INFO: \w*\s(\w*)/, "<span class='gray'>\\1</span> | <span class='act'>act</span> | \\2")
129
+ end
130
+
131
+ end.join("</br>")
132
+ end
133
+
134
+ #TODO
135
+ def log_command(name, sample=false)
136
+ begin
137
+ Signal.trap('INT') { exit }
138
+ # name = @args[1]
139
+
140
+ unless name
141
+ puts "You must specify a Task or Group name"
142
+ exit!
143
+ end
144
+
145
+ t = Time.at(0)
146
+ if sample
147
+ @server.running_log(name, t)
148
+ else
149
+ loop do
150
+ @server.running_log(name, t)
151
+ t = Time.now
152
+ sleep 1
153
+ end
154
+ end
155
+ rescue God::NoSuchWatchError
156
+ puts "No such watch"
157
+ rescue DRb::DRbConnError
158
+ puts "The server went away"
159
+ end
160
+ end
161
+
162
+ def quit_command
163
+ begin
164
+ @server.terminate
165
+ return false
166
+ rescue DRb::DRbConnError
167
+ return true
168
+ end
169
+ end
170
+
171
+ def terminate_command
172
+ stopped_all = false
173
+ if @server.stop_all
174
+ stopped_all = true
175
+ end
176
+
177
+ begin
178
+ @server.terminate
179
+ return false
180
+ rescue DRb::DRbConnError
181
+ return stopped_all
182
+ end
183
+ end
184
+
185
+
186
+ def lifecycle_command(*args)
187
+ # get the name of the watch/group
188
+ name = args.first
189
+ command = args.last
190
+
191
+ # send @command
192
+ watches = @server.control(name, command)
193
+
194
+ watches.empty? ? [] : watches
195
+ end
196
+
197
+ end
@@ -0,0 +1,53 @@
1
+ module HttpAuthentication
2
+ module Basic
3
+
4
+ def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
5
+ authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
6
+ end
7
+
8
+ def authenticate_with_http_basic(&login_procedure)
9
+ authenticate(&login_procedure)
10
+ end
11
+
12
+ def request_http_basic_authentication(realm = "Application")
13
+ authentication_request(realm)
14
+ end
15
+
16
+ private
17
+
18
+ def authenticate(&login_procedure)
19
+ if authorization
20
+ login_procedure.call(*user_name_and_password)
21
+ end
22
+ end
23
+
24
+ def user_name_and_password
25
+ decode_credentials.split(/:/, 2)
26
+ end
27
+
28
+ def authorization
29
+ request.env['HTTP_AUTHORIZATION'] ||
30
+ request.env['X-HTTP_AUTHORIZATION'] ||
31
+ request.env['X_HTTP_AUTHORIZATION'] ||
32
+ request.env['REDIRECT_X_HTTP_AUTHORIZATION']
33
+ end
34
+
35
+ # Base64
36
+ def decode_credentials
37
+ (authorization.split.last || '').unpack("m").first
38
+ end
39
+
40
+ def authentication_request(realm)
41
+ status(401)
42
+ header("WWW-Authenticate" => %(Basic realm="#{realm.gsub(/"/, "")}"))
43
+ throw :halt, "HTTP Basic: Access denied.\n"
44
+ end
45
+
46
+ end
47
+ end
48
+
49
+ module Sinatra
50
+ class EventContext
51
+ include HttpAuthentication::Basic
52
+ end
53
+ end
data/public/.DS_Store ADDED
Binary file
data/public/app.css ADDED
@@ -0,0 +1,30 @@
1
+ body > ul > li {
2
+ padding-left: 30px;
3
+ }
4
+ li.up { background: transparent url(../icons/up.png) no-repeat scroll 8px 12px;}
5
+ li.unmonitored { background: transparent url(../icons/unmonitored.png) no-repeat scroll 8px 12px;}
6
+ li.monitor { background: transparent url(../icons/monitor.png) no-repeat scroll 8px 12px;}
7
+ li.start { background: transparent url(../icons/start.png) no-repeat scroll 8px 12px;}
8
+ li.stop { background: transparent url(../icons/stop.png) no-repeat scroll 8px 12px; }
9
+ li.restart { background: transparent url(../icons/restart.png) no-repeat scroll 8px 12px;}
10
+ li.unmonitor { background: transparent url(../icons/unmonitor.png) no-repeat scroll 8px 12px;}
11
+ li.groups { background: transparent url(../icons/groups.png) no-repeat scroll 8px 12px;}
12
+
13
+ .info {
14
+ font-size: 0.8em;
15
+ }
16
+
17
+ .gray { color: #aaa; }
18
+
19
+ .green {
20
+ color: #00aa00;
21
+ font-weight: bold;
22
+ }
23
+ .red {
24
+ color: #aa0000;
25
+ font-weight: bold;
26
+ }
27
+ .act {
28
+ color: #999900;
29
+ font-weight: bold;
30
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,396 @@
1
+ /* iui.css (c) 2007-9 by iUI Project Members, see LICENSE.txt for license */
2
+ body {
3
+ margin: 0;
4
+ font-family: Helvetica;
5
+ background: #FFFFFF;
6
+ color: #000000;
7
+ overflow-x: hidden;
8
+ -webkit-user-select: none;
9
+ -webkit-text-size-adjust: none;
10
+ }
11
+
12
+ body > *:not(.toolbar) {
13
+ display: none;
14
+ position: absolute;
15
+ margin: 0;
16
+ padding: 0;
17
+ left: 0;
18
+ top: 45px;
19
+ width: 100%;
20
+ min-height: 372px;
21
+ -webkit-transition-duration: 300ms;
22
+ -webkit-transition-property: -webkit-transform;
23
+ -webkit-transform: translateX(0%);
24
+ }
25
+
26
+ body[orient="landscape"] > *:not(.toolbar) {
27
+ min-height: 268px;
28
+ }
29
+
30
+ body > *[selected="true"] {
31
+ display: block;
32
+ }
33
+
34
+ a[selected], a:active {
35
+ background-color: #194fdb !important;
36
+ background-image: url(listArrowSel.png), url(selection.png) !important;
37
+ background-repeat: no-repeat, repeat-x;
38
+ background-position: right center, left top;
39
+ color: #FFFFFF !important;
40
+ }
41
+
42
+ a[selected="progress"] {
43
+ background-image: url(loading.gif), url(selection.png) !important;
44
+ }
45
+
46
+ /************************************************************************************************/
47
+
48
+ body > .toolbar {
49
+ box-sizing: border-box;
50
+ -moz-box-sizing: border-box;
51
+ -webkit-box-sizing: border-box;
52
+ border-bottom: 1px solid #2d3642;
53
+ border-top: 1px solid #6d84a2;
54
+ padding: 10px;
55
+ height: 45px;
56
+ background: url(toolbar.png) #6d84a2 repeat-x;
57
+ }
58
+
59
+ .toolbar > h1 {
60
+ position: absolute;
61
+ overflow: hidden;
62
+ left: 50%;
63
+ margin: 1px 0 0 -75px;
64
+ height: 45px;
65
+ font-size: 20px;
66
+ width: 150px;
67
+ font-weight: bold;
68
+ text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0;
69
+ text-align: center;
70
+ text-overflow: ellipsis;
71
+ white-space: nowrap;
72
+ color: #FFFFFF;
73
+ }
74
+
75
+ body[orient="landscape"] > .toolbar > h1 {
76
+ margin-left: -125px;
77
+ width: 250px;
78
+ }
79
+
80
+ .button {
81
+ position: absolute;
82
+ overflow: hidden;
83
+ top: 8px;
84
+ right: 6px;
85
+ margin: 0;
86
+ border-width: 0 5px;
87
+ padding: 0 3px;
88
+ width: auto;
89
+ height: 30px;
90
+ line-height: 30px;
91
+ font-family: inherit;
92
+ font-size: 12px;
93
+ font-weight: bold;
94
+ color: #FFFFFF;
95
+ text-shadow: rgba(0, 0, 0, 0.6) 0px -1px 0;
96
+ text-overflow: ellipsis;
97
+ text-decoration: none;
98
+ white-space: nowrap;
99
+ background: none;
100
+ -webkit-border-image: url(toolButton.png) 0 5 0 5;
101
+ }
102
+
103
+ .blueButton {
104
+ -webkit-border-image: url(blueButton.png) 0 5 0 5;
105
+ border-width: 0 5px;
106
+ }
107
+
108
+ .leftButton {
109
+ left: 6px;
110
+ right: auto;
111
+ }
112
+
113
+ #backButton {
114
+ display: none;
115
+ left: 6px;
116
+ right: auto;
117
+ padding: 0;
118
+ max-width: 55px;
119
+ border-width: 0 8px 0 14px;
120
+ -webkit-border-image: url(backButton.png) 0 8 0 14;
121
+ }
122
+
123
+ .whiteButton,
124
+ .redButton,
125
+ .grayButton {
126
+ display: block;
127
+ border-width: 0 12px;
128
+ padding: 10px;
129
+ text-align: center;
130
+ font-size: 20px;
131
+ font-weight: bold;
132
+ text-decoration: inherit;
133
+ color: inherit;
134
+ }
135
+
136
+ .whiteButton {
137
+ -webkit-border-image: url(whiteButton.png) 0 12 0 12;
138
+ text-shadow: rgba(255, 255, 255, 0.7) 0 1px 0;
139
+ }
140
+
141
+ .redButton {
142
+ -webkit-border-image: url(redButton.png) 0 12 0 12;
143
+ color:#fff;
144
+ text-shadow: #7a0001 0 -1px 0;
145
+ }
146
+
147
+ .grayButton {
148
+ -webkit-border-image: url(grayButton.png) 0 12 0 12;
149
+ color: #FFFFFF;
150
+ }
151
+
152
+ /************************************************************************************************/
153
+
154
+ body > ul > li {
155
+ position: relative;
156
+ margin: 0;
157
+ border-bottom: 1px solid #E0E0E0;
158
+ padding: 8px 0 8px 10px;
159
+ font-size: 20px;
160
+ font-weight: bold;
161
+ list-style: none;
162
+ }
163
+
164
+ body > ul > li.group {
165
+ position: relative;
166
+ top: -1px;
167
+ margin-bottom: -2px;
168
+ border-top: 1px solid #7d7d7d;
169
+ border-bottom: 1px solid #999999;
170
+ padding: 1px 10px;
171
+ background: url(listGroup.png) repeat-x;
172
+ font-size: 17px;
173
+ font-weight: bold;
174
+ text-shadow: rgba(0, 0, 0, 0.4) 0 1px 0;
175
+ color: #FFFFFF;
176
+ }
177
+
178
+ body > ul > li.group:first-child {
179
+ top: 0;
180
+ border-top: none;
181
+ }
182
+
183
+ body > ul > li > a {
184
+ display: block;
185
+ margin: -8px 0 -8px -10px;
186
+ padding: 8px 32px 8px 10px;
187
+ text-decoration: none;
188
+ color: inherit;
189
+ background: url(listArrow.png) no-repeat right center;
190
+ }
191
+
192
+ a[target="_replace"] {
193
+ box-sizing: border-box;
194
+ -webkit-box-sizing: border-box;
195
+ padding-top: 25px;
196
+ padding-bottom: 25px;
197
+ font-size: 18px;
198
+ color: cornflowerblue;
199
+ background-color: #FFFFFF;
200
+ background-image: none;
201
+ }
202
+
203
+ /************************************************************************************************/
204
+
205
+ body > .dialog {
206
+ top: 0;
207
+ width: 100%;
208
+ min-height: 417px;
209
+ z-index: 2;
210
+ background: rgba(0, 0, 0, 0.8);
211
+ padding: 0;
212
+ text-align: right;
213
+ }
214
+
215
+ .dialog > fieldset {
216
+ box-sizing: border-box;
217
+ -webkit-box-sizing: border-box;
218
+ width: 100%;
219
+ margin: 0;
220
+ border: none;
221
+ border-top: 1px solid #6d84a2;
222
+ padding: 10px 6px;
223
+ background: url(toolbar.png) #7388a5 repeat-x;
224
+ }
225
+
226
+ .dialog > fieldset > h1 {
227
+ margin: 0 10px 0 10px;
228
+ padding: 0;
229
+ font-size: 20px;
230
+ font-weight: bold;
231
+ color: #FFFFFF;
232
+ text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0;
233
+ text-align: center;
234
+ }
235
+
236
+ .dialog > fieldset > label {
237
+ position: absolute;
238
+ margin: 16px 0 0 6px;
239
+ font-size: 14px;
240
+ color: #999999;
241
+ }
242
+
243
+ input:not(input[type|=radio]):not(input[type|=checkbox]) {
244
+ box-sizing: border-box;
245
+ -webkit-box-sizing: border-box;
246
+ width: 100%;
247
+ margin: 8px 0 0 0;
248
+ padding: 6px 6px 6px 44px;
249
+ font-size: 16px;
250
+ font-weight: normal;
251
+ }
252
+
253
+ /************************************************************************************************/
254
+
255
+ body > .panel {
256
+ box-sizing: border-box;
257
+ -moz-box-sizing: border-box;
258
+ -webkit-box-sizing: border-box;
259
+ padding: 10px;
260
+ background: #c8c8c8 url(pinstripes.png);
261
+ }
262
+
263
+ .panel > fieldset {
264
+ position: relative;
265
+ margin: 0 0 20px 0;
266
+ padding: 0;
267
+ background: #FFFFFF;
268
+ -webkit-border-radius: 10px;
269
+ -moz-border-radius: 10px;
270
+ border: 1px solid #999999;
271
+ text-align: right;
272
+ font-size: 16px;
273
+ }
274
+
275
+ .row {
276
+ position: relative;
277
+ min-height: 42px;
278
+ border-bottom: 1px solid #999999;
279
+ -webkit-border-radius: 0;
280
+ text-align: right;
281
+ }
282
+
283
+ fieldset > .row:last-child {
284
+ border-bottom: none !important;
285
+ }
286
+
287
+ .row > input:not(input[type|=radio]):not(input[type|=checkbox]) {
288
+ box-sizing: border-box;
289
+ -moz-box-sizing: border-box;
290
+ -webkit-box-sizing: border-box;
291
+ margin: 0;
292
+ border: none;
293
+ padding: 12px 10px 0 110px;
294
+ height: 42px;
295
+ background: none;
296
+ }
297
+ .row > input[type|=radio], .row > input[type|=checkbox] {
298
+ margin: 7px 7px 0 0;
299
+ height: 25px;
300
+ width: 25px;
301
+ }
302
+
303
+ .row > label {
304
+ position: absolute;
305
+ margin: 0 0 0 14px;
306
+ line-height: 42px;
307
+ font-weight: bold;
308
+ }
309
+
310
+ .row > span {
311
+ position: absolute;
312
+ padding: 12px 10px 0 110px;
313
+ margin: 0;
314
+ }
315
+
316
+ .row > .toggle {
317
+ position: absolute;
318
+ top: 6px;
319
+ right: 6px;
320
+ width: 100px;
321
+ height: 28px;
322
+ }
323
+
324
+ .toggle {
325
+ border: 1px solid #888888;
326
+ -webkit-border-radius: 6px;
327
+ background: #FFFFFF url(toggle.png) repeat-x;
328
+ font-size: 19px;
329
+ font-weight: bold;
330
+ line-height: 30px;
331
+ }
332
+
333
+ .toggle[toggled="true"] {
334
+ border: 1px solid #143fae;
335
+ background: #194fdb url(toggleOn.png) repeat-x;
336
+ }
337
+
338
+ .toggleOn {
339
+ display: none;
340
+ position: absolute;
341
+ width: 60px;
342
+ text-align: center;
343
+ left: 0;
344
+ top: 0;
345
+ color: #FFFFFF;
346
+ text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0;
347
+ }
348
+
349
+ .toggleOff {
350
+ position: absolute;
351
+ width: 60px;
352
+ text-align: center;
353
+ right: 0;
354
+ top: 0;
355
+ color: #666666;
356
+ }
357
+
358
+ .toggle[toggled="true"] > .toggleOn {
359
+ display: block;
360
+ }
361
+
362
+ .toggle[toggled="true"] > .toggleOff {
363
+ display: none;
364
+ }
365
+
366
+ .thumb {
367
+ position: absolute;
368
+ top: -1px;
369
+ left: -1px;
370
+ width: 40px;
371
+ height: 28px;
372
+ border: 1px solid #888888;
373
+ -webkit-border-radius: 6px;
374
+ background: #ffffff url(thumb.png) repeat-x;
375
+ }
376
+
377
+ .toggle[toggled="true"] > .thumb {
378
+ left: auto;
379
+ right: -1px;
380
+ }
381
+
382
+ .panel > h2 {
383
+ margin: 0 0 8px 14px;
384
+ font-size: inherit;
385
+ font-weight: bold;
386
+ color: #4d4d70;
387
+ text-shadow: rgba(255, 255, 255, 0.75) 2px 2px 0;
388
+ }
389
+
390
+ /************************************************************************************************/
391
+
392
+ #preloader {
393
+ display: none;
394
+ background-image: url(loading.gif), url(selection.png),
395
+ url(blueButton.png), url(listArrowSel.png), url(listGroup.png);
396
+ }