vines-services 0.1.0 → 0.1.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.
@@ -1,12 +1,12 @@
1
1
  class SystemsPage
2
2
  constructor: (@session) ->
3
3
  @session.onRoster ( ) => this.roster()
4
- @session.onCard (c) => this.card(c)
5
4
  @session.onMessage (m) => this.message(m)
6
5
  @session.onPresence (p) => this.presence(p)
7
6
  @commands = new Commands
8
7
  @chats = {}
9
8
  @currentContact = null
9
+ @layout = null
10
10
 
11
11
  datef: (millis) ->
12
12
  d = new Date(millis)
@@ -17,51 +17,50 @@ class SystemsPage
17
17
  minutes = '0' + minutes if minutes.length == 1
18
18
  hour + ':' + minutes + meridian
19
19
 
20
- card: (card) ->
21
- this.eachContact card.jid, (node) =>
22
- $('.vcard-img', node).attr 'src', @session.avatar card.jid
20
+ groupContacts: ->
21
+ groups = {}
22
+ for jid, contact of @session.roster
23
+ for group in contact.groups
24
+ (groups[group] ||= []).push contact
25
+ groups
23
26
 
24
27
  roster: ->
25
- roster = $('#roster')
26
-
27
- $('li', roster).each (ix, node) =>
28
- jid = $(node).attr('data-jid')
29
- $(node).remove() unless @session.roster[jid]
30
-
31
- setName = (node, contact) ->
32
- $('.text', node).text contact.name || contact.jid
33
- node.attr 'data-name', contact.name || ''
34
- node.attr 'data-groups', contact.groups || ''
35
-
36
- for jid, contact of @session.roster
37
- found = $("#roster li[data-jid='#{jid}']")
38
- setName(found, contact)
39
- if found.length == 0
40
- if contact.groups[0]=="Vines"
41
- img_src = "/lib/images/default-service.png"
42
- else
43
- img_src = "#{@session.avatar jid}"
44
- node = $("""
45
- <li data-jid="#{jid}" data-name="" data-group="" class="offline">
28
+ groups = this.groupContacts()
29
+ sorted = (group for group, contacts of groups)
30
+ sorted = sorted.sort (a, b) ->
31
+ a = a.toLowerCase()
32
+ b = b.toLowerCase()
33
+ if a > b then 1 else if a < b then -1 else 0
34
+
35
+ items = $('#roster-items').empty()
36
+ for group in sorted
37
+ contacts = groups[group]
38
+ optgroup = $('<li class="group"></li>').appendTo items
39
+ optgroup.text group
40
+ optgroup.attr 'data-group', group
41
+ for contact in contacts
42
+ option = $("""
43
+ <li data-jid="#{contact.jid}">
46
44
  <span class="text"></span>
47
- <span class="status-msg">Offline</span>
48
45
  <span class="unread" style="display:none;"></span>
49
- <img class="vcard-img" id="#{jid}-avatar" alt="#{jid}" src="#{img_src}"/>
50
46
  </li>
51
- """).appendTo roster
52
- setName(node, contact)
53
- node.click (event) => this.selectContact(event)
47
+ """).appendTo items
48
+ option.addClass 'offline' if contact.offline()
49
+ option.click (event) => this.selectContact event
50
+ name = contact.name || contact.jid.split('@')[0]
51
+ option.attr 'data-name', name
52
+ option.attr 'data-group', group
53
+ $('.text', option).text name
54
54
 
55
55
  message: (message) ->
56
56
  this.queueMessage message
57
- me = message.from == @session.jid()
58
- from = message.from.split('/')[0]
59
- thread = message.thread
57
+ me = message.from == @session.jid()
58
+ from = message.from.split('/')[0]
60
59
 
61
- if me || from || thread == @currentContact
60
+ if me || from == @currentContact
62
61
  bottom = this.atBottom()
63
62
  this.appendMessage message
64
- this.scroll() if bottom
63
+ this.scroll({animate: true}) if bottom
65
64
  else
66
65
  chat = this.chat message.from
67
66
  chat.unread++
@@ -69,28 +68,26 @@ class SystemsPage
69
68
  $('.unread', node).text(chat.unread).show()
70
69
 
71
70
  eachContact: (jid, callback) ->
72
- for node in $("#roster li[data-jid='#{jid}']").get()
71
+ for node in $("#roster-items li[data-jid='#{jid}']").get()
73
72
  callback $(node)
74
73
 
75
74
  appendMessage: (message) ->
