jschat 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/MIT-LICENSE +23 -0
  2. data/README.textile +71 -0
  3. data/bin/jschat-client +3 -0
  4. data/bin/jschat-server +18 -0
  5. data/bin/jschat-web +7 -0
  6. data/lib/jschat/client.rb +745 -0
  7. data/lib/jschat/errors.rb +41 -0
  8. data/lib/jschat/flood_protection.rb +39 -0
  9. data/lib/jschat/http/config/sprockets.yml +7 -0
  10. data/lib/jschat/http/config.ru +12 -0
  11. data/lib/jschat/http/jschat.rb +264 -0
  12. data/lib/jschat/http/public/favicon.ico +0 -0
  13. data/lib/jschat/http/public/images/emoticons/angry.gif +0 -0
  14. data/lib/jschat/http/public/images/emoticons/arr.gif +0 -0
  15. data/lib/jschat/http/public/images/emoticons/blink.gif +0 -0
  16. data/lib/jschat/http/public/images/emoticons/blush.gif +0 -0
  17. data/lib/jschat/http/public/images/emoticons/brucelee.gif +0 -0
  18. data/lib/jschat/http/public/images/emoticons/btw.gif +0 -0
  19. data/lib/jschat/http/public/images/emoticons/chuckle.gif +0 -0
  20. data/lib/jschat/http/public/images/emoticons/clap.gif +0 -0
  21. data/lib/jschat/http/public/images/emoticons/cool.gif +0 -0
  22. data/lib/jschat/http/public/images/emoticons/drool.gif +0 -0
  23. data/lib/jschat/http/public/images/emoticons/drunk.gif +0 -0
  24. data/lib/jschat/http/public/images/emoticons/dry.gif +0 -0
  25. data/lib/jschat/http/public/images/emoticons/eek.gif +0 -0
  26. data/lib/jschat/http/public/images/emoticons/flex.gif +0 -0
  27. data/lib/jschat/http/public/images/emoticons/happy.gif +0 -0
  28. data/lib/jschat/http/public/images/emoticons/holmes.gif +0 -0
  29. data/lib/jschat/http/public/images/emoticons/huh.gif +0 -0
  30. data/lib/jschat/http/public/images/emoticons/laugh.gif +0 -0
  31. data/lib/jschat/http/public/images/emoticons/lol.gif +0 -0
  32. data/lib/jschat/http/public/images/emoticons/mad.gif +0 -0
  33. data/lib/jschat/http/public/images/emoticons/mellow.gif +0 -0
  34. data/lib/jschat/http/public/images/emoticons/noclue.gif +0 -0
  35. data/lib/jschat/http/public/images/emoticons/oh.gif +0 -0
  36. data/lib/jschat/http/public/images/emoticons/ohmy.gif +0 -0
  37. data/lib/jschat/http/public/images/emoticons/ph34r.gif +0 -0
  38. data/lib/jschat/http/public/images/emoticons/pimp.gif +0 -0
  39. data/lib/jschat/http/public/images/emoticons/punch.gif +0 -0
  40. data/lib/jschat/http/public/images/emoticons/realmad.gif +0 -0
  41. data/lib/jschat/http/public/images/emoticons/rock.gif +0 -0
  42. data/lib/jschat/http/public/images/emoticons/rofl.gif +0 -0
  43. data/lib/jschat/http/public/images/emoticons/rolleyes.gif +0 -0
  44. data/lib/jschat/http/public/images/emoticons/sad.gif +0 -0
  45. data/lib/jschat/http/public/images/emoticons/scratch.gif +0 -0
  46. data/lib/jschat/http/public/images/emoticons/shifty.gif +0 -0
  47. data/lib/jschat/http/public/images/emoticons/shock.gif +0 -0
  48. data/lib/jschat/http/public/images/emoticons/shrug.gif +0 -0
  49. data/lib/jschat/http/public/images/emoticons/sleep.gif +0 -0
  50. data/lib/jschat/http/public/images/emoticons/sleeping.gif +0 -0
  51. data/lib/jschat/http/public/images/emoticons/smile.gif +0 -0
  52. data/lib/jschat/http/public/images/emoticons/suicide.gif +0 -0
  53. data/lib/jschat/http/public/images/emoticons/sweat.gif +0 -0
  54. data/lib/jschat/http/public/images/emoticons/thumbs.gif +0 -0
  55. data/lib/jschat/http/public/images/emoticons/tongue.gif +0 -0
  56. data/lib/jschat/http/public/images/emoticons/unsure.gif +0 -0
  57. data/lib/jschat/http/public/images/emoticons/w00t.gif +0 -0
  58. data/lib/jschat/http/public/images/emoticons/wacko.gif +0 -0
  59. data/lib/jschat/http/public/images/emoticons/whistling.gif +0 -0
  60. data/lib/jschat/http/public/images/emoticons/wink.gif +0 -0
  61. data/lib/jschat/http/public/images/emoticons/worship.gif +0 -0
  62. data/lib/jschat/http/public/images/emoticons/yucky.gif +0 -0
  63. data/lib/jschat/http/public/images/jschat.gif +0 -0
  64. data/lib/jschat/http/public/images/shadow.png +0 -0
  65. data/lib/jschat/http/public/javascripts/app/controllers/chat_controller.js +191 -0
  66. data/lib/jschat/http/public/javascripts/app/controllers/signon_controller.js +56 -0
  67. data/lib/jschat/http/public/javascripts/app/helpers/emote_helper.js +23 -0
  68. data/lib/jschat/http/public/javascripts/app/helpers/form_helpers.js +37 -0
  69. data/lib/jschat/http/public/javascripts/app/helpers/link_helper.js +47 -0
  70. data/lib/jschat/http/public/javascripts/app/helpers/page_helper.js +27 -0
  71. data/lib/jschat/http/public/javascripts/app/helpers/text_helper.js +92 -0
  72. data/lib/jschat/http/public/javascripts/app/lib/split.js +78 -0
  73. data/lib/jschat/http/public/javascripts/app/models/cookie.js +27 -0
  74. data/lib/jschat/http/public/javascripts/app/protocol/change.js +15 -0
  75. data/lib/jschat/http/public/javascripts/app/protocol/chat_request.js +13 -0
  76. data/lib/jschat/http/public/javascripts/app/protocol/display.js +147 -0
  77. data/lib/jschat/http/public/javascripts/app/ui/commands.js +55 -0
  78. data/lib/jschat/http/public/javascripts/app/ui/tab_completion.js +122 -0
  79. data/lib/jschat/http/public/javascripts/init.js +19 -0
  80. data/lib/jschat/http/public/stylesheets/iphone.css +3 -0
  81. data/lib/jschat/http/public/stylesheets/screen.css +68 -0
  82. data/lib/jschat/http/script/sprockets.rb +14 -0
  83. data/lib/jschat/http/tmp/restart.txt +0 -0
  84. data/lib/jschat/http/views/index.erb +23 -0
  85. data/lib/jschat/http/views/iphone.erb +29 -0
  86. data/lib/jschat/http/views/layout.erb +29 -0
  87. data/lib/jschat/http/views/message_form.erb +15 -0
  88. data/lib/jschat/server.rb +503 -0
  89. data/test/server_test.rb +175 -0
  90. data/test/stateless_test.rb +33 -0
  91. data/test/test_helper.rb +61 -0
  92. metadata +223 -0
