vines 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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))