mail_grabber 1.0.0.rc1

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.
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MailGrabber
4
+ class DeliveryMethod
5
+ include DatabaseHelper
6
+
7
+ # Initialize MailGrabber delivery method (Rails needs it).
8
+ def initialize(options = {}); end
9
+
10
+ # Catch and save messages into the database that we can check those messages
11
+ # in MailGrabber web application.
12
+ #
13
+ # @param [Mail::Message] message what we would like to send
14
+ def deliver!(message)
15
+ unless message.is_a?(Mail::Message)
16
+ raise Error::WrongParameter,
17
+ 'The given parameter is not a Mail::Message'
18
+ end
19
+
20
+ store_mail(message)
21
+ end
22
+
23
+ # Delivery method settings (needed when run mail.deliver! method)
24
+ def settings
25
+ {}
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MailGrabber
4
+ class Error < StandardError
5
+ # Specific error class for errors if database error happen
6
+ class DatabaseHelperError < Error; end
7
+
8
+ # Specific error class for errors if parameter is not given
9
+ class WrongParameter < Error; end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MailGrabber
4
+ class Railtie < Rails::Railtie
5
+ initializer 'mail_grabber.add_delivery_method' do
6
+ ActiveSupport.on_load :action_mailer do
7
+ ActionMailer::Base.add_delivery_method(
8
+ :mail_grabber,
9
+ MailGrabber::DeliveryMethod
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MailGrabber
4
+ VERSION = '1.0.0.rc1'
5
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'rack'
5
+
6
+ require 'mail_grabber/error'
7
+ require 'mail_grabber/database_helper'
8
+
9
+ require 'mail_grabber/web/application_helper'
10
+ require 'mail_grabber/web/application_router'
11
+ require 'mail_grabber/web/application'
12
+
13
+ module MailGrabber
14
+ module Web
15
+ module_function
16
+
17
+ # Method which build a Rack application and run the Web::Application
18
+ def app
19
+ @app ||= Rack::Builder.new do
20
+ use Rack::Static,
21
+ urls: ['/images', '/javascripts', '/stylesheets'],
22
+ root: File.expand_path('web/assets', __dir__)
23
+
24
+ run Web::Application
25
+ end
26
+ end
27
+
28
+ # Method to call MailGrabber::Web. This method will call the app method
29
+ # above.
30
+ #
31
+ # @param [Hash] env the environment variables
32
+ def call(env)
33
+ app.call(env)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module MailGrabber
6
+ module Web
7
+ class Application
8
+ extend ApplicationRouter
9
+
10
+ include ApplicationHelper
11
+ include DatabaseHelper
12
+
13
+ attr_reader :request, :response
14
+
15
+ # Method to call MailGrabber::Web::Application. This method will call the
16
+ # initialize method and returns back with response of the application.
17
+ #
18
+ # @param [Hash] env the environment variables
19
+ #
20
+ # @return [Array] the response of the web application
21
+ # e.g. [200, {}, ['Hello World']]
22
+ def self.call(env)
23
+ new(env).response.finish
24
+ end
25
+
26
+ # Initialize web application request and response then process the given
27
+ # request.
28
+ #
29
+ # @param [Hash] env the environment variables
30
+ def initialize(env)
31
+ @request = Rack::Request.new(env)
32
+ @response = Rack::Response.new
33
+
34
+ process_request
35
+ end
36
+
37
+ # Extract env['PATH_INFO'] value. If the path info is empty then it will
38
+ # return with root path.
39
+ #
40
+ # @return [String] path the requested path or the root path if this value
41
+ # is empty
42
+ def path
43
+ @path ||= request.path_info.empty? ? '/' : request.path_info
44
+ end
45
+
46
+ # This method returns back with extracted request parameters.
47
+ #
48
+ # @return [Hash] params
49
+ def params
50
+ request.params
51
+ end
52
+
53
+ # Extract env['REQUEST_METHOD'] value.
54
+ #
55
+ # @return [String] request_method with e.g. GET, POST or DELETE etc.
56
+ def request_method
57
+ request.request_method
58
+ end
59
+
60
+ # Extract env['SCRIPT_NAME'] value.
61
+ #
62
+ # @return [String] script_name the initial portion of the request 'path'
63
+ def script_name
64
+ request.script_name
65
+ end
66
+
67
+ # Parse the routes of the ApplicationRouter and tries to find matching
68
+ # route for the request method, which was defined in the
69
+ # get, post, put, patch or delete blocks. If the 'extracted_params' is nil
70
+ # then it could not found any defined routes. If it can find a defined
71
+ # route then it saves the params and call the given block. If it cannot
72
+ # find anything then it will set response with 404 Not Found.
73
+ def process_request
74
+ self.class.routes[request_method].each do |route|
75
+ extracted_params = route.extract_params(path)
76
+
77
+ next unless extracted_params
78
+
79
+ request.update_param('request_params', extracted_params)
80
+
81
+ return instance_exec(&route.block)
82
+ end
83
+
84
+ response.status = 404
85
+ response.write('Not Found')
86
+ end
87
+
88
+ get '/' do
89
+ response.write(render('index.html.erb'))
90
+ end
91
+
92
+ get '/messages.json' do
93
+ result =
94
+ if params['page'].nil? || params['per_page'].nil?
95
+ select_all_messages
96
+ else
97
+ select_messages_by(params['page'], params['per_page'])
98
+ end
99
+
100
+ response.write(result.to_json)
101
+ end
102
+
103
+ get '/message/:id.json' do
104
+ message = select_message_by(params['request_params']['id'])
105
+ message_parts = select_message_parts_by(params['request_params']['id'])
106
+
107
+ response.write(
108
+ { message: message, message_parts: message_parts }.to_json
109
+ )
110
+ end
111
+
112
+ delete '/messages.json' do
113
+ delete_all_messages
114
+
115
+ response.write({ info: 'All messages have been deleted' }.to_json)
116
+ end
117
+
118
+ delete '/message/:id.json' do
119
+ delete_message_by(params['request_params']['id'])
120
+
121
+ response.write({ info: 'Message has been deleted' }.to_json)
122
+ end
123
+
124
+ # Render erb template from the views folder.
125
+ #
126
+ # @param [String] template
127
+ #
128
+ # @return [String] template with the result
129
+ def render(template)
130
+ path = File.expand_path("views/#{template}", __dir__)
131
+ ERB.new(File.read(path)).result(binding)
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MailGrabber
4
+ module Web
5
+ # Helper module for views
6
+ module ApplicationHelper
7
+ # This method helps us that e.g. we can load style or javascript files
8
+ # when we are running this application standalone or in Ruby on Rails.
9
+ def root_path
10
+ script_name.to_s
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MailGrabber
4
+ module Web
5
+ module ApplicationRouter
6
+ NAMED_SEGMENTS_PATTERN = %r{/([^/]*):([^.:$/]+)}.freeze
7
+
8
+ attr_reader :routes
9
+
10
+ Route =
11
+ Struct.new(:pattern, :block) do
12
+ # Extract parameters from the given path. All routes has a
13
+ # path pattern which helps to the router to find which block it should
14
+ # execute. If path contains request parameters like '/test/1' then
15
+ # it will match with the '/test/:id' pattern. In this case it will
16
+ # return with '{"id" => "1"}' hash. If it is just a simple path like
17
+ # '/' and it has a pattern to match then it will return with '{}'.
18
+ # In the other case it will return with nil.
19
+ #
20
+ # @param [String] path
21
+ #
22
+ # @return [Hash/NilClass] with the extracted parameters, empty Hash
23
+ # or nil
24
+ def extract_params(path)
25
+ if pattern.match?(NAMED_SEGMENTS_PATTERN)
26
+ named_pattern =
27
+ pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
28
+
29
+ path.match(Regexp.new("\\A#{named_pattern}\\Z"))&.named_captures
30
+ elsif path == pattern
31
+ {}
32
+ end
33
+ end
34
+ end
35
+
36
+ # Define route method that we can define routing blocks.
37
+ #
38
+ # @example
39
+ #
40
+ # get '/' do
41
+ # response.write 'Hello MailGrabber'
42
+ # end
43
+ #
44
+ # @example
45
+ #
46
+ # method pattern block
47
+ # | | |
48
+ # get '/test' do ... end
49
+ #
50
+ %w[GET POST PUT PATCH DELETE].each do |method|
51
+ define_method(method.downcase) do |pattern, &block|
52
+ route(method, pattern, &block)
53
+ end
54
+ end
55
+
56
+ # Store routes with the request method, the provided pattern and the given
57
+ # block.
58
+ #
59
+ # @param [String] method e.g. GET, POST etc.
60
+ # @param [String] pattern the path what we are looking for
61
+ # @param [Proc] block what we will run
62
+ def route(method, pattern, &block)
63
+ @routes ||= {}
64
+
65
+ set_route('HEAD', pattern, &block) if method == 'GET'
66
+ set_route(method, pattern, &block)
67
+ end
68
+
69
+ # Set routes Hash with the Route object.
70
+ #
71
+ # @param [String] method e.g. GET, POST etc.
72
+ # @param [String] pattern the path what we are looking for
73
+ # @param [Proc] block what we will run
74
+ def set_route(method, pattern, &block)
75
+ (@routes[method] ||= []) << Route.new(pattern, block)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,539 @@
1
+ (function() {
2
+ var MailGrabber = {
3
+ /**
4
+ * Change which content (message part) should show. When the page is loading
5
+ * the HTML content will be active and the others will be hidden. When we
6
+ * click on tab e.g. Plain Text then this content will active and other
7
+ * hidden.
8
+ *
9
+ * @param {Object} event - which part we clicked
10
+ */
11
+ changeMessageContent: function(event) {
12
+ var messageTabs =
13
+ document
14
+ .querySelectorAll('[data-message-tab]');
15
+
16
+ var messageContents =
17
+ document
18
+ .querySelectorAll('[data-message-content]');
19
+
20
+ var clickedTabType = event.target.dataset.messageTab;
21
+
22
+ messageTabs.forEach(function(messageTab, index) {
23
+ messageTab.classList.remove('active');
24
+ messageContents[index].classList.remove('hide');
25
+
26
+ if(messageTab.dataset.messageTab === clickedTabType) {
27
+ messageTab.classList.add('active');
28
+ }
29
+
30
+ if(messageContents[index].dataset.messageContent !== clickedTabType) {
31
+ messageContents[index].classList.add('hide');
32
+ }
33
+ });
34
+ },
35
+
36
+ /**
37
+ * Show default backgroud image instead of any content.
38
+ */
39
+ defaultBackground: function() {
40
+ var messageContent =
41
+ document
42
+ .querySelector('div[data-content-type=message-content]');
43
+
44
+ messageContent.innerHTML = '';
45
+
46
+ messageContent.style.background =
47
+ "url('" + messageContent.dataset.backgroundImage +
48
+ "') center center no-repeat";
49
+ messageContent.style.backgroundSize = '50%';
50
+ messageContent.style.opacity = '10%';
51
+ },
52
+
53
+ /**
54
+ * Delete all content (message list and message content as well),
55
+ * set default backgroud and the infinite scroll params when we click on
56
+ * the Reload or Delete tabs.
57
+ */
58
+ deleteContent: function() {
59
+ MailGrabber.lastMessageId = -1;
60
+ MailGrabber.page = 1;
61
+ MailGrabber.defaultBackground();
62
+
63
+ document
64
+ .querySelector('ul[data-content-type=message-list]')
65
+ .innerHTML = '';
66
+ },
67
+
68
+ /**
69
+ * Delete a message when we click on the Delete tab. It will remove all
70
+ * content and reload the message list.
71
+ *
72
+ * @param {Number} messageId - which message we would like to delete
73
+ */
74
+ deleteMessage: function(messageId) {
75
+ MailGrabber.request('DELETE', '/message/' + messageId + '.json',
76
+ function() {
77
+ MailGrabber.deleteContent();
78
+ MailGrabber.getMessageList();
79
+ }
80
+ );
81
+ },
82
+
83
+ /**
84
+ * Delete message list when we click on the Clear tab. It will remove
85
+ * everything.
86
+ */
87
+ deleteMessageList: function() {
88
+ MailGrabber.request('DELETE', '/messages.json', function() {
89
+ MailGrabber.deleteContent();
90
+ });
91
+ },
92
+
93
+ /**
94
+ * Get a message and render the message content, when we click on an item
95
+ * from the message list.
96
+ *
97
+ * @param {Number} messageId - which message we would like to see
98
+ */
99
+ getMessage: function(messageId) {
100
+ MailGrabber.request('GET', '/message/' + messageId + '.json',
101
+ function(response) {
102
+ MailGrabber.renderMessageContent(JSON.parse(response));
103
+ }
104
+ );
105
+ },
106
+
107
+ /**
108
+ * Get a list of the messages. Also change the params of the infinite scroll
109
+ * that we can know which page we are on. It checks the message ids to not
110
+ * load those messages which are already on the list.
111
+ */
112
+ getMessageList: function() {
113
+ var messageIds;
114
+
115
+ MailGrabber.request('GET', '/messages.json?page=' + MailGrabber.page +
116
+ '&per_page=' + MailGrabber.perPage,
117
+ function(response) {
118
+ response = JSON.parse(response);
119
+ messageIds = response.map(function(hash) { return hash['id'] });
120
+
121
+ if(response.length > 0) {
122
+ if(messageIds.indexOf(MailGrabber.lastMessageId) === -1) {
123
+ MailGrabber.lastMessageId = messageIds.pop();
124
+ MailGrabber.page++;
125
+ MailGrabber.renderMessageList(response);
126
+ } else {
127
+ MailGrabber.reloadMessageList();
128
+ }
129
+ }
130
+ }
131
+ );
132
+ },
133
+
134
+ /**
135
+ * Scrolling infinitely. Count the height of the list and if we reached the
136
+ * bottom of the list then tries to load more message.
137
+ *
138
+ * @param {Object} messageList - the message list container
139
+ */
140
+ infiniteScroll: function(messageList) {
141
+ var scrollHeight = messageList.scrollHeight;
142
+ var scrollTop = messageList.scrollTop;
143
+ var clientHeight = messageList.clientHeight;
144
+
145
+ if(scrollHeight - scrollTop === clientHeight) {
146
+ MailGrabber.getMessageList();
147
+ }
148
+ },
149
+
150
+ /**
151
+ * Initialize MailGrabber. Add some event listeners to the Reload and the
152
+ * Clear tabs then load messages and the default background.
153
+ */
154
+ init: function() {
155
+ document
156
+ .querySelector('li[data-content-type=message-reload-tab]')
157
+ .addEventListener('click', MailGrabber.reloadMessageList);
158
+
159
+ document
160
+ .querySelector('li[data-content-type=message-clear-tab]')
161
+ .addEventListener('click', MailGrabber.deleteMessageList);
162
+
163
+ MailGrabber.loadMessageList();
164
+ MailGrabber.defaultBackground();
165
+ },
166
+
167
+ /**
168
+ * Format the given date or time.
169
+ *
170
+ * @param {DateTime} dateTime - the message created at attribute
171
+ * @param {String} ouputType - what type we would like to see
172
+ *
173
+ * @return {String} the new format of the date or time
174
+ */
175
+ formatDateTime: function(dateTime, outputType) {
176
+ dateTime = new Date(dateTime);
177
+ var dateTimeNow = new Date();
178
+ // Sun Feb 21 2021 21:00:00 GMT+0100 (Central European Standard Time)
179
+ // 0 1 2 3 4 5 6 7 8 9
180
+ var dateTimeComponents = dateTime.toString().split(' ');
181
+ var output;
182
+
183
+ switch(outputType) {
184
+ case 'messageListDateOrTime':
185
+ if(dateTime.getDate() === dateTimeNow.getDate()) {
186
+ output = MailGrabber.formatTime(dateTimeComponents[4]);
187
+ } else if(dateTime.getFullYear() === dateTimeNow.getFullYear()) {
188
+ output = dateTimeComponents[1] + ' ' + dateTimeComponents[2];
189
+ } else {
190
+ output = dateTimeComponents[3] + ' ' + dateTimeComponents[1] + ' ' +
191
+ dateTimeComponents[2];
192
+ }
193
+
194
+ break;
195
+ default:
196
+ output = dateTimeComponents[3] + ' ' + dateTimeComponents[1] + ' ' +
197
+ dateTimeComponents[2] + ' - ' +
198
+ MailGrabber.formatTime(dateTimeComponents[4]);
199
+ }
200
+
201
+ return output;
202
+ },
203
+
204
+ /**
205
+ * Format the given number (attachment size).
206
+ *
207
+ * @param {Number} size - the size of the attachment in bytes
208
+ *
209
+ * @return {String} the formatted number with unit
210
+ */
211
+ formatSize: function(size) {
212
+ var exponent = (Math.log(size) / Math.log(1024)) | 0;
213
+ var number = +(size / Math.pow(1024, exponent)).toFixed(1);
214
+
215
+ return number + ' ' + ('KMGTPEZY'[exponent - 1] || '') + 'B';
216
+ },
217
+
218
+ /**
219
+ * Format the given time.
220
+ *
221
+ * @param {String} time
222
+ *
223
+ * @return {String} the new format of the time
224
+ */
225
+ formatTime: function(time) {
226
+ var timeComponents = time.split(':');
227
+
228
+ return timeComponents[0] + ':' + timeComponents[1];
229
+ },
230
+
231
+ /**
232
+ * The last message id what loaded. If it is -1 then we have no any
233
+ * messages.
234
+ */
235
+ lastMessageId: -1,
236
+
237
+ /**
238
+ * Load the message list. Also add event listener for infinite scroll.
239
+ */
240
+ loadMessageList: function() {
241
+ var messageList =
242
+ document
243
+ .querySelector('ul[data-content-type=message-list]');
244
+
245
+ messageList.addEventListener('scroll', function() {
246
+ MailGrabber.infiniteScroll(messageList);
247
+ });
248
+
249
+ MailGrabber.getMessageList();
250
+ },
251
+
252
+ /**
253
+ * Params that we can follow how many messages we have on the list. We are
254
+ * loading 15 messages in every requests.
255
+ */
256
+ page: 1,
257
+ perPage: 15,
258
+
259
+ /**
260
+ * Reload the message list. When we have new messages in the database, but
261
+ * we scrolled down or clicked on the Reload tab.
262
+ */
263
+ reloadMessageList: function() {
264
+ MailGrabber.deleteContent();
265
+ MailGrabber.getMessageList();
266
+ },
267
+
268
+ /**
269
+ * Render message attachment. Show the list of attachments at the top of the
270
+ * message content.
271
+ *
272
+ * @param {Object} messageAttachments - the message attachments container
273
+ * @param {Object} messagePart - a message part which we loaded
274
+ */
275
+ renderMessageAttachment: function(messageAttachments, messagePart) {
276
+ var messageAttachment = document.createElement('a');
277
+ var fileSize = document.createElement('span');
278
+ var messageAttachmentTemplate =
279
+ document
280
+ .querySelector(
281
+ 'template[data-content-type=message-attachment-template]'
282
+ )
283
+ .content
284
+ .cloneNode(true);
285
+
286
+ messageAttachment.href =
287
+ 'data:' + messagePart.mime_type + ';base64,' + messagePart.body;
288
+ messageAttachment.download = messagePart.filename;
289
+ messageAttachment.innerHTML = messagePart.filename;
290
+ messageAttachment.classList.add('color-black', 'no-text-decoration');
291
+
292
+ fileSize.innerHTML = MailGrabber.formatSize(messagePart.size);
293
+ fileSize.classList.add('color-gray', 'font-size-0_9', 'padding-left-10');
294
+
295
+ [messageAttachment, fileSize].forEach(function(node) {
296
+ messageAttachmentTemplate
297
+ .querySelector('li[data-content-type=message-attachment]')
298
+ .appendChild(node);
299
+ });
300
+
301
+ messageAttachments.classList.remove('hide');
302
+ messageAttachments.appendChild(messageAttachmentTemplate);
303
+ },
304
+
305
+ /**
306
+ * Render the HTML part of the message. If the message has inline images
307
+ * then it will render those images as well. An iframe will contain this
308
+ * content.
309
+ *
310
+ * @param {Object} iFrame - the iframe HTML tag
311
+ * @param {Object} messageParts - the parts of the message
312
+ * @param {Object} messageHtmlPart - the HTML part of the message
313
+ */
314
+ renderMessageHtmlPart: function(iFrame, messageParts, messageHtmlPart) {
315
+ var messageInlineAttachmentRegExp = new RegExp("cid:");
316
+
317
+ if(messageInlineAttachmentRegExp.test(messageHtmlPart)) {
318
+ messageHtmlPart =
319
+ MailGrabber.renderMessageInlineAttachments(
320
+ messageParts, messageHtmlPart
321
+ );
322
+ }
323
+
324
+ iFrame.srcdoc = messageHtmlPart;
325
+ iFrame.onload = function() {
326
+ iFrame.height = iFrame.contentDocument.body.scrollHeight + 65;
327
+ }
328
+ },
329
+
330
+ /**
331
+ * Render inline images of the message. Change the cid:something12345 with
332
+ * the encoded image content.
333
+ *
334
+ * @param {Object} messageParts - the parts of the message
335
+ * @param {Object} messageHtmlPart - the HTML part of the message
336
+ *
337
+ * @return {Object} messageHtmlPart - the modified HTML part of the message
338
+ */
339
+ renderMessageInlineAttachments: function(messageParts, messageHtmlPart) {
340
+ messageParts.forEach(function(messagePart) {
341
+ if(messagePart.is_attachment === 1 && messagePart.is_inline === 1) {
342
+ messageHtmlPart = messageHtmlPart.replace('cid:' + messagePart.cid,
343
+ 'data:' + messagePart.mime_type + ';base64,' + messagePart.body);
344
+ }
345
+ });
346
+
347
+ return messageHtmlPart;
348
+ },
349
+
350
+ /**
351
+ * Render the content of the message (all parts, inline attachments and
352
+ * attachments). Also it sets up event listeners of the HTML, PlainText,
353
+ * Raw, Delete and Close tabs.
354
+ *
355
+ * @param {Object} response - the response the get message request
356
+ */
357
+ renderMessageContent: function(response) {
358
+ var message = response.message;
359
+ var messageParts = response.message_parts;
360
+ var messageContentTemplate =
361
+ document
362
+ .querySelector('template[data-content-type=message-content-template]')
363
+ .content
364
+ .cloneNode(true);
365
+
366
+ var messageContent =
367
+ document
368
+ .querySelector('div[data-content-type=message-content]');
369
+
370
+ messageContent.removeAttribute('style');
371
+ messageContent.innerHTML = '';
372
+
373
+ messageContentTemplate
374
+ .querySelector('div[data-content-type=message-subject]')
375
+ .innerHTML = message.subject;
376
+
377
+ messageContentTemplate
378
+ .querySelector('dl[data-content-type=metadata]')
379
+ .innerHTML = MailGrabber.renderMetadata(message);
380
+
381
+ messageContentTemplate
382
+ .querySelector('time[data-content-type=message-sent-at]')
383
+ .innerHTML = MailGrabber.formatDateTime(message.created_at);
384
+
385
+ messageContentTemplate
386
+ .querySelectorAll('[data-message-tab]')
387
+ .forEach(function(tab) {
388
+ tab.addEventListener('click', MailGrabber.changeMessageContent);
389
+ });
390
+
391
+ messageContentTemplate
392
+ .querySelector('li[data-content-type=message-delete-tab]')
393
+ .addEventListener('click', function() {
394
+ MailGrabber.deleteMessage(message.id);
395
+ });
396
+
397
+ messageContentTemplate
398
+ .querySelector('li[data-content-type=message-close-tab]')
399
+ .addEventListener('click', MailGrabber.defaultBackground);
400
+
401
+ messageParts.forEach(function(messagePart) {
402
+ if(messagePart.is_attachment === 0 && messagePart.is_inline === 0) {
403
+ switch(messagePart.mime_type) {
404
+ case 'text/html':
405
+ MailGrabber.renderMessageHtmlPart(
406
+ messageContentTemplate
407
+ .querySelector('iframe[data-content-type=message-html-body]'),
408
+ messageParts,
409
+ messagePart.body
410
+ );
411
+
412
+ break;
413
+ case 'text/plain':
414
+ messageContentTemplate
415
+ .querySelector('pre[data-content-type=message-text-body]')
416
+ .innerText = messagePart.body;
417
+
418
+ break;
419
+ }
420
+ } else if(messagePart.is_attachment === 1 &&
421
+ messagePart.is_inline === 0) {
422
+ MailGrabber.renderMessageAttachment(
423
+ messageContentTemplate
424
+ .querySelector('ul[data-content-type=message-attachments]'),
425
+ messagePart
426
+ );
427
+ }
428
+ });
429
+
430
+ messageContentTemplate
431
+ .querySelector('pre[data-content-type=message-raw-body]')
432
+ .innerText = message.raw;
433
+
434
+ messageContent.appendChild(messageContentTemplate);
435
+ },
436
+
437
+ /**
438
+ * Render the list of the messages. Also add event listener when click on a
439
+ * message then it will load that conent.
440
+ *
441
+ * @param {Object} messages - the list of the given message.
442
+ */
443
+ renderMessageList: function(messages) {
444
+ var messageListTemplate;
445
+
446
+ var messageList =
447
+ document
448
+ .querySelector('ul[data-content-type=message-list]');
449
+
450
+ messages.forEach(function(message) {
451
+ messageListTemplate =
452
+ document
453
+ .querySelector('template[data-content-type=message-list-template]')
454
+ .content
455
+ .cloneNode(true);
456
+
457
+ messageListTemplate
458
+ .querySelector('li')
459
+ .addEventListener('click', function() {
460
+ MailGrabber.getMessage(message.id);
461
+ });
462
+
463
+ messageListTemplate
464
+ .querySelector('div[data-content-type=message-senders]')
465
+ .innerHTML = message.senders;
466
+
467
+ messageListTemplate
468
+ .querySelector('time[data-content-type=message-sent-at]')
469
+ .innerHTML =
470
+ MailGrabber
471
+ .formatDateTime(message.created_at, 'messageListDateOrTime');
472
+
473
+ messageListTemplate
474
+ .querySelector('div[data-content-type=message-subject]')
475
+ .innerHTML = message.subject;
476
+
477
+ messageList.appendChild(messageListTemplate);
478
+ });
479
+ },
480
+
481
+ /**
482
+ * Render senders and recipients data of the message.
483
+ *
484
+ * @param {Object} message - the requested message
485
+ *
486
+ * @return {Object} the rendered metadata
487
+ */
488
+ renderMetadata: function(message) {
489
+ var metadata =
490
+ '<dt>From:</dt><dd>' + message.senders + '</dd>' +
491
+ '<dt>To:</dt><dd>' + message.recipients + '</dd>';
492
+
493
+ if(message.carbon_copy) {
494
+ metadata += '<dt>Cc:</dt><dd>' + message.carbon_copy + '</dd>';
495
+ }
496
+
497
+ if(message.blind_carbon_copy) {
498
+ metadata += '<dt>Bcc:</dt><dd>' + message.blind_carbon_copy + '</dd>';
499
+ }
500
+
501
+ return metadata;
502
+ },
503
+
504
+ /**
505
+ * Request function to get data from the server.
506
+ *
507
+ * @param {String} method - the request method e.g. GET, POST, DELETE
508
+ * @param {String} path - the path which we can get/send data
509
+ * @param {Function} fn - the function which handle the response
510
+ */
511
+ request: function(method, path, fn) {
512
+ var xhr = new XMLHttpRequest();
513
+
514
+ xhr.onload = function() {
515
+ if(xhr.status === 200) {
516
+ fn(xhr.responseText);
517
+ } else {
518
+ console.log('MailGrabberRequestError:', xhr.status, xhr.statusText);
519
+ }
520
+ };
521
+ xhr.open(method, MailGrabber.rootPath() + path, true);
522
+ xhr.send();
523
+ },
524
+
525
+ /**
526
+ * Root path which returns back with the server's root path. It can be empty
527
+ * string or a string. It depends on how the server is running (standalone
528
+ * or in Ruby on Rails).
529
+ */
530
+ rootPath: function() {
531
+ return document.querySelector('body').dataset.rootPath;
532
+ }
533
+ };
534
+
535
+ /**
536
+ * When DOM loaded then call MailGrabber's init function.
537
+ */
538
+ document.addEventListener('DOMContentLoaded', MailGrabber.init);
539
+ }).call(this);