vines-services 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/LICENSE +19 -0
  2. data/README +40 -0
  3. data/Rakefile +130 -0
  4. data/bin/vines-services +95 -0
  5. data/conf/config.rb +25 -0
  6. data/lib/vines/services/command/init.rb +209 -0
  7. data/lib/vines/services/command/restart.rb +14 -0
  8. data/lib/vines/services/command/start.rb +30 -0
  9. data/lib/vines/services/command/stop.rb +20 -0
  10. data/lib/vines/services/command/views.rb +26 -0
  11. data/lib/vines/services/component.rb +26 -0
  12. data/lib/vines/services/config.rb +105 -0
  13. data/lib/vines/services/connection.rb +120 -0
  14. data/lib/vines/services/controller/attributes_controller.rb +19 -0
  15. data/lib/vines/services/controller/base_controller.rb +99 -0
  16. data/lib/vines/services/controller/disco_info_controller.rb +61 -0
  17. data/lib/vines/services/controller/labels_controller.rb +17 -0
  18. data/lib/vines/services/controller/members_controller.rb +44 -0
  19. data/lib/vines/services/controller/messages_controller.rb +66 -0
  20. data/lib/vines/services/controller/probes_controller.rb +45 -0
  21. data/lib/vines/services/controller/services_controller.rb +81 -0
  22. data/lib/vines/services/controller/subscriptions_controller.rb +39 -0
  23. data/lib/vines/services/controller/systems_controller.rb +45 -0
  24. data/lib/vines/services/controller/transfers_controller.rb +58 -0
  25. data/lib/vines/services/controller/uploads_controller.rb +62 -0
  26. data/lib/vines/services/controller/users_controller.rb +127 -0
  27. data/lib/vines/services/core_ext/blather.rb +46 -0
  28. data/lib/vines/services/core_ext/couchrest.rb +33 -0
  29. data/lib/vines/services/indexer.rb +195 -0
  30. data/lib/vines/services/priority_queue.rb +94 -0
  31. data/lib/vines/services/roster.rb +70 -0
  32. data/lib/vines/services/storage/couchdb/fragment.rb +23 -0
  33. data/lib/vines/services/storage/couchdb/service.rb +170 -0
  34. data/lib/vines/services/storage/couchdb/system.rb +141 -0
  35. data/lib/vines/services/storage/couchdb/upload.rb +66 -0
  36. data/lib/vines/services/storage/couchdb/user.rb +137 -0
  37. data/lib/vines/services/storage/couchdb/vcard.rb +13 -0
  38. data/lib/vines/services/storage/couchdb.rb +157 -0
  39. data/lib/vines/services/storage.rb +33 -0
  40. data/lib/vines/services/throttle.rb +26 -0
  41. data/lib/vines/services/version.rb +7 -0
  42. data/lib/vines/services/vql/compiler.rb +94 -0
  43. data/lib/vines/services/vql/vql.citrus +115 -0
  44. data/lib/vines/services/vql/vql.rb +186 -0
  45. data/lib/vines/services.rb +71 -0
  46. data/test/config_test.rb +242 -0
  47. data/test/priority_queue_test.rb +23 -0
  48. data/test/storage/couchdb_test.rb +30 -0
  49. data/test/vql/compiler_test.rb +96 -0
  50. data/test/vql/vql_test.rb +233 -0
  51. data/web/coffeescripts/api.coffee +51 -0
  52. data/web/coffeescripts/commands.coffee +18 -0
  53. data/web/coffeescripts/files.coffee +315 -0
  54. data/web/coffeescripts/init.coffee +21 -0
  55. data/web/coffeescripts/services.coffee +356 -0
  56. data/web/coffeescripts/setup.coffee +503 -0
  57. data/web/coffeescripts/systems.coffee +371 -0
  58. data/web/images/default-service.png +0 -0
  59. data/web/images/linux.png +0 -0
  60. data/web/images/mac.png +0 -0
  61. data/web/images/run.png +0 -0
  62. data/web/images/windows.png +0 -0
  63. data/web/index.html +17 -0
  64. data/web/stylesheets/common.css +52 -0
  65. data/web/stylesheets/files.css +218 -0
  66. data/web/stylesheets/services.css +181 -0
  67. data/web/stylesheets/setup.css +117 -0
  68. data/web/stylesheets/systems.css +142 -0
  69. metadata +230 -0
