vines 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/README +1 -1
  2. data/Rakefile +10 -10
  3. data/conf/certs/ca-bundle.crt +112 -378
  4. data/conf/config.rb +18 -9
  5. data/lib/vines.rb +8 -1
  6. data/lib/vines/command/cert.rb +2 -1
  7. data/lib/vines/command/init.rb +11 -0
  8. data/lib/vines/command/ldap.rb +6 -3
  9. data/lib/vines/command/schema.rb +1 -1
  10. data/lib/vines/config.rb +57 -146
  11. data/lib/vines/config/host.rb +85 -0
  12. data/lib/vines/config/port.rb +111 -0
  13. data/lib/vines/contact.rb +1 -1
  14. data/lib/vines/jid.rb +26 -4
  15. data/lib/vines/kit.rb +6 -0
  16. data/lib/vines/log.rb +24 -0
  17. data/lib/vines/router.rb +70 -38
  18. data/lib/vines/stanza.rb +45 -8
  19. data/lib/vines/stanza/iq.rb +3 -3
  20. data/lib/vines/stanza/iq/disco_info.rb +5 -1
  21. data/lib/vines/stanza/iq/disco_items.rb +3 -0
  22. data/lib/vines/stanza/iq/private_storage.rb +9 -5
  23. data/lib/vines/stanza/iq/roster.rb +11 -12
  24. data/lib/vines/stanza/iq/vcard.rb +4 -4
  25. data/lib/vines/stanza/iq/version.rb +25 -0
  26. data/lib/vines/stanza/message.rb +4 -5
  27. data/lib/vines/stanza/presence.rb +20 -18
  28. data/lib/vines/stanza/presence/probe.rb +3 -4
  29. data/lib/vines/stanza/presence/subscribe.rb +4 -3
  30. data/lib/vines/stanza/presence/subscribed.rb +6 -5
  31. data/lib/vines/stanza/presence/unsubscribe.rb +4 -4
  32. data/lib/vines/stanza/presence/unsubscribed.rb +4 -3
  33. data/lib/vines/storage/couchdb.rb +3 -3
  34. data/lib/vines/storage/ldap.rb +19 -8
  35. data/lib/vines/storage/local.rb +23 -12
  36. data/lib/vines/storage/redis.rb +3 -3
  37. data/lib/vines/storage/sql.rb +5 -5
  38. data/lib/vines/stream.rb +40 -6
  39. data/lib/vines/stream/client.rb +5 -6
  40. data/lib/vines/stream/client/auth.rb +3 -2
  41. data/lib/vines/stream/client/bind.rb +2 -2
  42. data/lib/vines/stream/client/bind_restart.rb +1 -2
  43. data/lib/vines/stream/client/ready.rb +2 -0
  44. data/lib/vines/stream/client/session.rb +13 -4
  45. data/lib/vines/stream/client/tls.rb +1 -0
  46. data/lib/vines/stream/component.rb +6 -5
  47. data/lib/vines/stream/component/ready.rb +5 -6
  48. data/lib/vines/stream/http.rb +10 -4
  49. data/lib/vines/stream/http/request.rb +23 -2
  50. data/lib/vines/stream/server.rb +13 -11
  51. data/lib/vines/stream/server/outbound/auth_result.rb +1 -0
  52. data/lib/vines/stream/server/outbound/tls_result.rb +1 -0
  53. data/lib/vines/stream/server/ready.rb +2 -2
  54. data/lib/vines/user.rb +2 -1
  55. data/lib/vines/version.rb +1 -1
  56. data/test/config/host_test.rb +292 -0
  57. data/test/config_test.rb +244 -103
  58. data/test/contact_test.rb +7 -1
  59. data/test/jid_test.rb +48 -0
  60. data/test/router_test.rb +16 -47
  61. data/test/stanza/iq/disco_info_test.rb +76 -0
  62. data/test/stanza/iq/disco_items_test.rb +47 -0
  63. data/test/stanza/iq/private_storage_test.rb +33 -10
  64. data/test/stanza/iq/roster_test.rb +15 -5
  65. data/test/stanza/iq/vcard_test.rb +8 -25
  66. data/test/stanza/iq/version_test.rb +62 -0
  67. data/test/stanza/iq_test.rb +13 -10
  68. data/test/stanza/message_test.rb +16 -24
  69. data/test/stanza/presence/probe_test.rb +52 -0
  70. data/test/stanza/presence/subscribe_test.rb +1 -5
  71. data/test/stanza_test.rb +77 -0
  72. data/test/stream/client/auth_test.rb +1 -0
  73. data/test/stream/client/ready_test.rb +2 -0
  74. data/test/stream/client/session_test.rb +7 -2
  75. data/test/stream/component/ready_test.rb +19 -36
  76. data/test/stream/http/request_test.rb +22 -2
  77. data/test/stream/server/ready_test.rb +14 -21
  78. data/web/404.html +9 -3
  79. data/web/chat/index.html +2 -2
  80. data/web/chat/javascripts/app.js +1 -1
  81. data/web/chat/stylesheets/chat.css +4 -9
  82. data/web/lib/coffeescripts/layout.coffee +2 -2
  83. data/web/{chat → lib}/coffeescripts/logout.coffee +0 -0
  84. data/web/lib/coffeescripts/notification.coffee +14 -0
  85. data/web/lib/coffeescripts/session.coffee +28 -24
  86. data/web/lib/coffeescripts/transfer.coffee +37 -34
  87. data/web/lib/javascripts/base.js +8 -8
  88. data/web/lib/javascripts/icons.js +3 -0
  89. data/web/lib/javascripts/jquery.js +4 -18
  90. data/web/lib/javascripts/layout.js +2 -2
  91. data/web/{chat → lib}/javascripts/logout.js +0 -0
  92. data/web/lib/javascripts/notification.js +26 -0
  93. data/web/lib/javascripts/session.js +20 -16
  94. data/web/lib/javascripts/transfer.js +45 -55
  95. data/web/lib/stylesheets/base.css +45 -9
  96. 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(:router, router)
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
 
