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 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/vines.rb file.
8
+ # conf/config.rb file.
9
9
  class Config
10
10
  LOG_LEVELS = %w[debug info warn error fatal].freeze
11
11
 
@@ -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.request(request)
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
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Vines
4
- VERSION = '0.2.0'
4
+ VERSION = '0.2.1'
5
5
  end
@@ -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="notify-form">
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
- $('.contact-form').each ->
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
- <form id="search-roster-form" style="display:none;">
272
- <input id="search-roster-text" type="search" placeholder="Filter" results="5"/>
273
- </form>
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="contact-form" style="display:none;">
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="contact-form" style="display:none;">
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="contact-form" style="display:none;">
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="notification-controls">
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
- $('#message').focus -> $('.contact-form').fadeOut()
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
@@ -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=\"notify-form\">\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');
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
- $('.contact-form').each(function() {
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 <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');
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
- this.button('clear-notices', ICONS.no);
331
- this.button('add-contact', ICONS.plus);
332
- this.button('remove-contact', ICONS.minus);
333
- this.button('edit-contact', ICONS.user);
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 $('.contact-form').fadeOut();
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
- return this.resize();
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 #roster-controls,
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))