postmortem 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +4 -46
- data/.ruby-version +1 -1
- data/Gemfile +0 -2
- data/README.md +20 -12
- data/doc/screenshot.png +0 -0
- data/layout/default.html.erb +32 -6
- data/layout/favicon.b64 +1 -0
- data/layout/layout.css +108 -14
- data/layout/layout.js +179 -66
- data/lib/postmortem.rb +3 -2
- data/lib/postmortem/adapters.rb +1 -1
- data/lib/postmortem/adapters/base.rb +6 -2
- data/lib/postmortem/adapters/mail.rb +3 -3
- data/lib/postmortem/adapters/pony.rb +3 -6
- data/lib/postmortem/index.rb +2 -1
- data/lib/postmortem/layout.rb +4 -0
- data/lib/postmortem/plugins/action_mailer.rb +7 -0
- data/lib/postmortem/version.rb +1 -1
- data/postmortem.gemspec +4 -2
- metadata +35 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba1b63c74d80acb294e4bf377084882db2dd7f3c5d6422079923830b43405de2
|
4
|
+
data.tar.gz: 7b359315a60e5c44d1ea0efbf1f049c04d7cf3258b7db5bcb53636ceacbfbe13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57b6d1d1c02fe3f4445dfc038ef3d0762d700fca755d63a20827000d5805484a2f293f4b164bac0e22d341f24648129aa55e80f318db56243cdd32dd79dc2286
|
7
|
+
data.tar.gz: '02768d56a8cf860d2328af1922354edaf7a7caff7d481a080b27cfd3102f215084692d376f260c725a15cbdbbe0e5ec4bce2c20c0517091ad5c59495645bab75'
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -3,49 +3,7 @@ Metrics/BlockLength:
|
|
3
3
|
- 'spec/**/*_spec.rb'
|
4
4
|
- 'postmortem.gemspec'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
Lint/DeprecatedOpenSSLConstant:
|
11
|
-
Enabled: true
|
12
|
-
Lint/DuplicateElsifCondition:
|
13
|
-
Enabled: true
|
14
|
-
Lint/MixedRegexpCaptureTypes:
|
15
|
-
Enabled: true
|
16
|
-
Lint/RaiseException:
|
17
|
-
Enabled: true
|
18
|
-
Lint/StructNewOverride:
|
19
|
-
Enabled: true
|
20
|
-
Style/AccessorGrouping:
|
21
|
-
Enabled: true
|
22
|
-
Style/ArrayCoercion:
|
23
|
-
Enabled: true
|
24
|
-
Style/BisectedAttrAccessor:
|
25
|
-
Enabled: true
|
26
|
-
Style/CaseLikeIf:
|
27
|
-
Enabled: true
|
28
|
-
Style/ExponentialNotation:
|
29
|
-
Enabled: true
|
30
|
-
Style/HashAsLastArrayItem:
|
31
|
-
Enabled: true
|
32
|
-
Style/HashEachMethods:
|
33
|
-
Enabled: true
|
34
|
-
Style/HashLikeCase:
|
35
|
-
Enabled: true
|
36
|
-
Style/HashTransformKeys:
|
37
|
-
Enabled: true
|
38
|
-
Style/HashTransformValues:
|
39
|
-
Enabled: true
|
40
|
-
Style/RedundantAssignment:
|
41
|
-
Enabled: true
|
42
|
-
Style/RedundantFetchBlock:
|
43
|
-
Enabled: true
|
44
|
-
Style/RedundantFileExtensionInRequire:
|
45
|
-
Enabled: true
|
46
|
-
Style/RedundantRegexpCharacterClass:
|
47
|
-
Enabled: true
|
48
|
-
Style/RedundantRegexpEscape:
|
49
|
-
Enabled: true
|
50
|
-
Style/SlicingWithRange:
|
51
|
-
Enabled: true
|
6
|
+
AllCops:
|
7
|
+
NewCops: enable
|
8
|
+
Exclude:
|
9
|
+
- 'preview/**/*'
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.5.
|
1
|
+
2.5.8
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,21 +1,24 @@
|
|
1
|
-
#
|
1
|
+
# PostMortem
|
2
2
|
|
3
|
-
|
3
|
+
_PostMortem_ provides a simple and clean preview of all outgoing mails sent by your _Ruby_ application to make email development a little less painful.
|
4
4
|
|
5
5
|
Every time your application sends an email a clearly-visible log entry will be written which provides a path to a temporary file containing your preview.
|
6
6
|
|
7
|
-
Take a look at a [live example](https://postmortem.surge.sh/) to see
|
7
|
+
Take a look at a [live example](https://postmortem.surge.sh/) to see _PostMortem_ in action.
|
8
8
|
|
9
|
-
|
9
|
+
_PostMortem_ should only be enabled in test or development environments.
|
10
10
|
|
11
11
|
## Features
|
12
12
|
|
13
13
|
* Seamless integration with [_ActionMailer_](https://guides.rubyonrails.org/action_mailer_basics.html), [_Pony_](https://github.com/benprew/pony), [_Mail_](https://github.com/mikel/mail), etc.
|
14
14
|
* Email deliveries are always intercepted (can be configured to pass through).
|
15
|
+
* Live inbox monitors incoming emails so you can view them as soon as they are delivered.
|
15
16
|
* Preview email content as well as typical email headers (recipients, subject, etc.).
|
16
17
|
* View rendered _HTML_, plaintext, or _HTML_ source with syntax highlighting (courtesy of [highlight.js](https://highlightjs.org/)).
|
17
|
-
* Dual or single column view to suit your requirements.
|
18
18
|
* Content is loaded inside an `<iframe>` to ensure document isolation and validity.
|
19
|
+
* Local images are located and embedded in HTML so you can see the full version of outgoing emails.
|
20
|
+
* Runs without a server - single page app runs on file system with no need to run a local web server to access UI.
|
21
|
+
* Any captured email can be downloaded into a standalone HTML file which can be shared with others.
|
19
22
|
|
20
23
|
## Installation
|
21
24
|
|
@@ -23,7 +26,7 @@ Add the gem to your application's Gemfile:
|
|
23
26
|
|
24
27
|
```ruby
|
25
28
|
group :development, :test do
|
26
|
-
gem 'postmortem', '~> 0.2.
|
29
|
+
gem 'postmortem', '~> 0.2.4'
|
27
30
|
end
|
28
31
|
```
|
29
32
|
|
@@ -37,9 +40,7 @@ Or install it yourself as:
|
|
37
40
|
|
38
41
|
## Usage
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
The path to the preview file is based on the current time and the subject of the email. If you would prefer to use the same path for each email you can disable timestamps (see [configuration](#configuration)) and simply reload your browser every time an email is sent.
|
43
|
+
_PostMortem_ automatically integrates with _Rails ActionMailer_ and _Pony_. When an email is sent an entry will be visible in your application's log output.
|
43
44
|
|
44
45
|
If you are using assets (images etc.) with _ActionMailer_ make sure to configure the asset host, e.g.:
|
45
46
|
|
@@ -50,15 +51,22 @@ Rails.application.configure do
|
|
50
51
|
end
|
51
52
|
```
|
52
53
|
|
53
|
-
Load the provided
|
54
|
+
A log entry will be generated every time an email is sent. Load the path provided in the log entry in your browser to launch _PostMortem_:
|
54
55
|
|
55
56
|
![Screenshot](doc/screenshot.png)
|
56
57
|
|
58
|
+
### Clearing the inbox
|
59
|
+
|
60
|
+
The inbox can be cleared at any time (e.g. at the start of a test run):
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
Postmortem.clear_inbox
|
64
|
+
```
|
57
65
|
|
58
66
|
## Configuration
|
59
67
|
<a name="configuration"></a>
|
60
68
|
|
61
|
-
Configure
|
69
|
+
Configure _PostMortem_ by calling `Postmortem.configure`, e.g. in a _Rails_ initializer.
|
62
70
|
|
63
71
|
```ruby
|
64
72
|
# config/initializers/postmortem.rb
|
@@ -66,7 +74,7 @@ Postmortem.configure do |config|
|
|
66
74
|
# Colorize output in logs (path to preview HTML file) to improve visibility (default: true).
|
67
75
|
config.colorize = true
|
68
76
|
|
69
|
-
# Path to the
|
77
|
+
# Path to the PostMortem log file, where preview paths are written (default: STDOUT).
|
70
78
|
config.log_path = '/path/to/postmortem.log'
|
71
79
|
|
72
80
|
# Path to save preview .html files (default: OS-provided temp directory).
|
data/doc/screenshot.png
CHANGED
Binary file
|
data/layout/default.html.erb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
|
+
<title>PostMortem Email</title>
|
3
4
|
<head>
|
4
5
|
<style>
|
5
6
|
<%= css_dependencies %>
|
6
7
|
<%= styles %>
|
7
8
|
</style>
|
9
|
+
|
10
|
+
<link href="data:image/x-icon;base64,<%= favicon_b64 %>" rel="icon" type="image/x-icon" />
|
8
11
|
</head>
|
12
|
+
|
9
13
|
<body>
|
10
14
|
<div class="toolbar">
|
11
15
|
|
@@ -19,11 +23,11 @@
|
|
19
23
|
|
20
24
|
<i data-toggle="tooltip"
|
21
25
|
title="View HTML Source"
|
22
|
-
class="fa fa-
|
26
|
+
class="fa fa-code source-view-switch"></i>
|
23
27
|
|
24
28
|
<i data-toggle="tooltip"
|
25
29
|
title="View HTML Part"
|
26
|
-
class="fa fa-code html-view-switch"></i>
|
30
|
+
class="fa fa-file-code-o html-view-switch"></i>
|
27
31
|
|
28
32
|
<i data-toggle="tooltip"
|
29
33
|
title="View Plaintext Part"
|
@@ -40,10 +44,26 @@
|
|
40
44
|
class="fa fa-envelope-open-o headers-view-switch"></i>
|
41
45
|
|
42
46
|
</div>
|
47
|
+
|
43
48
|
<div class="content">
|
44
49
|
<div class="container full-width">
|
45
50
|
<div class="row main-row">
|
46
|
-
<div id="inbox" class="col inbox">
|
51
|
+
<div id="inbox-container" class="col inbox-container">
|
52
|
+
<div class="row">
|
53
|
+
<div class="col inbox-header">
|
54
|
+
<h5><i class="fa fa-inbox"></i> Inbox <span class="text-secondary" id="inbox-info"></span></h5>
|
55
|
+
<button data-toggle="tooltip" title="Hide read messages" class="btn btn-light show-hide-read-button"><i class="fa fa-envelope-open-o"></i> <i data-state="show" class="fa fa-eye show-hide-read-icon"></i></button>
|
56
|
+
<button data-toggle="tooltip" title="Mark all as read" class="btn btn-light read-all-button"><i class="fa fa-envelope-open-o"></i> All</button>
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div class="row">
|
61
|
+
<!--INBOX-START-->
|
62
|
+
<div id="inbox" class="col inbox">
|
63
|
+
<div class="inbox-loading">Loading — <i class="fa fa-clock-o"></i></div>
|
64
|
+
</div>
|
65
|
+
<!--INBOX-END-->
|
66
|
+
</div>
|
47
67
|
</div>
|
48
68
|
|
49
69
|
<div class="col right-column">
|
@@ -52,9 +72,11 @@
|
|
52
72
|
<div class="preview row html-view">
|
53
73
|
<iframe id="html-iframe"></iframe>
|
54
74
|
</div>
|
75
|
+
|
55
76
|
<div class="preview row text-view">
|
56
77
|
<iframe id="text-iframe"></iframe>
|
57
78
|
</div>
|
79
|
+
|
58
80
|
<div class="preview row source-view">
|
59
81
|
<iframe id="source-iframe"></iframe>
|
60
82
|
</div>
|
@@ -68,10 +90,14 @@
|
|
68
90
|
<%= javascript_dependencies %>
|
69
91
|
</script>
|
70
92
|
<%= headers_template %>
|
93
|
+
<script id="initialize-script" type="text/javascript">
|
94
|
+
const POSTMORTEM = {};
|
95
|
+
POSTMORTEM.downloadedPreview = false;
|
96
|
+
POSTMORTEM.initialData = <%= mail.serializable.to_json %>;
|
97
|
+
POSTMORTEM.hasHtml = <%= (!mail.html_body.nil?).to_json %>;
|
98
|
+
POSTMORTEM.hasText = <%= (!mail.text_body.nil?).to_json %>;
|
99
|
+
</script>
|
71
100
|
<script>
|
72
|
-
var initialData = <%= mail.serializable.to_json %>;
|
73
|
-
var hasHtml = <%= (!mail.html_body.nil?).to_json %>;
|
74
|
-
var hasText = <%= (!mail.text_body.nil?).to_json %>;
|
75
101
|
<%= javascript %>
|
76
102
|
</script>
|
77
103
|
</body>
|
data/layout/favicon.b64
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
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zI3d1cyN3dXMjd3VzI3d1cyN3dXMjd3VzI3d1cyN3dXMjd3VzI3d1cyN3dXMjd7XzIvd2M1h4+PGCQAAAAC4raMZtqub2NDHu//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9DHu/+2q5vYuK2jGc/Iu6a3rJv/tqua/9DHu//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/Qx7v/tqua/7esm//PyLum3NbM89PLv/+3rJv/tqua/9DHu//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/0Me7/7armv+3rJv/08u//9zWzPPd1sz/3dbM/9PLv/+3rJv/tqua/9DHu//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/c1Mr/3NXK/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9DHu/+2q5r/t6yb/9PLv//d1sz/3dbM/93WzP/d1sz/3dbM/9PLv/+3rJv/tqua/9DHuv/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/Ryb7/vbOj/7Wqmf+1qpn/vrSk/9PLv//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/Qx7v/tqua/7esm//Ty7//3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9PLv/+3rJv/tqua/9DHu//d1sz/3dbM/93WzP/d1sz/zcS3/7Wqmf+1qpn/taqZ/7Wqmf+1qpn/taqa/87Guf/d1sz/3dbM/93WzP/d1sz/0Mi7/7armv+3rJv/08u//93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9PLv/+3rJv/tqua/9DHu//d1sz/3dbM/83Et/+1qpn/taqZ/8S7rf/WzsP/1s7D/8S7rf+1qpn/taqZ/87Guf/d1sz/3dbM/9DHu/+2q5r/t6yb/9PLv//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9PLv/+3rJv/tqua/9DHuv/NxLf/taqZ/7Wqmf/WzsP/7ejh/+3o4f/t6OH/7ejh/9bOw/+1qpn/taqZ/87Guf/Qx7v/tqua/7esm//Ty7//3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9PLv/+3rJv/taqa/7Wqmf+1qpn/18/E/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9fPxP+1qpn/taqZ/7armv+3rJv/08u//93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/8a9r/+1qpn/taqZ/9fPxP/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9fPxP+1qpn/taqZ/8e+sP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/MxLf/taqZ/7Wqmf/Xz8T/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9fPxP+1qpn/taqZ/87Fuf/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/zMS3/7Wqmf+1qpn/1s/E/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9bPxP+1qpn/taqZ/83FuP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/8zEt/+1qpn/taqZ/9bPxP/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9bPxP+1qpn/taqZ/83FuP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/MxLf/taqZ/7Wqmf/Wz8T/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9bPxP+1qpn/taqZ/83FuP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/zMS3/7Wqmf+1qpn/1s/E/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9bPxP+1qpn/taqZ/83FuP/d1sz/3dbM/93WzP/d1sz/3dbM/8zEt/+1qpn/taqZ/9bPxP/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9bPxP+1qpn/taqZ/83Ft//d1sz/3dbM/93WzP/MxLf/taqZ/7Wqmf/Wz8P/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9bPxP+1qpn/taqZ/83Et//d1sz/zMS3/7Wqmf+1qpn/1s7D/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9bPxP+1qpn/taqZ/83Et/+1qpn/taqZ/9bPw//t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9bPxP+1qpn/taqZ/7WrmvXWzsP/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9bPxP+1q5r12dLJqe3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9vVyqnr4tga7Ofg2e3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/s5+DZ6+LYGgAAAADR0dEL6+beZezm35Dq5d6T6uXek+rl3pPq5d6T6uXek+rl3pPq5d6T6uXek+rl3pPq5d6T6uXek+rl3pPq5d6T6uXek+rl3pPq5d6T6uXek+rl3pPq5d6T6uXek+rl3pPq5d6T6uXek+rl3pPs5t+Q6+beZdHR0QsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAADAAAABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8zMBdzTyh3g1swy3NTMQdzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzVzULc1c1C3NXNQtzUzEHg1swy3NPKHf/MzAUAAAAAAAAAAAAAAAC6sZ0avbOjgNrSx9Pd1sz53dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz52tLH072xo4C6sZ0aAAAAALa2pA63rJuctqqZ+sG3qf/a0sj/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/a0sj/wbep/7aqmfq3rJuctrakDs3GulG9saL6taqZ/7Wqmv/Euqz/2tLI/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9rSyP/Euqz/taqa/7Wqmf+9saL6zca6Ud3Vy7zTy7//vLGh/7Wqmf+2q5r/xLqs/9rSyP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/2tLI/8S6rP+2q5r/taqZ/7yxof/Ty7//3dXLvN3WzPXd1sz/1c7D/7qwn/+1qpn/taqa/8G3qP/a0sj/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/a0sj/wbep/7Wqmv+1qpn/urCf/9XOw//d1sz/3dbM9d3WzP/d1sz/3dbM/9XOw/+8saH/taqZ/7Wqmf/Euqz/2tLI/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3NXL/9zVy//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9rSyP/Euqz/taqa/7Wqmf+8saH/1c7D/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/Ty7//vLGh/7Wqmf+2q5r/xLqs/9rSx//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3NXL/9PMwP/LwrX/x76w/8e+sP/Mw7b/1M3B/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/2tLI/8S6rP+2q5r/taqZ/7yxof/Ty7//3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/1c7D/7qwn/+1qpn/taqZ/8G3qP/a0sf/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/Z0sf/w7qs/7iunf+2q5r/taqZ/7Wqmf+2q5r/ua6e/8W8rv/a08j/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/a0sj/wbep/7Wqmv+1qpn/urCf/9XOw//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9XOw/+8saH/taqZ/7Wqmf/Euqz/2tLI/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9jRxv/Bt6j/taqZ/7Wqmf+1qpn/taqZ/7Wqmf+1qpn/taqZ/7Wqmf/Cuar/2dLH/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9rTyP/Euqz/taqa/7Wqmf+8saH/1c7D/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/Ty7//vLGh/7Wqmf+2q5r/xLqs/9rSyP/d1sz/3dbM/93WzP/d1sz/2NDF/8G3qP+1qpn/taqZ/7Wqmf+2rJv/u7Gh/7uxof+2q5v/taqZ/7Wqmf+1qpn/wrmq/9nRx//d1sz/3dbM/93WzP/d1sz/2tPI/8S6rP+2q5r/taqZ/7yxof/Ty7//3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/1s7D/7qwn/+1qpn/taqZ/8G3qP/a0sf/3dbM/93WzP/Y0MX/vrSl/7Wqmf+1qpn/taqZ/8i/sf/f2M//5uDY/+bg1//f2M//yL+x/7Wqmf+1qpn/taqZ/8C2pv/Z0cf/3dbM/93WzP/a0sj/wbep/7Wqmv+1qpn/urCf/9XOw//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9XOw/+8saH/taqZ/7Wqmf/Euqv/2tLI/9jRxv/Bt6j/taqZ/7Wqmf+5rp7/1s/F/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9fPxf+5rp7/taqZ/7Wqmf/Cuar/2dLH/9rSyP/Euqz/taqa/7Wqmf+8saH/1c7D/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/Ty7//vLGh/7Wqmf+2q5r/w7qr/8G3qP+1qpn/taqZ/7munv/Xz8T/7Obf/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+zm3//Xz8T/ua6e/7Wqmf+1qpn/wrmq/8S6rP+2q5r/taqZ/7yxof/Ty7//3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/1s7D/7qwn/+1qpn/taqZ/7Wqmf+1qpn/tqua/9nSx//s5t//7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/s5t//2dLH/7armv+1qpn/taqZ/7Wqmv+1qpn/urCf/9bOw//d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9PLv/+3rZz/taqZ/7Wqmf+5rp7/2dLH/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9nSx/+5rp7/taqZ/7Wqmf+4rZz/1MvA/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/19DF/8C3qP+1qpn/taqZ/7munv/Xz8T/7Obf/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+zm3//Xz8T/ua6e/7Wqmf+1qpn/wrip/9jRxv/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/X0MX/vrSl/7Wqmf+1qpn/tqua/9nSx//s5t//7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/s5t//2dLH/7armv+1qpn/taqZ/7+1pv/Y0cb/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9jRxv/At6j/taqZ/7Wqmf+5rp7/2NLH/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9nSx/+5rp7/taqZ/7Wqmf/Ct6n/2dHG/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/19DF/8C3qP+1qpn/taqZ/7munv/Wz8T/6+bf/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+vm3//Wz8T/ua6e/7Wqmf+1qpn/wbep/9jRxv/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/X0MX/vrSl/7Wqmf+1qpn/tqua/9jRx//r5t//7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/r5t//2dLH/7armv+1qpn/taqZ/7+1pf/Y0cb/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9jRxv/At6j/taqZ/7Wqmf+5rp7/2NHH/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9nSx/+5rp7/taqZ/7Wqmf/Bt6j/2dHG/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/19DF/8C3qP+1qpn/taqZ/7munv/Wz8T/6+bf/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+vm3//Wz8T/ua6e/7Wqmf+1qpn/wbeo/9jRxv/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/93WzP/X0MX/vrSl/7Wqmf+1qpn/tqua/9jRx//r5t//7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/r5t//2NLH/7armv+1qpn/taqZ/7+1pf/Y0cb/3dbM/93WzP/d1sz/3dbM/93WzP/d1sz/3dbM/9jRxv/At6j/taqZ/7Wqmf+5rp7/2NHH/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9jRx/+5rp7/taqZ/7Wqmf/Bt6j/2NHG/93WzP/d1sz/3dbM/93WzP/d1sz/19DF/8C3qP+1qpn/taqZ/7munv/Wz8T/6+bf/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+vm3//Wz8T/ua6e/7Wqmf+1qpn/wbeo/9jQxf/d1sz/3dbM/93WzP/X0MX/vrSl/7Wqmf+1qpn/tqua/9jRxv/r5t//7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/r5t//2NLH/7armv+1qpn/taqZ/760pf/Y0MX/3dbM/9jRxv/At6j/taqZ/7Wqmf+5rp7/2NDG/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9jSx/+5r57/taqZ/7Wqmf/Bt6j/2NHG/8C3qP+1qpn/taqZ/7munv/Wz8P/6+bf/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+vm3//Wz8T/ua+e/7Wqmf+1qpn/wbeo/7Wqmf+1qpn/tqua/9jRxv/r5t//7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/r5t//2NLH/7armv+1qpn/taqZ/7armvi5rp7/2NHG/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/9nSx/+5r57/tqua+LuxocDWzsP/6+bf/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+vm3//Wz8T/vbKiwNvRyFTr5t777ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/r5t7729XMVd3d3Q/s59+f7ejg+u3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4Prs59+f3d3dDwAAAADk5Nsc6ubeg+zm4Nft6OH77ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH/7ejh/+3o4f/t6OH77Obg1+rm3oPk5NscAAAAAAAAAAAAAAAA1NTUBubm3R7s49425+DZSufg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2kvn4NpL5+DaS+fg2Urs49425ubdHtTU1AYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
data/layout/layout.css
CHANGED
@@ -18,14 +18,73 @@
|
|
18
18
|
color: #007bff !important;
|
19
19
|
}
|
20
20
|
|
21
|
-
|
22
|
-
overflow-y: auto;
|
23
|
-
height: 100%;
|
24
|
-
max-height: 100%;
|
25
|
-
display: none;
|
21
|
+
.inbox-container {
|
26
22
|
width: 35rem;
|
27
23
|
max-width: 35rem;
|
24
|
+
height: calc(100vh - 6rem);
|
25
|
+
max-height: calc(100vh - 6rem);
|
26
|
+
overflow-y: auto;
|
27
|
+
display: none;
|
28
|
+
}
|
29
|
+
|
30
|
+
.inbox-header {
|
31
|
+
padding-left: 0.8rem;
|
32
|
+
padding-right: 0.8rem;
|
33
|
+
padding-top: 0.9rem;
|
34
|
+
padding-bottom: 0.2rem;
|
35
|
+
background-color: #fff;
|
36
|
+
border: 1px solid #ddd;
|
37
|
+
height: 3.5rem;
|
38
|
+
position: fixed;
|
39
|
+
z-index: 100;
|
40
|
+
width: 32rem;
|
41
|
+
max-width: 32rem;
|
42
|
+
}
|
43
|
+
|
44
|
+
#inbox-info {
|
45
|
+
font-size: 0.7rem;
|
46
|
+
margin-left: 0.2rem;
|
47
|
+
display: inline-block;
|
48
|
+
vertical-align: middle;
|
49
|
+
}
|
50
|
+
|
51
|
+
.inbox-loading {
|
52
|
+
font-size: 1.6rem;
|
53
|
+
font-weight: 200;
|
54
|
+
padding-top: 4rem;
|
55
|
+
padding-left: 0.2rem;
|
56
|
+
animation: fade 3s infinite;
|
57
|
+
text-align: center;
|
58
|
+
}
|
59
|
+
|
60
|
+
@keyframes fade {
|
61
|
+
0% {
|
62
|
+
color: #555;
|
63
|
+
}
|
64
|
+
50% {
|
65
|
+
color: #ddd;
|
66
|
+
}
|
67
|
+
100% {
|
68
|
+
color: #555;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
.show-hide-read-button {
|
73
|
+
position: absolute;
|
74
|
+
right: 5rem;
|
75
|
+
top: 0.4rem;
|
76
|
+
}
|
77
|
+
|
78
|
+
.read-all-button {
|
79
|
+
position: absolute;
|
80
|
+
right: 0.5rem;
|
81
|
+
top: 0.4rem;
|
82
|
+
}
|
83
|
+
|
84
|
+
#inbox {
|
28
85
|
padding-right: 0.2rem;
|
86
|
+
padding-left: 0;
|
87
|
+
padding-top: 4rem;
|
29
88
|
}
|
30
89
|
|
31
90
|
#inbox.visible {
|
@@ -42,24 +101,59 @@
|
|
42
101
|
top: 0.8rem;
|
43
102
|
}
|
44
103
|
|
104
|
+
#inbox ul {
|
105
|
+
list-style-type: none;
|
106
|
+
}
|
107
|
+
|
108
|
+
.list-group-item {
|
109
|
+
padding-left: 0.75rem;
|
110
|
+
}
|
111
|
+
|
112
|
+
.inbox-container.hide-read li {
|
113
|
+
display: none;
|
114
|
+
}
|
115
|
+
|
116
|
+
.inbox-container.hide-read li.unread, .inbox-container.hide-read li.active {
|
117
|
+
display: block;
|
118
|
+
}
|
119
|
+
|
120
|
+
.inbox-container li .unread-icon, .inbox-container li .read-icon {
|
121
|
+
padding-right: 0.75rem;
|
122
|
+
font-size: 1.2rem;
|
123
|
+
}
|
124
|
+
|
125
|
+
#inbox li .read-icon {
|
126
|
+
opacity: 0.1;
|
127
|
+
display: inline;
|
128
|
+
}
|
129
|
+
|
130
|
+
#inbox li.active .read-icon {
|
131
|
+
color: #fff;
|
132
|
+
opacity: 0.3;
|
133
|
+
}
|
134
|
+
|
135
|
+
#inbox li .unread-icon {
|
136
|
+
display: none;
|
137
|
+
}
|
138
|
+
|
139
|
+
#inbox li.unread .read-icon {
|
140
|
+
display: none;
|
141
|
+
}
|
142
|
+
|
143
|
+
#inbox li.unread .unread-icon {
|
144
|
+
display: inline;
|
145
|
+
}
|
146
|
+
|
45
147
|
#inbox li a {
|
46
148
|
text-decoration: none;
|
47
149
|
background-color: transparent;
|
48
|
-
max-width:
|
150
|
+
max-width: 23rem;
|
49
151
|
overflow: hidden;
|
50
152
|
display: block;
|
51
153
|
white-space: nowrap;
|
52
154
|
text-overflow: ellipsis
|
53
155
|
}
|
54
156
|
|
55
|
-
#inbox li a .unread-icon {
|
56
|
-
color: #007bff;
|
57
|
-
}
|
58
|
-
|
59
|
-
#inbox li a:visited .unread-icon {
|
60
|
-
color: #ffffff00;
|
61
|
-
}
|
62
|
-
|
63
157
|
#inbox li a:hover {
|
64
158
|
text-decoration: none;
|
65
159
|
}
|
data/layout/layout.js
CHANGED
@@ -4,14 +4,25 @@
|
|
4
4
|
let reloadIdentityIframeTimeout;
|
5
5
|
let identityUuid;
|
6
6
|
let indexUuid;
|
7
|
-
let
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
let twoColumnView;
|
8
|
+
let headersView;
|
9
|
+
let indexIframeTimeout;
|
10
|
+
|
11
|
+
const inboxContent = [];
|
12
|
+
const headers = document.querySelector('.headers');
|
13
|
+
const inbox = document.querySelector('#inbox-container');
|
14
|
+
const inboxInfo = document.querySelector('#inbox-info');
|
15
|
+
const columnSwitch = document.querySelector('.column-switch');
|
16
|
+
const headersViewSwitch = document.querySelector('.headers-view-switch');
|
17
|
+
const readAllButton = document.querySelector('.read-all-button');
|
18
|
+
const showHideReadButton = document.querySelector('.show-hide-read-button');
|
19
|
+
const showHideReadIcon = document.querySelector('.show-hide-read-icon');
|
20
|
+
const htmlIframeDocument = document.querySelector("#html-iframe").contentDocument;
|
21
|
+
const indexIframe = document.querySelector("#index-iframe");
|
22
|
+
const identityIframe = document.querySelector("#identity-iframe");
|
23
|
+
const textIframeDocument = document.querySelector("#text-iframe").contentDocument;
|
24
|
+
const sourceIframeDocument = document.querySelector("#source-iframe").contentDocument;
|
25
|
+
const sourceHighlightBundle = [
|
15
26
|
'<link rel="stylesheet"',
|
16
27
|
' href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.3/styles/agate.min.css"',
|
17
28
|
' integrity="sha512-mMMMPADD4HAIogAWZbv+WjZTC0itafUZNI0jQm48PMBTApXt11omF5jhS7U1kp3R2Pr6oGJ+JwQKiUkUwCQaUQ=="',
|
@@ -22,52 +33,55 @@
|
|
22
33
|
'<script>hljs.initHighlightingOnLoad();</' + 'script>',
|
23
34
|
].join('\n');
|
24
35
|
|
25
|
-
const
|
26
|
-
loadMail(initialData);
|
36
|
+
const storage = window.localStorage;
|
27
37
|
|
38
|
+
const initialize = () => {
|
28
39
|
reloadIdentityIframeTimeout = setTimeout(() => identityIframe.src += '', 3000);
|
29
40
|
|
30
|
-
window.addEventListener('message', function (ev) {
|
31
|
-
switch (ev.data.type) {
|
32
|
-
case 'index':
|
33
|
-
renderInbox(ev.data.uuid, ev.data.mails);
|
34
|
-
break;
|
35
|
-
case 'identity':
|
36
|
-
compareIdentity(ev.data.uuid);
|
37
|
-
break;
|
38
|
-
};
|
39
|
-
});
|
40
|
-
|
41
|
-
setInterval(function () { indexIframe.contentWindow.postMessage('HELO', '*'); }, 1000);
|
42
41
|
setInterval(function () { identityIframe.contentWindow.postMessage('HELO', '*'); }, 1000);
|
43
42
|
|
44
|
-
toolbar.html.onclick
|
45
|
-
toolbar.text.onclick
|
46
|
-
toolbar.source.onclick
|
47
|
-
toolbar.headers.onclick
|
48
|
-
columnSwitch.onclick
|
43
|
+
toolbar.html.onclick = (ev) => setView('html', ev);
|
44
|
+
toolbar.text.onclick = (ev) => setView('text', ev);
|
45
|
+
toolbar.source.onclick = (ev) => setView('source', ev);
|
46
|
+
toolbar.headers.onclick = () => setHeadersView(!headersView);
|
47
|
+
columnSwitch.onclick = () => setColumnView(!twoColumnView);
|
48
|
+
readAllButton.onclick = () => markAllAsRead();
|
49
|
+
showHideReadButton.onclick = () => toggleHideReadMessages();
|
49
50
|
|
50
|
-
if (hasHtml) {
|
51
|
+
if (POSTMORTEM.hasHtml) {
|
51
52
|
setView('html');
|
52
53
|
} else {
|
53
54
|
setView('text');
|
54
55
|
}
|
55
56
|
|
56
|
-
setColumnView(
|
57
|
+
setColumnView(!POSTMORTEM.downloadedPreview);
|
57
58
|
setHeadersView(true);
|
59
|
+
setEnabled(columnSwitch);
|
60
|
+
setOn(columnSwitch);
|
61
|
+
setHidden(inbox, POSTMORTEM.downloadedPreview);
|
58
62
|
|
59
|
-
|
60
|
-
|
63
|
+
if (POSTMORTEM.downloadedPreview) {
|
64
|
+
setHidden(toolbar.download, true);
|
65
|
+
setHidden(columnSwitch, true);
|
66
|
+
} else {
|
67
|
+
window.addEventListener('message', function (ev) {
|
68
|
+
switch (ev.data.type) {
|
69
|
+
case 'index':
|
70
|
+
loadInbox(ev.data.uuid, ev.data.mails);
|
71
|
+
break;
|
72
|
+
case 'identity':
|
73
|
+
compareIdentity(ev.data.uuid);
|
74
|
+
break;
|
75
|
+
};
|
76
|
+
});
|
77
|
+
}
|
61
78
|
|
79
|
+
loadMail(POSTMORTEM.initialData);
|
62
80
|
|
63
|
-
|
64
|
-
|
65
|
-
var headers = document.querySelector('.headers');
|
66
|
-
var inbox = document.querySelector('#inbox');
|
67
|
-
var columnSwitch = document.querySelector('.column-switch');
|
68
|
-
var headersViewSwitch = document.querySelector('.headers-view-switch');
|
81
|
+
$('[data-toggle="tooltip"]').tooltip();
|
82
|
+
}
|
69
83
|
|
70
|
-
|
84
|
+
const setHeadersView = (enableHeadersView) => {
|
71
85
|
headersView = enableHeadersView;
|
72
86
|
if (enableHeadersView) {
|
73
87
|
setOn(headersViewSwitch);
|
@@ -78,11 +92,11 @@
|
|
78
92
|
}
|
79
93
|
};
|
80
94
|
|
81
|
-
|
82
|
-
if (!
|
95
|
+
const setColumnView = (enableTwoColumnView) => {
|
96
|
+
if (!inbox) return;
|
83
97
|
|
84
|
-
|
85
|
-
twoColumnView = enableTwoColumnView;
|
98
|
+
const container = document.querySelector('.container');
|
99
|
+
twoColumnView = POSTMORTEM.downloadedPreview ? false : enableTwoColumnView;
|
86
100
|
if (twoColumnView) {
|
87
101
|
setVisible(inbox, true);
|
88
102
|
setOn(columnSwitch);
|
@@ -94,42 +108,51 @@
|
|
94
108
|
}
|
95
109
|
};
|
96
110
|
|
97
|
-
|
111
|
+
const contexts = ['source', 'text', 'html'];
|
98
112
|
|
99
|
-
|
113
|
+
const views = {
|
100
114
|
source: document.querySelector('.source-view'),
|
101
115
|
html: document.querySelector('.html-view'),
|
102
116
|
text: document.querySelector('.text-view'),
|
103
117
|
};
|
104
118
|
|
105
|
-
|
119
|
+
const toolbar = {
|
106
120
|
source: document.querySelector('.source-view-switch'),
|
107
121
|
html: document.querySelector('.html-view-switch'),
|
108
122
|
text: document.querySelector('.text-view-switch'),
|
109
123
|
headers: document.querySelector('.headers-view-switch'),
|
124
|
+
download: document.querySelector('#download-link'),
|
110
125
|
};
|
111
126
|
|
112
|
-
|
127
|
+
const setOn = function(element) {
|
113
128
|
element.classList.add('text-primary');
|
114
129
|
element.classList.remove('text-secondary');
|
115
130
|
};
|
116
131
|
|
117
|
-
|
132
|
+
const setOff = function(element) {
|
118
133
|
element.classList.add('text-secondary');
|
119
134
|
element.classList.remove('text-primary');
|
120
135
|
};
|
121
136
|
|
122
|
-
|
137
|
+
const setDisabled = function(element) {
|
123
138
|
element.classList.add('disabled');
|
124
139
|
element.classList.remove('text-secondary');
|
125
140
|
};
|
126
141
|
|
127
|
-
|
142
|
+
const setEnabled = function(element) {
|
128
143
|
element.classList.remove('disabled');
|
129
144
|
element.classList.add('text-secondary');
|
130
145
|
};
|
131
146
|
|
132
|
-
|
147
|
+
const setHidden = function(element, hidden) {
|
148
|
+
if (hidden) {
|
149
|
+
element.classList.add('hidden');
|
150
|
+
} else {
|
151
|
+
element.classList.remove('hidden');
|
152
|
+
}
|
153
|
+
};
|
154
|
+
|
155
|
+
const setVisible = function(element, visible) {
|
133
156
|
if (visible) {
|
134
157
|
element.classList.add('visible');
|
135
158
|
} else {
|
@@ -137,9 +160,9 @@
|
|
137
160
|
}
|
138
161
|
};
|
139
162
|
|
140
|
-
|
163
|
+
const setView = function(context, ev) {
|
141
164
|
if (ev && $(ev.target).hasClass('disabled')) return;
|
142
|
-
|
165
|
+
let key;
|
143
166
|
for (i = 0; i < contexts.length; i++) {
|
144
167
|
key = contexts[i];
|
145
168
|
if (key === context) {
|
@@ -194,11 +217,22 @@
|
|
194
217
|
setEnabled(toolbar.source);
|
195
218
|
}
|
196
219
|
|
197
|
-
setDisabled(columnSwitch);
|
198
220
|
setView(currentView);
|
199
221
|
};
|
200
222
|
|
201
223
|
const loadMail = (mail) => {
|
224
|
+
const initializeScript = document.querySelector("#initialize-script");
|
225
|
+
const initObject = {
|
226
|
+
initialData: mail,
|
227
|
+
hasHtml: !!mail.htmlBody,
|
228
|
+
hasText: !!mail.textBody,
|
229
|
+
downloadedPreview: true,
|
230
|
+
};
|
231
|
+
|
232
|
+
initializeScript.text = [
|
233
|
+
`const POSTMORTEM = ${JSON.stringify(initObject)};`
|
234
|
+
].join('\n\n');
|
235
|
+
|
202
236
|
htmlIframeDocument.open();
|
203
237
|
htmlIframeDocument.write(mail.htmlBody);
|
204
238
|
htmlIframeDocument.close();
|
@@ -215,10 +249,73 @@
|
|
215
249
|
loadHeaders(mail);
|
216
250
|
loadToolbar(mail);
|
217
251
|
loadDownloadLink();
|
252
|
+
|
253
|
+
highlightMail(mail);
|
254
|
+
markAsRead(mail);
|
255
|
+
updateInboxInfo();
|
256
|
+
};
|
257
|
+
|
258
|
+
const markAsRead = (mail) => {
|
259
|
+
storage.setItem(mail.id, 'read');
|
260
|
+
$(`li[data-email-id="${mail.id}"]`).removeClass('unread');
|
261
|
+
$(readAllButton).blur();
|
262
|
+
};
|
263
|
+
|
264
|
+
const showReadMessages = (show) => {
|
265
|
+
if (show) {
|
266
|
+
inbox.classList.remove('hide-read');
|
267
|
+
} else {
|
268
|
+
inbox.classList.add('hide-read');
|
269
|
+
}
|
270
|
+
};
|
271
|
+
|
272
|
+
const toggleHideReadMessages = () => {
|
273
|
+
const $target = $(showHideReadButton);
|
274
|
+
const $icon = $(showHideReadIcon);
|
275
|
+
|
276
|
+
if ($target.data('state') === 'hide') {
|
277
|
+
$target.data('state', 'show');
|
278
|
+
$target.attr('data-original-title', 'Hide read messages');
|
279
|
+
$target.attr('title', 'Hide read messages');
|
280
|
+
$icon.removeClass('fa-eye-slash');
|
281
|
+
$icon.addClass('fa fa-eye text-primary');
|
282
|
+
showReadMessages(true);
|
283
|
+
} else {
|
284
|
+
$target.data('state', 'hide');
|
285
|
+
$target.attr('data-original-title', 'Show read messages');
|
286
|
+
$target.attr('title', 'Show read messages');
|
287
|
+
$icon.removeClass('fa-eye text-primary');
|
288
|
+
$icon.addClass('fa fa-eye-slash');
|
289
|
+
showReadMessages(false);
|
290
|
+
}
|
291
|
+
|
292
|
+
$target.blur();
|
293
|
+
};
|
294
|
+
|
295
|
+
const markAllAsRead = () => {
|
296
|
+
inboxContent.forEach(mail => markAsRead(mail));
|
297
|
+
updateInboxInfo();
|
298
|
+
};
|
299
|
+
|
300
|
+
const isNewMail = (mail) => {
|
301
|
+
if (!storage.getItem(mail.id)) return true;
|
302
|
+
|
303
|
+
return false;
|
304
|
+
};
|
305
|
+
|
306
|
+
const highlightMail = (mail) => {
|
307
|
+
window.location.hash = mail.id;
|
308
|
+
const $target = $(`li[data-email-id="${mail.id}"]`);
|
309
|
+
$('.inbox-item').removeClass('active');
|
310
|
+
$target.addClass('active');
|
218
311
|
};
|
219
312
|
|
220
313
|
const loadDownloadLink = () => {
|
221
|
-
const
|
314
|
+
const html = document.documentElement.innerHTML;
|
315
|
+
const start = html.indexOf('<!--INBOX-START-->');
|
316
|
+
const end = html.indexOf('<!--INBOX-END-->') + '<!--INBOX-END-->'.length;
|
317
|
+
const modifiedHtml = [html.substring(0, start), html.substring(end + 1, html.length)].join('');
|
318
|
+
const blob = new Blob([modifiedHtml], { type: 'application/octet-stream' });
|
222
319
|
const uri = window.URL.createObjectURL(blob);
|
223
320
|
$("#download-link").attr('href', uri);
|
224
321
|
};
|
@@ -226,11 +323,26 @@
|
|
226
323
|
const compareIdentity = (uuid) => {
|
227
324
|
clearTimeout(reloadIdentityIframeTimeout);
|
228
325
|
reloadIdentityIframeTimeout = setTimeout(() => identityIframe.src += '', 3000);
|
229
|
-
if (identityUuid !== uuid)
|
326
|
+
if (identityUuid !== uuid) {
|
327
|
+
indexIframe.src += '';
|
328
|
+
clearTimeout(indexIframeTimeout);
|
329
|
+
indexIframeTimeout = setInterval(function () { indexIframe.contentWindow.postMessage('HELO', '*'); }, 200);
|
330
|
+
}
|
230
331
|
identityUuid = uuid;
|
231
332
|
};
|
232
333
|
|
233
|
-
const
|
334
|
+
const updateInboxInfo = () => {
|
335
|
+
if (!inboxContent.length) return;
|
336
|
+
|
337
|
+
const unreadCount = inboxContent.filter((mail) => isNewMail(mail)).length;
|
338
|
+
document.title = `PostMortem ${unreadCount}/${inboxContent.length} (unread/total)`;
|
339
|
+
inboxInfo.textContent = `${inboxContent.length} emails (${unreadCount} unread)`;
|
340
|
+
inboxInfo.innerHTML = `— ${inboxInfo.innerHTML}`;
|
341
|
+
};
|
342
|
+
|
343
|
+
const loadInbox = (uuid, mails) => {
|
344
|
+
clearTimeout(indexIframeTimeout);
|
345
|
+
inboxContent.splice(0, Infinity, ...mails)
|
234
346
|
if (uuid === indexUuid) {
|
235
347
|
return;
|
236
348
|
}
|
@@ -239,28 +351,29 @@
|
|
239
351
|
const parsedTimestamp = new Date(mail.timestamp);
|
240
352
|
const timestampSpan = `<span class="timestamp">${parsedTimestamp.toLocaleString()}</span>`;
|
241
353
|
const classes = ['list-group-item', 'inbox-item'];
|
354
|
+
|
242
355
|
if (window.location.hash === '#' + mail.id) classes.push('active');
|
356
|
+
if (isNewMail(mail)) classes.push('unread');
|
357
|
+
|
243
358
|
mailsById[mail.id] = mail;
|
244
|
-
|
359
|
+
|
360
|
+
return [`<li data-email-id="${mail.id}" class="${classes.join(' ')}">`,
|
361
|
+
`<a title="${mail.subject}" href="javascript:void(0)">`,
|
362
|
+
`<i class="fa fa-envelope-open read-icon"></i>`,
|
363
|
+
`<i class="fa fa-envelope unread-icon"></i>${mail.subject}`,
|
364
|
+
`</a>`,
|
365
|
+
`${timestampSpan}</li>`].join('');
|
245
366
|
});
|
367
|
+
updateInboxInfo();
|
246
368
|
if (arrayIdentical(html, previousInbox)) return;
|
247
369
|
previousInbox = html;
|
248
370
|
$('#inbox').html('<ul class="list-group">' + html.join('\n') + '</ul>');
|
249
371
|
$('.inbox-item').click((ev) => {
|
250
372
|
const $target = $(ev.currentTarget);
|
251
373
|
const id = $target.data('email-id');
|
252
|
-
$('.inbox-item').removeClass('active');
|
253
|
-
$target.addClass('active');
|
254
|
-
window.location.hash = id;
|
255
374
|
setTimeout(() => loadMail(mailsById[id].content), 0);
|
256
375
|
});
|
257
376
|
|
258
|
-
if (!inboxInitialized) {
|
259
|
-
setEnabled(columnSwitch);
|
260
|
-
setColumnView(true);
|
261
|
-
setVisible(inbox, true);
|
262
|
-
}
|
263
|
-
inboxInitialized = true;
|
264
377
|
indexUuid = uuid;
|
265
378
|
};
|
266
379
|
|
data/lib/postmortem.rb
CHANGED
@@ -9,6 +9,7 @@ require 'erb'
|
|
9
9
|
require 'json'
|
10
10
|
require 'cgi'
|
11
11
|
require 'digest'
|
12
|
+
require 'securerandom'
|
12
13
|
|
13
14
|
require 'postmortem/version'
|
14
15
|
require 'postmortem/adapters'
|
@@ -56,7 +57,7 @@ module Postmortem
|
|
56
57
|
private
|
57
58
|
|
58
59
|
def log_delivery(delivery)
|
59
|
-
output_file.write(colorized(delivery.path.to_s)
|
60
|
+
output_file.write("#{colorized(delivery.path.to_s)}\n")
|
60
61
|
output_file.flush
|
61
62
|
end
|
62
63
|
|
@@ -67,7 +68,7 @@ module Postmortem
|
|
67
68
|
end
|
68
69
|
|
69
70
|
def output_file
|
70
|
-
return
|
71
|
+
return $stdout if config.log_path.nil?
|
71
72
|
|
72
73
|
@output_file ||= File.open(config.log_path, mode: 'a')
|
73
74
|
end
|
data/lib/postmortem/adapters.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Postmortem
|
4
4
|
module Adapters
|
5
|
-
FIELDS = %i[from reply_to to cc bcc subject text_body html_body].freeze
|
5
|
+
FIELDS = %i[from reply_to to cc bcc subject text_body html_body message_id].freeze
|
6
6
|
|
7
7
|
# Base interface implementation for all Postmortem adapters.
|
8
8
|
class Base
|
@@ -16,7 +16,11 @@ module Postmortem
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def serializable
|
19
|
-
FIELDS.map { |field| [camelize(field.to_s), public_send(field)] }.to_h
|
19
|
+
(%i[id] + FIELDS).map { |field| [camelize(field.to_s), public_send(field)] }.to_h
|
20
|
+
end
|
21
|
+
|
22
|
+
def id
|
23
|
+
@id ||= SecureRandom.uuid
|
20
24
|
end
|
21
25
|
|
22
26
|
FIELDS.each do |method_name|
|
@@ -7,10 +7,10 @@ module Postmortem
|
|
7
7
|
private
|
8
8
|
|
9
9
|
def adapted
|
10
|
-
%i[from reply_to to cc bcc subject]
|
11
|
-
.map { |field| [field,
|
10
|
+
%i[from reply_to to cc bcc subject message_id]
|
11
|
+
.map { |field| [field, mail.public_send(field)] }
|
12
12
|
.to_h
|
13
|
-
.merge({ text_body:
|
13
|
+
.merge({ text_body: mail.text_part&.decoded, html_body: mail.html_part&.decoded })
|
14
14
|
end
|
15
15
|
|
16
16
|
def mail
|
@@ -8,14 +8,11 @@ module Postmortem
|
|
8
8
|
|
9
9
|
def adapted
|
10
10
|
{
|
11
|
-
from: mail.from,
|
12
|
-
reply_to: mail.reply_to,
|
13
|
-
to: mail.to,
|
14
|
-
cc: mail.cc,
|
15
|
-
bcc: mail.bcc,
|
11
|
+
from: mail.from, reply_to: mail.reply_to, to: mail.to, cc: mail.cc, bcc: mail.bcc,
|
16
12
|
subject: mail.subject,
|
17
13
|
text_body: @data[:body],
|
18
|
-
html_body: @data[:html_body]
|
14
|
+
html_body: @data[:html_body],
|
15
|
+
message_id: mail.message_id # We use a synthetic Mail instance so this is a bit useless.
|
19
16
|
}
|
20
17
|
end
|
21
18
|
|
data/lib/postmortem/index.rb
CHANGED
@@ -35,7 +35,7 @@ module Postmortem
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def encoded_mail
|
38
|
-
Base64.encode64(mail_data.
|
38
|
+
Base64.encode64(mail_data.to_json).split("\n").join
|
39
39
|
end
|
40
40
|
|
41
41
|
def mail_data
|
@@ -43,6 +43,7 @@ module Postmortem
|
|
43
43
|
subject: @mail.subject || '(no subject)',
|
44
44
|
timestamp: timestamp,
|
45
45
|
path: @mail_path,
|
46
|
+
id: @mail.id,
|
46
47
|
content: @mail.serializable
|
47
48
|
}
|
48
49
|
end
|
data/lib/postmortem/layout.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
ActiveSupport::Notifications.subscribe 'deliver.action_mailer' do |*args|
|
4
|
+
delivery_method = Rails.try(:application)
|
5
|
+
&.try(:config)
|
6
|
+
&.try(:action_mailer)
|
7
|
+
&.try(:delivery_method)
|
8
|
+
next if delivery_method.nil?
|
9
|
+
next if %i[sendmail smtp].include?(delivery_method&.to_sym) # Delegate to Mail plugin.
|
10
|
+
|
4
11
|
Postmortem.record_delivery(Postmortem::Adapters::ActionMailer.new(args.extract_options!))
|
5
12
|
end
|
data/lib/postmortem/version.rb
CHANGED
data/postmortem.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = 'Preview HTML emails in your browser during development'
|
13
13
|
spec.homepage = 'https://github.com/bobf/postmortem'
|
14
14
|
spec.license = 'MIT'
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 2.
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
16
16
|
|
17
17
|
spec.metadata['homepage_uri'] = spec.homepage
|
18
18
|
spec.metadata['source_code_uri'] = spec.homepage
|
@@ -28,10 +28,12 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_runtime_dependency 'mail', '~> 2.7'
|
29
29
|
|
30
30
|
spec.add_development_dependency 'actionmailer', '~> 6.0'
|
31
|
+
spec.add_development_dependency 'devpack', '~> 0.3.2'
|
31
32
|
spec.add_development_dependency 'pony', '~> 1.13'
|
32
33
|
spec.add_development_dependency 'rspec', '~> 3.9'
|
33
34
|
spec.add_development_dependency 'rspec-its', '~> 1.3'
|
34
|
-
spec.add_development_dependency 'rubocop', '~>
|
35
|
+
spec.add_development_dependency 'rubocop', '~> 1.10'
|
36
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.2'
|
35
37
|
spec.add_development_dependency 'strong_versions', '~> 0.4.5'
|
36
38
|
spec.add_development_dependency 'timecop', '~> 0.9.1'
|
37
39
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postmortem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Farrell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02-
|
11
|
+
date: 2021-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mail
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '6.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: devpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.3.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.3.2
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: pony
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +100,28 @@ dependencies:
|
|
86
100
|
requirements:
|
87
101
|
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
103
|
+
version: '1.10'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.10'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '2.2'
|
90
118
|
type: :development
|
91
119
|
prerelease: false
|
92
120
|
version_requirements: !ruby/object:Gem::Requirement
|
93
121
|
requirements:
|
94
122
|
- - "~>"
|
95
123
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
124
|
+
version: '2.2'
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
126
|
name: strong_versions
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,6 +172,7 @@ files:
|
|
144
172
|
- layout/default.html.erb
|
145
173
|
- layout/dependencies.css
|
146
174
|
- layout/dependencies.js
|
175
|
+
- layout/favicon.b64
|
147
176
|
- layout/headers_template.html
|
148
177
|
- layout/layout.css
|
149
178
|
- layout/layout.js
|
@@ -180,7 +209,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
180
209
|
requirements:
|
181
210
|
- - ">="
|
182
211
|
- !ruby/object:Gem::Version
|
183
|
-
version: 2.
|
212
|
+
version: 2.5.0
|
184
213
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
214
|
requirements:
|
186
215
|
- - ">="
|
@@ -188,7 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
217
|
version: '0'
|
189
218
|
requirements: []
|
190
219
|
rubyforge_project:
|
191
|
-
rubygems_version: 2.7.6
|
220
|
+
rubygems_version: 2.7.6.2
|
192
221
|
signing_key:
|
193
222
|
specification_version: 4
|
194
223
|
summary: Development HTML Email Inspection Tool
|