@@ -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])
@@ -15,6 +15,8 @@ class ClientReadyTest < MiniTest::Unit::TestCase
15
15
  else
16
16
  MiniTest::Mock.new.tap do |stanza|
17
17
  stanza.expect(:process, nil)
18
+ stanza.expect(:validate_to, nil)
19
+ stanza.expect(:validate_from, nil)
18
20
  ClientReadyTest::STANZAS << stanza
19
21
  end
20
22
  end
@@ -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(nil)
9
- two = Vines::Stream::Client::Session.new(nil)
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 = Vines::Stream::Component::Ready.new(@stream, nil)
13
- def @state.to_stanza(node)
14
- if node.name == 'bogus'
15
- nil
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::ImproperAddressing) { @state.node(node) }
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
- @router = MiniTest::Mock.new
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
- if node.name == 'bogus'
14
- nil
15
- else
16
- MiniTest::Mock.new.tap do |stanza|
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
- assert_equal 1, STANZAS.size
45
+ assert STANZAS.empty?
53
46
  assert @stream.verify
54
47
  end
55
48
 
56
- def test_improper_addressing_empty_to
49
+ def test_improper_addressing_invalid_to
57
50
  node = node(%Q{<message from="alice@wonderland.lit" to=" "/>})
58
- assert_raises(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
59
- assert_equal 1, STANZAS.size
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
- assert_equal 1, STANZAS.size
59
+ assert STANZAS.empty?
67
60
  assert @stream.verify
68
61
  end
69
62
 
70
- def test_improper_addressing_empty_from
63
+ def test_improper_addressing_invalid_from
71
64
  node = node(%Q{<message from=" " to="romeo@verona.lit"/>})
72
- assert_raises(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
73
- assert_equal 1, STANZAS.size
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
- assert_equal 1, STANZAS.size
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
- assert_equal 1, STANZAS.size
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: #fff;
30
+ color: rgba(255, 255, 255, 0.8);
31
31
  font-size: 11pt;
32
32
  margin: 20px auto;
33
- text-shadow: 0 1px 1px #000;
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>This is not the page you're looking for.</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="/chat/stylesheets/chat.css">
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="/chat/javascripts/app.js"></script>
13
+ <script type="text/javascript" src="javascripts/app.js"></script>
14
14
  </head>
15
15
  <body>
16
16
  </body>
@@ -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: #4693FF;
115
- background: -moz-linear-gradient(#4693FF, #015de6);
116
- background: -o-linear-gradient(#4693FF, #015de6);
117
- background: -webkit-gradient(linear, left top, left bottom, from(#4693FF), to(#015de6));
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;