76
- from = message.from.split('/')[0]
75
+ me = message.from == @session.jid()
76
+ proxied = $('jid', message.node).text()
77
+ from = (proxied || message.from).split('/')[0]
77
78
  contact = @session.roster[from]
78
79
  name = if contact then (contact.name || from) else from
79
- name = 'Me' if message.from == @session.jid()
80
- node = $("""
81
- <li data-jid="#{from}" style="display:none;">
82
- <p><pre></pre></p>
83
- <img alt="#{from}" src="#{@session.avatar from}"/>
80
+ node = $("""<li data-jid="#{from}"><pre></pre></li>""").appendTo '#messages'
81
+ prefix = if me then '$ ' else ''
82
+ $('pre', node).text prefix + message.text
83
+ unless me
84
+ node.append("""
84
85
  <footer>
85
- <span class="author"></span>
86
+ <span class="author"></span> @
86
87
  <span class="time">#{this.datef message.received}</span>
87
88
  </footer>
88
- </li>
89
- """).appendTo '#messages'
90
-
91
- $('pre', node).text message.text
92
- $('.author', node).text name
93
- node.fadeIn 200
89
+ """)
90
+ $('.author', node).text name
94
91
 
95
92
  queueMessage: (message) ->
96
93
  me = message.from == @session.jid()
@@ -113,7 +110,6 @@ class SystemsPage
113
110
  if !presence.type || presence.offline
114
111
  contact = @session.roster[from]
115
112
  this.eachContact from, (node) ->
116
- $('.status-msg', node).text contact.status()
117
113
  if contact.offline()
118
114
  node.addClass 'offline'
119
115
  else
@@ -122,44 +118,23 @@ class SystemsPage
122
118
  if presence.offline
123
119
  this.chat(from).jid = from
124
120
 
125
- if presence.type == 'subscribe'
126
- node = $("""
127
- <li data-jid="#{presence.from}" style="display:none;">
128
- <form class="inset">
129
- <h2>Buddy Approval</h2>
130
- <p>#{presence.from} wants to add you as a buddy.</p>
131
- <fieldset class="buttons">
132
- <input type="button" value="Decline"/>
133
- <input type="submit" value="Accept"/>
134
- </fieldset>
135
- </form>
136
- </li>
137
- """).appendTo '#notifications'
138
- node.fadeIn 200
139
- $('form', node).submit => this.acceptContact node, presence.from
140
- $('input[type="button"]', node).click => this.rejectContact node, presence.from
141
-
142
- acceptContact: (node, jid) ->
143
- node.fadeOut 200, -> node.remove()
144
- @session.sendSubscribed jid
145
- @session.sendSubscribe jid
146
- false
147
-
148
- rejectContact: (node, jid) ->
149
- node.fadeOut 200, -> node.remove()
150
- @session.sendUnsubscribed jid
151
-
152
121
  selectContact: (event) ->
153
- jid = $(event.currentTarget).attr 'data-jid'
122
+ $('#blank-slate').fadeOut(200, -> $(this).remove())
123
+ $('#roster').hide()
124
+
125
+ selected = $(event.currentTarget)
126
+ jid = selected.attr 'data-jid'
154
127
  contact = @session.roster[jid]
155
128
  return if @currentContact == jid
156
129
  @currentContact = jid
157
130
 
158
- $('#roster li').removeClass 'selected'
159
- $(event.currentTarget).addClass 'selected'
160
- $('#chat-title').text('Chat with ' + (contact.name || contact.jid))
131
+ $('#message-label').text $('.text', selected).text()
161
132
  $('#messages').empty()
133
+ $('#message').focus()
134
+ @layout.resize()
135
+ this.restoreChat(jid)
162
136
 
137
+ restoreChat: (jid) ->
163
138
  chat = @chats[jid]
164
139
  messages = []
165
140
  if chat
@@ -167,27 +142,21 @@ class SystemsPage
167
142
  chat.unread = 0
168
143
  this.eachContact jid, (node) ->
169
144
  $('.unread', node).text('').hide()
170
-
171
145
  this.appendMessage msg for msg in messages
172
146
  this.scroll()
173
147
 
174
- $('#remove-contact-msg').html "Are you sure you want to remove " +
175
- "<strong>#{@currentContact}</strong> from your buddy list?"
176
- $('#remove-contact-form .buttons').fadeIn 200
177
-
178
- $('#edit-contact-jid').text @currentContact
179
- $('#edit-contact-name').val @session.roster[@currentContact].name
180
- $('#edit-contact-form input').fadeIn 200
181
- $('#edit-contact-form .buttons').fadeIn 200
182
-
183
- scroll: ->
148
+ scroll: (opts) ->
149
+ opts ||= {}
184
150
  msgs = $ '#messages'
