vines 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/Rakefile +10 -10
- data/conf/certs/ca-bundle.crt +112 -378
- data/conf/config.rb +18 -9
- data/lib/vines.rb +8 -1
- data/lib/vines/command/cert.rb +2 -1
- data/lib/vines/command/init.rb +11 -0
- data/lib/vines/command/ldap.rb +6 -3
- data/lib/vines/command/schema.rb +1 -1
- data/lib/vines/config.rb +57 -146
- data/lib/vines/config/host.rb +85 -0
- data/lib/vines/config/port.rb +111 -0
- data/lib/vines/contact.rb +1 -1
- data/lib/vines/jid.rb +26 -4
- data/lib/vines/kit.rb +6 -0
- data/lib/vines/log.rb +24 -0
- data/lib/vines/router.rb +70 -38
- data/lib/vines/stanza.rb +45 -8
- data/lib/vines/stanza/iq.rb +3 -3
- data/lib/vines/stanza/iq/disco_info.rb +5 -1
- data/lib/vines/stanza/iq/disco_items.rb +3 -0
- data/lib/vines/stanza/iq/private_storage.rb +9 -5
- data/lib/vines/stanza/iq/roster.rb +11 -12
- data/lib/vines/stanza/iq/vcard.rb +4 -4
- data/lib/vines/stanza/iq/version.rb +25 -0
- data/lib/vines/stanza/message.rb +4 -5
- data/lib/vines/stanza/presence.rb +20 -18
- data/lib/vines/stanza/presence/probe.rb +3 -4
- data/lib/vines/stanza/presence/subscribe.rb +4 -3
- data/lib/vines/stanza/presence/subscribed.rb +6 -5
- data/lib/vines/stanza/presence/unsubscribe.rb +4 -4
- data/lib/vines/stanza/presence/unsubscribed.rb +4 -3
- data/lib/vines/storage/couchdb.rb +3 -3
- data/lib/vines/storage/ldap.rb +19 -8
- data/lib/vines/storage/local.rb +23 -12
- data/lib/vines/storage/redis.rb +3 -3
- data/lib/vines/storage/sql.rb +5 -5
- data/lib/vines/stream.rb +40 -6
- data/lib/vines/stream/client.rb +5 -6
- data/lib/vines/stream/client/auth.rb +3 -2
- data/lib/vines/stream/client/bind.rb +2 -2
- data/lib/vines/stream/client/bind_restart.rb +1 -2
- data/lib/vines/stream/client/ready.rb +2 -0
- data/lib/vines/stream/client/session.rb +13 -4
- data/lib/vines/stream/client/tls.rb +1 -0
- data/lib/vines/stream/component.rb +6 -5
- data/lib/vines/stream/component/ready.rb +5 -6
- data/lib/vines/stream/http.rb +10 -4
- data/lib/vines/stream/http/request.rb +23 -2
- data/lib/vines/stream/server.rb +13 -11
- data/lib/vines/stream/server/outbound/auth_result.rb +1 -0
- data/lib/vines/stream/server/outbound/tls_result.rb +1 -0
- data/lib/vines/stream/server/ready.rb +2 -2
- data/lib/vines/user.rb +2 -1
- data/lib/vines/version.rb +1 -1
- data/test/config/host_test.rb +292 -0
- data/test/config_test.rb +244 -103
- data/test/contact_test.rb +7 -1
- data/test/jid_test.rb +48 -0
- data/test/router_test.rb +16 -47
- data/test/stanza/iq/disco_info_test.rb +76 -0
- data/test/stanza/iq/disco_items_test.rb +47 -0
- data/test/stanza/iq/private_storage_test.rb +33 -10
- data/test/stanza/iq/roster_test.rb +15 -5
- data/test/stanza/iq/vcard_test.rb +8 -25
- data/test/stanza/iq/version_test.rb +62 -0
- data/test/stanza/iq_test.rb +13 -10
- data/test/stanza/message_test.rb +16 -24
- data/test/stanza/presence/probe_test.rb +52 -0
- data/test/stanza/presence/subscribe_test.rb +1 -5
- data/test/stanza_test.rb +77 -0
- data/test/stream/client/auth_test.rb +1 -0
- data/test/stream/client/ready_test.rb +2 -0
- data/test/stream/client/session_test.rb +7 -2
- data/test/stream/component/ready_test.rb +19 -36
- data/test/stream/http/request_test.rb +22 -2
- data/test/stream/server/ready_test.rb +14 -21
- data/web/404.html +9 -3
- data/web/chat/index.html +2 -2
- data/web/chat/javascripts/app.js +1 -1
- data/web/chat/stylesheets/chat.css +4 -9
- data/web/lib/coffeescripts/layout.coffee +2 -2
- data/web/{chat → lib}/coffeescripts/logout.coffee +0 -0
- data/web/lib/coffeescripts/notification.coffee +14 -0
- data/web/lib/coffeescripts/session.coffee +28 -24
- data/web/lib/coffeescripts/transfer.coffee +37 -34
- data/web/lib/javascripts/base.js +8 -8
- data/web/lib/javascripts/icons.js +3 -0
- data/web/lib/javascripts/jquery.js +4 -18
- data/web/lib/javascripts/layout.js +2 -2
- data/web/{chat → lib}/javascripts/logout.js +0 -0
- data/web/lib/javascripts/notification.js +26 -0
- data/web/lib/javascripts/session.js +20 -16
- data/web/lib/javascripts/transfer.js +45 -55
- data/web/lib/stylesheets/base.css +45 -9
- metadata +31 -15
@@ -28,14 +28,11 @@ class SubscribeTest < MiniTest::Unit::TestCase
|
|
28
28
|
@nodes << node
|
29
29
|
end
|
30
30
|
|
31
|
-
router = MiniTest::Mock.new
|
32
|
-
router.expect(:interested_resources, [recipient], [alice])
|
33
|
-
|
34
31
|
stream = MiniTest::Mock.new
|
35
32
|
stream.expect(:domain, 'wonderland.lit')
|
36
33
|
stream.expect(:storage, storage, ['wonderland.lit'])
|
37
34
|
stream.expect(:user, user)
|
38
|
-
stream.expect(:
|
35
|
+
stream.expect(:interested_resources, [recipient], [alice])
|
39
36
|
stream.expect(:update_user_streams, nil, [user])
|
40
37
|
def stream.nodes; @nodes; end
|
41
38
|
def stream.write(node)
|
@@ -53,7 +50,6 @@ class SubscribeTest < MiniTest::Unit::TestCase
|
|
53
50
|
assert stream.verify
|
54
51
|
assert user.verify
|
55
52
|
assert storage.verify
|
56
|
-
assert router.verify
|
57
53
|
assert_equal 1, stream.nodes.size
|
58
54
|
assert_equal 1, recipient.nodes.size
|
59
55
|
|
data/test/stanza_test.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'vines'
|
4
|
+
require 'ext/nokogiri'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
|
7
|
+
class StanzaTest < MiniTest::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@alice = Vines::JID.new('alice@wonderland.lit/tea')
|
10
|
+
@romeo = Vines::JID.new('romeo@verona.lit/balcony')
|
11
|
+
@stream = MiniTest::Mock.new
|
12
|
+
@config = Vines::Config.new do
|
13
|
+
host 'wonderland.lit' do
|
14
|
+
storage(:fs) { dir '.' }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_validate_missing_addresses
|
20
|
+
node = node(%Q{<message>hello!</message>})
|
21
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
22
|
+
assert_nil stanza.validate_to
|
23
|
+
assert_nil stanza.validate_from
|
24
|
+
assert @stream.verify
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_validate_valid_addresses
|
28
|
+
node = node(%Q{<message from="#{@alice}" to="#{@romeo}">hello!</message>})
|
29
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
30
|
+
assert_equal @romeo, stanza.validate_to
|
31
|
+
assert_equal @alice, stanza.validate_from
|
32
|
+
assert @stream.verify
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_validate_invalid_addresses
|
36
|
+
node = node(%Q{<message from="a lice@wonderland.lit" to="romeo@v erona.lit">hello!</message>})
|
37
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
38
|
+
assert_raises(Vines::StanzaErrors::JidMalformed) { stanza.validate_to }
|
39
|
+
assert_raises(Vines::StanzaErrors::JidMalformed) { stanza.validate_from }
|
40
|
+
assert @stream.verify
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_non_routable_stanza_is_local
|
44
|
+
stanza = Vines::Stanza.new(node('<auth/>'), @stream)
|
45
|
+
assert stanza.local?
|
46
|
+
assert @stream.verify
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_stanza_missing_to_is_local
|
50
|
+
node = node(%Q{<message>hello!</message>})
|
51
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
52
|
+
assert stanza.local?
|
53
|
+
assert @stream.verify
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_stanza_with_local_jid_is_local
|
57
|
+
node = node(%Q{<message to="#{@alice}">hello!</message>})
|
58
|
+
@stream.expect(:config, @config)
|
59
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
60
|
+
assert stanza.local?
|
61
|
+
assert @stream.verify
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_stanza_with_remote_jid_is_not_local
|
65
|
+
node = node(%Q{<message to="#{@romeo}">hello!</message>})
|
66
|
+
@stream.expect(:config, @config)
|
67
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
68
|
+
refute stanza.local?
|
69
|
+
assert @stream.verify
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def node(xml)
|
75
|
+
Nokogiri::XML(xml).root
|
76
|
+
end
|
77
|
+
end
|
@@ -90,6 +90,7 @@ class ClientAuthTest < MiniTest::Unit::TestCase
|
|
90
90
|
|
91
91
|
def test_plain_auth_valid_password
|
92
92
|
user = Vines::User.new(:jid => 'alice@wonderland.lit')
|
93
|
+
@stream.expect(:reset, nil)
|
93
94
|
@stream.expect(:storage, MockStorage.new)
|
94
95
|
@stream.expect(:user, user)
|
95
96
|
@stream.expect(:user=, nil, [user])
|
@@ -4,9 +4,14 @@ require 'vines'
|
|
4
4
|
require 'minitest/autorun'
|
5
5
|
|
6
6
|
class ClientSessionTest < MiniTest::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@stream = MiniTest::Mock.new
|
9
|
+
@stream.expect(:config, nil)
|
10
|
+
end
|
11
|
+
|
7
12
|
def test_equality
|
8
|
-
one = Vines::Stream::Client::Session.new(
|
9
|
-
two = Vines::Stream::Client::Session.new(
|
13
|
+
one = Vines::Stream::Client::Session.new(@stream)
|
14
|
+
two = Vines::Stream::Client::Session.new(@stream)
|
10
15
|
|
11
16
|
assert_nil one <=> 42
|
12
17
|
|
@@ -5,94 +5,77 @@ require 'ext/nokogiri'
|
|
5
5
|
require 'minitest/autorun'
|
6
6
|
|
7
7
|
class ComponentReadyTest < MiniTest::Unit::TestCase
|
8
|
-
STANZAS = []
|
9
|
-
|
10
8
|
def setup
|
11
9
|
@stream = MiniTest::Mock.new
|
12
|
-
@state
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
else
|
17
|
-
MiniTest::Mock.new.tap do |stanza|
|
18
|
-
if node['to'] == 'hatter@wonderland.lit'
|
19
|
-
stanza.expect(:local?, true)
|
20
|
-
else
|
21
|
-
stanza.expect(:local?, false)
|
22
|
-
stanza.expect(:route, nil)
|
23
|
-
end
|
24
|
-
ComponentReadyTest::STANZAS << stanza
|
25
|
-
end
|
10
|
+
@state = Vines::Stream::Component::Ready.new(@stream, nil)
|
11
|
+
@config = Vines::Config.new do
|
12
|
+
host 'wonderland.lit' do
|
13
|
+
storage(:fs) { dir '.' }
|
26
14
|
end
|
27
15
|
end
|
28
16
|
end
|
29
17
|
|
30
|
-
def teardown
|
31
|
-
STANZAS.clear
|
32
|
-
end
|
33
|
-
|
34
18
|
def test_missing_to_and_from_addresses
|
35
19
|
node = node('<message/>')
|
36
20
|
assert_raises(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
|
37
|
-
assert_equal 1, STANZAS.size
|
38
21
|
assert @stream.verify
|
39
22
|
end
|
40
23
|
|
41
24
|
def test_missing_from_address
|
42
|
-
@stream.expect(:remote_domain, 'tea.wonderland.lit')
|
43
25
|
node = node(%q{<message to="hatter@wonderland.lit"/>})
|
44
26
|
assert_raises(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
|
45
|
-
assert_equal 1, STANZAS.size
|
46
27
|
assert @stream.verify
|
47
28
|
end
|
48
29
|
|
49
30
|
def test_missing_to_address
|
50
31
|
node = node(%q{<message from="alice@tea.wonderland.lit"/>})
|
51
32
|
assert_raises(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
|
52
|
-
assert_equal 1, STANZAS.size
|
53
33
|
assert @stream.verify
|
54
34
|
end
|
55
35
|
|
56
36
|
def test_invalid_from_address
|
57
37
|
@stream.expect(:remote_domain, 'tea.wonderland.lit')
|
58
38
|
node = node(%q{<message from="alice@bogus.wonderland.lit" to="hatter@wonderland.lit"/>})
|
59
|
-
assert_raises(Vines::StreamErrors::
|
60
|
-
assert_equal 1, STANZAS.size
|
39
|
+
assert_raises(Vines::StreamErrors::InvalidFrom) { @state.node(node) }
|
61
40
|
assert @stream.verify
|
62
41
|
end
|
63
42
|
|
64
43
|
def test_unsupported_stanza_type
|
65
44
|
node = node('<bogus/>')
|
66
45
|
assert_raises(Vines::StreamErrors::UnsupportedStanzaType) { @state.node(node) }
|
67
|
-
assert STANZAS.empty?
|
68
46
|
assert @stream.verify
|
69
47
|
end
|
70
48
|
|
71
49
|
def test_remote_message_routes
|
72
|
-
@stream.expect(:remote_domain, 'tea.wonderland.lit')
|
73
50
|
node = node(%q{<message from="alice@tea.wonderland.lit" to="romeo@verona.lit"/>})
|
51
|
+
@stream.expect(:remote_domain, 'tea.wonderland.lit')
|
52
|
+
@stream.expect(:config, @config)
|
53
|
+
@stream.expect(:user=, nil, [Vines::User.new(:jid => 'alice@tea.wonderland.lit')])
|
54
|
+
|
55
|
+
@router = MiniTest::Mock.new
|
56
|
+
@router.expect(:route, nil, [node])
|
57
|
+
@stream.expect(:router, @router)
|
58
|
+
|
74
59
|
@state.node(node)
|
75
|
-
assert_equal 1, STANZAS.size
|
76
|
-
assert STANZAS.map {|s| s.verify }.all?
|
77
60
|
assert @stream.verify
|
61
|
+
assert @router.verify
|
78
62
|
end
|
79
63
|
|
80
64
|
def test_local_message_processes
|
81
65
|
node = node(%q{<message from="alice@tea.wonderland.lit" to="hatter@wonderland.lit"/>})
|
82
66
|
@stream.expect(:remote_domain, 'tea.wonderland.lit')
|
67
|
+
@stream.expect(:config, @config)
|
68
|
+
@stream.expect(:user=, nil, [Vines::User.new(:jid => 'alice@tea.wonderland.lit')])
|
69
|
+
@stream.expect(:user, Vines::User.new(:jid => 'alice@tea.wonderland.lit'))
|
83
70
|
|
84
71
|
@recipient = MiniTest::Mock.new
|
72
|
+
@recipient.expect(:user, Vines::User.new(:jid => 'hatter@wonderland.lit'))
|
85
73
|
@recipient.expect(:write, nil, [node])
|
86
74
|
|
87
|
-
@
|
88
|
-
@router.expect(:connected_resources, [@recipient], ['hatter@wonderland.lit'])
|
89
|
-
@stream.expect(:router, @router)
|
75
|
+
@stream.expect(:connected_resources, [@recipient], ['hatter@wonderland.lit'])
|
90
76
|
|
91
77
|
@state.node(node)
|
92
|
-
assert_equal 1, STANZAS.size
|
93
|
-
assert STANZAS.map {|s| s.verify }.all?
|
94
78
|
assert @stream.verify
|
95
|
-
assert @router.verify
|
96
79
|
assert @recipient.verify
|
97
80
|
end
|
98
81
|
|
@@ -13,7 +13,7 @@ class RequestTest < MiniTest::Unit::TestCase
|
|
13
13
|
|
14
14
|
@stream = MiniTest::Mock.new
|
15
15
|
@parser = MiniTest::Mock.new
|
16
|
-
@parser.expect(:headers, {'Content-Type' => 'text/html'})
|
16
|
+
@parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
|
17
17
|
@parser.expect(:http_method, 'GET')
|
18
18
|
@parser.expect(:request_path, '/blogs/12')
|
19
19
|
@parser.expect(:request_url, '/blogs/12?ok=true')
|
@@ -27,7 +27,7 @@ class RequestTest < MiniTest::Unit::TestCase
|
|
27
27
|
|
28
28
|
def test_copies_request_info_from_parser
|
29
29
|
request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
|
30
|
-
assert_equal request.headers, {'Content-Type' => 'text/html'}
|
30
|
+
assert_equal request.headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'}
|
31
31
|
assert_equal request.method, 'GET'
|
32
32
|
assert_equal request.path, '/blogs/12'
|
33
33
|
assert_equal request.url, '/blogs/12?ok=true'
|
@@ -64,6 +64,7 @@ class RequestTest < MiniTest::Unit::TestCase
|
|
64
64
|
|
65
65
|
def test_reply_with_file_for_directory_serves_index_html
|
66
66
|
@parser.expect(:request_path, '/')
|
67
|
+
@parser.expect(:request_url, '/?ok=true')
|
67
68
|
request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
|
68
69
|
|
69
70
|
mtime = File.mtime(INDEX).utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
@@ -83,4 +84,23 @@ class RequestTest < MiniTest::Unit::TestCase
|
|
83
84
|
assert @stream.verify
|
84
85
|
assert @parser.verify
|
85
86
|
end
|
87
|
+
|
88
|
+
def test_reply_with_file_redirects_for_missing_slash
|
89
|
+
@parser.expect(:request_path, '/http')
|
90
|
+
@parser.expect(:request_url, '/http?ok=true')
|
91
|
+
request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
|
92
|
+
|
93
|
+
headers = [
|
94
|
+
"HTTP/1.1 301 Moved Permanently",
|
95
|
+
"Connection: close",
|
96
|
+
"Location: http://wonderland.lit/http/?ok=true"
|
97
|
+
].join("\r\n")
|
98
|
+
|
99
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
100
|
+
@stream.expect(:close_connection_after_writing, nil)
|
101
|
+
# so the /http url above will work
|
102
|
+
request.reply_with_file(File.expand_path('../../', __FILE__))
|
103
|
+
assert @stream.verify
|
104
|
+
assert @parser.verify
|
105
|
+
end
|
86
106
|
end
|
@@ -10,16 +10,10 @@ class ServerReadyTest < MiniTest::Unit::TestCase
|
|
10
10
|
@stream = MiniTest::Mock.new
|
11
11
|
@state = Vines::Stream::Server::Ready.new(@stream, nil)
|
12
12
|
def @state.to_stanza(node)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
stanza.expect(:process, nil)
|
18
|
-
stanza.class.send(:define_method, :[]) do |a|
|
19
|
-
node[a]
|
20
|
-
end
|
21
|
-
ServerReadyTest::STANZAS << stanza
|
22
|
-
end
|
13
|
+
Vines::Stanza.from_node(node, @stream).tap do |stanza|
|
14
|
+
def stanza.process
|
15
|
+
ServerReadyTest::STANZAS << self
|
16
|
+
end if stanza
|
23
17
|
end
|
24
18
|
end
|
25
19
|
end
|
@@ -35,7 +29,6 @@ class ServerReadyTest < MiniTest::Unit::TestCase
|
|
35
29
|
node = node(%Q{<message from="alice@wonderland.lit" to="romeo@verona.lit"/>})
|
36
30
|
@state.node(node)
|
37
31
|
assert_equal 1, STANZAS.size
|
38
|
-
assert STANZAS.map {|s| s.verify }.all?
|
39
32
|
assert @stream.verify
|
40
33
|
end
|
41
34
|
|
@@ -49,28 +42,28 @@ class ServerReadyTest < MiniTest::Unit::TestCase
|
|
49
42
|
def test_improper_addressing_missing_to
|
50
43
|
node = node(%Q{<message from="alice@wonderland.lit"/>})
|
51
44
|
assert_raises(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
|
52
|
-
|
45
|
+
assert STANZAS.empty?
|
53
46
|
assert @stream.verify
|
54
47
|
end
|
55
48
|
|
56
|
-
def
|
49
|
+
def test_improper_addressing_invalid_to
|
57
50
|
node = node(%Q{<message from="alice@wonderland.lit" to=" "/>})
|
58
|
-
assert_raises(Vines::
|
59
|
-
|
51
|
+
assert_raises(Vines::StanzaErrors::JidMalformed) { @state.node(node) }
|
52
|
+
assert STANZAS.empty?
|
60
53
|
assert @stream.verify
|
61
54
|
end
|
62
55
|
|
63
56
|
def test_improper_addressing_missing_from
|
64
57
|
node = node(%Q{<message to="romeo@verona.lit"/>})
|
65
58
|
assert_raises(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
|
66
|
-
|
59
|
+
assert STANZAS.empty?
|
67
60
|
assert @stream.verify
|
68
61
|
end
|
69
62
|
|
70
|
-
def
|
63
|
+
def test_improper_addressing_invalid_from
|
71
64
|
node = node(%Q{<message from=" " to="romeo@verona.lit"/>})
|
72
|
-
assert_raises(Vines::
|
73
|
-
|
65
|
+
assert_raises(Vines::StanzaErrors::JidMalformed) { @state.node(node) }
|
66
|
+
assert STANZAS.empty?
|
74
67
|
assert @stream.verify
|
75
68
|
end
|
76
69
|
|
@@ -78,7 +71,7 @@ class ServerReadyTest < MiniTest::Unit::TestCase
|
|
78
71
|
@stream.expect(:remote_domain, 'wonderland.lit')
|
79
72
|
node = node(%Q{<message from="alice@bogus.lit" to="romeo@verona.lit"/>})
|
80
73
|
assert_raises(Vines::StreamErrors::InvalidFrom) { @state.node(node) }
|
81
|
-
|
74
|
+
assert STANZAS.empty?
|
82
75
|
assert @stream.verify
|
83
76
|
end
|
84
77
|
|
@@ -87,7 +80,7 @@ class ServerReadyTest < MiniTest::Unit::TestCase
|
|
87
80
|
@stream.expect(:domain, 'verona.lit')
|
88
81
|
node = node(%Q{<message from="alice@wonderland.lit" to="romeo@bogus.lit"/>})
|
89
82
|
assert_raises(Vines::StreamErrors::HostUnknown) { @state.node(node) }
|
90
|
-
|
83
|
+
assert STANZAS.empty?
|
91
84
|
assert @stream.verify
|
92
85
|
end
|
93
86
|
|
data/web/404.html
CHANGED
@@ -27,17 +27,23 @@
|
|
27
27
|
width: 100%;
|
28
28
|
}
|
29
29
|
p {
|
30
|
-
color:
|
30
|
+
color: rgba(255, 255, 255, 0.8);
|
31
31
|
font-size: 11pt;
|
32
32
|
margin: 20px auto;
|
33
|
-
|
33
|
+
width: 400px;
|
34
|
+
}
|
35
|
+
a {
|
36
|
+
color: inherit;
|
34
37
|
}
|
35
38
|
</style>
|
36
39
|
</head>
|
37
40
|
<body>
|
38
41
|
<header>
|
39
42
|
<h1>Page not found</h1>
|
40
|
-
<p>
|
43
|
+
<p>
|
44
|
+
This is not the page you're looking for. You probably wanted the
|
45
|
+
<a href="/chat/">chat</a> application.
|
46
|
+
</p>
|
41
47
|
</header>
|
42
48
|
</body>
|
43
49
|
</html>
|
data/web/chat/index.html
CHANGED
@@ -8,9 +8,9 @@
|
|
8
8
|
<link rel="shortcut icon" type="image/png" href="/favicon.png">
|
9
9
|
<link rel="stylesheet" href="/lib/stylesheets/base.css">
|
10
10
|
<link rel="stylesheet" href="/lib/stylesheets/login.css">
|
11
|
-
<link rel="stylesheet" href="
|
11
|
+
<link rel="stylesheet" href="stylesheets/chat.css">
|
12
12
|
<script type="text/javascript" src="/lib/javascripts/base.js"></script>
|
13
|
-
<script type="text/javascript" src="
|
13
|
+
<script type="text/javascript" src="javascripts/app.js"></script>
|
14
14
|
</head>
|
15
15
|
<body>
|
16
16
|
</body>
|
data/web/chat/javascripts/app.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
var ChatPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};ChatPage=function(){function a(a){this.session=a,this.session.onRoster(__bind(function(){return this.roster()},this)),this.session.onCard(__bind(function(a){return this.card(a)},this)),this.session.onMessage(__bind(function(a){return this.message(a)},this)),this.session.onPresence(__bind(function(a){return this.presence(a)},this)),this.chats={},this.currentContact=null}a.prototype.datef=function(a){var b,c,d,e;b=new Date(a),d=b.getHours()>=12?" pm":" am",c=b.getHours()>12?b.getHours()-12:b.getHours(),c===0&&(c=12),e=b.getMinutes()+"",e.length===1&&(e="0"+e);return c+":"+e+d},a.prototype.card=function(a){return this.eachContact(a.jid,__bind(function(b){return $(".vcard-img",b).attr("src",this.session.avatar(a.jid))},this))},a.prototype.roster=function(){var a,b,c,d,e,f,g,h;e=$("#roster"),$("li",e).each(__bind(function(a,b){var c;c=$(b).attr("data-jid");if(!this.session.roster[c])return $(b).remove()},this)),f=function(a,b){$(".text",a).text(b.name||b.jid);return a.attr("data-name",b.name||"")},g=this.session.roster,h=[];for(c in g)a=g[c],b=$("#roster li[data-jid='"+c+"']"),f(b,a),h.push(b.length===0?(d=$('<li data-jid="'+c+'" data-name="" class="offline">\n <span class="text"></span>\n <span class="status-msg">Offline</span>\n <span class="unread" style="display:none;"></span>\n <img class="vcard-img" alt="'+c+'" src="'+this.session.avatar(c)+'"/>\n</li>').appendTo(e),f(d,a),d.click(__bind(function(a){return this.selectContact(a)},this))):void 0);return h},a.prototype.message=function(a){var b,c,d,e;this.queueMessage(a),e=a.from===this.session.jid(),d=a.from.split("/")[0];if(!e&&d!==this.currentContact){c=this.chat(a.from),c.unread++;return this.eachContact(d,function(a){return $(".unread",a).text(c.unread).show()})}b=this.atBottom(),this.appendMessage(a);if(b)return this.scroll()},a.prototype.eachContact=function(a,b){var c,d,e,f,g;f=$("#roster li[data-jid='"+a+"']").get(),g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(b($(c)));return g},a.prototype.appendMessage=function(a){var b,c,d,e;c=a.from.split("/")[0],b=this.session.roster[c],d=b?b.name||c:c,a.from===this.session.jid()&&(d="Me"),e=$('<li data-jid="'+c+'" style="display:none;">\n <p></p>\n <img alt="'+c+'" src="'+this.session.avatar(c)+'"/>\n <footer>\n <span class="author"></span>\n <span class="time">'+this.datef(a.received)+"</span>\n </footer>\n</li>").appendTo("#messages"),$("p",e).text(a.text),$(".author",e).text(d);return e.fadeIn(200)},a.prototype.queueMessage=function(a){var b,c,d;d=a.from===this.session.jid(),c=a[d?"to":"from"],b=this.chat(c),b.jid=c;return b.messages.push(a)},a.prototype.chat=function(a){var b,c;b=a.split("/")[0],c=this.chats[b],c||(c={jid:a,messages:[],unread:0},this.chats[b]=c);return c},a.prototype.presence=function(a){var b,c,d;c=a.from.split("/")[0];if(c!==this.session.bareJid()){if(!a.type||a.offline)b=this.session.roster[c],this.eachContact(c,function(a){$(".status-msg",a).text(b.status());return b.offline()?a.addClass("offline"):a.removeClass("offline")});a.offline&&(this.chat(c).jid=c);if(a.type==="subscribe"){d=$('<li data-jid="'+a.from+'" style="display:none;">\n <form class="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())})
|
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}return a.prototype.datef=function(a){var b,c,d,e;return b=new Date(a),d=b.getHours()<12?" am":" pm",c=b.getHours()>12?b.getHours()-12:b.getHours(),c===0&&(c=12),e=b.getMinutes()+"",e.length===1&&(e="0"+e),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){return $(".text",a).text(b.name||b.jid),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)return c=this.chat(a.from),c.unread++,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;return 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),e.fadeIn(200)},a.prototype.queueMessage=function(a){var b,c,d;return d=a.from===this.session.jid(),c=a[d?"to":"from"],b=this.chat(c),b.jid=c,b.messages.push(a)},a.prototype.chat=function(a){var b,c;return b=a.split("/")[0],c=this.chats[b],c||(c={jid:a,messages:[],unread:0},this.chats[b]=c),c},a.prototype.presence=function(a){var b,c,d;c=a.from.split("/")[0];if(c===this.session.bareJid())return;if(!a.type||a.offline)b=this.session.roster[c],this.eachContact(c,function(a){return $(".status-msg",a).text(b.status()),b.offline()?a.addClass("offline"):a.removeClass("offline")});a.offline&&(this.chat(c).jid=c);if(a.type==="subscribe")return 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)),$('input[type="button"]',d).click(__bind(function(){return this.rejectContact(d,a.from)},this))},a.prototype.acceptContact=function(a,b){return a.fadeOut(200,function(){return a.remove()}),this.session.sendSubscribed(b),this.session.sendSubscribe(b),!1},a.prototype.rejectContact=function(a,b){return a.fadeOut(200,function(){return a.remove()}),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)return;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);return 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),$("#edit-contact-form .buttons").fadeIn(200)},a.prototype.scroll=function(){var a;return a=$("#messages"),a.animate({scrollTop:a.prop("scrollHeight")},400)},a.prototype.atBottom=function(){var a,b;return b=$("#messages"),a=b.prop("scrollHeight")-b.height(),b.scrollTop()===a},a.prototype.send=function(){var a,b,c,d;return this.currentContact?(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(""),!1):!1},a.prototype.addContact=function(){var a;return 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),!1},a.prototype.removeContact=function(){return 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(),!1},a.prototype.updateContact=function(){var a;return 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),!1},a.prototype.toggleForm=function(a,b){return a=$(a),$("form.overlay").each(function(){if(this.id!==a.attr("id"))return $(this).hide()}),a.is(":hidden")?(b&&b(),a.fadeIn(100)):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="";return}return $("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)return $("#edit-contact-jid").text(this.currentContact),$("#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(){return b.resize(),b.resize()},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;return a=$("#alpha"),b=$("#beta"),c=$("#charlie"),e=$("#message"),d=$("#message-form"),new Layout(function(){return c.css("left",a.width()+b.width()),e.width(d.width()-32)})},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);return e={"/messages":new ChatPage(f),"/logout":new LogoutPage(f),"default":new LoginPage(f,"/messages/")},(new Router(e)).draw(),d.select($("#nav-link-messages").parent())})
|
@@ -55,11 +55,6 @@
|
|
55
55
|
bottom: 0;
|
56
56
|
width: 100%;
|
57
57
|
}
|
58
|
-
#chat-page input[type="text"]:focus,
|
59
|
-
#chat-page input[type="email"]:focus {
|
60
|
-
-webkit-box-shadow: inset 1px 1px 2px rgba(0, 0, 0, 0.6);
|
61
|
-
box-shadow: inset 1px 1px 2px rgba(0, 0, 0, 0.6);
|
62
|
-
}
|
63
58
|
#chat-page #message {
|
64
59
|
display: block;
|
65
60
|
position: relative;
|
@@ -111,10 +106,10 @@
|
|
111
106
|
line-height: 11px;
|
112
107
|
}
|
113
108
|
#chat-page #roster .unread {
|
114
|
-
background: #
|
115
|
-
background: -moz-linear-gradient(#
|
116
|
-
background: -o-linear-gradient(#
|
117
|
-
background: -webkit-gradient(linear, left top, left bottom, from(#
|
109
|
+
background: #319be7;
|
110
|
+
background: -moz-linear-gradient(#319be7, #1b78d9);
|
111
|
+
background: -o-linear-gradient(#319be7, #1b78d9);
|
112
|
+
background: -webkit-gradient(linear, left top, left bottom, from(#319be7), to(#1b78d9));
|
118
113
|
border-radius: 30px;
|
119
114
|
color: #fff;
|
120
115
|
display: inline-block;
|