mailcatcher-jruby 1.1.4 → 1.1.5

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.
@@ -1,3 +1,3 @@
1
- module MailCatcher
2
- VERSION = "1.1.4"
3
- end
1
+ module MailCatcher
2
+ VERSION = "1.1.5"
3
+ end
@@ -1,152 +1,152 @@
1
- require 'sinatra'
2
- require 'pathname'
3
- require 'net/http'
4
- require 'uri'
5
- require 'haml'
6
- #require 'skinny'
7
-
8
-
9
- class Sinatra::Request
10
- #include Skinny::Helpers
11
- end
12
-
13
- class MailCatcher::Web < Sinatra::Base
14
- use ActiveRecord::ConnectionAdapters::ConnectionManagement
15
-
16
- set :root, File.expand_path("#{__FILE__}/../../..")
17
- set :haml, :format => :html5
18
-
19
- get '/' do
20
- haml :index
21
- end
22
-
23
- delete '/' do
24
- if MailCatcher.quittable?
25
- MailCatcher.quit!
26
- status 204
27
- else
28
- status 403
29
- end
30
- end
31
-
32
- get '/messages' do
33
- # if request.websocket?
34
- # request.websocket!(
35
- # :on_start => proc do |websocket|
36
- # subscription = MailCatcher::Events::MessageAdded.subscribe { |message| websocket.send_message message.to_json }
37
- # websocket.on_close do |websocket|
38
- # MailCatcher::Events::MessageAdded.unsubscribe subscription
39
- # end
40
- # end)
41
- # else
42
- MailCatcher::Mail.messages.to_json
43
- # end
44
- end
45
-
46
- delete '/messages' do
47
- MailCatcher::Mail.delete!
48
- status 204
49
- end
50
-
51
- get '/messages/:id.json' do
52
- id = params[:id].to_i
53
- if message = MailCatcher::Mail.message(id)
54
- message.merge({
55
- "formats" => [
56
- "source",
57
- ("html" if MailCatcher::Mail.message_has_html? id),
58
- ("plain" if MailCatcher::Mail.message_has_plain? id)
59
- ].compact,
60
- "attachments" => MailCatcher::Mail.message_attachments(id).map do |attachment|
61
- attachment.merge({"href" => "/messages/#{escape(id)}/parts/#{escape(attachment['cid'])}"})
62
- end,
63
- }).to_json
64
- else
65
- not_found
66
- end
67
- end
68
-
69
- get '/messages/:id.html' do
70
- id = params[:id].to_i
71
- if part = MailCatcher::Mail.message_part_html(id)
72
- content_type part["type"], :charset => (part["charset"] || "utf8")
73
-
74
- body = part["body"]
75
-
76
- # Rewrite body to link to embedded attachments served by cid
77
- body.gsub! /cid:([^'"> ]+)/, "#{id}/parts/\\1"
78
-
79
- body
80
- else
81
- not_found
82
- end
83
- end
84
-
85
- get "/messages/:id.plain" do
86
- id = params[:id].to_i
87
- if part = MailCatcher::Mail.message_part_plain(id)
88
- content_type part["type"], :charset => (part["charset"] || "utf8")
89
- part["body"]
90
- else
91
- not_found
92
- end
93
- end
94
-
95
- get "/messages/:id.source" do
96
- id = params[:id].to_i
97
- if message = MailCatcher::Mail.message(id)
98
- content_type "text/plain"
99
- message["source"]
100
- else
101
- not_found
102
- end
103
- end
104
-
105
- get "/messages/:id.eml" do
106
- id = params[:id].to_i
107
- if message = MailCatcher::Mail.message(id)
108
- content_type "message/rfc822"
109
- message["source"]
110
- else
111
- not_found
112
- end
113
- end
114
-
115
- get "/messages/:id/parts/:cid" do
116
- id = params[:id].to_i
117
- if part = MailCatcher::Mail.message_part_cid(id, params[:cid])
118
- content_type part["type"], :charset => (part["charset"] || "utf8")
119
- attachment part["filename"] if part["is_attachment"] == 1
120
- body part["body"].to_s
121
- else
122
- not_found
123
- end
124
- end
125
-
126
- get "/messages/:id/analysis.?:format?" do
127
- id = params[:id].to_i
128
- if part = MailCatcher::Mail.message_part_html(id)
129
- # TODO: Server-side cache? Make the browser cache based on message create time? Hmm.
130
- uri = URI.parse("http://api.getfractal.com/api/v2/validate#{"/format/#{params[:format]}" if params[:format].present?}")
131
- response = Net::HTTP.post_form(uri, :api_key => "5c463877265251386f516f7428", :html => part["body"])
132
- content_type ".#{params[:format]}" if params[:format].present?
133
- body response.body
134
- else
135
- not_found
136
- end
137
- end
138
-
139
- delete '/messages/:id' do
140
- id = params[:id].to_i
141
- if message = MailCatcher::Mail.message(id)
142
- MailCatcher::Mail.delete_message!(id)
143
- status 204
144
- else
145
- not_found
146
- end
147
- end
148
-
149
- not_found do
150
- "<html><body><h1>No Dice</h1><p>The message you were looking for does not exist, or doesn't have content of this type.</p></body></html>"
151
- end
152
- end
1
+ require 'sinatra'
2
+ require 'pathname'
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'haml'
6
+ #require 'skinny'
7
+
8
+
9
+ class Sinatra::Request
10
+ #include Skinny::Helpers
11
+ end
12
+
13
+ class MailCatcher::Web < Sinatra::Base
14
+ use ActiveRecord::ConnectionAdapters::ConnectionManagement
15
+
16
+ set :root, File.expand_path("#{__FILE__}/../../..")
17
+ set :haml, :format => :html5
18
+
19
+ get '/' do
20
+ haml :index
21
+ end
22
+
23
+ delete '/' do
24
+ if MailCatcher.quittable?
25
+ MailCatcher.quit!
26
+ status 204
27
+ else
28
+ status 403
29
+ end
30
+ end
31
+
32
+ get '/messages' do
33
+ # if request.websocket?
34
+ # request.websocket!(
35
+ # :on_start => proc do |websocket|
36
+ # subscription = MailCatcher::Events::MessageAdded.subscribe { |message| websocket.send_message message.to_json }
37
+ # websocket.on_close do |websocket|
38
+ # MailCatcher::Events::MessageAdded.unsubscribe subscription
39
+ # end
40
+ # end)
41
+ # else
42
+ MailCatcher::Mail.messages.to_json
43
+ # end
44
+ end
45
+
46
+ delete '/messages' do
47
+ MailCatcher::Mail.delete!
48
+ status 204
49
+ end
50
+
51
+ get '/messages/:id.json' do
52
+ id = params[:id].to_i
53
+ if message = MailCatcher::Mail.message(id)
54
+ message.merge({
55
+ "formats" => [
56
+ "source",
57
+ ("html" if MailCatcher::Mail.message_has_html? id),
58
+ ("plain" if MailCatcher::Mail.message_has_plain? id)
59
+ ].compact,
60
+ "attachments" => MailCatcher::Mail.message_attachments(id).map do |attachment|
61
+ attachment.merge({"href" => "/messages/#{escape(id)}/parts/#{escape(attachment['cid'])}"})
62
+ end,
63
+ }).to_json
64
+ else
65
+ not_found
66
+ end
67
+ end
68
+
69
+ get '/messages/:id.html' do
70
+ id = params[:id].to_i
71
+ if part = MailCatcher::Mail.message_part_html(id)
72
+ content_type part["type"], :charset => (part["charset"] || "utf8")
73
+
74
+ body = part["body"]
75
+
76
+ # Rewrite body to link to embedded attachments served by cid
77
+ body.gsub! /cid:([^'"> ]+)/, "#{id}/parts/\\1"
78
+
79
+ body
80
+ else
81
+ not_found
82
+ end
83
+ end
84
+
85
+ get "/messages/:id.plain" do
86
+ id = params[:id].to_i
87
+ if part = MailCatcher::Mail.message_part_plain(id)
88
+ content_type part["type"], :charset => (part["charset"] || "utf8")
89
+ part["body"]
90
+ else
91
+ not_found
92
+ end
93
+ end
94
+
95
+ get "/messages/:id.source" do
96
+ id = params[:id].to_i
97
+ if message = MailCatcher::Mail.message(id)
98
+ content_type "text/plain"
99
+ message["source"]
100
+ else
101
+ not_found
102
+ end
103
+ end
104
+
105
+ get "/messages/:id.eml" do
106
+ id = params[:id].to_i
107
+ if message = MailCatcher::Mail.message(id)
108
+ content_type "message/rfc822"
109
+ message["source"]
110
+ else
111
+ not_found
112
+ end
113
+ end
114
+
115
+ get "/messages/:id/parts/:cid" do
116
+ id = params[:id].to_i
117
+ if part = MailCatcher::Mail.message_part_cid(id, params[:cid])
118
+ content_type part["type"], :charset => (part["charset"] || "utf8")
119
+ attachment part["filename"] if part["is_attachment"] == 1
120
+ body part["body"].to_s
121
+ else
122
+ not_found
123
+ end
124
+ end
125
+
126
+ get "/messages/:id/analysis.?:format?" do
127
+ id = params[:id].to_i
128
+ if part = MailCatcher::Mail.message_part_html(id)
129
+ # TODO: Server-side cache? Make the browser cache based on message create time? Hmm.
130
+ uri = URI.parse("http://api.getfractal.com/api/v2/validate#{"/format/#{params[:format]}" if params[:format].present?}")
131
+ response = Net::HTTP.post_form(uri, :api_key => "5c463877265251386f516f7428", :html => part["body"])
132
+ content_type ".#{params[:format]}" if params[:format].present?
133
+ body response.body
134
+ else
135
+ not_found
136
+ end
137
+ end
138
+
139
+ delete '/messages/:id' do
140
+ id = params[:id].to_i
141
+ if message = MailCatcher::Mail.message(id)
142
+ MailCatcher::Mail.delete_message!(id)
143
+ status 204
144
+ else
145
+ not_found
146
+ end
147
+ end
148
+
149
+ not_found do
150
+ "<html><body><h1>No Dice</h1><p>The message you were looking for does not exist, or doesn't have content of this type.</p></body></html>"
151
+ end
152
+ end
@@ -1,474 +1,441 @@
1
- (function() {
2
- var MailCatcher,
3
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
4
-
5
- jQuery.expr[':'].icontains = function(a, i, m) {
6
- var _ref, _ref1;
7
- return ((_ref = (_ref1 = a.textContent) != null ? _ref1 : a.innerText) != null ? _ref : "").toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
8
- };
9
-
10
- MailCatcher = (function() {
11
- function MailCatcher() {
12
- this.nextTab = __bind(this.nextTab, this);
13
- this.previousTab = __bind(this.previousTab, this);
14
- this.openTab = __bind(this.openTab, this);
15
- this.selectedTab = __bind(this.selectedTab, this);
16
- this.getTab = __bind(this.getTab, this);
17
- $('#messages tr').live('click', (function(_this) {
18
- return function(e) {
19
- e.preventDefault();
20
- return _this.loadMessage($(e.currentTarget).attr('data-message-id'));
21
- };
22
- })(this));
23
- $('input[name=search]').keyup((function(_this) {
24
- return function(e) {
25
- var query;
26
- query = $.trim($(e.currentTarget).val());
27
- if (query) {
28
- return _this.searchMessages(query);
29
- } else {
30
- return _this.clearSearch();
31
- }
32
- };
33
- })(this));
34
- $('#message .views .format.tab a').live('click', (function(_this) {
35
- return function(e) {
36
- e.preventDefault();
37
- return _this.loadMessageBody(_this.selectedMessage(), $($(e.currentTarget).parent('li')).data('message-format'));
38
- };
39
- })(this));
40
- $('#message .views .analysis.tab a').live('click', (function(_this) {
41
- return function(e) {
42
- e.preventDefault();
43
- return _this.loadMessageAnalysis(_this.selectedMessage());
44
- };
45
- })(this));
46
- $('#message iframe').load((function(_this) {
47
- return function() {
48
- return _this.decorateMessageBody();
49
- };
50
- })(this));
51
- $('#resizer').live({
52
- mousedown: (function(_this) {
53
- return function(e) {
54
- var events;
55
- e.preventDefault();
56
- return $(window).bind(events = {
57
- mouseup: function(e) {
58
- e.preventDefault();
59
- return $(window).unbind(events);
60
- },
61
- mousemove: function(e) {
62
- e.preventDefault();
63
- return _this.resizeTo(e.clientY);
64
- }
65
- });
66
- };
67
- })(this)
68
- });
69
- this.resizeToSaved();
70
- $('nav.app .clear a').live('click', (function(_this) {
71
- return function(e) {
72
- e.preventDefault();
73
- if (confirm("You will lose all your received messages.\n\nAre you sure you want to clear all messages?")) {
74
- return $.ajax({
75
- url: '/messages',
76
- type: 'DELETE',
77
- success: function() {
78
- return _this.unselectMessage();
79
- },
80
- error: function() {
81
- return alert('Error while clearing all messages.');
82
- }
83
- });
84
- }
85
- };
86
- })(this));
87
- $('nav.app .quit a').live('click', (function(_this) {
88
- return function(e) {
89
- e.preventDefault();
90
- if (confirm("You will lose all your received messages.\n\nAre you sure you want to quit?")) {
91
- return $.ajax({
92
- type: 'DELETE',
93
- success: function() {
94
- return location.replace($('body > header h1 a').attr('href'));
95
- },
96
- error: function() {
97
- return alert('Error while quitting.');
98
- }
99
- });
100
- }
101
- };
102
- })(this));
103
- key('up', (function(_this) {
104
- return function() {
105
- if (_this.selectedMessage()) {
106
- _this.loadMessage($('#messages tr.selected').prev().data('message-id'));
107
- } else {
108
- _this.loadMessage($('#messages tbody tr[data-message-id]:first').data('message-id'));
109
- }
110
- return false;
111
- };
112
- })(this));
113
- key('down', (function(_this) {
114
- return function() {
115
- if (_this.selectedMessage()) {
116
- _this.loadMessage($('#messages tr.selected').next().data('message-id'));
117
- } else {
118
- _this.loadMessage($('#messages tbody tr[data-message-id]:first').data('message-id'));
119
- }
120
- return false;
121
- };
122
- })(this));
123
- key('⌘+up, ctrl+up', (function(_this) {
124
- return function() {
125
- _this.loadMessage($('#messages tbody tr[data-message-id]:first').data('message-id'));
126
- return false;
127
- };
128
- })(this));
129
- key('⌘+down, ctrl+down', (function(_this) {
130
- return function() {
131
- _this.loadMessage($('#messages tbody tr[data-message-id]:last').data('message-id'));
132
- return false;
133
- };
134
- })(this));
135
- key('left', (function(_this) {
136
- return function() {
137
- _this.openTab(_this.previousTab());
138
- return false;
139
- };
140
- })(this));
141
- key('right', (function(_this) {
142
- return function() {
143
- _this.openTab(_this.nextTab());
144
- return false;
145
- };
146
- })(this));
147
- key('backspace, delete', (function(_this) {
148
- return function() {
149
- var id;
150
- id = _this.selectedMessage();
151
- if (id != null) {
152
- $.ajax({
153
- url: '/messages/' + id,
154
- type: 'DELETE',
155
- success: function() {
156
- var messageRow, switchTo;
157
- messageRow = $("#messages tbody tr[data-message-id='" + id + "']");
158
- switchTo = messageRow.next().data('message-id') || messageRow.prev().data('message-id');
159
- messageRow.remove();
160
- if (switchTo) {
161
- return _this.loadMessage(switchTo);
162
- } else {
163
- return _this.unselectMessage();
164
- }
165
- },
166
- error: function() {
167
- return alert('Error while removing message.');
168
- }
169
- });
170
- }
171
- return false;
172
- };
173
- })(this));
174
- this.refresh();
175
- this.subscribe();
176
- }
177
-
178
- MailCatcher.prototype.parseDateRegexp = /^(\d{4})[-\/\\](\d{2})[-\/\\](\d{2})(?:\s+|T)(\d{2})[:-](\d{2})[:-](\d{2})(?:([ +-]\d{2}:\d{2}|\s*\S+|Z?))?$/;
179
-
180
- MailCatcher.prototype.parseDate = function(date) {
181
- var match;
182
- if (match = this.parseDateRegexp.exec(date)) {
183
- return new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6], 0);
184
- }
185
- };
186
-
187
- MailCatcher.prototype.offsetTimeZone = function(date) {
188
- var offset;
189
- offset = Date.now().getTimezoneOffset() * 60000;
190
- date.setTime(date.getTime() - offset);
191
- return date;
192
- };
193
-
194
- MailCatcher.prototype.formatDate = function(date) {
195
- if (typeof date === "string") {
196
- date && (date = this.parseDate(date));
197
- }
198
- date && (date = this.offsetTimeZone(date));
199
- return date && (date = date.toString("dddd, d MMM yyyy h:mm:ss tt"));
200
- };
201
-
202
- MailCatcher.prototype.messagesCount = function() {
203
- return $('#messages tr').length - 1;
204
- };
205
-
206
- MailCatcher.prototype.tabs = function() {
207
- return $('#message ul').children('.tab');
208
- };
209
-
210
- MailCatcher.prototype.getTab = function(i) {
211
- return $(this.tabs()[i]);
212
- };
213
-
214
- MailCatcher.prototype.selectedTab = function() {
215
- return this.tabs().index($('#message li.tab.selected'));
216
- };
217
-
218
- MailCatcher.prototype.openTab = function(i) {
219
- return this.getTab(i).children('a').click();
220
- };
221
-
222
- MailCatcher.prototype.previousTab = function(tab) {
223
- var i;
224
- i = tab || tab === 0 ? tab : this.selectedTab() - 1;
225
- if (i < 0) {
226
- i = this.tabs().length - 1;
227
- }
228
- if (this.getTab(i).is(":visible")) {
229
- return i;
230
- } else {
231
- return this.previousTab(i - 1);
232
- }
233
- };
234
-
235
- MailCatcher.prototype.nextTab = function(tab) {
236
- var i;
237
- i = tab ? tab : this.selectedTab() + 1;
238
- if (i > this.tabs().length - 1) {
239
- i = 0;
240
- }
241
- if (this.getTab(i).is(":visible")) {
242
- return i;
243
- } else {
244
- return this.nextTab(i + 1);
245
- }
246
- };
247
-
248
- MailCatcher.prototype.haveMessage = function(message) {
249
- if (message.id != null) {
250
- message = message.id;
251
- }
252
- return $("#messages tbody tr[data-message-id=\"" + message + "\"]").length > 0;
253
- };
254
-
255
- MailCatcher.prototype.selectedMessage = function() {
256
- return $('#messages tr.selected').data('message-id');
257
- };
258
-
259
- MailCatcher.prototype.searchMessages = function(query) {
260
- var $rows, selector, token;
261
- selector = ((function() {
262
- var _i, _len, _ref, _results;
263
- _ref = query.split(/\s+/);
264
- _results = [];
265
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
266
- token = _ref[_i];
267
- _results.push(":icontains('" + token + "')");
268
- }
269
- return _results;
270
- })()).join("");
271
- $rows = $("#messages tbody tr");
272
- $rows.not(selector).hide();
273
- return $rows.filter(selector).show();
274
- };
275
-
276
- MailCatcher.prototype.clearSearch = function() {
277
- return $('#messages tbody tr').show();
278
- };
279
-
280
- MailCatcher.prototype.addMessage = function(message) {
281
- return $('#messages tbody').prepend($('<tr />').attr('data-message-id', message.id.toString())).append($('<td/>').text(message.sender || "No sender").toggleClass("blank", !message.sender)).append($('<td/>').text((message.recipients || []).join(', ') || "No recipients").toggleClass("blank", !message.recipients.length)).append($('<td/>').text(message.subject || "No subject").toggleClass("blank", !message.subject)).append($('<td/>').text(this.formatDate(message.created_at)));
282
- };
283
-
284
- MailCatcher.prototype.scrollToRow = function(row) {
285
- var overflow, relativePosition;
286
- relativePosition = row.offset().top - $('#messages').offset().top;
287
- if (relativePosition < 0) {
288
- return $('#messages').scrollTop($('#messages').scrollTop() + relativePosition - 20);
289
- } else {
290
- overflow = relativePosition + row.height() - $('#messages').height();
291
- if (overflow > 0) {
292
- return $('#messages').scrollTop($('#messages').scrollTop() + overflow + 20);
293
- }
294
- }
295
- };
296
-
297
- MailCatcher.prototype.unselectMessage = function() {
298
- $('#messages tbody, #message .metadata dd').empty();
299
- $('#message .metadata .attachments').hide();
300
- $('#message iframe').attr('src', 'about:blank');
301
- return null;
302
- };
303
-
304
- MailCatcher.prototype.loadMessage = function(id) {
305
- var messageRow;
306
- if ((id != null ? id.id : void 0) != null) {
307
- id = id.id;
308
- }
309
- id || (id = $('#messages tr.selected').attr('data-message-id'));
310
- if (id != null) {
311
- $("#messages tbody tr:not([data-message-id='" + id + "'])").removeClass('selected');
312
- messageRow = $("#messages tbody tr[data-message-id='" + id + "']");
313
- messageRow.addClass('selected');
314
- this.scrollToRow(messageRow);
315
- return $.getJSON("/messages/" + id + ".json", (function(_this) {
316
- return function(message) {
317
- var $ul;
318
- if (message.recipients != null) {
319
- message.recipients = JSON.parse(message.recipients);
320
- }
321
- $('#message .metadata dd.created_at').text(_this.formatDate(message.created_at));
322
- $('#message .metadata dd.from').text(message.sender);
323
- $('#message .metadata dd.to').text((message.recipients || []).join(', '));
324
- $('#message .metadata dd.subject').text(message.subject);
325
- $('#message .views .tab.format').each(function(i, el) {
326
- var $el, format;
327
- $el = $(el);
328
- format = $el.attr('data-message-format');
329
- if ($.inArray(format, message.formats) >= 0) {
330
- $el.find('a').attr('href', "/messages/" + id + "." + format);
331
- return $el.show();
332
- } else {
333
- return $el.hide();
334
- }
335
- });
336
- if ($("#message .views .tab.selected:not(:visible)").length) {
337
- $("#message .views .tab.selected").removeClass("selected");
338
- $("#message .views .tab:visible:first").addClass("selected");
339
- }
340
- if (message.attachments.length) {
341
- $ul = $('<ul/>').appendTo($('#message .metadata dd.attachments').empty());
342
- $.each(message.attachments, function(i, attachment) {
343
- return $ul.append($('<li>').append($('<a>').attr('href', attachment['href']).addClass(attachment['type'].split('/', 1)[0]).addClass(attachment['type'].replace('/', '-')).text(attachment['filename'])));
344
- });
345
- $('#message .metadata .attachments').show();
346
- } else {
347
- $('#message .metadata .attachments').hide();
348
- }
349
- $('#message .views .download a').attr('href', "/messages/" + id + ".eml");
350
- if ($('#message .views .tab.analysis.selected').length) {
351
- return _this.loadMessageAnalysis();
352
- } else {
353
- return _this.loadMessageBody();
354
- }
355
- };
356
- })(this));
357
- }
358
- };
359
-
360
- MailCatcher.prototype.loadMessageBody = function(id, format) {
361
- var app;
362
- id || (id = this.selectedMessage());
363
- format || (format = $('#message .views .tab.format.selected').attr('data-message-format'));
364
- format || (format = 'html');
365
- $("#message .views .tab[data-message-format=\"" + format + "\"]:not(.selected)").addClass('selected');
366
- $("#message .views .tab:not([data-message-format=\"" + format + "\"]).selected").removeClass('selected');
367
- if (id != null) {
368
- $('#message iframe').attr("src", "/messages/" + id + "." + format);
369
- return app = this;
370
- }
371
- };
372
-
373
- MailCatcher.prototype.decorateMessageBody = function() {
374
- var body, format, message_iframe, text;
375
- format = $('#message .views .tab.format.selected').attr('data-message-format');
376
- switch (format) {
377
- case 'html':
378
- body = $('#message iframe').contents().find('body');
379
- return $("a", body).attr("target", "_blank");
380
- case 'plain':
381
- message_iframe = $('#message iframe').contents();
382
- text = message_iframe.text();
383
- text = text.replace(/((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:\/~\+#]*[\w\-\@?^=%&amp;\/~\+#])?)/g, '<a href="$1" target="_blank">$1</a>');
384
- text = text.replace(/\n/g, '<br/>');
385
- return message_iframe.find('html').html('<html><body>' + text + '</html></body>');
386
- }
387
- };
388
-
389
- MailCatcher.prototype.loadMessageAnalysis = function(id) {
390
- var $form, $iframe;
391
- id || (id = this.selectedMessage());
392
- $("#message .views .analysis.tab:not(.selected)").addClass('selected');
393
- $("#message .views :not(.analysis).tab.selected").removeClass('selected');
394
- if (id != null) {
395
- $iframe = $('#message iframe').contents().children().html("<html>\n<head>\n<title>Analysis</title>\n" + ($('link[rel="stylesheet"]')[0].outerHTML) + "\n</head>\n<body class=\"iframe\">\n<h1>Analyse your email with Fractal</h1>\n<p><a href=\"http://getfractal.com/\" target=\"_blank\">Fractal</a> is a really neat service that applies common email design and development knowledge from <a href=\"http://www.email-standards.org/\" target=\"_blank\">Email Standards Project</a> to your HTML email and tells you what you've done wrong or what you should do instead.</p>\n<p>Please note that this <strong>sends your email to the Fractal service</strong> for analysis. Read their <a href=\"https://www.getfractal.com/page/terms\" target=\"_blank\">terms of service</a> if you're paranoid.</p>\n<form>\n<input type=\"submit\" value=\"Analyse\" /><span class=\"loading\" style=\"color: #999; display: none\">Analysing&hellip;</span>\n</form>\n</body>\n</html>");
396
- return $form = $iframe.find('form').submit(function(e) {
397
- e.preventDefault();
398
- $(this).find('input[type="submit"]').attr('disabled', 'disabled').end().find('.loading').show();
399
- return $('#message iframe').contents().find('body').xslt("/messages/" + id + "/analysis.xml", "/stylesheets/analysis.xsl");
400
- });
401
- }
402
- };
403
-
404
- MailCatcher.prototype.refresh = function() {
405
- return $.getJSON('/messages', (function(_this) {
406
- return function(messages) {
407
- return $.each(messages, function(i, message) {
408
- if (!_this.haveMessage(message)) {
409
- if (message.recipients != null) {
410
- message.recipients = JSON.parse(message.recipients);
411
- }
412
- return _this.addMessage(message);
413
- }
414
- });
415
- };
416
- })(this));
417
- };
418
-
419
- MailCatcher.prototype.subscribe = function() {
420
- if (typeof WebSocket !== "undefined" && WebSocket !== null) {
421
-
422
- } else {
423
- return this.subscribePoll();
424
- }
425
- };
426
-
427
- MailCatcher.prototype.subscribeWebSocket = function() {
428
- var secure;
429
- secure = window.location.scheme === 'https';
430
- this.websocket = new WebSocket("" + (secure ? 'wss' : 'ws') + "://" + window.location.host + "/messages");
431
- return this.websocket.onmessage = (function(_this) {
432
- return function(event) {
433
- return _this.addMessage($.parseJSON(event.data));
434
- };
435
- })(this);
436
- };
437
-
438
- MailCatcher.prototype.subscribePoll = function() {
439
- if (this.refreshInterval == null) {
440
- return this.refreshInterval = setInterval(((function(_this) {
441
- return function() {
442
- return _this.refresh();
443
- };
444
- })(this)), 1000);
445
- }
446
- };
447
-
448
- MailCatcher.prototype.resizeToSavedKey = 'mailcatcherSeparatorHeight';
449
-
450
- MailCatcher.prototype.resizeTo = function(height) {
451
- var _ref;
452
- $('#messages').css({
453
- height: height - $('#messages').offset().top
454
- });
455
- return (_ref = window.localStorage) != null ? _ref.setItem(this.resizeToSavedKey, height) : void 0;
456
- };
457
-
458
- MailCatcher.prototype.resizeToSaved = function() {
459
- var height, _ref;
460
- height = parseInt((_ref = window.localStorage) != null ? _ref.getItem(this.resizeToSavedKey) : void 0);
461
- if (!isNaN(height)) {
462
- return this.resizeTo(height);
463
- }
464
- };
465
-
466
- return MailCatcher;
467
-
468
- })();
469
-
470
- $(function() {
471
- return window.MailCatcher = new MailCatcher;
472
- });
473
-
474
- }).call(this);
1
+ (function() {
2
+ var MailCatcher,
3
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
4
+
5
+ jQuery.expr[':'].icontains = function(a, i, m) {
6
+ var _ref, _ref1;
7
+ return ((_ref = (_ref1 = a.textContent) != null ? _ref1 : a.innerText) != null ? _ref : "").toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
8
+ };
9
+
10
+ MailCatcher = (function() {
11
+ function MailCatcher() {
12
+ this.nextTab = __bind(this.nextTab, this);
13
+ this.previousTab = __bind(this.previousTab, this);
14
+ this.openTab = __bind(this.openTab, this);
15
+ this.selectedTab = __bind(this.selectedTab, this);
16
+ this.getTab = __bind(this.getTab, this);
17
+ var _this = this;
18
+ $('#messages tr').live('click', function(e) {
19
+ e.preventDefault();
20
+ return _this.loadMessage($(e.currentTarget).attr('data-message-id'));
21
+ });
22
+ $('input[name=search]').keyup(function(e) {
23
+ var query;
24
+ query = $.trim($(e.currentTarget).val());
25
+ if (query) {
26
+ return _this.searchMessages(query);
27
+ } else {
28
+ return _this.clearSearch();
29
+ }
30
+ });
31
+ $('#message .views .format.tab a').live('click', function(e) {
32
+ e.preventDefault();
33
+ return _this.loadMessageBody(_this.selectedMessage(), $($(e.currentTarget).parent('li')).data('message-format'));
34
+ });
35
+ $('#message .views .analysis.tab a').live('click', function(e) {
36
+ e.preventDefault();
37
+ return _this.loadMessageAnalysis(_this.selectedMessage());
38
+ });
39
+ $('#message iframe').load(function() {
40
+ return _this.decorateMessageBody();
41
+ });
42
+ $('#resizer').live({
43
+ mousedown: function(e) {
44
+ var events;
45
+ e.preventDefault();
46
+ return $(window).bind(events = {
47
+ mouseup: function(e) {
48
+ e.preventDefault();
49
+ return $(window).unbind(events);
50
+ },
51
+ mousemove: function(e) {
52
+ e.preventDefault();
53
+ return _this.resizeTo(e.clientY);
54
+ }
55
+ });
56
+ }
57
+ });
58
+ this.resizeToSaved();
59
+ $('nav.app .clear a').live('click', function(e) {
60
+ e.preventDefault();
61
+ if (confirm("You will lose all your received messages.\n\nAre you sure you want to clear all messages?")) {
62
+ return $.ajax({
63
+ url: '/messages',
64
+ type: 'DELETE',
65
+ success: function() {
66
+ return _this.unselectMessage();
67
+ },
68
+ error: function() {
69
+ return alert('Error while clearing all messages.');
70
+ }
71
+ });
72
+ }
73
+ });
74
+ $('nav.app .quit a').live('click', function(e) {
75
+ e.preventDefault();
76
+ if (confirm("You will lose all your received messages.\n\nAre you sure you want to quit?")) {
77
+ return $.ajax({
78
+ type: 'DELETE',
79
+ success: function() {
80
+ return location.replace($('body > header h1 a').attr('href'));
81
+ },
82
+ error: function() {
83
+ return alert('Error while quitting.');
84
+ }
85
+ });
86
+ }
87
+ });
88
+ key('up', function() {
89
+ if (_this.selectedMessage()) {
90
+ _this.loadMessage($('#messages tr.selected').prev().data('message-id'));
91
+ } else {
92
+ _this.loadMessage($('#messages tbody tr[data-message-id]:first').data('message-id'));
93
+ }
94
+ return false;
95
+ });
96
+ key('down', function() {
97
+ if (_this.selectedMessage()) {
98
+ _this.loadMessage($('#messages tr.selected').next().data('message-id'));
99
+ } else {
100
+ _this.loadMessage($('#messages tbody tr[data-message-id]:first').data('message-id'));
101
+ }
102
+ return false;
103
+ });
104
+ key('Γîÿ+up, ctrl+up', function() {
105
+ _this.loadMessage($('#messages tbody tr[data-message-id]:first').data('message-id'));
106
+ return false;
107
+ });
108
+ key('Γîÿ+down, ctrl+down', function() {
109
+ _this.loadMessage($('#messages tbody tr[data-message-id]:last').data('message-id'));
110
+ return false;
111
+ });
112
+ key('left', function() {
113
+ _this.openTab(_this.previousTab());
114
+ return false;
115
+ });
116
+ key('right', function() {
117
+ _this.openTab(_this.nextTab());
118
+ return false;
119
+ });
120
+ key('backspace, delete', function() {
121
+ var id;
122
+ id = _this.selectedMessage();
123
+ if (id != null) {
124
+ $.ajax({
125
+ url: '/messages/' + id,
126
+ type: 'DELETE',
127
+ success: function() {
128
+ var messageRow, switchTo;
129
+ messageRow = $("#messages tbody tr[data-message-id='" + id + "']");
130
+ switchTo = messageRow.next().data('message-id') || messageRow.prev().data('message-id');
131
+ messageRow.remove();
132
+ if (switchTo) {
133
+ return _this.loadMessage(switchTo);
134
+ } else {
135
+ return _this.unselectMessage();
136
+ }
137
+ },
138
+ error: function() {
139
+ return alert('Error while removing message.');
140
+ }
141
+ });
142
+ }
143
+ return false;
144
+ });
145
+ this.refresh();
146
+ this.subscribe();
147
+ }
148
+
149
+ MailCatcher.prototype.parseDateRegexp = /^(\d{4})[-\/\\](\d{2})[-\/\\](\d{2})(?:\s+|T)(\d{2})[:-](\d{2})[:-](\d{2})(?:([ +-]\d{2}:\d{2}|\s*\S+|Z?))?$/;
150
+
151
+ MailCatcher.prototype.parseDate = function(date) {
152
+ var match;
153
+ if (match = this.parseDateRegexp.exec(date)) {
154
+ return new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6], 0);
155
+ }
156
+ };
157
+
158
+ MailCatcher.prototype.offsetTimeZone = function(date) {
159
+ var offset;
160
+ offset = Date.now().getTimezoneOffset() * 60000;
161
+ date.setTime(date.getTime() - offset);
162
+ return date;
163
+ };
164
+
165
+ MailCatcher.prototype.formatDate = function(date) {
166
+ if (typeof date === "string") {
167
+ date && (date = this.parseDate(date));
168
+ }
169
+ date && (date = this.offsetTimeZone(date));
170
+ return date && (date = date.toString("dddd, d MMM yyyy h:mm:ss tt"));
171
+ };
172
+
173
+ MailCatcher.prototype.messagesCount = function() {
174
+ return $('#messages tr').length - 1;
175
+ };
176
+
177
+ MailCatcher.prototype.tabs = function() {
178
+ return $('#message ul').children('.tab');
179
+ };
180
+
181
+ MailCatcher.prototype.getTab = function(i) {
182
+ return $(this.tabs()[i]);
183
+ };
184
+
185
+ MailCatcher.prototype.selectedTab = function() {
186
+ return this.tabs().index($('#message li.tab.selected'));
187
+ };
188
+
189
+ MailCatcher.prototype.openTab = function(i) {
190
+ return this.getTab(i).children('a').click();
191
+ };
192
+
193
+ MailCatcher.prototype.previousTab = function(tab) {
194
+ var i;
195
+ i = tab || tab === 0 ? tab : this.selectedTab() - 1;
196
+ if (i < 0) {
197
+ i = this.tabs().length - 1;
198
+ }
199
+ if (this.getTab(i).is(":visible")) {
200
+ return i;
201
+ } else {
202
+ return this.previousTab(i - 1);
203
+ }
204
+ };
205
+
206
+ MailCatcher.prototype.nextTab = function(tab) {
207
+ var i;
208
+ i = tab ? tab : this.selectedTab() + 1;
209
+ if (i > this.tabs().length - 1) {
210
+ i = 0;
211
+ }
212
+ if (this.getTab(i).is(":visible")) {
213
+ return i;
214
+ } else {
215
+ return this.nextTab(i + 1);
216
+ }
217
+ };
218
+
219
+ MailCatcher.prototype.haveMessage = function(message) {
220
+ if (message.id != null) {
221
+ message = message.id;
222
+ }
223
+ return $("#messages tbody tr[data-message-id=\"" + message + "\"]").length > 0;
224
+ };
225
+
226
+ MailCatcher.prototype.selectedMessage = function() {
227
+ return $('#messages tr.selected').data('message-id');
228
+ };
229
+
230
+ MailCatcher.prototype.searchMessages = function(query) {
231
+ var $rows, selector, token;
232
+ selector = ((function() {
233
+ var _i, _len, _ref, _results;
234
+ _ref = query.split(/\s+/);
235
+ _results = [];
236
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
237
+ token = _ref[_i];
238
+ _results.push(":icontains('" + token + "')");
239
+ }
240
+ return _results;
241
+ })()).join("");
242
+ $rows = $("#messages tbody tr");
243
+ $rows.not(selector).hide();
244
+ return $rows.filter(selector).show();
245
+ };
246
+
247
+ MailCatcher.prototype.clearSearch = function() {
248
+ return $('#messages tbody tr').show();
249
+ };
250
+
251
+ MailCatcher.prototype.addMessage = function(message) {
252
+ return $('#messages tbody').prepend($('<tr />').attr('data-message-id', message.id.toString()).append($('<td/>').text(message.sender || "No sender").toggleClass("blank", !message.sender)).append($('<td/>').text((message.recipients || []).join(', ') || "No recipients").toggleClass("blank", !message.recipients.length)).append($('<td/>').text(message.subject || "No subject").toggleClass("blank", !message.subject)).append($('<td/>').text(this.formatDate(message.created_at))));
253
+ };
254
+
255
+ MailCatcher.prototype.scrollToRow = function(row) {
256
+ var overflow, relativePosition;
257
+ relativePosition = row.offset().top - $('#messages').offset().top;
258
+ if (relativePosition < 0) {
259
+ return $('#messages').scrollTop($('#messages').scrollTop() + relativePosition - 20);
260
+ } else {
261
+ overflow = relativePosition + row.height() - $('#messages').height();
262
+ if (overflow > 0) {
263
+ return $('#messages').scrollTop($('#messages').scrollTop() + overflow + 20);
264
+ }
265
+ }
266
+ };
267
+
268
+ MailCatcher.prototype.unselectMessage = function() {
269
+ $('#messages tbody, #message .metadata dd').empty();
270
+ $('#message .metadata .attachments').hide();
271
+ $('#message iframe').attr('src', 'about:blank');
272
+ return null;
273
+ };
274
+
275
+ MailCatcher.prototype.loadMessage = function(id) {
276
+ var messageRow,
277
+ _this = this;
278
+ if ((id != null ? id.id : void 0) != null) {
279
+ id = id.id;
280
+ }
281
+ id || (id = $('#messages tr.selected').attr('data-message-id'));
282
+ if (id != null) {
283
+ $("#messages tbody tr:not([data-message-id='" + id + "'])").removeClass('selected');
284
+ messageRow = $("#messages tbody tr[data-message-id='" + id + "']");
285
+ messageRow.addClass('selected');
286
+ this.scrollToRow(messageRow);
287
+ return $.getJSON("/messages/" + id + ".json", function(message) {
288
+ var $ul;
289
+ if (message.recipients != null) {
290
+ message.recipients = JSON.parse(message.recipients);
291
+ }
292
+ $('#message .metadata dd.created_at').text(_this.formatDate(message.created_at));
293
+ $('#message .metadata dd.from').text(message.sender);
294
+ $('#message .metadata dd.to').text((message.recipients || []).join(', '));
295
+ $('#message .metadata dd.subject').text(message.subject);
296
+ $('#message .views .tab.format').each(function(i, el) {
297
+ var $el, format;
298
+ $el = $(el);
299
+ format = $el.attr('data-message-format');
300
+ if ($.inArray(format, message.formats) >= 0) {
301
+ $el.find('a').attr('href', "/messages/" + id + "." + format);
302
+ return $el.show();
303
+ } else {
304
+ return $el.hide();
305
+ }
306
+ });
307
+ if ($("#message .views .tab.selected:not(:visible)").length) {
308
+ $("#message .views .tab.selected").removeClass("selected");
309
+ $("#message .views .tab:visible:first").addClass("selected");
310
+ }
311
+ if (message.attachments.length) {
312
+ $ul = $('<ul/>').appendTo($('#message .metadata dd.attachments').empty());
313
+ $.each(message.attachments, function(i, attachment) {
314
+ return $ul.append($('<li>').append($('<a>').attr('href', attachment['href']).addClass(attachment['type'].split('/', 1)[0]).addClass(attachment['type'].replace('/', '-')).text(attachment['filename'])));
315
+ });
316
+ $('#message .metadata .attachments').show();
317
+ } else {
318
+ $('#message .metadata .attachments').hide();
319
+ }
320
+ $('#message .views .download a').attr('href', "/messages/" + id + ".eml");
321
+ if ($('#message .views .tab.analysis.selected').length) {
322
+ return _this.loadMessageAnalysis();
323
+ } else {
324
+ return _this.loadMessageBody();
325
+ }
326
+ });
327
+ }
328
+ };
329
+
330
+ MailCatcher.prototype.loadMessageBody = function(id, format) {
331
+ var app;
332
+ id || (id = this.selectedMessage());
333
+ format || (format = $('#message .views .tab.format.selected').attr('data-message-format'));
334
+ format || (format = 'html');
335
+ $("#message .views .tab[data-message-format=\"" + format + "\"]:not(.selected)").addClass('selected');
336
+ $("#message .views .tab:not([data-message-format=\"" + format + "\"]).selected").removeClass('selected');
337
+ if (id != null) {
338
+ $('#message iframe').attr("src", "/messages/" + id + "." + format);
339
+ return app = this;
340
+ }
341
+ };
342
+
343
+ MailCatcher.prototype.decorateMessageBody = function() {
344
+ var body, format, message_iframe, text;
345
+ format = $('#message .views .tab.format.selected').attr('data-message-format');
346
+ switch (format) {
347
+ case 'html':
348
+ body = $('#message iframe').contents().find('body');
349
+ return $("a", body).attr("target", "_blank");
350
+ case 'plain':
351
+ message_iframe = $('#message iframe').contents();
352
+ text = message_iframe.text();
353
+ text = text.replace(/((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:\/~\+#]*[\w\-\@?^=%&amp;\/~\+#])?)/g, '<a href="$1" target="_blank">$1</a>');
354
+ text = text.replace(/\n/g, '<br/>');
355
+ return message_iframe.find('html').html('<html><body>' + text + '</html></body>');
356
+ }
357
+ };
358
+
359
+ MailCatcher.prototype.loadMessageAnalysis = function(id) {
360
+ var $form, $iframe;
361
+ id || (id = this.selectedMessage());
362
+ $("#message .views .analysis.tab:not(.selected)").addClass('selected');
363
+ $("#message .views :not(.analysis).tab.selected").removeClass('selected');
364
+ if (id != null) {
365
+ $iframe = $('#message iframe').contents().children().html("<html>\n<head>\n<title>Analysis</title>\n" + ($('link[rel="stylesheet"]')[0].outerHTML) + "\n</head>\n<body class=\"iframe\">\n<h1>Analyse your email with Fractal</h1>\n<p><a href=\"http://getfractal.com/\" target=\"_blank\">Fractal</a> is a really neat service that applies common email design and development knowledge from <a href=\"http://www.email-standards.org/\" target=\"_blank\">Email Standards Project</a> to your HTML email and tells you what you've done wrong or what you should do instead.</p>\n<p>Please note that this <strong>sends your email to the Fractal service</strong> for analysis. Read their <a href=\"https://www.getfractal.com/page/terms\" target=\"_blank\">terms of service</a> if you're paranoid.</p>\n<form>\n<input type=\"submit\" value=\"Analyse\" /><span class=\"loading\" style=\"color: #999; display: none\">Analysing&hellip;</span>\n</form>\n</body>\n</html>");
366
+ return $form = $iframe.find('form').submit(function(e) {
367
+ e.preventDefault();
368
+ $(this).find('input[type="submit"]').attr('disabled', 'disabled').end().find('.loading').show();
369
+ return $('#message iframe').contents().find('body').xslt("/messages/" + id + "/analysis.xml", "/stylesheets/analysis.xsl");
370
+ });
371
+ }
372
+ };
373
+
374
+ MailCatcher.prototype.refresh = function() {
375
+ var _this = this;
376
+ return $.getJSON('/messages', function(messages) {
377
+ return $.each(messages, function(i, message) {
378
+ if (!_this.haveMessage(message)) {
379
+ if (message.recipients != null) {
380
+ message.recipients = JSON.parse(message.recipients);
381
+ }
382
+ return _this.addMessage(message);
383
+ }
384
+ });
385
+ });
386
+ };
387
+
388
+ MailCatcher.prototype.subscribe = function() {
389
+ if (typeof WebSocket !== "undefined" && WebSocket !== null) {
390
+
391
+ } else {
392
+ return this.subscribePoll();
393
+ }
394
+ };
395
+
396
+ MailCatcher.prototype.subscribeWebSocket = function() {
397
+ var secure,
398
+ _this = this;
399
+ secure = window.location.scheme === 'https';
400
+ this.websocket = new WebSocket("" + (secure ? 'wss' : 'ws') + "://" + window.location.host + "/messages");
401
+ return this.websocket.onmessage = function(event) {
402
+ return _this.addMessage($.parseJSON(event.data));
403
+ };
404
+ };
405
+
406
+ MailCatcher.prototype.subscribePoll = function() {
407
+ var _this = this;
408
+ if (this.refreshInterval == null) {
409
+ return this.refreshInterval = setInterval((function() {
410
+ return _this.refresh();
411
+ }), 1000);
412
+ }
413
+ };
414
+
415
+ MailCatcher.prototype.resizeToSavedKey = 'mailcatcherSeparatorHeight';
416
+
417
+ MailCatcher.prototype.resizeTo = function(height) {
418
+ var _ref;
419
+ $('#messages').css({
420
+ height: height - $('#messages').offset().top
421
+ });
422
+ return (_ref = window.localStorage) != null ? _ref.setItem(this.resizeToSavedKey, height) : void 0;
423
+ };
424
+
425
+ MailCatcher.prototype.resizeToSaved = function() {
426
+ var height, _ref;
427
+ height = parseInt((_ref = window.localStorage) != null ? _ref.getItem(this.resizeToSavedKey) : void 0);
428
+ if (!isNaN(height)) {
429
+ return this.resizeTo(height);
430
+ }
431
+ };
432
+
433
+ return MailCatcher;
434
+
435
+ })();
436
+
437
+ $(function() {
438
+ return window.MailCatcher = new MailCatcher;
439
+ });
440
+
441
+ }).call(this);