185
- msgs.animate(scrollTop: msgs.prop('scrollHeight'), 400)
151
+ if opts.animate
152
+ msgs.animate(scrollTop: msgs.prop('scrollHeight'), 400)
153
+ else
154
+ msgs.scrollTop msgs.prop('scrollHeight')
186
155
 
187
156
  atBottom: ->
188
157
  msgs = $('#messages')
189
- bottom = msgs.prop('scrollHeight') - msgs.height()
190
- msgs.scrollTop() == bottom
158
+ bottom = msgs.prop('scrollHeight') - msgs.outerHeight()
159
+ msgs.scrollTop() >= bottom
191
160
 
192
161
  send: ->
193
162
  return false unless @currentContact
@@ -206,166 +175,84 @@ class SystemsPage
206
175
  input.val ''
207
176
  false
208
177
 
209
- addContact: ->
210
- this.toggleForm '#add-contact-form'
211
- contact =
212
- jid: $('#add-contact-jid').val()
213
- name: $('#add-contact-name').val()
214
- groups: ['Buddies']
215
- @session.updateContact contact, true if contact.jid
216
- false
217
-
218
- removeContact: ->
219
- this.toggleForm '#remove-contact-form'
220
- @session.removeContact @currentContact
221
- @currentContact = null
222
-
223
- $('#chat-title').text 'Select a buddy to chat'
224
- $('#messages').empty()
225
-
226
- $('#remove-contact-msg').html "Select a buddy in the list above to remove."
227
- $('#remove-contact-form .buttons').hide()
228
-
229
- $('#edit-contact-jid').text "Select a buddy in the list above to update."
230
- $('#edit-contact-name').val ''
231
- $('#edit-contact-form input').hide()
232
- $('#edit-contact-form .buttons').hide()
233
- false
234
-
235
- updateContact: ->
236
- this.toggleForm '#edit-contact-form'
237
- contact =
238
- jid: @currentContact
239
- name: $('#edit-contact-name').val()
240
- groups: @session.roster[@currentContact].groups
241
- @session.updateContact contact
242
- false
243
-
244
- toggleForm: (form, fn) ->
245
- form = $(form)
246
- $('form.overlay').each ->
247
- $(this).hide() unless this.id == form.attr 'id'
248
- if form.is ':hidden'
249
- fn() if fn
250
- form.fadeIn 100
251
- else
252
- form.fadeOut 100, ->
253
- form[0].reset()
254
- fn() if fn
178
+ drawBlankSlate: ->
179
+ $("""
180
+ <form id="blank-slate" class="float">
181
+ <p>
182
+ Services, and individual systems, can be controlled by sending
183
+ them shell commands through this terminal. Select a system to chat with
184
+ to get started.
185
+ </p>
186
+ <input type="submit" value="Select System"/>
187
+ </form>
188
+ """).appendTo '#alpha'
189
+ $('#blank-slate').submit =>
190
+ $('#roster').show()
191
+ @layout.resize()
192
+ false
255
193
 
256
194
  draw: ->
257
195
  unless @session.connected()
258
196
  window.location.hash = ''
259
197
  return
260
198
 
261
- $('body').attr 'id', 'servers-page'
199
+ $('body').attr 'id', 'systems-page'
262
200
  $('#container').hide().empty()
