vines 0.2.1 → 0.3.0
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 +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;
|