vines 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +5 -0
- data/lib/vines/config.rb +1 -1
- data/lib/vines/stream/http.rb +3 -3
- data/lib/vines/version.rb +1 -1
- data/web/chat/coffeescripts/chat.coffee +34 -61
- data/web/chat/javascripts/app.js +1 -1
- data/web/chat/javascripts/chat.js +22 -74
- data/web/chat/stylesheets/chat.css +2 -145
- data/web/lib/coffeescripts/button.coffee +21 -0
- data/web/lib/coffeescripts/filter.coffee +48 -0
- data/web/lib/coffeescripts/session.coffee +63 -48
- data/web/lib/coffeescripts/transfer.coffee +103 -0
- data/web/lib/javascripts/base.js +2 -1
- data/web/lib/javascripts/button.js +31 -0
- data/web/lib/javascripts/filter.js +86 -0
- data/web/lib/javascripts/session.js +43 -77
- data/web/lib/javascripts/transfer.js +134 -0
- data/web/lib/stylesheets/base.css +129 -5
- metadata +8 -2
@@ -0,0 +1,48 @@
|
|
1
|
+
class Filter
|
2
|
+
constructor: (options) ->
|
3
|
+
@list = options.list
|
4
|
+
@icon = options.icon
|
5
|
+
@form = options.form
|
6
|
+
@attrs = options.attrs
|
7
|
+
@open = options.open
|
8
|
+
@close = options.close
|
9
|
+
this.draw()
|
10
|
+
|
11
|
+
draw: ->
|
12
|
+
$(@icon).addClass 'filter-button'
|
13
|
+
form = $('<form class="filter-form" style="display:none;"></form>').appendTo @form
|
14
|
+
text = $('<input class="filter-text" type="search" placeholder="Filter" results="5"/>').appendTo form
|
15
|
+
|
16
|
+
new Button @icon, ICONS.search,
|
17
|
+
scale: 0.5
|
18
|
+
translation: '-8 -8'
|
19
|
+
|
20
|
+
form.submit -> false
|
21
|
+
text.keyup => this.filter(text)
|
22
|
+
text.change => this.filter(text)
|
23
|
+
text.click => this.filter(text)
|
24
|
+
$(@icon).click =>
|
25
|
+
if form.is ':hidden'
|
26
|
+
this.filter(text)
|
27
|
+
form.show()
|
28
|
+
this.open() if this.open
|
29
|
+
else
|
30
|
+
form.hide()
|
31
|
+
form[0].reset()
|
32
|
+
this.filter(text)
|
33
|
+
this.close() if this.close
|
34
|
+
|
35
|
+
filter: (input) ->
|
36
|
+
text = input.val().toLowerCase()
|
37
|
+
if text == ''
|
38
|
+
$('li', @list).show()
|
39
|
+
return
|
40
|
+
|
41
|
+
test = (node, attr) ->
|
42
|
+
val = (node.attr(attr) || '').toLowerCase()
|
43
|
+
val.indexOf(text) != -1
|
44
|
+
|
45
|
+
$('li', @list).each (ix, node) =>
|
46
|
+
node = $ node
|
47
|
+
matches = (true for attr in @attrs when test node, attr)
|
48
|
+
if matches.length > 0 then node.show() else node.hide()
|
@@ -20,7 +20,7 @@ class Session
|
|
20
20
|
callback true
|
21
21
|
this.findRoster =>
|
22
22
|
this.notify('roster')
|
23
|
-
@xmpp.send $
|
23
|
+
@xmpp.send $('<presence/>').get 0
|
24
24
|
this.findCards()
|
25
25
|
|
26
26
|
disconnect: -> @xmpp.disconnect()
|
@@ -44,6 +44,8 @@ class Session
|
|
44
44
|
|
45
45
|
bareJid: -> @xmpp.jid.split('/')[0]
|
46
46
|
|
47
|
+
uniqueId: -> @xmpp.getUniqueId()
|
48
|
+
|
47
49
|
avatar: (jid) ->
|
48
50
|
card = this.loadCard(jid)
|
49
51
|
if card && card.photo
|
@@ -73,8 +75,12 @@ class Session
|
|
73
75
|
|
74
76
|
findCard: (jid, callback) ->
|
75
77
|
return unless jid
|
76
|
-
|
77
|
-
|
78
|
+
node = this.xml """
|
79
|
+
<iq id="#{this.uniqueId()}" to="#{jid}" type="get">
|
80
|
+
<vCard xmlns="vcard-temp"/>
|
81
|
+
</iq>
|
82
|
+
"""
|
83
|
+
this.sendIQ node, (result) ->
|
78
84
|
card = $('vCard', result)
|
79
85
|
photo = $('PHOTO', card)
|
80
86
|
type = $('TYPE', photo).text()
|
@@ -86,74 +92,81 @@ class Session
|
|
86
92
|
vcard = jid: jid, photo: photo, retrieved: new Date()
|
87
93
|
callback if card.size() > 0 then vcard else null
|
88
94
|
|
89
|
-
iq = $iq(type: 'get', to: jid, id: @xmpp.getUniqueId())
|
90
|
-
.c('vCard', xmlns: 'vcard-temp').up()
|
91
|
-
|
92
|
-
@xmpp.sendIQ iq, handler, handler, 5000
|
93
|
-
|
94
95
|
parseRoster: (node) ->
|
95
96
|
$('item', node).map(-> new Contact this ).get()
|
96
97
|
|
97
98
|
findRoster: (callback) ->
|
98
|
-
|
99
|
+
node = $("""
|
100
|
+
<iq id='#{this.uniqueId()}' type="get">
|
101
|
+
<query xmlns="jabber:iq:roster"/>
|
102
|
+
</iq>
|
103
|
+
""")
|
104
|
+
this.sendIQ node.get(0), (result) =>
|
99
105
|
contacts = this.parseRoster(result)
|
100
106
|
@roster[contact.jid] = contact for contact in contacts
|
101
107
|
callback()
|
102
108
|
|
103
|
-
iq = $iq(type: 'get', id: @xmpp.getUniqueId())
|
104
|
-
.c('query', xmlns: 'jabber:iq:roster').up()
|
105
|
-
|
106
|
-
@xmpp.sendIQ iq, handler, handler, 5000
|
107
|
-
|
108
109
|
sendMessage: (jid, message) ->
|
109
|
-
|
110
|
-
|
111
|
-
|
110
|
+
node = this.xml """
|
111
|
+
<message id="#{this.uniqueId()}" to="#{jid}" type="chat">
|
112
|
+
<body></body>
|
113
|
+
</message>
|
114
|
+
"""
|
115
|
+
$('body', node).text message
|
116
|
+
@xmpp.send node
|
112
117
|
|
113
118
|
sendPresence: (away, status) ->
|
114
|
-
|
119
|
+
node = $ '<presence/>'
|
115
120
|
if away
|
116
|
-
|
117
|
-
|
121
|
+
node.append $('<show>xa</show>')
|
122
|
+
node.append $('<status/>').text status if status != 'Away'
|
118
123
|
else
|
119
|
-
|
120
|
-
@xmpp.send
|
124
|
+
node.append $('<status/>').text status if status != 'Available'
|
125
|
+
@xmpp.send node.get 0
|
126
|
+
|
127
|
+
sendIQ: (node, callback) ->
|
128
|
+
@xmpp.sendIQ node, callback, callback, 5000
|
121
129
|
|
122
130
|
updateContact: (contact, add) ->
|
123
|
-
|
124
|
-
.
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
131
|
+
node = $("""
|
132
|
+
<iq id="#{this.uniqueId()}" type="set">
|
133
|
+
<query xmlns="jabber:iq:roster">
|
134
|
+
<item name="" jid="#{contact.jid}"/>
|
135
|
+
</query>
|
136
|
+
</iq>
|
137
|
+
""")
|
138
|
+
$('item', node).attr 'name', contact.name
|
139
|
+
for group in contact.groups
|
140
|
+
$('item', node).append $('<group></group>').text group
|
141
|
+
@xmpp.send node.get 0
|
142
|
+
this.sendSubscribe(contact.jid) if add
|
129
143
|
|
130
144
|
removeContact: (jid) ->
|
131
|
-
|
132
|
-
.
|
133
|
-
|
134
|
-
|
135
|
-
|
145
|
+
node = $("""
|
146
|
+
<iq id="#{this.uniqueId()}" type="set">
|
147
|
+
<query xmlns="jabber:iq:roster">
|
148
|
+
<item jid="#{jid}" subscription="remove"/>
|
149
|
+
</query>
|
150
|
+
</iq>
|
151
|
+
""")
|
152
|
+
@xmpp.send node.get 0
|
136
153
|
|
137
154
|
sendSubscribe: (jid) ->
|
138
|
-
@xmpp.send
|
139
|
-
type: 'subscribe'
|
140
|
-
to: jid
|
141
|
-
id: @xmpp.getUniqueId()
|
142
|
-
).tree()
|
155
|
+
@xmpp.send this.presence jid, 'subscribe'
|
143
156
|
|
144
157
|
sendSubscribed: (jid) ->
|
145
|
-
@xmpp.send
|
146
|
-
type: 'subscribed'
|
147
|
-
to: jid
|
148
|
-
id: @xmpp.getUniqueId()
|
149
|
-
).tree()
|
158
|
+
@xmpp.send this.presence jid, 'subscribed'
|
150
159
|
|
151
160
|
sendUnsubscribed: (jid) ->
|
152
|
-
@xmpp.send
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
161
|
+
@xmpp.send this.presence jid, 'unsubscribed'
|
162
|
+
|
163
|
+
presence: (to, type) ->
|
164
|
+
$("""
|
165
|
+
<presence
|
166
|
+
id="#{this.uniqueId()}"
|
167
|
+
to="#{to}"
|
168
|
+
type="#{type}"/>
|
169
|
+
""").get 0
|
157
170
|
|
158
171
|
handleIq: (node) ->
|
159
172
|
node = $(node)
|
@@ -209,3 +222,5 @@ class Session
|
|
209
222
|
|
210
223
|
notify: (type, obj) ->
|
211
224
|
callback(obj) for callback in (@listeners[type] || [])
|
225
|
+
|
226
|
+
xml: (xml) -> $.parseXML(xml).documentElement
|
@@ -0,0 +1,103 @@
|
|
1
|
+
class Transfer
|
2
|
+
constructor: (options) ->
|
3
|
+
@session = options.session
|
4
|
+
@file = options.file
|
5
|
+
@to = options.to
|
6
|
+
@progress = options.progress
|
7
|
+
@complete = options.complete
|
8
|
+
@chunks = new Chunks(@file)
|
9
|
+
@opened = false
|
10
|
+
@closed = false
|
11
|
+
@sid = @session.uniqueId()
|
12
|
+
@seq = 0
|
13
|
+
@sent = 0
|
14
|
+
|
15
|
+
start: ->
|
16
|
+
node = $("""
|
17
|
+
<iq id="#{@session.uniqueId()}" to="#{@to}" type="set">
|
18
|
+
<si xmlns="http://jabber.org/protocol/si" id="#{@session.uniqueId()}" profile="http://jabber.org/protocol/si/profile/file-transfer">
|
19
|
+
<file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="" size="#{@file.size}"/>
|
20
|
+
<feature xmlns="http://jabber.org/protocol/feature-neg">
|
21
|
+
<x xmlns="jabber:x:data" type="form">
|
22
|
+
<field var="stream-method" type="list-single">
|
23
|
+
<option><value>http://jabber.org/protocol/ibb</value></option>
|
24
|
+
</field>
|
25
|
+
</x>
|
26
|
+
</feature>
|
27
|
+
</si>
|
28
|
+
</iq>
|
29
|
+
""")
|
30
|
+
$('file', node).attr 'name', @file.name
|
31
|
+
|
32
|
+
callback = (result) =>
|
33
|
+
methods = $('si feature x field[var="stream-method"] value', result)
|
34
|
+
ok = (true for m in methods when $(m).text() == 'http://jabber.org/protocol/ibb').length > 0
|
35
|
+
this.open() if ok
|
36
|
+
|
37
|
+
@session.sendIQ node.get(0), callback
|
38
|
+
|
39
|
+
open: ->
|
40
|
+
node = $("""
|
41
|
+
<iq id="#{@session.uniqueId()}" to="#{@to}" type="set">
|
42
|
+
<open xmlns="http://jabber.org/protocol/ibb" sid="#{@sid}" block-size="4096"/>
|
43
|
+
</iq>
|
44
|
+
""")
|
45
|
+
callback = (result) =>
|
46
|
+
if this.ok result
|
47
|
+
@opened = true
|
48
|
+
@chunks.start => this.sendChunk()
|
49
|
+
@session.sendIQ node.get(0), callback
|
50
|
+
|
51
|
+
sendChunk: ->
|
52
|
+
return if @closed
|
53
|
+
unless chunk = @chunks.chunk()
|
54
|
+
this.close()
|
55
|
+
return
|
56
|
+
|
57
|
+
node = $("""
|
58
|
+
<iq id="#{@session.uniqueId()}" to="#{@to}" type="set">
|
59
|
+
<data xmlns="http://jabber.org/protocol/ibb" sid="#{@sid}" seq="#{@seq++}">#{chunk}</data>
|
60
|
+
</iq>
|
61
|
+
""")
|
62
|
+
@seq = 0 if @seq > 65535
|
63
|
+
callback = (result) =>
|
64
|
+
return unless this.ok result
|
65
|
+
pct = Math.ceil ++@sent / @chunks.total * 100
|
66
|
+
this.progress pct
|
67
|
+
this.sendChunk()
|
68
|
+
@session.sendIQ node.get(0), callback
|
69
|
+
|
70
|
+
close: ->
|
71
|
+
return if @closed
|
72
|
+
@closed = true
|
73
|
+
node = $("""
|
74
|
+
<iq id="#{@session.uniqueId()}" to="#{@to}" type="set">
|
75
|
+
<close xmlns="http://jabber.org/protocol/ibb" sid="#{@sid}"/>
|
76
|
+
</iq>
|
77
|
+
""")
|
78
|
+
@session.sendIQ node.get(0), ->
|
79
|
+
this.complete()
|
80
|
+
|
81
|
+
stop: ->
|
82
|
+
if @opened
|
83
|
+
this.close()
|
84
|
+
else
|
85
|
+
this.complete()
|
86
|
+
|
87
|
+
ok: (result) -> $(result).attr('type') == 'result'
|
88
|
+
|
89
|
+
class Chunks
|
90
|
+
constructor: (@file) ->
|
91
|
+
@chunks = []
|
92
|
+
@total = 0
|
93
|
+
|
94
|
+
chunk: -> @chunks.shift()
|
95
|
+
|
96
|
+
start: (callback) ->
|
97
|
+
reader = new FileReader()
|
98
|
+
reader.onload = (event) =>
|
99
|
+
data = btoa event.target.result
|
100
|
+
@chunks = (chunk for chunk in data.split /(.{1,4096})/ when chunk)
|
101
|
+
@total = @chunks.length
|
102
|
+
callback()
|
103
|
+
reader.readAsBinaryString @file
|