vines 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +5 -0
- data/lib/vines/config.rb +1 -1
- data/lib/vines/stream/http.rb +3 -3
- data/lib/vines/version.rb +1 -1
- data/web/chat/coffeescripts/chat.coffee +34 -61
- data/web/chat/javascripts/app.js +1 -1
- data/web/chat/javascripts/chat.js +22 -74
- data/web/chat/stylesheets/chat.css +2 -145
- data/web/lib/coffeescripts/button.coffee +21 -0
- data/web/lib/coffeescripts/filter.coffee +48 -0
- data/web/lib/coffeescripts/session.coffee +63 -48
- data/web/lib/coffeescripts/transfer.coffee +103 -0
- data/web/lib/javascripts/base.js +2 -1
- data/web/lib/javascripts/button.js +31 -0
- data/web/lib/javascripts/filter.js +86 -0
- data/web/lib/javascripts/session.js +43 -77
- data/web/lib/javascripts/transfer.js +134 -0
- data/web/lib/stylesheets/base.css +129 -5
- metadata +8 -2
data/README
CHANGED
@@ -6,6 +6,11 @@ CouchDB, Redis, the file system, or a custom storage implementation that you pro
|
|
6
6
|
LDAP authentication can be used so user names and passwords aren't stored in the chat
|
7
7
|
database. SSL encryption is mandatory on all client and server connections.
|
8
8
|
|
9
|
+
The Vines XMPP server includes a web chat client. The web application is available
|
10
|
+
immediately after starting the chat server at http://localhost:5280/chat/
|
11
|
+
|
12
|
+
Additional documentation can be found at www.getvines.com.
|
13
|
+
|
9
14
|
== Usage
|
10
15
|
|
11
16
|
1. gem install vines
|
data/lib/vines/config.rb
CHANGED
@@ -5,7 +5,7 @@ module Vines
|
|
5
5
|
# A Config object is passed to the stream handlers to give them access
|
6
6
|
# to server configuration information like virtual host names, storage
|
7
7
|
# systems, etc. This class provides the DSL methods used in the
|
8
|
-
# config
|
8
|
+
# conf/config.rb file.
|
9
9
|
class Config
|
10
10
|
LOG_LEVELS = %w[debug info warn error fatal].freeze
|
11
11
|
|
data/lib/vines/stream/http.rb
CHANGED
@@ -43,11 +43,11 @@ module Vines
|
|
43
43
|
if request.path == self.bind
|
44
44
|
body = Nokogiri::XML(request.body).root
|
45
45
|
if session = Sessions[body['sid']]
|
46
|
-
session
|
46
|
+
@session = session
|
47
47
|
else
|
48
48
|
@session = Http::Session.new(self)
|
49
|
-
@session.request(request)
|
50
49
|
end
|
50
|
+
@session.request(request)
|
51
51
|
@nodes.push(body)
|
52
52
|
else
|
53
53
|
request.reply_with_file(self.root)
|
@@ -152,4 +152,4 @@ module Vines
|
|
152
152
|
end
|
153
153
|
end
|
154
154
|
end
|
155
|
-
end
|
155
|
+
end
|
data/lib/vines/version.rb
CHANGED
@@ -118,7 +118,7 @@ class ChatPage
|
|
118
118
|
if presence.type == 'subscribe'
|
119
119
|
node = $("""
|
120
120
|
<li data-jid="#{presence.from}" style="display:none;">
|
121
|
-
<form class="
|
121
|
+
<form class="inset">
|
122
122
|
<h2>Buddy Approval</h2>
|
123
123
|
<p>#{presence.from} wants to add you as a buddy.</p>
|
124
124
|
<fieldset class="buttons">
|
@@ -235,7 +235,7 @@ class ChatPage
|
|
235
235
|
|
236
236
|
toggleForm: (form, fn) ->
|
237
237
|
form = $(form)
|
238
|
-
$('.
|
238
|
+
$('form.overlay').each ->
|
239
239
|
$(this).hide() unless this.id == form.attr 'id'
|
240
240
|
if form.is ':hidden'
|
241
241
|
fn() if fn
|
@@ -245,19 +245,6 @@ class ChatPage
|
|
245
245
|
form[0].reset()
|
246
246
|
fn() if fn
|
247
247
|
|
248
|
-
filterRoster: ->
|
249
|
-
text = $('#search-roster-text').val().toLowerCase()
|
250
|
-
if text == ''
|
251
|
-
$('#roster li').show()
|
252
|
-
return
|
253
|
-
|
254
|
-
$('#roster li').each ->
|
255
|
-
node = $(this)
|
256
|
-
jid = (node.attr('data-jid') || '').toLowerCase()
|
257
|
-
name = (node.attr('data-name') || '').toLowerCase()
|
258
|
-
match = jid.indexOf(text) != -1 || name.indexOf(text) != -1
|
259
|
-
if match then node.show() else node.hide()
|
260
|
-
|
261
248
|
draw: ->
|
262
249
|
unless @session.connected()
|
263
250
|
window.location.hash = ''
|
@@ -266,18 +253,16 @@ class ChatPage
|
|
266
253
|
$('body').attr 'id', 'chat-page'
|
267
254
|
$('#container').hide().empty()
|
268
255
|
$("""
|
269
|
-
<div id="alpha" class="y-fill">
|
270
|
-
<h2>Buddies <div id="search-roster"></div></h2>
|
271
|
-
<
|
272
|
-
|
273
|
-
|
274
|
-
<ul id="roster" class="y-fill"></ul>
|
275
|
-
<div id="roster-controls">
|
256
|
+
<div id="alpha" class="sidebar column y-fill">
|
257
|
+
<h2>Buddies <div id="search-roster-icon"></div></h2>
|
258
|
+
<div id="search-roster-form"></div>
|
259
|
+
<ul id="roster" class="selectable scroll y-fill"></ul>
|
260
|
+
<div id="alpha-controls" class="controls">
|
276
261
|
<div id="add-contact"></div>
|
277
262
|
<div id="remove-contact"></div>
|
278
263
|
<div id="edit-contact"></div>
|
279
264
|
</div>
|
280
|
-
<form id="add-contact-form" class="
|
265
|
+
<form id="add-contact-form" class="overlay" style="display:none;">
|
281
266
|
<h2>Add Buddy</h2>
|
282
267
|
<input id="add-contact-jid" type="email" maxlength="1024" placeholder="Account name"/>
|
283
268
|
<input id="add-contact-name" type="text" maxlength="1024" placeholder="Real name"/>
|
@@ -286,7 +271,7 @@ class ChatPage
|
|
286
271
|
<input id="add-contact-ok" type="submit" value="Add"/>
|
287
272
|
</fieldset>
|
288
273
|
</form>
|
289
|
-
<form id="remove-contact-form" class="
|
274
|
+
<form id="remove-contact-form" class="overlay" style="display:none;">
|
290
275
|
<h2>Remove Buddy</h2>
|
291
276
|
<p id="remove-contact-msg">Select a buddy in the list above to remove.</p>
|
292
277
|
<fieldset class="buttons" style="display:none;">
|
@@ -294,7 +279,7 @@ class ChatPage
|
|
294
279
|
<input id="remove-contact-ok" type="submit" value="Remove"/>
|
295
280
|
</fieldset>
|
296
281
|
</form>
|
297
|
-
<form id="edit-contact-form" class="
|
282
|
+
<form id="edit-contact-form" class="overlay" style="display:none;">
|
298
283
|
<h2>Update Profile</h2>
|
299
284
|
<p id="edit-contact-jid">Select a buddy in the list above to update.</p>
|
300
285
|
<input id="edit-contact-name" type="text" maxlength="1024" placeholder="Real name" style="display:none;"/>
|
@@ -304,30 +289,30 @@ class ChatPage
|
|
304
289
|
</fieldset>
|
305
290
|
</form>
|
306
291
|
</div>
|
307
|
-
<div id="beta" class="x-fill y-fill">
|
292
|
+
<div id="beta" class="primary column x-fill y-fill">
|
308
293
|
<h2 id="chat-title">Select a buddy to chat</h2>
|
309
|
-
<ul id="messages" class="y-fill"></ul>
|
294
|
+
<ul id="messages" class="scroll y-fill"></ul>
|
310
295
|
<form id="message-form">
|
311
296
|
<input id="message" name="message" type="text" maxlength="1024" placeholder="Type a message and press enter to send"/>
|
312
297
|
</form>
|
313
298
|
</div>
|
314
|
-
<div id="charlie" class="y-fill">
|
299
|
+
<div id="charlie" class="sidebar column y-fill">
|
315
300
|
<h2>Notifications</h2>
|
316
|
-
<ul id="notifications" class="y-fill"></ul>
|
317
|
-
<div id="
|
301
|
+
<ul id="notifications" class="scroll y-fill"></ul>
|
302
|
+
<div id="charlie-controls" class="controls">
|
318
303
|
<div id="clear-notices"></div>
|
319
304
|
</div>
|
320
305
|
</div>
|
321
306
|
""").appendTo '#container'
|
322
307
|
|
323
308
|
this.roster()
|
324
|
-
this.button 'clear-notices', ICONS.no
|
325
|
-
this.button 'add-contact', ICONS.plus
|
326
|
-
this.button 'remove-contact', ICONS.minus
|
327
|
-
this.button 'edit-contact', ICONS.user
|
328
|
-
this.button 'search-roster', ICONS.search, scale: 0.5, translation: '-8 -8'
|
329
309
|
|
330
|
-
|
310
|
+
new Button '#clear-notices', ICONS.no
|
311
|
+
new Button '#add-contact', ICONS.plus
|
312
|
+
new Button '#remove-contact', ICONS.minus
|
313
|
+
new Button '#edit-contact', ICONS.user
|
314
|
+
|
315
|
+
$('#message').focus -> $('form.overlay').fadeOut()
|
331
316
|
$('#message-form').submit => this.send()
|
332
317
|
|
333
318
|
$('#clear-notices').click -> $('#notifications li').fadeOut 200
|
@@ -346,16 +331,21 @@ class ChatPage
|
|
346
331
|
$('#add-contact-form').submit => this.addContact()
|
347
332
|
$('#remove-contact-form').submit => this.removeContact()
|
348
333
|
$('#edit-contact-form').submit => this.updateContact()
|
349
|
-
$('#search-roster-form').submit -> false
|
350
|
-
|
351
|
-
$('#search-roster-text').keyup => this.filterRoster()
|
352
|
-
$('#search-roster-text').change => this.filterRoster()
|
353
|
-
$('#search-roster-text').click => this.filterRoster()
|
354
|
-
$('#search-roster').click =>
|
355
|
-
this.toggleForm '#search-roster-form', => this.filterRoster()
|
356
334
|
|
357
335
|
$('#container').fadeIn 200
|
358
|
-
this.resize()
|
336
|
+
layout = this.resize()
|
337
|
+
|
338
|
+
fn = ->
|
339
|
+
layout.resize()
|
340
|
+
layout.resize() # not sure why two are needed
|
341
|
+
|
342
|
+
new Filter
|
343
|
+
list: '#roster'
|
344
|
+
icon: '#search-roster-icon'
|
345
|
+
form: '#search-roster-form'
|
346
|
+
attrs: ['data-jid', 'data-name']
|
347
|
+
open: fn
|
348
|
+
close: fn
|
359
349
|
|
360
350
|
resize: ->
|
361
351
|
a = $ '#alpha'
|
@@ -366,20 +356,3 @@ class ChatPage
|
|
366
356
|
new Layout ->
|
367
357
|
c.css 'left', a.width() + b.width()
|
368
358
|
msg.width form.width() - 32
|
369
|
-
|
370
|
-
button: (id, path, options) ->
|
371
|
-
options ||= {}
|
372
|
-
paper = Raphael(id)
|
373
|
-
icon = paper.path(path).attr
|
374
|
-
fill: '#000'
|
375
|
-
stroke: '#fff'
|
376
|
-
'stroke-width': 0.3
|
377
|
-
opacity: 0.6
|
378
|
-
scale: options.scale || 0.85
|
379
|
-
translation: options.translation || ''
|
380
|
-
|
381
|
-
node = $('#' + id)
|
382
|
-
node.hover(
|
383
|
-
-> icon.animate(opacity: 1.0, 200),
|
384
|
-
-> icon.animate(opacity: 0.6, 200))
|
385
|
-
node.get 0
|
data/web/chat/javascripts/app.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
var ChatPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};ChatPage=function(){function a(a){this.session=a,this.session.onRoster(__bind(function(){return this.roster()},this)),this.session.onCard(__bind(function(a){return this.card(a)},this)),this.session.onMessage(__bind(function(a){return this.message(a)},this)),this.session.onPresence(__bind(function(a){return this.presence(a)},this)),this.chats={},this.currentContact=null}a.prototype.datef=function(a){var b,c,d,e;b=new Date(a),d=b.getHours()>=12?" pm":" am",c=b.getHours()>12?b.getHours()-12:b.getHours(),c===0&&(c=12),e=b.getMinutes()+"",e.length===1&&(e="0"+e);return c+":"+e+d},a.prototype.card=function(a){return this.eachContact(a.jid,__bind(function(b){return $(".vcard-img",b).attr("src",this.session.avatar(a.jid))},this))},a.prototype.roster=function(){var a,b,c,d,e,f,g,h;e=$("#roster"),$("li",e).each(__bind(function(a,b){var c;c=$(b).attr("data-jid");if(!this.session.roster[c])return $(b).remove()},this)),f=function(a,b){$(".text",a).text(b.name||b.jid);return a.attr("data-name",b.name||"")},g=this.session.roster,h=[];for(c in g)a=g[c],b=$("#roster li[data-jid='"+c+"']"),f(b,a),h.push(b.length===0?(d=$('<li data-jid="'+c+'" data-name="" class="offline">\n <span class="text"></span>\n <span class="status-msg">Offline</span>\n <span class="unread" style="display:none;"></span>\n <img class="vcard-img" alt="'+c+'" src="'+this.session.avatar(c)+'"/>\n</li>').appendTo(e),f(d,a),d.click(__bind(function(a){return this.selectContact(a)},this))):void 0);return h},a.prototype.message=function(a){var b,c,d,e;this.queueMessage(a),e=a.from===this.session.jid(),d=a.from.split("/")[0];if(!e&&d!==this.currentContact){c=this.chat(a.from),c.unread++;return this.eachContact(d,function(a){return $(".unread",a).text(c.unread).show()})}b=this.atBottom(),this.appendMessage(a);if(b)return this.scroll()},a.prototype.eachContact=function(a,b){var c,d,e,f,g;f=$("#roster li[data-jid='"+a+"']").get(),g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(b($(c)));return g},a.prototype.appendMessage=function(a){var b,c,d,e;c=a.from.split("/")[0],b=this.session.roster[c],d=b?b.name||c:c,a.from===this.session.jid()&&(d="Me"),e=$('<li data-jid="'+c+'" style="display:none;">\n <p></p>\n <img alt="'+c+'" src="'+this.session.avatar(c)+'"/>\n <footer>\n <span class="author"></span>\n <span class="time">'+this.datef(a.received)+"</span>\n </footer>\n</li>").appendTo("#messages"),$("p",e).text(a.text),$(".author",e).text(d);return e.fadeIn(200)},a.prototype.queueMessage=function(a){var b,c,d;d=a.from===this.session.jid(),c=a[d?"to":"from"],b=this.chat(c),b.jid=c;return b.messages.push(a)},a.prototype.chat=function(a){var b,c;b=a.split("/")[0],c=this.chats[b],c||(c={jid:a,messages:[],unread:0},this.chats[b]=c);return c},a.prototype.presence=function(a){var b,c,d;c=a.from.split("/")[0];if(c!==this.session.bareJid()){if(!a.type||a.offline)b=this.session.roster[c],this.eachContact(c,function(a){$(".status-msg",a).text(b.status());return b.offline()?a.addClass("offline"):a.removeClass("offline")});a.offline&&(this.chat(c).jid=c);if(a.type==="subscribe"){d=$('<li data-jid="'+a.from+'" style="display:none;">\n <form class="notify-form">\n <h2>Buddy Approval</h2>\n <p>'+a.from+' wants to add you as a buddy.</p>\n <fieldset class="buttons">\n <input type="button" value="Decline"/>\n <input type="submit" value="Accept"/>\n </fieldset>\n </form>\n</li>').appendTo("#notifications"),d.fadeIn(200),$("form",d).submit(__bind(function(){return this.acceptContact(d,a.from)},this));return $('input[type="button"]',d).click(__bind(function(){return this.rejectContact(d,a.from)},this))}}},a.prototype.acceptContact=function(a,b){a.fadeOut(200,function(){return a.remove()}),this.session.sendSubscribed(b),this.session.sendSubscribe(b);return!1},a.prototype.rejectContact=function(a,b){a.fadeOut(200,function(){return a.remove()});return this.session.sendUnsubscribed(b)},a.prototype.selectContact=function(a){var b,c,d,e,f,g,h;d=$(a.currentTarget).attr("data-jid"),c=this.session.roster[d];if(this.currentContact!==d){this.currentContact=d,$("#roster li").removeClass("selected"),$(a.currentTarget).addClass("selected"),$("#chat-title").text("Chat with "+(c.name||c.jid)),$("#messages").empty(),b=this.chats[d],e=[],b&&(e=b.messages,b.unread=0,this.eachContact(d,function(a){return $(".unread",a).text("").hide()}));for(g=0,h=e.length;g<h;g++)f=e[g],this.appendMessage(f);this.scroll(),$("#remove-contact-msg").html("Are you sure you want to remove "+("<strong>"+this.currentContact+"</strong> from your buddy list?")),$("#remove-contact-form .buttons").fadeIn(200),$("#edit-contact-jid").text(this.currentContact),$("#edit-contact-name").val(this.session.roster[this.currentContact].name),$("#edit-contact-form input").fadeIn(200);return $("#edit-contact-form .buttons").fadeIn(200)}},a.prototype.scroll=function(){var a;a=$("#messages");return a.animate({scrollTop:a.prop("scrollHeight")},400)},a.prototype.atBottom=function(){var a,b;b=$("#messages"),a=b.prop("scrollHeight")-b.height();return b.scrollTop()===a},a.prototype.send=function(){var a,b,c,d;if(!this.currentContact)return!1;b=$("#message"),d=b.val().trim(),d&&(a=this.chats[this.currentContact],c=a?a.jid:this.currentContact,this.message({from:this.session.jid(),text:d,to:c,received:new Date}),this.session.sendMessage(c,d)),b.val("");return!1},a.prototype.addContact=function(){var a;this.toggleForm("#add-contact-form"),a={jid:$("#add-contact-jid").val(),name:$("#add-contact-name").val(),groups:["Buddies"]},a.jid&&this.session.updateContact(a,!0);return!1},a.prototype.removeContact=function(){this.toggleForm("#remove-contact-form"),this.session.removeContact(this.currentContact),this.currentContact=null,$("#chat-title").text("Select a buddy to chat"),$("#messages").empty(),$("#remove-contact-msg").html("Select a buddy in the list above to remove."),$("#remove-contact-form .buttons").hide(),$("#edit-contact-jid").text("Select a buddy in the list above to update."),$("#edit-contact-name").val(""),$("#edit-contact-form input").hide(),$("#edit-contact-form .buttons").hide();return!1},a.prototype.updateContact=function(){var a;this.toggleForm("#edit-contact-form"),a={jid:this.currentContact,name:$("#edit-contact-name").val(),groups:this.session.roster[this.currentContact].groups},this.session.updateContact(a);return!1},a.prototype.toggleForm=function(a,b){a=$(a),$(".contact-form").each(function(){if(this.id!==a.attr("id"))return $(this).hide()});if(a.is(":hidden")){b&&b();return a.fadeIn(100)}return a.fadeOut(100,function(){a[0].reset();if(b)return b()})},a.prototype.filterRoster=function(){var a;a=$("#search-roster-text").val().toLowerCase();if(a==="")$("#roster li").show();else return $("#roster li").each(function(){var b,c,d,e;e=$(this),b=(e.attr("data-jid")||"").toLowerCase(),d=(e.attr("data-name")||"").toLowerCase(),c=b.indexOf(a)!==-1||d.indexOf(a)!==-1;return c?e.show():e.hide()})},a.prototype.draw=function(){if(!this.session.connected())window.location.hash="";else{$("body").attr("id","chat-page"),$("#container").hide().empty(),$('<div id="alpha" class="y-fill">\n <h2>Buddies <div id="search-roster"></div></h2>\n <form id="search-roster-form" style="display:none;">\n <input id="search-roster-text" type="search" placeholder="Filter" results="5"/>\n </form>\n <ul id="roster" class="y-fill"></ul>\n <div id="roster-controls">\n <div id="add-contact"></div>\n <div id="remove-contact"></div>\n <div id="edit-contact"></div>\n </div>\n <form id="add-contact-form" class="contact-form" style="display:none;">\n <h2>Add Buddy</h2>\n <input id="add-contact-jid" type="email" maxlength="1024" placeholder="Account name"/>\n <input id="add-contact-name" type="text" maxlength="1024" placeholder="Real name"/>\n <fieldset class="buttons">\n <input id="add-contact-cancel" type="button" value="Cancel"/>\n <input id="add-contact-ok" type="submit" value="Add"/>\n </fieldset>\n </form>\n <form id="remove-contact-form" class="contact-form" style="display:none;">\n <h2>Remove Buddy</h2>\n <p id="remove-contact-msg">Select a buddy in the list above to remove.</p>\n <fieldset class="buttons" style="display:none;">\n <input id="remove-contact-cancel" type="button" value="Cancel"/>\n <input id="remove-contact-ok" type="submit" value="Remove"/>\n </fieldset>\n </form>\n <form id="edit-contact-form" class="contact-form" style="display:none;">\n <h2>Update Profile</h2>\n <p id="edit-contact-jid">Select a buddy in the list above to update.</p>\n <input id="edit-contact-name" type="text" maxlength="1024" placeholder="Real name" style="display:none;"/>\n <fieldset class="buttons" style="display:none;">\n <input id="edit-contact-cancel" type="button" value="Cancel"/>\n <input id="edit-contact-ok" type="submit" value="Save"/>\n </fieldset>\n </form>\n</div>\n<div id="beta" class="x-fill y-fill">\n <h2 id="chat-title">Select a buddy to chat</h2>\n <ul id="messages" class="y-fill"></ul>\n <form id="message-form">\n <input id="message" name="message" type="text" maxlength="1024" placeholder="Type a message and press enter to send"/>\n </form>\n</div>\n<div id="charlie" class="y-fill">\n <h2>Notifications</h2>\n <ul id="notifications" class="y-fill"></ul>\n <div id="notification-controls">\n <div id="clear-notices"></div>\n </div>\n</div>').appendTo("#container"),this.roster(),this.button("clear-notices",ICONS.no),this.button("add-contact",ICONS.plus),this.button("remove-contact",ICONS.minus),this.button("edit-contact",ICONS.user),this.button("search-roster",ICONS.search,{scale:.5,translation:"-8 -8"}),$("#message").focus(function(){return $(".contact-form").fadeOut()}),$("#message-form").submit(__bind(function(){return this.send()},this)),$("#clear-notices").click(function(){return $("#notifications li").fadeOut(200)}),$("#add-contact").click(__bind(function(){return this.toggleForm("#add-contact-form")},this)),$("#remove-contact").click(__bind(function(){return this.toggleForm("#remove-contact-form")},this)),$("#edit-contact").click(__bind(function(){return this.toggleForm("#edit-contact-form",__bind(function(){if(this.currentContact){$("#edit-contact-jid").text(this.currentContact);return $("#edit-contact-name").val(this.session.roster[this.currentContact].name)}},this))},this)),$("#add-contact-cancel").click(__bind(function(){return this.toggleForm("#add-contact-form")},this)),$("#remove-contact-cancel").click(__bind(function(){return this.toggleForm("#remove-contact-form")},this)),$("#edit-contact-cancel").click(__bind(function(){return this.toggleForm("#edit-contact-form")},this)),$("#add-contact-form").submit(__bind(function(){return this.addContact()},this)),$("#remove-contact-form").submit(__bind(function(){return this.removeContact()},this)),$("#edit-contact-form").submit(__bind(function(){return this.updateContact()},this)),$("#search-roster-form").submit(function(){return!1}),$("#search-roster-text").keyup(__bind(function(){return this.filterRoster()},this)),$("#search-roster-text").change(__bind(function(){return this.filterRoster()},this)),$("#search-roster-text").click(__bind(function(){return this.filterRoster()},this)),$("#search-roster").click(__bind(function(){return this.toggleForm("#search-roster-form",__bind(function(){return this.filterRoster()},this))},this)),$("#container").fadeIn(200);return this.resize()}},a.prototype.resize=function(){var a,b,c,d,e;a=$("#alpha"),b=$("#beta"),c=$("#charlie"),e=$("#message"),d=$("#message-form");return new Layout(function(){c.css("left",a.width()+b.width());return e.width(d.width()-32)})},a.prototype.button=function(a,b,c){var d,e,f;c||(c={}),f=Raphael(a),d=f.path(b).attr({fill:"#000",stroke:"#fff","stroke-width":.3,opacity:.6,scale:c.scale||.85,translation:c.translation||""}),e=$("#"+a),e.hover(function(){return d.animate({opacity:1},200)},function(){return d.animate({opacity:.6},200)});return e.get(0)};return a}();var LogoutPage;LogoutPage=function(){function a(a){this.session=a}a.prototype.draw=function(){window.location.hash="";return window.location.reload()};return a}(),$(function(){var a,b,c,d,e,f;f=new Session,d=new NavBar(f),d.draw(),a={Messages:ICONS.chat,Logout:ICONS.power};for(c in a)b=a[c],d.addButton(c,b);e={"/messages":new ChatPage(f),"/logout":new LogoutPage(f),"default":new LoginPage(f,"/messages/")},(new Router(e)).draw();return d.select($("#nav-link-messages").parent())})
|
1
|
+
var ChatPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};ChatPage=function(){function a(a){this.session=a,this.session.onRoster(__bind(function(){return this.roster()},this)),this.session.onCard(__bind(function(a){return this.card(a)},this)),this.session.onMessage(__bind(function(a){return this.message(a)},this)),this.session.onPresence(__bind(function(a){return this.presence(a)},this)),this.chats={},this.currentContact=null}a.prototype.datef=function(a){var b,c,d,e;b=new Date(a),d=b.getHours()>=12?" pm":" am",c=b.getHours()>12?b.getHours()-12:b.getHours(),c===0&&(c=12),e=b.getMinutes()+"",e.length===1&&(e="0"+e);return c+":"+e+d},a.prototype.card=function(a){return this.eachContact(a.jid,__bind(function(b){return $(".vcard-img",b).attr("src",this.session.avatar(a.jid))},this))},a.prototype.roster=function(){var a,b,c,d,e,f,g,h;e=$("#roster"),$("li",e).each(__bind(function(a,b){var c;c=$(b).attr("data-jid");if(!this.session.roster[c])return $(b).remove()},this)),f=function(a,b){$(".text",a).text(b.name||b.jid);return a.attr("data-name",b.name||"")},g=this.session.roster,h=[];for(c in g)a=g[c],b=$("#roster li[data-jid='"+c+"']"),f(b,a),h.push(b.length===0?(d=$('<li data-jid="'+c+'" data-name="" class="offline">\n <span class="text"></span>\n <span class="status-msg">Offline</span>\n <span class="unread" style="display:none;"></span>\n <img class="vcard-img" alt="'+c+'" src="'+this.session.avatar(c)+'"/>\n</li>').appendTo(e),f(d,a),d.click(__bind(function(a){return this.selectContact(a)},this))):void 0);return h},a.prototype.message=function(a){var b,c,d,e;this.queueMessage(a),e=a.from===this.session.jid(),d=a.from.split("/")[0];if(!e&&d!==this.currentContact){c=this.chat(a.from),c.unread++;return this.eachContact(d,function(a){return $(".unread",a).text(c.unread).show()})}b=this.atBottom(),this.appendMessage(a);if(b)return this.scroll()},a.prototype.eachContact=function(a,b){var c,d,e,f,g;f=$("#roster li[data-jid='"+a+"']").get(),g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(b($(c)));return g},a.prototype.appendMessage=function(a){var b,c,d,e;c=a.from.split("/")[0],b=this.session.roster[c],d=b?b.name||c:c,a.from===this.session.jid()&&(d="Me"),e=$('<li data-jid="'+c+'" style="display:none;">\n <p></p>\n <img alt="'+c+'" src="'+this.session.avatar(c)+'"/>\n <footer>\n <span class="author"></span>\n <span class="time">'+this.datef(a.received)+"</span>\n </footer>\n</li>").appendTo("#messages"),$("p",e).text(a.text),$(".author",e).text(d);return e.fadeIn(200)},a.prototype.queueMessage=function(a){var b,c,d;d=a.from===this.session.jid(),c=a[d?"to":"from"],b=this.chat(c),b.jid=c;return b.messages.push(a)},a.prototype.chat=function(a){var b,c;b=a.split("/")[0],c=this.chats[b],c||(c={jid:a,messages:[],unread:0},this.chats[b]=c);return c},a.prototype.presence=function(a){var b,c,d;c=a.from.split("/")[0];if(c!==this.session.bareJid()){if(!a.type||a.offline)b=this.session.roster[c],this.eachContact(c,function(a){$(".status-msg",a).text(b.status());return b.offline()?a.addClass("offline"):a.removeClass("offline")});a.offline&&(this.chat(c).jid=c);if(a.type==="subscribe"){d=$('<li data-jid="'+a.from+'" style="display:none;">\n <form class="inset">\n <h2>Buddy Approval</h2>\n <p>'+a.from+' wants to add you as a buddy.</p>\n <fieldset class="buttons">\n <input type="button" value="Decline"/>\n <input type="submit" value="Accept"/>\n </fieldset>\n </form>\n</li>').appendTo("#notifications"),d.fadeIn(200),$("form",d).submit(__bind(function(){return this.acceptContact(d,a.from)},this));return $('input[type="button"]',d).click(__bind(function(){return this.rejectContact(d,a.from)},this))}}},a.prototype.acceptContact=function(a,b){a.fadeOut(200,function(){return a.remove()}),this.session.sendSubscribed(b),this.session.sendSubscribe(b);return!1},a.prototype.rejectContact=function(a,b){a.fadeOut(200,function(){return a.remove()});return this.session.sendUnsubscribed(b)},a.prototype.selectContact=function(a){var b,c,d,e,f,g,h;d=$(a.currentTarget).attr("data-jid"),c=this.session.roster[d];if(this.currentContact!==d){this.currentContact=d,$("#roster li").removeClass("selected"),$(a.currentTarget).addClass("selected"),$("#chat-title").text("Chat with "+(c.name||c.jid)),$("#messages").empty(),b=this.chats[d],e=[],b&&(e=b.messages,b.unread=0,this.eachContact(d,function(a){return $(".unread",a).text("").hide()}));for(g=0,h=e.length;g<h;g++)f=e[g],this.appendMessage(f);this.scroll(),$("#remove-contact-msg").html("Are you sure you want to remove "+("<strong>"+this.currentContact+"</strong> from your buddy list?")),$("#remove-contact-form .buttons").fadeIn(200),$("#edit-contact-jid").text(this.currentContact),$("#edit-contact-name").val(this.session.roster[this.currentContact].name),$("#edit-contact-form input").fadeIn(200);return $("#edit-contact-form .buttons").fadeIn(200)}},a.prototype.scroll=function(){var a;a=$("#messages");return a.animate({scrollTop:a.prop("scrollHeight")},400)},a.prototype.atBottom=function(){var a,b;b=$("#messages"),a=b.prop("scrollHeight")-b.height();return b.scrollTop()===a},a.prototype.send=function(){var a,b,c,d;if(!this.currentContact)return!1;b=$("#message"),d=b.val().trim(),d&&(a=this.chats[this.currentContact],c=a?a.jid:this.currentContact,this.message({from:this.session.jid(),text:d,to:c,received:new Date}),this.session.sendMessage(c,d)),b.val("");return!1},a.prototype.addContact=function(){var a;this.toggleForm("#add-contact-form"),a={jid:$("#add-contact-jid").val(),name:$("#add-contact-name").val(),groups:["Buddies"]},a.jid&&this.session.updateContact(a,!0);return!1},a.prototype.removeContact=function(){this.toggleForm("#remove-contact-form"),this.session.removeContact(this.currentContact),this.currentContact=null,$("#chat-title").text("Select a buddy to chat"),$("#messages").empty(),$("#remove-contact-msg").html("Select a buddy in the list above to remove."),$("#remove-contact-form .buttons").hide(),$("#edit-contact-jid").text("Select a buddy in the list above to update."),$("#edit-contact-name").val(""),$("#edit-contact-form input").hide(),$("#edit-contact-form .buttons").hide();return!1},a.prototype.updateContact=function(){var a;this.toggleForm("#edit-contact-form"),a={jid:this.currentContact,name:$("#edit-contact-name").val(),groups:this.session.roster[this.currentContact].groups},this.session.updateContact(a);return!1},a.prototype.toggleForm=function(a,b){a=$(a),$("form.overlay").each(function(){if(this.id!==a.attr("id"))return $(this).hide()});if(a.is(":hidden")){b&&b();return a.fadeIn(100)}return a.fadeOut(100,function(){a[0].reset();if(b)return b()})},a.prototype.draw=function(){var a,b;if(!this.session.connected())window.location.hash="";else{$("body").attr("id","chat-page"),$("#container").hide().empty(),$('<div id="alpha" class="sidebar column y-fill">\n <h2>Buddies <div id="search-roster-icon"></div></h2>\n <div id="search-roster-form"></div>\n <ul id="roster" class="selectable scroll y-fill"></ul>\n <div id="alpha-controls" class="controls">\n <div id="add-contact"></div>\n <div id="remove-contact"></div>\n <div id="edit-contact"></div>\n </div>\n <form id="add-contact-form" class="overlay" style="display:none;">\n <h2>Add Buddy</h2>\n <input id="add-contact-jid" type="email" maxlength="1024" placeholder="Account name"/>\n <input id="add-contact-name" type="text" maxlength="1024" placeholder="Real name"/>\n <fieldset class="buttons">\n <input id="add-contact-cancel" type="button" value="Cancel"/>\n <input id="add-contact-ok" type="submit" value="Add"/>\n </fieldset>\n </form>\n <form id="remove-contact-form" class="overlay" style="display:none;">\n <h2>Remove Buddy</h2>\n <p id="remove-contact-msg">Select a buddy in the list above to remove.</p>\n <fieldset class="buttons" style="display:none;">\n <input id="remove-contact-cancel" type="button" value="Cancel"/>\n <input id="remove-contact-ok" type="submit" value="Remove"/>\n </fieldset>\n </form>\n <form id="edit-contact-form" class="overlay" style="display:none;">\n <h2>Update Profile</h2>\n <p id="edit-contact-jid">Select a buddy in the list above to update.</p>\n <input id="edit-contact-name" type="text" maxlength="1024" placeholder="Real name" style="display:none;"/>\n <fieldset class="buttons" style="display:none;">\n <input id="edit-contact-cancel" type="button" value="Cancel"/>\n <input id="edit-contact-ok" type="submit" value="Save"/>\n </fieldset>\n </form>\n</div>\n<div id="beta" class="primary column x-fill y-fill">\n <h2 id="chat-title">Select a buddy to chat</h2>\n <ul id="messages" class="scroll y-fill"></ul>\n <form id="message-form">\n <input id="message" name="message" type="text" maxlength="1024" placeholder="Type a message and press enter to send"/>\n </form>\n</div>\n<div id="charlie" class="sidebar column y-fill">\n <h2>Notifications</h2>\n <ul id="notifications" class="scroll y-fill"></ul>\n <div id="charlie-controls" class="controls">\n <div id="clear-notices"></div>\n </div>\n</div>').appendTo("#container"),this.roster(),new Button("#clear-notices",ICONS.no),new Button("#add-contact",ICONS.plus),new Button("#remove-contact",ICONS.minus),new Button("#edit-contact",ICONS.user),$("#message").focus(function(){return $("form.overlay").fadeOut()}),$("#message-form").submit(__bind(function(){return this.send()},this)),$("#clear-notices").click(function(){return $("#notifications li").fadeOut(200)}),$("#add-contact").click(__bind(function(){return this.toggleForm("#add-contact-form")},this)),$("#remove-contact").click(__bind(function(){return this.toggleForm("#remove-contact-form")},this)),$("#edit-contact").click(__bind(function(){return this.toggleForm("#edit-contact-form",__bind(function(){if(this.currentContact){$("#edit-contact-jid").text(this.currentContact);return $("#edit-contact-name").val(this.session.roster[this.currentContact].name)}},this))},this)),$("#add-contact-cancel").click(__bind(function(){return this.toggleForm("#add-contact-form")},this)),$("#remove-contact-cancel").click(__bind(function(){return this.toggleForm("#remove-contact-form")},this)),$("#edit-contact-cancel").click(__bind(function(){return this.toggleForm("#edit-contact-form")},this)),$("#add-contact-form").submit(__bind(function(){return this.addContact()},this)),$("#remove-contact-form").submit(__bind(function(){return this.removeContact()},this)),$("#edit-contact-form").submit(__bind(function(){return this.updateContact()},this)),$("#container").fadeIn(200),b=this.resize(),a=function(){b.resize();return b.resize()};return new Filter({list:"#roster",icon:"#search-roster-icon",form:"#search-roster-form",attrs:["data-jid","data-name"],open:a,close:a})}},a.prototype.resize=function(){var a,b,c,d,e;a=$("#alpha"),b=$("#beta"),c=$("#charlie"),e=$("#message"),d=$("#message-form");return new Layout(function(){c.css("left",a.width()+b.width());return e.width(d.width()-32)})};return a}();var LogoutPage;LogoutPage=function(){function a(a){this.session=a}a.prototype.draw=function(){window.location.hash="";return window.location.reload()};return a}(),$(function(){var a,b,c,d,e,f;f=new Session,d=new NavBar(f),d.draw(),a={Messages:ICONS.chat,Logout:ICONS.power};for(c in a)b=a[c],d.addButton(c,b);e={"/messages":new ChatPage(f),"/logout":new LogoutPage(f),"default":new LoginPage(f,"/messages/")},(new Router(e)).draw();return d.select($("#nav-link-messages").parent())})
|
@@ -148,7 +148,7 @@ ChatPage = (function() {
|
|
148
148
|
this.chat(from).jid = from;
|
149
149
|
}
|
150
150
|
if (presence.type === 'subscribe') {
|
151
|
-
node = $("<li data-jid=\"" + presence.from + "\" style=\"display:none;\">\n <form class=\"
|
151
|
+
node = $("<li data-jid=\"" + presence.from + "\" style=\"display:none;\">\n <form class=\"inset\">\n <h2>Buddy Approval</h2>\n <p>" + presence.from + " wants to add you as a buddy.</p>\n <fieldset class=\"buttons\">\n <input type=\"button\" value=\"Decline\"/>\n <input type=\"submit\" value=\"Accept\"/>\n </fieldset>\n </form>\n</li>").appendTo('#notifications');
|
152
152
|
node.fadeIn(200);
|
153
153
|
$('form', node).submit(__bind(function() {
|
154
154
|
return this.acceptContact(node, presence.from);
|
@@ -279,7 +279,7 @@ ChatPage = (function() {
|
|
279
279
|
};
|
280
280
|
ChatPage.prototype.toggleForm = function(form, fn) {
|
281
281
|
form = $(form);
|
282
|
-
$('.
|
282
|
+
$('form.overlay').each(function() {
|
283
283
|
if (this.id !== form.attr('id')) {
|
284
284
|
return $(this).hide();
|
285
285
|
}
|
@@ -298,45 +298,22 @@ ChatPage = (function() {
|
|
298
298
|
});
|
299
299
|
}
|
300
300
|
};
|
301
|
-
ChatPage.prototype.filterRoster = function() {
|
302
|
-
var text;
|
303
|
-
text = $('#search-roster-text').val().toLowerCase();
|
304
|
-
if (text === '') {
|
305
|
-
$('#roster li').show();
|
306
|
-
return;
|
307
|
-
}
|
308
|
-
return $('#roster li').each(function() {
|
309
|
-
var jid, match, name, node;
|
310
|
-
node = $(this);
|
311
|
-
jid = (node.attr('data-jid') || '').toLowerCase();
|
312
|
-
name = (node.attr('data-name') || '').toLowerCase();
|
313
|
-
match = jid.indexOf(text) !== -1 || name.indexOf(text) !== -1;
|
314
|
-
if (match) {
|
315
|
-
return node.show();
|
316
|
-
} else {
|
317
|
-
return node.hide();
|
318
|
-
}
|
319
|
-
});
|
320
|
-
};
|
321
301
|
ChatPage.prototype.draw = function() {
|
302
|
+
var fn, layout;
|
322
303
|
if (!this.session.connected()) {
|
323
304
|
window.location.hash = '';
|
324
305
|
return;
|
325
306
|
}
|
326
307
|
$('body').attr('id', 'chat-page');
|
327
308
|
$('#container').hide().empty();
|
328
|
-
$("<div id=\"alpha\" class=\"y-fill\">\n <h2>Buddies <div id=\"search-roster\"></div></h2>\n <
|
309
|
+
$("<div id=\"alpha\" class=\"sidebar column y-fill\">\n <h2>Buddies <div id=\"search-roster-icon\"></div></h2>\n <div id=\"search-roster-form\"></div>\n <ul id=\"roster\" class=\"selectable scroll y-fill\"></ul>\n <div id=\"alpha-controls\" class=\"controls\">\n <div id=\"add-contact\"></div>\n <div id=\"remove-contact\"></div>\n <div id=\"edit-contact\"></div>\n </div>\n <form id=\"add-contact-form\" class=\"overlay\" style=\"display:none;\">\n <h2>Add Buddy</h2>\n <input id=\"add-contact-jid\" type=\"email\" maxlength=\"1024\" placeholder=\"Account name\"/>\n <input id=\"add-contact-name\" type=\"text\" maxlength=\"1024\" placeholder=\"Real name\"/>\n <fieldset class=\"buttons\">\n <input id=\"add-contact-cancel\" type=\"button\" value=\"Cancel\"/>\n <input id=\"add-contact-ok\" type=\"submit\" value=\"Add\"/>\n </fieldset>\n </form>\n <form id=\"remove-contact-form\" class=\"overlay\" style=\"display:none;\">\n <h2>Remove Buddy</h2>\n <p id=\"remove-contact-msg\">Select a buddy in the list above to remove.</p>\n <fieldset class=\"buttons\" style=\"display:none;\">\n <input id=\"remove-contact-cancel\" type=\"button\" value=\"Cancel\"/>\n <input id=\"remove-contact-ok\" type=\"submit\" value=\"Remove\"/>\n </fieldset>\n </form>\n <form id=\"edit-contact-form\" class=\"overlay\" style=\"display:none;\">\n <h2>Update Profile</h2>\n <p id=\"edit-contact-jid\">Select a buddy in the list above to update.</p>\n <input id=\"edit-contact-name\" type=\"text\" maxlength=\"1024\" placeholder=\"Real name\" style=\"display:none;\"/>\n <fieldset class=\"buttons\" style=\"display:none;\">\n <input id=\"edit-contact-cancel\" type=\"button\" value=\"Cancel\"/>\n <input id=\"edit-contact-ok\" type=\"submit\" value=\"Save\"/>\n </fieldset>\n </form>\n</div>\n<div id=\"beta\" class=\"primary column x-fill y-fill\">\n <h2 id=\"chat-title\">Select a buddy to chat</h2>\n <ul id=\"messages\" class=\"scroll y-fill\"></ul>\n <form id=\"message-form\">\n <input id=\"message\" name=\"message\" type=\"text\" maxlength=\"1024\" placeholder=\"Type a message and press enter to send\"/>\n </form>\n</div>\n<div id=\"charlie\" class=\"sidebar column y-fill\">\n <h2>Notifications</h2>\n <ul id=\"notifications\" class=\"scroll y-fill\"></ul>\n <div id=\"charlie-controls\" class=\"controls\">\n <div id=\"clear-notices\"></div>\n </div>\n</div>").appendTo('#container');
|
329
310
|
this.roster();
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
this.button('search-roster', ICONS.search, {
|
335
|
-
scale: 0.5,
|
336
|
-
translation: '-8 -8'
|
337
|
-
});
|
311
|
+
new Button('#clear-notices', ICONS.no);
|
312
|
+
new Button('#add-contact', ICONS.plus);
|
313
|
+
new Button('#remove-contact', ICONS.minus);
|
314
|
+
new Button('#edit-contact', ICONS.user);
|
338
315
|
$('#message').focus(function() {
|
339
|
-
return $('.
|
316
|
+
return $('form.overlay').fadeOut();
|
340
317
|
});
|
341
318
|
$('#message-form').submit(__bind(function() {
|
342
319
|
return this.send();
|
@@ -376,25 +353,20 @@ ChatPage = (function() {
|
|
376
353
|
$('#edit-contact-form').submit(__bind(function() {
|
377
354
|
return this.updateContact();
|
378
355
|
}, this));
|
379
|
-
$('#search-roster-form').submit(function() {
|
380
|
-
return false;
|
381
|
-
});
|
382
|
-
$('#search-roster-text').keyup(__bind(function() {
|
383
|
-
return this.filterRoster();
|
384
|
-
}, this));
|
385
|
-
$('#search-roster-text').change(__bind(function() {
|
386
|
-
return this.filterRoster();
|
387
|
-
}, this));
|
388
|
-
$('#search-roster-text').click(__bind(function() {
|
389
|
-
return this.filterRoster();
|
390
|
-
}, this));
|
391
|
-
$('#search-roster').click(__bind(function() {
|
392
|
-
return this.toggleForm('#search-roster-form', __bind(function() {
|
393
|
-
return this.filterRoster();
|
394
|
-
}, this));
|
395
|
-
}, this));
|
396
356
|
$('#container').fadeIn(200);
|
397
|
-
|
357
|
+
layout = this.resize();
|
358
|
+
fn = function() {
|
359
|
+
layout.resize();
|
360
|
+
return layout.resize();
|
361
|
+
};
|
362
|
+
return new Filter({
|
363
|
+
list: '#roster',
|
364
|
+
icon: '#search-roster-icon',
|
365
|
+
form: '#search-roster-form',
|
366
|
+
attrs: ['data-jid', 'data-name'],
|
367
|
+
open: fn,
|
368
|
+
close: fn
|
369
|
+
});
|
398
370
|
};
|
399
371
|
ChatPage.prototype.resize = function() {
|
400
372
|
var a, b, c, form, msg;
|
@@ -408,29 +380,5 @@ ChatPage = (function() {
|
|
408
380
|
return msg.width(form.width() - 32);
|
409
381
|
});
|
410
382
|
};
|
411
|
-
ChatPage.prototype.button = function(id, path, options) {
|
412
|
-
var icon, node, paper;
|
413
|
-
options || (options = {});
|
414
|
-
paper = Raphael(id);
|
415
|
-
icon = paper.path(path).attr({
|
416
|
-
fill: '#000',
|
417
|
-
stroke: '#fff',
|
418
|
-
'stroke-width': 0.3,
|
419
|
-
opacity: 0.6,
|
420
|
-
scale: options.scale || 0.85,
|
421
|
-
translation: options.translation || ''
|
422
|
-
});
|
423
|
-
node = $('#' + id);
|
424
|
-
node.hover(function() {
|
425
|
-
return icon.animate({
|
426
|
-
opacity: 1.0
|
427
|
-
}, 200);
|
428
|
-
}, function() {
|
429
|
-
return icon.animate({
|
430
|
-
opacity: 0.6
|
431
|
-
}, 200);
|
432
|
-
});
|
433
|
-
return node.get(0);
|
434
|
-
};
|
435
383
|
return ChatPage;
|
436
384
|
})();
|
@@ -1,41 +1,6 @@
|
|
1
1
|
#chat-page #container {
|
2
2
|
height: 100%;
|
3
3
|
}
|
4
|
-
#chat-page #alpha,
|
5
|
-
#chat-page #beta,
|
6
|
-
#chat-page #charlie {
|
7
|
-
height: 100%;
|
8
|
-
position: absolute;
|
9
|
-
}
|
10
|
-
#chat-page #alpha {
|
11
|
-
background: #f8f8f8;
|
12
|
-
-webkit-box-shadow: 0 0 40px rgba(0, 0, 0, 0.1) inset;
|
13
|
-
box-shadow: 0 0 40px rgba(0, 0, 0, 0.1) inset;
|
14
|
-
width: 260px;
|
15
|
-
}
|
16
|
-
#chat-page #alpha h2,
|
17
|
-
#chat-page #beta h2,
|
18
|
-
#chat-page #charlie h2 {
|
19
|
-
border-bottom: 1px solid #ddd;
|
20
|
-
font-size: 10pt;
|
21
|
-
padding-left: 10px;
|
22
|
-
position: relative;
|
23
|
-
text-shadow: 0 -1px 1px #fff;
|
24
|
-
}
|
25
|
-
#chat-page #beta {
|
26
|
-
-webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.5), 0 0 40px rgba(0, 0, 0, 0.1) inset;
|
27
|
-
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5), 0 0 40px rgba(0, 0, 0, 0.1) inset;
|
28
|
-
width: 460px;
|
29
|
-
left: 260px;
|
30
|
-
z-index: 1;
|
31
|
-
}
|
32
|
-
#chat-page #charlie {
|
33
|
-
background: #f8f8f8;
|
34
|
-
-webkit-box-shadow: 0 0 40px rgba(0, 0, 0, 0.1) inset;
|
35
|
-
box-shadow: 0 0 40px rgba(0, 0, 0, 0.1) inset;
|
36
|
-
width: 260px;
|
37
|
-
left: 720px;
|
38
|
-
}
|
39
4
|
#chat-page #chat-title {
|
40
5
|
background: #f8f8f8;
|
41
6
|
}
|
@@ -43,25 +8,9 @@
|
|
43
8
|
background: #fff;
|
44
9
|
height: 100%;
|
45
10
|
list-style: none;
|
46
|
-
overflow-y: auto;
|
47
11
|
text-shadow: 0 1px 1px #ddd;
|
48
12
|
width: 100%;
|
49
13
|
}
|
50
|
-
#chat-page #messages::-webkit-scrollbar,
|
51
|
-
#chat-page #roster::-webkit-scrollbar,
|
52
|
-
#chat-page #notifications::-webkit-scrollbar {
|
53
|
-
width: 6px;
|
54
|
-
}
|
55
|
-
#chat-page #messages::-webkit-scrollbar-thumb,
|
56
|
-
#chat-page #roster::-webkit-scrollbar-thumb,
|
57
|
-
#chat-page #notifications::-webkit-scrollbar-thumb {
|
58
|
-
border-radius: 10px;
|
59
|
-
}
|
60
|
-
#chat-page #messages::-webkit-scrollbar-thumb:vertical,
|
61
|
-
#chat-page #roster::-webkit-scrollbar-thumb:vertical,
|
62
|
-
#chat-page #notifications::-webkit-scrollbar-thumb:vertical {
|
63
|
-
background: rgba(0, 0, 0, .2);
|
64
|
-
}
|
65
14
|
#chat-page #messages li {
|
66
15
|
border-bottom: 1px solid #f0f0f0;
|
67
16
|
min-height: 40px;
|
@@ -122,7 +71,6 @@
|
|
122
71
|
#chat-page #notifications {
|
123
72
|
height: 100%;
|
124
73
|
list-style: none;
|
125
|
-
overflow-y: auto;
|
126
74
|
text-shadow: 0 1px 1px #fff;
|
127
75
|
width: 260px;
|
128
76
|
}
|
@@ -143,7 +91,7 @@
|
|
143
91
|
font-weight: normal;
|
144
92
|
padding: 10px 0 0 0;
|
145
93
|
}
|
146
|
-
#chat-page #roster li:hover,
|
94
|
+
#chat-page #roster li:hover:not(.selected),
|
147
95
|
#chat-page #notifications li:hover {
|
148
96
|
background: rgba(255, 255, 255, 1.0);
|
149
97
|
}
|
@@ -153,15 +101,6 @@
|
|
153
101
|
#chat-page #roster li.selected > * {
|
154
102
|
opacity: 1.0;
|
155
103
|
}
|
156
|
-
#chat-page #roster li.selected {
|
157
|
-
background: #4693FF;
|
158
|
-
background: -moz-linear-gradient(#4693FF, #015de6);
|
159
|
-
background: -o-linear-gradient(#4693FF, #015de6);
|
160
|
-
background: -webkit-gradient(linear, left top, left bottom, from(#4693FF), to(#015de6));
|
161
|
-
border-bottom: 1px solid #fff;
|
162
|
-
color: #fff;
|
163
|
-
text-shadow: 0 -1px 1px #1b3a65;
|
164
|
-
}
|
165
104
|
#chat-page #roster li.selected .status-msg {
|
166
105
|
color: rgba(255, 255, 255, 0.85);
|
167
106
|
}
|
@@ -199,92 +138,10 @@
|
|
199
138
|
top: 4px;
|
200
139
|
right: 10px;
|
201
140
|
}
|
202
|
-
#chat-page #
|
203
|
-
#chat-page #notification-controls {
|
204
|
-
background: rgba(255, 255, 255, 0.05);
|
205
|
-
border-top: 1px solid #ddd;
|
206
|
-
height: 50px;
|
207
|
-
position: absolute;
|
208
|
-
bottom: 0;
|
209
|
-
width: 260px;
|
210
|
-
}
|
211
|
-
#chat-page #notification-controls {
|
212
|
-
text-align: right;
|
213
|
-
}
|
214
|
-
#chat-page #roster-controls > div,
|
215
|
-
#chat-page #notification-controls > div {
|
216
|
-
cursor: pointer;
|
217
|
-
display: inline-block;
|
218
|
-
height: 27px;
|
219
|
-
margin: 0 10px;
|
220
|
-
position: relative;
|
221
|
-
top: 10px;
|
222
|
-
width: 27px;
|
223
|
-
}
|
224
|
-
#chat-page #roster-controls > div > svg,
|
225
|
-
#chat-page #notification-controls > div > svg {
|
226
|
-
height: 27px;
|
227
|
-
width: 27px;
|
228
|
-
}
|
229
|
-
#chat-page .contact-form {
|
230
|
-
background: inherit;
|
231
|
-
border-bottom: 1px solid #ddd;
|
232
|
-
border-top: 1px solid #fff;
|
233
|
-
-webkit-box-shadow: 0px -3px 5px rgba(0, 0, 0, 0.1);
|
234
|
-
box-shadow: 0px -3px 5px rgba(0, 0, 0, 0.1);
|
235
|
-
padding-top: 10px;
|
236
|
-
position: absolute;
|
237
|
-
bottom: 50px;
|
238
|
-
left: 0;
|
239
|
-
width: 260px;
|
240
|
-
}
|
241
|
-
#chat-page .contact-form h2,
|
242
|
-
#chat-page .notify-form h2 {
|
243
|
-
border: none !important;
|
244
|
-
line-height: 1;
|
245
|
-
margin-bottom: 10px;
|
246
|
-
}
|
247
|
-
#chat-page .contact-form p,
|
248
|
-
#chat-page .notify-form p {
|
249
|
-
line-height: 1.5;
|
250
|
-
margin: 0 10px 10px 10px;
|
251
|
-
text-shadow: 0 1px 1px #fff;
|
252
|
-
}
|
253
|
-
#chat-page .notify-form p {
|
254
|
-
margin-top: -5px;
|
255
|
-
}
|
256
|
-
#chat-page .contact-form .buttons,
|
257
|
-
#chat-page .notify-form .buttons {
|
258
|
-
padding-right: 10px;
|
141
|
+
#chat-page #charlie-controls {
|
259
142
|
text-align: right;
|
260
143
|
}
|
261
|
-
#chat-page .contact-form input[type="text"],
|
262
|
-
#chat-page .contact-form input[type="email"] {
|
263
|
-
margin-bottom: 10px;
|
264
|
-
width: 228px;
|
265
|
-
position: relative;
|
266
|
-
left: 10px;
|
267
|
-
}
|
268
144
|
#chat-page #edit-contact-jid {
|
269
145
|
color: #444;
|
270
146
|
margin-top: -5px;
|
271
147
|
}
|
272
|
-
#chat-page #search-roster {
|
273
|
-
cursor: pointer;
|
274
|
-
display: inline-block;
|
275
|
-
line-height: 1;
|
276
|
-
position: absolute;
|
277
|
-
right: 10px;
|
278
|
-
top: 6px;
|
279
|
-
}
|
280
|
-
#chat-page #search-roster svg {
|
281
|
-
height: 16px;
|
282
|
-
width: 16px;
|
283
|
-
}
|
284
|
-
#chat-page #search-roster-form {
|
285
|
-
border-bottom: 1px solid #ddd;
|
286
|
-
padding: 5px 10px;
|
287
|
-
}
|
288
|
-
#chat-page #search-roster-text {
|
289
|
-
width: 100%;
|
290
|
-
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Button
|
2
|
+
constructor: (node, path, options) ->
|
3
|
+
@node = $ node
|
4
|
+
@path = path
|
5
|
+
@options = options || {}
|
6
|
+
this.draw()
|
7
|
+
|
8
|
+
draw: ->
|
9
|
+
paper = Raphael @node.get(0)
|
10
|
+
|
11
|
+
icon = paper.path(@path).attr
|
12
|
+
fill: @options.fill || '#000'
|
13
|
+
stroke: @options.stroke || '#fff'
|
14
|
+
'stroke-width': @options['stroke-width'] || 0.3
|
15
|
+
opacity: @options.opacity || 0.6
|
16
|
+
scale: @options.scale || 0.85
|
17
|
+
translation: @options.translation || ''
|
18
|
+
|
19
|
+
@node.hover(
|
20
|
+
-> icon.animate(opacity: 1.0, 200),
|
21
|
+
-> icon.animate(opacity: 0.6, 200))
|