263
201
  $("""
264
- <div id="alpha" class="sidebar column y-fill">
265
- <h2>Buddies <div id="search-roster-icon"></div></h2>
266
- <div id="search-roster-form"></div>
267
- <ul id="roster" class="selectable scroll y-fill"></ul>
268
- <div id="alpha-controls" class="controls">
269
- <div id="add-contact"></div>
270
- <div id="remove-contact"></div>
271
- <div id="edit-contact"></div>
272
- </div>
273
- <form id="add-contact-form" class="overlay" style="display:none;">
274
- <h2>Add Buddy</h2>
275
- <input id="add-contact-jid" type="email" maxlength="1024" placeholder="Account name"/>
276
- <input id="add-contact-name" type="text" maxlength="1024" placeholder="Real name"/>
277
- <fieldset class="buttons">
278
- <input id="add-contact-cancel" type="button" value="Cancel"/>
279
- <input id="add-contact-ok" type="submit" value="Add"/>
280
- </fieldset>
281
- </form>
282
- <form id="remove-contact-form" class="overlay" style="display:none;">
283
- <h2>Remove Buddy</h2>
284
- <p id="remove-contact-msg">Select a buddy in the list above to remove.</p>
285
- <fieldset class="buttons" style="display:none;">
286
- <input id="remove-contact-cancel" type="button" value="Cancel"/>
287
- <input id="remove-contact-ok" type="submit" value="Remove"/>
288
- </fieldset>
289
- </form>
290
- <form id="edit-contact-form" class="overlay" style="display:none;">
291
- <h2>Update Profile</h2>
292
- <p id="edit-contact-jid">Select a buddy in the list above to update.</p>
293
- <input id="edit-contact-name" type="text" maxlength="1024" placeholder="Real name" style="display:none;"/>
294
- <fieldset class="buttons" style="display:none;">
295
- <input id="edit-contact-cancel" type="button" value="Cancel"/>
296
- <input id="edit-contact-ok" type="submit" value="Save"/>
297
- </fieldset>
298
- </form>
299
- </div>
300
- <div id="beta" class="primary column x-fill y-fill">
301
- <h2 id="chat-title">Select a buddy or service to start communicating</h2>
202
+ <div id="alpha" class="primary column x-fill y-fill">
302
203
  <ul id="messages" class="scroll y-fill"></ul>
303
204
  <form id="message-form">
304
- <input id="message" name="message" type="text" maxlength="1024" placeholder="Type a message and press enter to send"/>
205
+ <label id="message-label"></label>
206
+ <input id="message" name="message" type="text" maxlength="1024" placeholder="Type a command and press enter to send"/>
305
207
  </form>
306
- </div>
307
- <div id="charlie" class="sidebar column y-fill">
308
- <h2>Notifications</h2>
309
- <ul id="notifications" class="scroll y-fill"></ul>
310
- <div id="charlie-controls" class="controls">
311
- <div id="clear-notices"></div>
208
+ <div id="roster" class="float" style="display:none;">
209
+ <ul id="roster-items"></ul>
210
+ <div id="roster-form"></div>
312
211
  </div>
313
212
  </div>
314
213
  """).appendTo '#container'
214
+ $('#message-form').submit => this.send()
215
+ $('#messages').click -> $('#roster').hide()
216
+ $('#message').focus -> $('#roster').hide()
315
217
 
316
218
  this.roster()
219
+ $('#message-label').click =>
220
+ $('#roster').toggle()
317
221
 
318
- new Button '#clear-notices', ICONS.no
319
- new Button '#add-contact', ICONS.plus
320
- new Button '#remove-contact', ICONS.minus
321
- new Button '#edit-contact', ICONS.user
322
-
323
- $('#message').focus -> $('form.overlay').fadeOut()
324
222
  $('#message').keyup (e) =>
325
223
  switch e.keyCode # up, down keys trigger history
326
224
  when 38 then $('#message').val @commands.prev()
327
225
  when 40 then $('#message').val @commands.next()
328
226
 
329
- $('#message-form').submit => this.send()
330
-
331
- $('#clear-notices').click -> $('#notifications li').fadeOut 200
332
-
333
- $('#add-contact').click => this.toggleForm '#add-contact-form'
334
- $('#remove-contact').click => this.toggleForm '#remove-contact-form'
335
- $('#edit-contact').click => this.toggleForm '#edit-contact-form', =>
336
- if @currentContact
337
- $('#edit-contact-jid').text @currentContact
338
- $('#edit-contact-name').val @session.roster[@currentContact].name
339
-
340
- $('#add-contact-cancel').click => this.toggleForm '#add-contact-form'
341
- $('#remove-contact-cancel').click => this.toggleForm '#remove-contact-form'
342
- $('#edit-contact-cancel').click => this.toggleForm '#edit-contact-form'
343
-
344
- $('#add-contact-form').submit => this.addContact()
345
- $('#remove-contact-form').submit => this.removeContact()
346
- $('#edit-contact-form').submit => this.updateContact()
227
+ if @currentContact
228
+ this.restoreChat(@currentContact) if @currentContact
229
+ contact = @session.roster[@currentContact]
230
+ name = contact.name || contact.jid.split('@')[0]
231
+ $('#message-label').text name
232
+ $('#message').focus()
233
+ else
234
+ this.drawBlankSlate()
347
235
 
348
236
  $('#container').show()
349
- layout = this.resize()
350
-
351
- fn = ->
352
- layout.resize()
353
- layout.resize() # not sure why two are needed
237
+ @layout = this.resize()
238
+ this.scroll()
354
239
 