@@ -0,0 +1,371 @@
1
+ class SystemsPage
2
+ constructor: (@session) ->
3
+ @session.onRoster ( ) => this.roster()
4
+ @session.onCard (c) => this.card(c)
5
+ @session.onMessage (m) => this.message(m)
6
+ @session.onPresence (p) => this.presence(p)
7
+ @commands = new Commands
8
+ @chats = {}
9
+ @currentContact = null
10
+
11
+ datef: (millis) ->
12
+ d = new Date(millis)
13
+ meridian = if d.getHours() >= 12 then ' pm' else ' am'
14
+ hour = if d.getHours() > 12 then d.getHours() - 12 else d.getHours()
15
+ hour = 12 if hour == 0
16
+ minutes = d.getMinutes() + ''
17
+ minutes = '0' + minutes if minutes.length == 1
18
+ hour + ':' + minutes + meridian
19
+
20
+ card: (card) ->
21
+ this.eachContact card.jid, (node) =>
22
+ $('.vcard-img', node).attr 'src', @session.avatar card.jid
23
+
24
+ roster: ->
25
+ roster = $('#roster')
26
+
27
+ $('li', roster).each (ix, node) =>
28
+ jid = $(node).attr('data-jid')
29
+ $(node).remove() unless @session.roster[jid]
30
+
31
+ setName = (node, contact) ->
32
+ $('.text', node).text contact.name || contact.jid
33
+ node.attr 'data-name', contact.name || ''
34
+ node.attr 'data-groups', contact.groups || ''
35
+
36
+ for jid, contact of @session.roster
37
+ found = $("#roster li[data-jid='#{jid}']")
38
+ setName(found, contact)
39
+ if found.length == 0
40
+ if contact.groups[0]=="Vines"
41
+ img_src = "/lib/images/default-service.png"
42
+ else
43
+ img_src = "#{@session.avatar jid}"
44
+ node = $("""
45
+ <li data-jid="#{jid}" data-name="" data-group="" class="offline">
46
+ <span class="text"></span>
47
+ <span class="status-msg">Offline</span>
48
+ <span class="unread" style="display:none;"></span>
49
+ <img class="vcard-img" id="#{jid}-avatar" alt="#{jid}" src="#{img_src}"/>
50
+ </li>
51
+ """).appendTo roster
52
+ setName(node, contact)
53
+ node.click (event) => this.selectContact(event)
54
+
55
+ message: (message) ->
56
+ this.queueMessage message
57
+ me = message.from == @session.jid()
58
+ from = message.from.split('/')[0]
59
+ thread = message.thread
60
+
61
+ if me || from || thread == @currentContact
62
+ bottom = this.atBottom()
63
+ this.appendMessage message
64
+ this.scroll() if bottom
65
+ else
66
+ chat = this.chat message.from
67
+ chat.unread++
68
+ this.eachContact from, (node) ->
69
+ $('.unread', node).text(chat.unread).show()
70
+
71
+ eachContact: (jid, callback) ->
72
+ for node in $("#roster li[data-jid='#{jid}']").get()
73
+ callback $(node)
74
+
75
+ appendMessage: (message) ->
76
+ from = message.from.split('/')[0]
77
+ contact = @session.roster[from]
78
+ name = if contact then (contact.name || from) else from
79
+ name = 'Me' if message.from == @session.jid()
80
+ node = $("""
81
+ <li data-jid="#{from}" style="display:none;">
82
+ <p><pre></pre></p>
83
+ <img alt="#{from}" src="#{@session.avatar from}"/>
84
+ <footer>
85
+ <span class="author"></span>
86
+ <span class="time">#{this.datef message.received}</span>
87
+ </footer>
88
+ </li>
89
+ """).appendTo '#messages'
90
+
91
+ $('pre', node).text message.text
92
+ $('.author', node).text name
93
+ node.fadeIn 200
94
+
95
+ queueMessage: (message) ->
96
+ me = message.from == @session.jid()
97
+ full = message[if me then 'to' else 'from']
98
+ chat = this.chat full
99
+ chat.jid = full
100
+ chat.messages.push message
101
+
102
+ chat: (jid) ->
103
+ bare = jid.split('/')[0]
104
+ chat = @chats[bare]
105
+ unless chat
106
+ chat = jid: jid, messages: [], unread: 0
107
+ @chats[bare] = chat
108
+ chat
109
+
110
+ presence: (presence) ->
111
+ from = presence.from.split('/')[0]
112
+ return if from == @session.bareJid()
113
+ if !presence.type || presence.offline
114
+ contact = @session.roster[from]
115
+ this.eachContact from, (node) ->
116
+ $('.status-msg', node).text contact.status()
117
+ if contact.offline()
118
+ node.addClass 'offline'
119
+ else
120
+ node.removeClass 'offline'
121
+
122
+ if presence.offline
123
+ this.chat(from).jid = from
124
+
125
+ if presence.type == 'subscribe'
126
+ node = $("""
127
+ <li data-jid="#{presence.from}" style="display:none;">
128
+ <form class="inset">
129
+ <h2>Buddy Approval</h2>
130
+ <p>#{presence.from} wants to add you as a buddy.</p>
131
+ <fieldset class="buttons">
132
+ <input type="button" value="Decline"/>
133
+ <input type="submit" value="Accept"/>
134
+ </fieldset>
135
+ </form>
136
+ </li>
137
+ """).appendTo '#notifications'
138
+ node.fadeIn 200
139
+ $('form', node).submit => this.acceptContact node, presence.from
140
+ $('input[type="button"]', node).click => this.rejectContact node, presence.from
141
+
142
+ acceptContact: (node, jid) ->
143
+ node.fadeOut 200, -> node.remove()
144
+ @session.sendSubscribed jid
145
+ @session.sendSubscribe jid
146
+ false
147
+
148
+ rejectContact: (node, jid) ->
149
+ node.fadeOut 200, -> node.remove()
150
+ @session.sendUnsubscribed jid
151
+
152
+ selectContact: (event) ->
153
+ jid = $(event.currentTarget).attr 'data-jid'
154
+ contact = @session.roster[jid]
155
+ return if @currentContact == jid
156
+ @currentContact = jid
157
+
158
+ $('#roster li').removeClass 'selected'
159
+ $(event.currentTarget).addClass 'selected'
160
+ $('#chat-title').text('Chat with ' + (contact.name || contact.jid))
161
+ $('#messages').empty()
162
+
163
+ chat = @chats[jid]
164
+ messages = []
165
+ if chat
166
+ messages = chat.messages
167
+ chat.unread = 0
168
+ this.eachContact jid, (node) ->
169
+ $('.unread', node).text('').hide()
170
+
171
+ this.appendMessage msg for msg in messages
172
+ this.scroll()
173
+
174
+ $('#remove-contact-msg').html "Are you sure you want to remove " +
175
+ "<strong>#{@currentContact}</strong> from your buddy list?"
176
+ $('#remove-contact-form .buttons').fadeIn 200
177
+
178
+ $('#edit-contact-jid').text @currentContact
179
+ $('#edit-contact-name').val @session.roster[@currentContact].name
180
+ $('#edit-contact-form input').fadeIn 200
181
+ $('#edit-contact-form .buttons').fadeIn 200
182
+
183
+ scroll: ->
184
+ msgs = $ '#messages'
185
+ msgs.animate(scrollTop: msgs.prop('scrollHeight'), 400)
186
+
187
+ atBottom: ->
188
+ msgs = $('#messages')
189
+ bottom = msgs.prop('scrollHeight') - msgs.height()
190
+ msgs.scrollTop() == bottom
191
+
192
+ send: ->
193
+ return false unless @currentContact
194
+ input = $('#message')
195
+ text = input.val().trim()
196
+ if text
197
+ chat = @chats[@currentContact]
198
+ jid = if chat then chat.jid else @currentContact
199
+ this.message
200
+ from: @session.jid()
201
+ text: text
202
+ to: jid
203
+ received: new Date()
204
+ @session.sendMessage jid, text
205
+ @commands.push text
206
+ input.val ''
207
+ false
208
+
209
+ addContact: ->
210
+ this.toggleForm '#add-contact-form'
211
+ contact =
212
+ jid: $('#add-contact-jid').val()
213
+ name: $('#add-contact-name').val()
214
+ groups: ['Buddies']
215
+ @session.updateContact contact, true if contact.jid
216
+ false
217
+
218
+ removeContact: ->
219
+ this.toggleForm '#remove-contact-form'
220
+ @session.removeContact @currentContact
221
+ @currentContact = null
222
+
223
+ $('#chat-title').text 'Select a buddy to chat'
224
+ $('#messages').empty()
225
+
226
+ $('#remove-contact-msg').html "Select a buddy in the list above to remove."
227
+ $('#remove-contact-form .buttons').hide()
228
+
229
+ $('#edit-contact-jid').text "Select a buddy in the list above to update."
230
+ $('#edit-contact-name').val ''
231
+ $('#edit-contact-form input').hide()
232
+ $('#edit-contact-form .buttons').hide()
233
+ false
234
+
235
+ updateContact: ->
236
+ this.toggleForm '#edit-contact-form'
237
+ contact =
238
+ jid: @currentContact
239
+ name: $('#edit-contact-name').val()
240
+ groups: @session.roster[@currentContact].groups
241
+ @session.updateContact contact
242
+ false
243
+
244
+ toggleForm: (form, fn) ->
245
+ form = $(form)
246
+ $('form.overlay').each ->
247
+ $(this).hide() unless this.id == form.attr 'id'
248
+ if form.is ':hidden'
249
+ fn() if fn
250
+ form.fadeIn 100
251
+ else
252
+ form.fadeOut 100, ->
253
+ form[0].reset()
254
+ fn() if fn
255
+
256
+ draw: ->
257
+ unless @session.connected()
258
+ window.location.hash = ''
259
+ return
260
+
261
+ $('body').attr 'id', 'servers-page'
262
+ $('#container').hide().empty()
263
+ $("""
264
+ <div id="alpha" class="sidebar column y-fill">
265
+ <h2>Buddies <div id="search-roster-icon"></div></h2>
266
+ <div id="search-roster-form"></div>
267
+ <ul id="roster" class="selectable scroll y-fill"></ul>
268
+ <div id="alpha-controls" class="controls">
269
+ <div id="add-contact"></div>
270
+ <div id="remove-contact"></div>
271
+ <div id="edit-contact"></div>
272
+ </div>
273
+ <form id="add-contact-form" class="overlay" style="display:none;">
274
+ <h2>Add Buddy</h2>
275
+ <input id="add-contact-jid" type="email" maxlength="1024" placeholder="Account name"/>
276
+ <input id="add-contact-name" type="text" maxlength="1024" placeholder="Real name"/>
277
+ <fieldset class="buttons">
278
+ <input id="add-contact-cancel" type="button" value="Cancel"/>
279
+ <input id="add-contact-ok" type="submit" value="Add"/>
280
+ </fieldset>
281
+ </form>
282
+ <form id="remove-contact-form" class="overlay" style="display:none;">
283
+ <h2>Remove Buddy</h2>
284
+ <p id="remove-contact-msg">Select a buddy in the list above to remove.</p>
285
+ <fieldset class="buttons" style="display:none;">
286
+ <input id="remove-contact-cancel" type="button" value="Cancel"/>
287
+ <input id="remove-contact-ok" type="submit" value="Remove"/>
288
+ </fieldset>
289
+ </form>
290
+ <form id="edit-contact-form" class="overlay" style="display:none;">
291
+ <h2>Update Profile</h2>
292
+ <p id="edit-contact-jid">Select a buddy in the list above to update.</p>
293
+ <input id="edit-contact-name" type="text" maxlength="1024" placeholder="Real name" style="display:none;"/>
294
+ <fieldset class="buttons" style="display:none;">
295
+ <input id="edit-contact-cancel" type="button" value="Cancel"/>
296
+ <input id="edit-contact-ok" type="submit" value="Save"/>
297
+ </fieldset>
298
+ </form>
299
+ </div>
300
+ <div id="beta" class="primary column x-fill y-fill">
301
+ <h2 id="chat-title">Select a buddy or service to start communicating</h2>
302
+ <ul id="messages" class="scroll y-fill"></ul>
303
+ <form id="message-form">
304
+ <input id="message" name="message" type="text" maxlength="1024" placeholder="Type a message and press enter to send"/>
305
+ </form>
306
+ </div>
307
+ <div id="charlie" class="sidebar column y-fill">
308
+ <h2>Notifications</h2>
309
+ <ul id="notifications" class="scroll y-fill"></ul>
310
+ <div id="charlie-controls" class="controls">
311
+ <div id="clear-notices"></div>
312
+ </div>
313
+ </div>
314
+ """).appendTo '#container'
315
+
316
+ this.roster()
317
+
318
+ new Button '#clear-notices', ICONS.no
319
+ new Button '#add-contact', ICONS.plus
320
+ new Button '#remove-contact', ICONS.minus
321
+ new Button '#edit-contact', ICONS.user
322
+
323
+ $('#message').focus -> $('form.overlay').fadeOut()
324
+ $('#message').keyup (e) =>
325
+ switch e.keyCode # up, down keys trigger history
326
+ when 38 then $('#message').val @commands.prev()
327
+ when 40 then $('#message').val @commands.next()
328
+
329
+ $('#message-form').submit => this.send()
330
+
331
+ $('#clear-notices').click -> $('#notifications li').fadeOut 200
332
+
333
+ $('#add-contact').click => this.toggleForm '#add-contact-form'
334
+ $('#remove-contact').click => this.toggleForm '#remove-contact-form'
335
+ $('#edit-contact').click => this.toggleForm '#edit-contact-form', =>
336
+ if @currentContact
337
+ $('#edit-contact-jid').text @currentContact
338
+ $('#edit-contact-name').val @session.roster[@currentContact].name
339
+
340
+ $('#add-contact-cancel').click => this.toggleForm '#add-contact-form'
341
+ $('#remove-contact-cancel').click => this.toggleForm '#remove-contact-form'
342
+ $('#edit-contact-cancel').click => this.toggleForm '#edit-contact-form'
343
+
344
+ $('#add-contact-form').submit => this.addContact()
345
+ $('#remove-contact-form').submit => this.removeContact()
346
+ $('#edit-contact-form').submit => this.updateContact()
347
+
348
+ $('#container').show()
349
+ layout = this.resize()
350
+
351
+ fn = ->
352
+ layout.resize()
353
+ layout.resize() # not sure why two are needed
354
+
355
+ new Filter
356
+ list: '#roster'
357
+ icon: '#search-roster-icon'
358
+ form: '#search-roster-form'
359
+ attrs: ['data-jid', 'data-name']
360
+ open: fn
361
+ close: fn
362
+
363
+ resize: ->
364
+ a = $ '#alpha'
365
+ b = $ '#beta'
366
+ c = $ '#charlie'
367
+ msg = $ '#message'
368
+ form = $ '#message-form'
369
+ new Layout ->
370
+ c.css 'left', a.width() + b.width()
371
+ msg.width form.width() - 32
Binary file
Binary file
Binary file
Binary file
Binary file
data/web/index.html ADDED
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5
+ <meta charset="utf-8">
6
+ <meta name="apple-mobile-web-app-capable" content="yes">
7
+ <title>Vines</title>
8
+ <link rel="shortcut icon" type="image/png" href="/favicon.png">
9
+ <script type="text/javascript" src="/lib/javascripts/base.js"></script>
10
+ <script type="text/javascript" src="javascripts/app.js"></script>
11
+ <link rel="stylesheet" href="/lib/stylesheets/base.css">
12
+ <link rel="stylesheet" href="/lib/stylesheets/login.css">
13
+ <link rel="stylesheet" href="stylesheets/app.css">
14
+ </head>
15
+ <body>
16
+ </body>
17
+ </html>
@@ -0,0 +1,52 @@
1
+ #editor-buttons {
2
+ background: #f8f8f8;
3
+ border-top: 1px solid #dfdfdf;
4
+ height: 50px;
5
+ position: absolute;
6
+ bottom: 0;
7
+ width: 100%;
8
+ }
9
+ #editor-buttons {
10
+ text-align: right;
11
+ }
12
+ #editor-buttons #save {
13
+ position: relative;
14
+ right: 10px;
15
+ top: 10px;
16
+ }
17
+ form.sections h2 {
18
+ background: none;
19
+ border: none;
20
+ padding-left: 0;
21
+ }
22
+ form.sections label {
23
+ display: block;
24
+ font-weight: bold;
25
+ font-size: 9pt;
26
+ color: rgba(0,0,0,0.8);
27
+ margin: 10px 0 -2px 0;
28
+ }
29
+ form.sections input[type="text"],
30
+ form.sections input[type="email"],
31
+ form.sections input[type="password"],
32
+ form.sections textarea {
33
+ width: 90%;
34
+ }
35
+ form.sections section {
36
+ border-bottom: 1px solid #ddd;
37
+ margin: 0 20px;
38
+ }
39
+ form.sections section fieldset {
40
+ padding-left: 30%;
41
+ margin-top: -33px;
42
+ margin-bottom: 10px;
43
+ }
44
+ form.sections section:last-child {
45
+ border-bottom: none;
46
+ margin-bottom: 20px;
47
+ }
48
+ form.sections .error {
49
+ color: #dd2828;
50
+ font-size: 8pt;
51
+ width: 89%;
52
+ }
@@ -0,0 +1,218 @@
1
+ #files-page #container {
2
+ height: 100%;
3
+ }
4
+ #files-page #files-title {
5
+ background: #f8f8f8;
6
+ }
7
+ #files-page #files {
8
+ background: #fff;
9
+ height: 100%;
10
+ list-style: none;
11
+ width: 100%;
12
+ }
13
+ #files-page #files li {
14
+ border-bottom: 1px solid #f0f0f0;
15
+ padding: 10px;
16
+ position: relative;
17
+ }
18
+ #files-page #files li .file-icon {
19
+ float: left;
20
+ }
21
+ #files-page #files li .file-icon > svg {
22
+ height: 30px;
23
+ width: 30px;
24
+ }
25
+ #files-page #files li .file-icon .size {
26
+ color: rgba(0, 0, 0, 0.7);
27
+ display: block;
28
+ font-size: 9pt;
29
+ line-height: 1;
30
+ }
31
+ #files-page #files li h2 {
32
+ border: none;
33
+ line-height: 1;
34
+ padding: 0 0 0 50px;
35
+ }
36
+ #files-page #files li:hover {
37
+ background: #fdfdfd;
38
+ }
39
+ #files-page #files li .file-form {
40
+ opacity: 0;
41
+ position: absolute;
42
+ right: 10px;
43
+ top: 22px;
44
+ -moz-transition: opacity 0.3s;
45
+ -o-transition: opacity 0.3s;
46
+ -webkit-transition: opacity 0.3s;
47
+ transition: opacity 0.3s;
48
+ }
49
+ #files-page #files li:hover .file-form {
50
+ opacity: 1.0;
51
+ }
52
+ #files-page #files li:hover footer {
53
+ color: rgba(0, 0, 0, 0.7);
54
+ }
55
+ #files-page #files li footer {
56
+ color: rgba(0, 0, 0, 0.3);
57
+ font-size: 9pt;
58
+ padding-left: 50px;
59
+ -moz-transition: color 0.3s;
60
+ -o-transition: color 0.3s;
61
+ -webkit-transition: color 0.3s;
62
+ transition: color 0.3s;
63
+ }
64
+ #files-page #files li .time {
65
+ display: block;
66
+ }
67
+ #files-page #files li .labels {
68
+ line-height: 1;
69
+ min-height: 10px;
70
+ width: 80%;
71
+ }
72
+ #files-page #files li .labels li {
73
+ border: none;
74
+ display: inline-block;
75
+ line-height: 1;
76
+ margin-right: 10px;
77
+ margin-bottom: 7px;
78
+ padding: 0 12px 0 0;
79
+ position: relative;
80
+ text-transform: capitalize;
81
+ }
82
+ #files-page #files li .labels li .remove {
83
+ cursor: pointer;
84
+ position: absolute;
85
+ right: 0;
86
+ top: -2px;
87
+ width: 14px;
88
+ }
89
+ #files-page #files li .labels li .remove svg {
90
+ height: 12px;
91
+ width: 12px;
92
+ }
93
+ #files-page #files .add-label-button {
94
+ cursor: pointer;
95
+ display: inline-block;
96
+ margin-right: 3px;
97
+ }
98
+ #files-page #files .add-label-button svg {
99
+ height: 14px;
100
+ width: 14px;
101
+ }
102
+ #files-page #files form.add-label input[type="text"] {
103
+ display: inline-block;
104
+ font-size: 8pt;
105
+ padding: 2px;
106
+ position: relative;
107
+ top: -4px;
108
+ width: 100px;
109
+ }
110
+ #files-page #file-chooser {
111
+ height: 30px;
112
+ opacity: 0;
113
+ position: absolute;
114
+ top: 10px;
115
+ left: 10px;
116
+ }
117
+ #files-page #open-file-chooser {
118
+ height: 30px;
119
+ margin: 0;
120
+ position: absolute;
121
+ top: 10px;
122
+ left: 10px;
123
+ width: 240px;
124
+ }
125
+ #files-page #upload-dnd {
126
+ color: #ababab;
127
+ font-weight: bold;
128
+ font-size: 14px;
129
+ position: absolute;
130
+ top: 27px;
131
+ text-align: center;
132
+ text-shadow: 0 1px 1px #fff;
133
+ width: 260px;
134
+ }
135
+ #files-page #uploads .progress {
136
+ background: #fff;
137
+ border: 1px solid #448ccd;
138
+ border-top: 1px solid #5da8db;
139
+ border-radius: 3px;
140
+ -webkit-box-shadow: 0 1px 1px #fff, inset 0 1px 1px rgba(255, 255, 255, 0.5);
141
+ box-shadow: 0 1px 1px #fff, inset 0 1px 1px rgba(255, 255, 255, 0.5);
142
+ color: #0d4078;
143
+ font-size: 11px;
144
+ font-weight: bold;
145
+ height: 15px;
146
+ line-height: 15px;
147
+ margin: 0 10px 10px 10px;
148
+ position: relative;
149
+ text-align: center;
150
+ text-shadow: 0 1px 1px #a2d8f8;
151
+ }
152
+ #files-page #uploads .progress .text {
153
+ position: absolute;
154
+ left: 0;
155
+ width: 100%;
156
+ }
157
+ #files-page #uploads .progress .meter {
158
+ background: #8dd2f7;
159
+ background: -moz-linear-gradient(#8dd2f7, #58b8f4);
160
+ background: -o-linear-gradient(#8dd2f7, #58b8f4);
161
+ background: -webkit-gradient(linear, left top, left bottom, from(#8dd2f7), to(#58b8f4));
162
+ height: 15px;
163
+ position: absolute;
164
+ top: 0;
165
+ }
166
+ #files-page #uploads .progress .cancel {
167
+ height: 100%;
168
+ width: 16px;
169
+ position: absolute;
170
+ right: 0;
171
+ }
172
+ #files-page #labels,
173
+ #files-page #uploads {
174
+ height: 100%;
175
+ list-style: none;
176
+ text-shadow: 0 1px 1px #fff;
177
+ width: 260px;
178
+ }
179
+ #files-page #labels li .text {
180
+ text-transform: capitalize;
181
+ }
182
+ #files-page #labels li,
183
+ #files-page #uploads li {
184
+ cursor: pointer;
185
+ border-bottom: 1px solid #ddd;
186
+ font-weight: bold;
187
+ min-height: 42px;
188
+ padding: 0 10px;
189
+ position: relative;
190
+ -moz-transition: background 0.3s;
191
+ -o-transition: background 0.3s;
192
+ -webkit-transition: background 0.3s;
193
+ transition: background 0.3s;
194
+ }
195
+ #files-page #uploads li {
196
+ font-weight: normal;
197
+ padding: 10px 0 0 0;
198
+ background: #f8f8f8;
199
+ }
200
+ #files-page #labels li:hover:not(.selected),
201
+ #files-page #uploads li:hover {
202
+ background: rgba(255, 255, 255, 1.0);
203
+ }
204
+ #files-page #labels li.selected .count {
205
+ color: rgba(255, 255, 255, 0.85);
206
+ }
207
+ #files-page #labels .count {
208
+ display: block;
209
+ font-size: 11px;
210
+ font-weight: normal;
211
+ line-height: 11px;
212
+ }
213
+ #files-page #charlie-controls {
214
+ text-align: right;
215
+ }
216
+ #files-page #search-files-form {
217
+ background: #f8f8f8;
218
+ }