@@ -0,0 +1,147 @@
1
+ var Display = {
2
+ scrolled: false,
3
+
4
+ add_message: function(text, className, time) {
5
+ var time_html = '<span class="time">\#{time}</span>'.interpolate({ time: TextHelper.dateText(time) });
6
+ $('messages').insert({ bottom: '<li class="' + className + '">' + time_html + ' ' + text + '</li>' });
7
+ this.scrollMessagesToTop();
8
+ },
9
+
10
+ addImageOnLoads: function() {
11
+ $$('#messages li').last().select('img').each(function(element) {
12
+ element.observe('load', this.scrollMessagesToTop);
13
+ }.bind(this));
14
+ },
15
+
16
+ message: function(message) {
17
+ var name = $('name').innerHTML;
18
+ var user_class = name == message['user'] ? 'user active' : 'user';
19
+ var text = '<span class="\#{user_class}">\#{user}</span> <span class="\#{message_class}">\#{message}</span>';
20
+
21
+ if (message['message'].match(new RegExp(name, 'i')) && name != message['user']) {
22
+ user_class = 'user mentioned';
23
+ }
24
+
25
+ Display.clearIdleState(message['user']);
26
+
27
+ text = text.interpolate({ user_class: user_class, room: message['room'], user: TextHelper.truncateName(message['user']), message: TextHelper.decorateMessage(message['message']), message_class: 'message' });
28
+ this.add_message(text, 'message', message['time']);
29
+
30
+ this.addImageOnLoads();
31
+
32
+ if (this.show_unread) {
33
+ this.unread++;
34
+ document.title = 'JsChat: (' + this.unread + ') new messages';
35
+ }
36
+ },
37
+
38
+ messages: function(messages) {
39
+ $('messages').innerHTML = '';
40
+ this.ignore_notices = true;
41
+
42
+ $A(messages).each(function(json) {
43
+ try {
44
+ if (json['change']) {
45
+ Change[json['change']](json[json['change']]);
46
+ } else {
47
+ this[json['display']](json[json['display']]);
48
+ }
49
+ } catch (exception) {
50
+ }
51
+ }.bind(this));
52
+
53
+ this.ignore_notices = false;
54
+ this.scrollMessagesToTop();
55
+ /* This is assumed to be the point at which displaying /lastlog completes */
56
+ $('loading').hide();
57
+ Cookie.create('jschat-name', $('name').innerHTML, 28, '/');
58
+ },
59
+
60
+ scrollMessagesToTop: function() {
61
+ if (!this.scrolled) {
62
+ $('messages').scrollTop = $('messages').scrollHeight;
63
+ }
64
+ },
65
+
66
+ clearIdleState: function(user_name) {
67
+ $$('#names li').each(function(element) {
68
+ if (element.innerHTML == user_name && element.hasClassName('idle')) {
69
+ element.lastIdle = (new Date());
70
+ element.removeClassName('idle');
71
+ }
72
+ });
73
+ },
74
+
75
+ isIdle: function(dateValue) {
76
+ try {
77
+ var d = typeof dateValue == 'string' ? new Date(Date.parse(dateValue)) : dateValue,
78
+ now = new Date();
79
+ if (((now - d) / 1000) > (60 * 5)) {
80
+ return true;
81
+ }
82
+ } catch (exception) {
83
+ console.log(exception);
84
+ }
85
+ return false;
86
+ },
87
+
88
+ names: function(users) {
89
+ $('names').innerHTML = '';
90
+ users.each(function(user) {
91
+ var name = user['name'],
92
+ list_class = this.isIdle(user['last_activity']) ? 'idle' : '',
93
+ element = $(document.createElement('li'));
94
+
95
+ element.addClassName(list_class);
96
+ element.innerHTML = TextHelper.truncateName(name);
97
+ $('names').insert({ bottom: element });
98
+
99
+ try {
100
+ // Record the last idle time so the idle state can be dynamically updated
101
+ element.lastIdle = new Date(Date.parse(user['last_activity']));
102
+ } catch (exception) {
103
+ element.lastIdle = null;
104
+ }
105
+ }.bind(this));
106
+ },
107
+
108
+ join: function(join) {
109
+ $('room-name').innerHTML = TextHelper.truncateRoomName(join['room']);
110
+ $('room-name').title = PageHelper.currentRoom();
111
+ },
112
+
113
+ join_notice: function(join) {
114
+ this.add_user(join['user']);
115
+ this.add_message(join['user'] + ' has joined the room', 'server', join['time']);
116
+ },
117
+
118
+ add_user: function(name) {
119
+ if (!this.ignore_notices) {
120
+ $('names').insert({ bottom: '<li>' + TextHelper.truncateName(name) + '</li>' });
121
+ }
122
+ },
123
+
124
+ remove_user: function(name) {
125
+ if (!this.ignore_notices) {
126
+ $$('#names li').each(function(element) { if (element.innerHTML == name) element.remove(); });
127
+ }
128
+ },
129
+
130
+ part_notice: function(part) {
131
+ this.remove_user(part['user']);
132
+ this.add_message(part['user'] + ' has left the room', 'server', part['time']);
133
+ },
134
+
135
+ quit_notice: function(quit) {
136
+ this.remove_user(quit['user']);
137
+ this.add_message(quit['user'] + ' has quit', 'server', quit['time']);
138
+ },
139
+
140
+ notice: function(notice) {
141
+ this.add_message(notice, 'server');
142
+ },
143
+
144
+ error: function(error) {
145
+ this.add_message(error['message'], 'error');
146
+ }
147
+ };
@@ -0,0 +1,55 @@
1
+ var UserCommands = {
2
+ '/emotes': function() {
3
+ var text = '';
4
+ Display.add_message('<strong>Available Emotes</strong> &mdash; Prefix with a : to use', 'help');
5
+ Display.add_message(EmoteHelper.legalEmotes.join(', '), 'help');
6
+ },
7
+
8
+ '/help': function() {
9
+ var help = [];
10
+ Display.add_message('<strong>JsChat Help</strong> &mdash; Type the following commands into the message field:', 'help')
11
+ help.push(['/clear', 'Clears messages']);
12
+ help.push(['/lastlog', 'Shows recent activity']);
13
+ help.push(['/names', 'Refreshes the names list']);
14
+ help.push(['/name new_name', 'Changes your name']);
15
+ help.push(['/emotes', 'Shows available emotes']);
16
+ $A(help).each(function(options) {
17
+ var help_text = '<span class="command">#{command}</span><span class="command_help">#{text}</span>'.interpolate({ command: options[0], text: options[1]});
18
+ Display.add_message(help_text, 'help');
19
+ });
20
+ },
21
+
22
+ '/clear': function() {
23
+ $('messages').innerHTML = '';
24
+ },
25
+
26
+ '/lastlog': function() {
27
+ $('messages').innerHTML = '';
28
+ JsChat.Request.get('/lastlog', function(transport) {
29
+ this.displayMessages(transport.responseText);
30
+ $('names').innerHTML = '';
31
+ this.updateNames();
32
+ }.bind(this));
33
+ },
34
+
35
+ '/(name|nick)\\s+(.*)': function(name) {
36
+ name = name[2];
37
+ new Ajax.Request('/change-name', {
38
+ method: 'post',
39
+ parameters: { name: name },
40
+ onSuccess: function(response) {
41
+ this.displayMessages(response.responseText, function() {
42
+ $('name').innerHTML = name;
43
+ Cookie.create('jschat-name', name, 28, '/');
44
+ });
45
+ }.bind(this),
46
+ onFailure: function() {
47
+ Display.add_message("Server error: couldn't access: #{url}".interpolate({ url: url }), 'server');
48
+ }
49
+ });
50
+ },
51
+
52
+ '/names': function() {
53
+ JsChat.Request.get('/names', function(t) { this.displayMessages(t.responseText); }.bind(this));
54
+ }
55
+ };
@@ -0,0 +1,122 @@
1
+ var TabCompletion = Class.create({
2
+ initialize: function(element) {
3
+ this.element = $(element);
4
+ this.matches = [];
5
+ this.match_offset = 0;
6
+ this.cycling = false;
7
+ this.has_focus = true;
8
+
9
+ document.observe('keydown', this.keyboardEvents.bindAsEventListener(this));
10
+ this.element.observe('focus', this.onFocus.bindAsEventListener(this));
11
+ this.element.observe('blur', this.onBlur.bindAsEventListener(this));
12
+ this.element.observe('click', this.onFocus.bindAsEventListener(this));
13
+ },
14
+
15
+ onBlur: function() {
16
+ this.has_focus = false;
17
+ this.reset();
18
+ },
19
+
20
+ onFocus: function() {
21
+ this.has_focus = true;
22
+ this.reset();
23
+ },
24
+
25
+ tabSearch: function(input) {
26
+ var names = $$('#names li').collect(function(element) { return element.innerHTML });
27
+ return names.findAll(function(name) { return name.toLowerCase().match(input.toLowerCase()) });
28
+ },
29
+
30
+ textToLeft: function() {
31
+ var text = this.element.value;
32
+ var caret_position = FormHelpers.getCaretPosition(this.element);
33
+ if (caret_position < text.length) {
34
+ text = text.slice(0, caret_position);
35
+ }
36
+
37
+ text = text.split(' ').last();
38
+ return text;
39
+ },
40
+
41
+ elementFocused: function(e) {
42
+ if (typeof document.activeElement == 'undefined') {
43
+ return this.has_focus;
44
+ } else {
45
+ return document.activeElement == this.element;
46
+ }
47
+ },
48
+
49
+ keyboardEvents: function(e) {
50
+ if (this.elementFocused()) {
51
+ switch (e.keyCode) {
52
+ case Event.KEY_TAB:
53
+ var caret_position = FormHelpers.getCaretPosition(this.element);
54
+
55
+ if (this.element.value.length > 0) {
56
+ var search_text = '';
57
+ var search_result = '';
58
+ var replace_inline = false;
59
+ var editedText = this.element.value.match(/[^a-z0-9]/i);
60
+
61
+ if (this.cycling) {
62
+ if (this.element.value == '#{last_result}: '.interpolate({ last_result: this.last_result })) {
63
+ editedText = false;
64
+ } else {
65
+ replace_inline = true;
66
+ }
67
+ search_text = this.last_result;
68
+ } else if (editedText && this.matches.length == 0) {
69
+ search_text = this.textToLeft();
70
+ replace_inline = true;
71
+ } else {
72
+ search_text = this.element.value;
73
+ }
74
+
75
+ if (this.matches.length == 0) {
76
+ this.matches = this.tabSearch(search_text);
77
+ search_result = this.matches.first();
78
+ this.cycling = true;
79
+ } else {
80
+ this.match_offset++;
81
+ if (this.match_offset >= this.matches.length) {
82
+ this.match_offset = 0;
83
+ }
84
+ search_result = this.matches[this.match_offset];
85
+ }
86
+
87
+ if (search_result && search_result.length > 0) {
88
+ if (this.cycling && this.last_result) {
89
+ search_text = this.last_result;
90
+ }
91
+ this.last_result = search_result;
92
+
93
+ if (replace_inline) {
94
+ var slice_start = caret_position - search_text.length;
95
+ if (slice_start > 0) {
96
+ this.element.value = this.element.value.substr(0, slice_start) + search_result + this.element.value.substr(caret_position, this.element.value.length);
97
+ FormHelpers.setCaretPosition(this.element, slice_start + search_result.length);
98
+ }
99
+ } else if (!editedText) {
100
+ this.element.value = '#{search_result}: '.interpolate({ search_result: search_result });
101
+ }
102
+ }
103
+ }
104
+
105
+ Event.stop(e);
106
+ return false;
107
+ break;
108
+
109
+ default:
110
+ this.reset();
111
+ break;
112
+ }
113
+ }
114
+ },
115
+
116
+ reset: function() {
117
+ this.matches = [];
118
+ this.match_offset = 0;
119
+ this.last_result = null;
120
+ this.cycling = false;
121
+ }
122
+ });
@@ -0,0 +1,19 @@
1
+ var JsChat = {};
2
+
3
+ document.observe('dom:loaded', function() {
4
+ if ($('post_message')) {
5
+ var chatController = new JsChat.ChatController();
6
+ }
7
+
8
+ if ($('sign-on')) {
9
+ if (Cookie.find('jschat-name')) {
10
+ $('name').value = Cookie.find('jschat-name');
11
+ }
12
+
13
+ if ($('room') && window.location.hash) {
14
+ $('room').value = window.location.hash;
15
+ }
16
+
17
+ var signOnController = new JsChat.SignOnController();
18
+ }
19
+ });
@@ -0,0 +1,3 @@
1
+ body { font-size: 200% }
2
+ input { font-size: 150% }
3
+ #info { display: none }
@@ -0,0 +1,68 @@
1
+ body { margin: 0 auto; padding: 0; font-family: 'Lucida Grande', arial, helvetica, sans-serif; color: #111; text-align: center; }
2
+ html, body { background: #f0f0f0; }
3
+
4
+ h1, h2, h3, h4 { margin: 0; padding: 0; }
5
+
6
+ h1 { text-align: left; margin-left: 20px }
7
+
8
+ .header { position: absolute; top: 0; height: 59px; left: 0; width: 100%; z-index: 10; background-color: #ffc; clear: both; }
9
+ .header img { border: none; margin: 5px 0 0 0 }
10
+ .header h1 { width: 200px; float: left }
11
+ .header .navigation { float: right; list-style-type: none; margin: 18px 0 0 0; padding: 0 }
12
+ .header .navigation li { float: left; margin: 0 20px 0 0; padding: 0 }
13
+ .header .navigation li a { text-decoration: none; color: #fff; background-color: #777; padding: 1px 3px }
14
+ .header .navigation li a:hover { background-color: #fff; color: #555 }
15
+ .header .navigation li#quit-nav a { background-color: #990000 }
16
+ .header .navigation li#quit-nav a:hover { color: #990000; background-color: #fff }
17
+ .header-shadow { width: 100%; height: 6px; position: absolute; top: 59px; left: 0; background-image: url('/images/shadow.png'); background-repeat: repeat-x }
18
+
19
+ .page { margin-top: 60px }
20
+
21
+ #messages { width: 500px; height: 300px; margin: 0 20px 10px 20px; padding: 0; overflow: auto; background-color: #fff; float: left; text-align: left; display: inline; }
22
+ #messages { list-style-type: none; overflow: auto }
23
+ #messages li { padding: 0.25em 0; border-bottom: 1px solid #f0f0f0; float: left; width: 100%; line-height: 1.5em }
24
+ #messages span.user { margin: 0; text-align: left; font-weight: bold; display: inline; float: left }
25
+ #messages .active { color: #000099 }
26
+ #messages .mentioned { color: #cccc00 }
27
+ #messages span.time { text-align: left; margin: 0 10px; display: block; float: left; color: #ccc !important; font-style: normal !important }
28
+ #messages span.message { display: inline; padding: 0 0 0 10px }
29
+ #messages .help { color: #990000 }
30
+ #messages .help span.command { width: 12em; display: block; float: left; font-weight: bold }
31
+ #messages li.server { color: #999; font-style: italic }
32
+ #messages li.error { color: #990000; font-style: italic; font-weight: bold }
33
+ #messages li.server span.time,
34
+ #messages li.error span.time { color: #999 }
35
+ img.inline-image { border: 1px solid #ccc; padding: 2px }
36
+ img.inline-image {
37
+ max-width: 200px;
38
+ width: expression(this.width > 200 ? 200: true);
39
+ }
40
+
41
+ #input { clear: both; text-align: left; margin: 0 0 0 20px; padding: 0; }
42
+ form { margin: 0; padding: 0; }
43
+ #message { width: 100% }
44
+ #message:focus { background-color: #ffc }
45
+
46
+ #info { text-align: left; margin: 0 0 0 20px }
47
+ #room-name { padding: 10px 0; font-size: 125% }
48
+
49
+ ul#names { margin: 0; padding: 0; list-style-type: none; height: 200px; overflow: auto; }
50
+ ul#names li { margin: 0; padding: 0.25em 0; }
51
+ ul#names li.idle { color: #777 }
52
+
53
+ .footer { border-top: 1px solid #ccc; padding: 8px 0 0 0; font-size: 80%; font-style: italic; color: #444; margin: 40px 0 0 0 }
54
+
55
+ /* Front page */
56
+ #sign-on { padding: 10px 0; margin: 10px auto; background-color: #fff; border: 1px solid #ccc; clear: both; text-align: center }
57
+ #sign-on input[type="text"] { width: 12em }
58
+ #sign-on input:focus { background-color: #ffc }
59
+ .content { text-align: left; margin: 0 20px; padding: 5px 0 }
60
+ .content em { padding: 1px 2px; font-style: normal; background-color: #ffc; color: #880000 }
61
+ .content h2 { margin-top: 20px }
62
+
63
+ #feedback .error { padding: 10px; border: 2px solid #990000; background-color: #fff; margin: 10px 0 }
64
+ #loading { background-color: #990000; color: #fff; font-weight: bold; padding: 3px; margin: 0 auto; position: absolute; z-index: 1020; top: 0; right: 10px; border-top: none }
65
+ .angry { color: #990000 }
66
+ .big_message { background-color: #fff; border: 1px solid #ccc; padding: 10px; }
67
+ .big_message h2, .big_message p { margin: 0; padding 0 }
68
+ .big_message h2 { margin-bottom: 10px }
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'sprockets'
4
+
5
+ sprockets_root = File.join(File.dirname(__FILE__), '..')
6
+ sprockets_config = YAML.load(IO.read(File.join(sprockets_root, 'config', 'sprockets.yml')))
7
+
8
+ secretary = Sprockets::Secretary.new(
9
+ :root => sprockets_root,
10
+ :load_path => sprockets_config[:load_path],
11
+ :source_files => sprockets_config[:source_files]
12
+ )
13
+
14
+ secretary.concatenation.save_to(File.join(sprockets_root, sprockets_config[:output_file]))
File without changes
@@ -0,0 +1,23 @@
1
+ <div class="content">
2
+ <h2>About</h2>
3
+ <p>JsChat is an <em>open source</em> chat system that uses a simple protocol based on <acronym title="JavaScript Object Notation">JSON</acronym>.</p>
4
+ <p>To download the code and an irssi-like <em>console client</em>, visit <a href="http://github.com/alexyoung/jschat/">GitHub</a>.</p>
5
+ <p>Read more on the <a href="http://blog.jschat.org">JsChat Blog</a>.</p>
6
+ <h2>Try JsChat Now</h2>
7
+ <div id="feedback" class="error" style="display: none"></div>
8
+ <form method="post" action="/identify" id="sign-on">
9
+ Enter name: <input name="name" id="name" value="" type="text" />
10
+ and room: <input name="room" id="room" value="#jschat" type="text" />
11
+ <input type="submit" value="Go" id="sign-on-submit" />
12
+ </form>
13
+ <h2>Features</h2>
14
+ <ul>
15
+ <li>Simple protocol that makes it easy to implement clients and bots</li>
16
+ <li>Console client designed to look and feel like IRC clients</li>
17
+ <li>Web client auto links and displays images/YouTube videos inline</li>
18
+ <li>Protocol designed to be close to executable code, so creating clients and bots is easy</li>
19
+ </ul>
20
+ </div>
21
+ <div class="footer">
22
+ An open source project by <a href="http://helicoid.net">Helicoid</a>
23
+ </div>
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <title>JsChat iPhone</title>
6
+ <link rel="icon" href="/favicon.ico" />
7
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
8
+ <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js" type="text/javascript"></script>
9
+ <script src="/javascripts/all.js?<%= Time.now.to_i %>" type="text/javascript"></script>
10
+ <link href="/stylesheets/screen.css?<%= Time.now.to_i %>" media="screen" rel="stylesheet" type="text/css" />
11
+ <link href="/stylesheets/iphone.css?<%= Time.now.to_i %>" media="screen" rel="stylesheet" type="text/css" />
12
+ </head>
13
+ <body class="iphone">
14
+ <div id="loading" style="display: none">Loading...</div>
15
+ <div class="header">
16
+ <h1><a href="/"><img src="/images/jschat.gif" alt="JsChat" /></a></h1>
17
+ <ul class="navigation">
18
+ <li><a href="/">Home</a></li>
19
+ <li><a href="http://github.com/alexyoung/jschat">Download</a>
20
+ <li id="help-nav" style="display: none"><a href="#" id="help-link">Help</a>
21
+ <li id="quit-nav" style="display: none"><a href="/quit">Quit</a>
22
+ </ul>
23
+ </div>
24
+ <div class="header-shadow"></div>
25
+ <div class="page">
26
+ <%= yield %>
27
+ </div>
28
+ </body>
29
+ </html>
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <title>JsChat</title>
6
+ <link rel="icon" href="/favicon.ico" />
7
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
8
+ <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js" type="text/javascript"></script>
9
+ <script src="/javascripts/all.js?<%= Time.now.to_i %>" type="text/javascript"></script>
10
+ <link href="/stylesheets/screen.css?<%= Time.now.to_i %>" media="screen" rel="stylesheet" type="text/css" />
11
+ </head>
12
+ <body>
13
+ <div id="loading" style="display: none">Loading...</div>
14
+ <div class="header">
15
+ <h1><a href="/"><img src="/images/jschat.gif" alt="JsChat" /></a></h1>
16
+ <ul class="navigation">
17
+ <li><a href="/">Home</a></li>
18
+ <li><a href="http://github.com/alexyoung/jschat">Download</a>
19
+ <li id="help-nav" style="display: none"><a href="#" id="help-link">Help</a>
20
+ <li id="quit-nav" style="display: none"><a href="/quit">Quit</a>
21
+ </ul>
22
+ </div>
23
+ <div class="header-shadow"></div>
24
+ <div class="page">
25
+ <%= yield %>
26
+ </div>
27
+ </body>
28
+ </html>
29
+
@@ -0,0 +1,15 @@
1
+ <ul id="messages">
2
+ </ul>
3
+ <div id="info">
4
+ <h2 id="room-name">Loading...</h2>
5
+ <div id="name" style="display: none"><%= nickname %></div>
6
+ <ul id="names">
7
+ </ul>
8
+ </div>
9
+ <div id="input">
10
+ <form method="post" action="/message" id="post_message">
11
+ <input name="message" id="message" value="" type="text" autocomplete="off" />
12
+ <input name="submit" type="submit" id="send_button" value="Send" />
13
+ </form>
14
+ </div>
15
+