355
240
  new Filter
356
- list: '#roster'
357
- icon: '#search-roster-icon'
358
- form: '#search-roster-form'
241
+ list: '#roster-items'
242
+ form: '#roster-form'
359
243
  attrs: ['data-jid', 'data-name']
360
- open: fn
361
- close: fn
244
+ $('form', '#roster-form').show()
362
245
 
363
246
  resize: ->
364
- a = $ '#alpha'
365
- b = $ '#beta'
366
- c = $ '#charlie'
367
- msg = $ '#message'
368
- form = $ '#message-form'
247
+ container = $ '#container'
248
+ roster = $ '#roster'
249
+ items = $ '#roster-items'
250
+ rform = $ '#roster-form'
251
+ msg = $ '#message'
252
+ form = $ '#message-form'
253
+ label = $ '#message-label'
369
254
  new Layout ->
370
- c.css 'left', a.width() + b.width()
371
- msg.width form.width() - 32
255
+ msg.width form.width() - label.width() - 32
256
+ height = container.height() - form.height() - 10
257
+ roster.css 'max-height', height
258
+ items.css 'max-height', height - rform.outerHeight() - 10
@@ -0,0 +1,69 @@
1
+ var Api;
2
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
3
+ Api = (function() {
4
+ var USERS;
5
+ USERS = 'http://getvines.com/protocol/users';
6
+ function Api(session) {
7
+ this.session = session;
8
+ this.user = null;
9
+ this.session.onRoster(__bind(function() {
10
+ return this.get(USERS, {
11
+ jid: this.session.bareJid()
12
+ }, __bind(function(result) {
13
+ return this.user = result;
14
+ }, this));
15
+ }, this));
16
+ }
17
+ Api.prototype.jid = function() {
18
+ return "vines." + (this.session.bareJid().split('@')[1]);
19
+ };
20
+ Api.prototype.get = function(ns, criteria, callback) {
21
+ var key, node, value;
22
+ node = this.session.xml("<iq id=\"" + (this.session.uniqueId()) + "\" to=\"" + (this.jid()) + "\" type=\"get\">\n <query xmlns=\"" + ns + "\"/>\n</iq>");
23
+ for (key in criteria) {
24
+ value = criteria[key];
25
+ $('query', node).attr(key, value);
26
+ }
27
+ return this.session.sendIQ(node, __bind(function(result) {
28
+ var ok;
29
+ ok = $(result).attr('type') === 'result';
30
+ if (!ok) {
31
+ return;
32
+ }
33
+ return callback(JSON.parse($('query', result).text()));
34
+ }, this));
35
+ };
36
+ Api.prototype.get2 = function(ns, body, callback) {
37
+ var node;
38
+ node = this.session.xml("<iq id=\"" + (this.session.uniqueId()) + "\" to=\"" + (this.jid()) + "\" type=\"get\">\n <query xmlns=\"" + ns + "\"/>\n</iq>");
39
+ $('query', node).text(body);
40
+ return this.session.sendIQ(node, __bind(function(result) {
41
+ var ok;
42
+ ok = $(result).attr('type') === 'result';
43
+ if (!ok) {
44
+ return;
45
+ }
46
+ return callback(JSON.parse($('query', result).text()));
47
+ }, this));
48
+ };
49
+ Api.prototype.remove = function(ns, id, callback) {
50
+ var node;
51
+ node = this.session.xml("<iq id=\"" + (this.session.uniqueId()) + "\" to=\"" + (this.jid()) + "\" type=\"set\">\n <query xmlns=\"" + ns + "\" action=\"delete\" id=\"\"/>\n</iq>");
52
+ $('query', node).attr('id', id);
53
+ return this.session.sendIQ(node, callback);
54
+ };
55
+ Api.prototype.save = function(ns, obj, callback) {
56
+ var node;
57
+ node = this.session.xml("<iq id=\"" + (this.session.uniqueId()) + "\" to=\"" + (this.jid()) + "\" type=\"set\">\n <query xmlns=\"" + ns + "\"/>\n</iq>");
58
+ $('query', node).text(JSON.stringify(obj));
59
+ return this.session.sendIQ(node, __bind(function(result) {
60
+ var ok;
61
+ ok = $(result).attr('type') === 'result';
62
+ if (!ok) {
63
+ return;
64
+ }
65
+ return callback(JSON.parse($('query', result).text()));
66
+ }, this));
67
+ };
68
+ return Api;
69
+ })();