mirror of
https://github.com/axllent/mailpit.git
synced 2025-06-15 00:05:15 +02:00
Chore: Refactor JavaScript, use arrow functions instead of "self" aliasing
This commit is contained in:
@ -18,7 +18,7 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
openAttachment: function (part, e) {
|
||||
openAttachment(part, e) {
|
||||
let filename = part.FileName
|
||||
let contentType = part.ContentType
|
||||
let href = this.resolve('/api/v1/message/' + this.message.ID + '/part/' + part.PartID)
|
||||
|
@ -41,9 +41,7 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
summary: function () {
|
||||
let self = this
|
||||
|
||||
summary() {
|
||||
if (!this.check) {
|
||||
return false
|
||||
}
|
||||
@ -65,8 +63,8 @@ export default {
|
||||
}
|
||||
|
||||
// filter by enabled platforms
|
||||
let results = o.Results.filter(function (w) {
|
||||
return self.platforms.indexOf(w.Platform) != -1
|
||||
let results = o.Results.filter((w) => {
|
||||
return this.platforms.indexOf(w.Platform) != -1
|
||||
})
|
||||
|
||||
if (results.length == 0) {
|
||||
@ -98,7 +96,7 @@ export default {
|
||||
}
|
||||
|
||||
let maxPartial = 0, maxUnsupported = 0
|
||||
result.Warnings.forEach(function (w) {
|
||||
result.Warnings.forEach((w) => {
|
||||
let scoreWeight = 1
|
||||
if (w.Score.Found < result.Total.Nodes) {
|
||||
// each error is weighted based on the number of occurrences vs: the total message nodes
|
||||
@ -108,7 +106,7 @@ export default {
|
||||
// pseudo-classes & at-rules need to be weighted lower as we do not know how many times they
|
||||
// are actually used in the HTML, and including things like bootstrap styles completely throws
|
||||
// off the calculation as these dominate.
|
||||
if (self.isPseudoClassOrAtRule(w.Title)) {
|
||||
if (this.isPseudoClassOrAtRule(w.Title)) {
|
||||
scoreWeight = 0.05
|
||||
w.PseudoClassOrAtRule = true
|
||||
}
|
||||
@ -124,15 +122,15 @@ export default {
|
||||
})
|
||||
|
||||
// sort warnings by final score
|
||||
result.Warnings.sort(function (a, b) {
|
||||
result.Warnings.sort((a, b) => {
|
||||
let aWeight = a.Score.Found > result.Total.Nodes ? result.Total.Nodes : a.Score.Found / result.Total.Nodes
|
||||
let bWeight = b.Score.Found > result.Total.Nodes ? result.Total.Nodes : b.Score.Found / result.Total.Nodes
|
||||
|
||||
if (self.isPseudoClassOrAtRule(a.Title)) {
|
||||
if (this.isPseudoClassOrAtRule(a.Title)) {
|
||||
aWeight = 0.05
|
||||
}
|
||||
|
||||
if (self.isPseudoClassOrAtRule(b.Title)) {
|
||||
if (this.isPseudoClassOrAtRule(b.Title)) {
|
||||
bWeight = 0.05
|
||||
}
|
||||
|
||||
@ -148,7 +146,7 @@ export default {
|
||||
return result
|
||||
},
|
||||
|
||||
graphSections: function () {
|
||||
graphSections() {
|
||||
let s = Math.round(this.summary.Total.Supported)
|
||||
let p = Math.round(this.summary.Total.Partial)
|
||||
let u = 100 - s - p
|
||||
@ -172,7 +170,7 @@ export default {
|
||||
},
|
||||
|
||||
// colors depend on both varying unsupported & partially unsupported percentages
|
||||
scoreColor: function () {
|
||||
scoreColor() {
|
||||
if (this.summary.Total.Unsupported < 5 && this.summary.Total.Partial < 10) {
|
||||
this.$emit('setBadgeStyle', 'bg-success')
|
||||
return 'text-success'
|
||||
@ -197,62 +195,51 @@ export default {
|
||||
platforms(v) {
|
||||
localStorage.setItem('html-check-platforms', JSON.stringify(v))
|
||||
},
|
||||
// enabled(v) {
|
||||
// if (!v) {
|
||||
// localStorage.setItem('htmlCheckDisabled', true)
|
||||
// this.$emit('setHtmlScore', false)
|
||||
// } else {
|
||||
// localStorage.removeItem('htmlCheckDisabled')
|
||||
// this.doCheck()
|
||||
// }
|
||||
// }
|
||||
},
|
||||
|
||||
methods: {
|
||||
doCheck: function () {
|
||||
doCheck() {
|
||||
this.check = false
|
||||
|
||||
if (this.message.HTML == "") {
|
||||
return
|
||||
}
|
||||
|
||||
let self = this
|
||||
|
||||
// ignore any error, do not show loader
|
||||
axios.get(self.resolve('/api/v1/message/' + self.message.ID + '/html-check'), null)
|
||||
.then(function (result) {
|
||||
self.check = result.data
|
||||
self.error = false
|
||||
axios.get(this.resolve('/api/v1/message/' + this.message.ID + '/html-check'), null)
|
||||
.then((result) => {
|
||||
this.check = result.data
|
||||
this.error = false
|
||||
|
||||
// set tooltips
|
||||
window.setTimeout(function () {
|
||||
window.setTimeout(() => {
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
[...tooltipTriggerList].map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl))
|
||||
}, 500)
|
||||
})
|
||||
.catch(function (error) {
|
||||
.catch((error) => {
|
||||
// handle error
|
||||
if (error.response && error.response.data) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
if (error.response.data.Error) {
|
||||
self.error = error.response.data.Error
|
||||
this.error = error.response.data.Error
|
||||
} else {
|
||||
self.error = error.response.data
|
||||
this.error = error.response.data
|
||||
}
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
self.error = 'Error sending data to the server. Please try again.'
|
||||
this.error = 'Error sending data to the server. Please try again.'
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
self.error = error.message
|
||||
this.error = error.message
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
loadConfig: function () {
|
||||
loadConfig() {
|
||||
let platforms = localStorage.getItem('html-check-platforms')
|
||||
if (platforms) {
|
||||
try {
|
||||
@ -268,7 +255,7 @@ export default {
|
||||
},
|
||||
|
||||
// return a platform's families (email clients)
|
||||
families: function (k) {
|
||||
families(k) {
|
||||
if (this.check.Platforms[k]) {
|
||||
return this.check.Platforms[k]
|
||||
}
|
||||
@ -277,19 +264,19 @@ export default {
|
||||
},
|
||||
|
||||
// return whether the test string is a pseudo class (:<test>) or at rule (@<test>)
|
||||
isPseudoClassOrAtRule: function (t) {
|
||||
isPseudoClassOrAtRule(t) {
|
||||
return t.match(/^(:|@)/)
|
||||
},
|
||||
|
||||
round: function (v) {
|
||||
round(v) {
|
||||
return Math.round(v)
|
||||
},
|
||||
|
||||
round2dm: function (v) {
|
||||
round2dm(v) {
|
||||
return Math.round(v * 100) / 100
|
||||
},
|
||||
|
||||
scrollToWarnings: function () {
|
||||
scrollToWarnings() {
|
||||
if (!this.$refs.warnings) {
|
||||
return
|
||||
}
|
||||
@ -312,9 +299,9 @@ export default {
|
||||
<div class="mt-5 mb-3">
|
||||
<div class="row w-100">
|
||||
<div class="col-md-8">
|
||||
<Donut :sections="graphSections" background="var(--bs-body-bg)" :size="180" unit="px" :thickness="20"
|
||||
has-legend legend-placement="bottom" :total="100" :start-angle="0" :auto-adjust-text-size="true"
|
||||
@section-click="scrollToWarnings">
|
||||
<Donut :sections="graphSections" background="var(--bs-body-bg)" :size="180" unit="px"
|
||||
:thickness="20" has-legend legend-placement="bottom" :total="100" :start-angle="0"
|
||||
:auto-adjust-text-size="true" @section-click="scrollToWarnings">
|
||||
<h2 class="m-0" :class="scoreColor" @click="scrollToWarnings">
|
||||
{{ round2dm(summary.Total.Supported) }}%
|
||||
</h2>
|
||||
@ -388,8 +375,9 @@ export default {
|
||||
<div class="col-sm mt-2 mt-sm-0">
|
||||
<div class="progress-stacked">
|
||||
<div class="progress" role="progressbar" aria-label="Supported"
|
||||
:aria-valuenow="warning.Score.Supported" aria-valuemin="0" aria-valuemax="100"
|
||||
:style="{ width: warning.Score.Supported + '%' }" title="Supported">
|
||||
:aria-valuenow="warning.Score.Supported" aria-valuemin="0"
|
||||
aria-valuemax="100" :style="{ width: warning.Score.Supported + '%' }"
|
||||
title="Supported">
|
||||
<div class="progress-bar bg-success">
|
||||
{{ round(warning.Score.Supported) + '%' }}
|
||||
</div>
|
||||
@ -402,8 +390,9 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress" role="progressbar" aria-label="No"
|
||||
:aria-valuenow="warning.Score.Unsupported" aria-valuemin="0" aria-valuemax="100"
|
||||
:style="{ width: warning.Score.Unsupported + '%' }" title="Not supported">
|
||||
:aria-valuenow="warning.Score.Unsupported" aria-valuemin="0"
|
||||
aria-valuemax="100" :style="{ width: warning.Score.Unsupported + '%' }"
|
||||
title="Not supported">
|
||||
<div class="progress-bar bg-danger">
|
||||
{{ round(warning.Score.Unsupported) + '%' }}
|
||||
</div>
|
||||
@ -420,7 +409,8 @@ export default {
|
||||
<i class="bi bi-info-circle me-2"></i>
|
||||
Detected {{ warning.Score.Found }} <code>{{ warning.Title }}</code>
|
||||
propert<template v-if="warning.Score.Found === 1">y</template><template
|
||||
v-else>ies</template> in the CSS styles, but unable to test if used or not.
|
||||
v-else>ies</template> in the CSS
|
||||
styles, but unable to test if used or not.
|
||||
</span>
|
||||
<span v-if="warning.Description != ''" v-html="warning.Description" class="me-2"></span>
|
||||
</p>
|
||||
@ -487,9 +477,9 @@ export default {
|
||||
<div id="col1" class="accordion-collapse collapse"
|
||||
data-bs-parent="#HTMLCheckAboutAccordion">
|
||||
<div class="accordion-body">
|
||||
The support for HTML/CSS messages varies greatly across email clients. HTML check
|
||||
attempts to calculate the overall support for your email for all selected platforms
|
||||
to give you some idea of the general compatibility of your HTML email.
|
||||
The support for HTML/CSS messages varies greatly across email clients. HTML
|
||||
check attempts to calculate the overall support for your email for all selected
|
||||
platforms to give you some idea of the general compatibility of your HTML email.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -507,29 +497,31 @@ export default {
|
||||
Internally the original HTML message is run against
|
||||
<b>{{ check.Total.Tests }}</b> different HTML and CSS tests. All tests
|
||||
(except for <code><script></code>) correspond to a test on
|
||||
<a href="https://www.caniemail.com/" target="_blank">caniemail.com</a>, and the
|
||||
final score is calculated using the available compatibility data.
|
||||
<a href="https://www.caniemail.com/" target="_blank">caniemail.com</a>, and
|
||||
the final score is calculated using the available compatibility data.
|
||||
</p>
|
||||
<p>
|
||||
CSS support is very difficult to programmatically test, especially if a message
|
||||
contains CSS style blocks or is linked to remote stylesheets. Remote stylesheets
|
||||
are, unless blocked via <code>--block-remote-css-and-fonts</code>, downloaded
|
||||
and injected into the message as style blocks. The email is then
|
||||
<a href="https://github.com/vanng822/go-premailer" target="_blank">inlined</a>
|
||||
CSS support is very difficult to programmatically test, especially if a
|
||||
message contains CSS style blocks or is linked to remote stylesheets. Remote
|
||||
stylesheets are, unless blocked via
|
||||
<code>--block-remote-css-and-fonts</code>,
|
||||
downloaded and injected into the message as style blocks. The email is then
|
||||
<a href="https://github.com/vanng822/go-premailer"
|
||||
target="_blank">inlined</a>
|
||||
to matching HTML elements. This gives Mailpit fairly accurate results.
|
||||
</p>
|
||||
<p>
|
||||
CSS properties such as <code>@font-face</code>, <code>:visited</code>,
|
||||
<code>:hover</code> etc cannot be inlined however, so these are searched for
|
||||
within CSS blocks. This method is not accurate as Mailpit does not know how many
|
||||
nodes it actually applies to, if any, so they are weighted lightly (5%) as not
|
||||
to affect the score. An example of this would be any email linking to the full
|
||||
bootstrap CSS which contains dozens of unused attributes.
|
||||
within CSS blocks. This method is not accurate as Mailpit does not know how
|
||||
many nodes it actually applies to, if any, so they are weighted lightly (5%)
|
||||
as not to affect the score. An example of this would be any email linking to
|
||||
the full bootstrap CSS which contains dozens of unused attributes.
|
||||
</p>
|
||||
<p>
|
||||
All warnings are displayed with their respective support, including any specific
|
||||
notes, and it is up to you to decide what you do with that information and how
|
||||
badly it may impact your message.
|
||||
All warnings are displayed with their respective support, including any
|
||||
specific notes, and it is up to you to decide what you do with that
|
||||
information and how badly it may impact your message.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -552,13 +544,15 @@ export default {
|
||||
<p>
|
||||
For each test, Mailpit calculates both the unsupported & partially-supported
|
||||
percentages in relation to the number of matches against the total number of
|
||||
nodes (elements) in the HTML. The maximum unsupported and partially-supported
|
||||
weighted scores are then used for the final score (ie: worst case scenario).
|
||||
nodes (elements) in the HTML. The maximum unsupported and
|
||||
partially-supported weighted scores are then used for the final score (ie:
|
||||
worst case scenario).
|
||||
</p>
|
||||
<p>
|
||||
To try explain this logic in very simple terms: Assuming a
|
||||
<code><script></code> node (element) has 100% failure (not supported in
|
||||
any email client), and a <code><p></code> node has 100% pass (supported).
|
||||
<code><script></code> node (element) has 100% failure (not supported
|
||||
in any email client), and a <code><p></code> node has 100% pass
|
||||
(supported).
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
@ -575,7 +569,8 @@ export default {
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Mailpit will sort the warnings according to their weighted unsupported scores.
|
||||
Mailpit will sort the warnings according to their weighted unsupported
|
||||
scores.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -591,9 +586,9 @@ export default {
|
||||
<div id="col4" class="accordion-collapse collapse"
|
||||
data-bs-parent="#HTMLCheckAboutAccordion">
|
||||
<div class="accordion-body">
|
||||
HTML check does not detect if the original HTML is valid. In order to detect applied
|
||||
styles to every node, the HTML email is run through a parser which is very good at
|
||||
turning invalid input into valid output. It is what it is...
|
||||
HTML check does not detect if the original HTML is valid. In order to detect
|
||||
applied styles to every node, the HTML email is run through a parser which is
|
||||
very good at turning invalid input into valid output. It is what it is...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
<script>
|
||||
import commonMixins from '../../mixins/CommonMixins'
|
||||
|
||||
@ -16,10 +15,9 @@ export default {
|
||||
},
|
||||
|
||||
mounted() {
|
||||
let self = this;
|
||||
let uri = self.resolve('/api/v1/message/' + self.message.ID + '/headers')
|
||||
self.get(uri, false, function (response) {
|
||||
self.headers = response.data
|
||||
let uri = this.resolve('/api/v1/message/' + this.message.ID + '/headers')
|
||||
this.get(uri, false, (response) => {
|
||||
this.headers = response.data
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -64,7 +64,7 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
groupedStatuses: function () {
|
||||
groupedStatuses() {
|
||||
let results = {}
|
||||
|
||||
if (!this.check) {
|
||||
@ -114,7 +114,7 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
doCheck: function () {
|
||||
doCheck() {
|
||||
this.check = false
|
||||
this.loading = true
|
||||
let uri = this.resolve('/api/v1/message/' + this.message.ID + '/link-check')
|
||||
@ -122,38 +122,37 @@ export default {
|
||||
uri += '?follow=true'
|
||||
}
|
||||
|
||||
let self = this
|
||||
// ignore any error, do not show loader
|
||||
axios.get(uri, null)
|
||||
.then(function (result) {
|
||||
self.check = result.data
|
||||
self.error = false
|
||||
.then((result) => {
|
||||
this.check = result.data
|
||||
this.error = false
|
||||
|
||||
self.$emit('setLinkErrors', result.data.Errors)
|
||||
this.$emit('setLinkErrors', result.data.Errors)
|
||||
})
|
||||
.catch(function (error) {
|
||||
.catch((error) => {
|
||||
// handle error
|
||||
if (error.response && error.response.data) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
if (error.response.data.Error) {
|
||||
self.error = error.response.data.Error
|
||||
this.error = error.response.data.Error
|
||||
} else {
|
||||
self.error = error.response.data
|
||||
this.error = error.response.data
|
||||
}
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
self.error = 'Error sending data to the server. Please try again.'
|
||||
this.error = 'Error sending data to the server. Please try again.'
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
self.error = error.message
|
||||
this.error = error.message
|
||||
}
|
||||
})
|
||||
.then(function (result) {
|
||||
.then((result) => {
|
||||
// always run
|
||||
self.loading = false
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
}
|
||||
@ -239,7 +238,8 @@ export default {
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="LinkCheckOptions" tabindex="-1" aria-labelledby="LinkCheckOptionsLabel" aria-hidden="true">
|
||||
<div class="modal fade" id="LinkCheckOptions" tabindex="-1" aria-labelledby="LinkCheckOptionsLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@ -296,11 +296,12 @@ export default {
|
||||
What is Link check?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="col1" class="accordion-collapse collapse" data-bs-parent="#LinkCheckAboutAccordion">
|
||||
<div id="col1" class="accordion-collapse collapse"
|
||||
data-bs-parent="#LinkCheckAboutAccordion">
|
||||
<div class="accordion-body">
|
||||
Link check scans your message HTML and text for all unique links, images and linked
|
||||
stylesheets. It then does a HTTP <code>HEAD</code> request to each link, 5 at a time, to
|
||||
test whether the link/image/stylesheet exists.
|
||||
stylesheets. It then does a HTTP <code>HEAD</code> request to each link, 5 at a
|
||||
time, to test whether the link/image/stylesheet exists.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -311,14 +312,16 @@ export default {
|
||||
What are "301" and "302" links?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="col2" class="accordion-collapse collapse" data-bs-parent="#LinkCheckAboutAccordion">
|
||||
<div id="col2" class="accordion-collapse collapse"
|
||||
data-bs-parent="#LinkCheckAboutAccordion">
|
||||
<div class="accordion-body">
|
||||
<p>
|
||||
These are links that redirect you to another URL, for example newsletters
|
||||
often use redirect links to track user clicks.
|
||||
</p>
|
||||
<p>
|
||||
By default Link check will not follow these links, however you can turn this on via
|
||||
By default Link check will not follow these links, however you can turn this on
|
||||
via
|
||||
the settings and Link check will "follow" those redirects.
|
||||
</p>
|
||||
</div>
|
||||
@ -331,7 +334,8 @@ export default {
|
||||
Why are some links returning an error but work in my browser?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="col3" class="accordion-collapse collapse" data-bs-parent="#LinkCheckAboutAccordion">
|
||||
<div id="col3" class="accordion-collapse collapse"
|
||||
data-bs-parent="#LinkCheckAboutAccordion">
|
||||
<div class="accordion-body">
|
||||
<p>This may be due to various reasons, for instance:</p>
|
||||
<ul>
|
||||
@ -353,11 +357,12 @@ export default {
|
||||
What are the risks of running Link check automatically?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="col4" class="accordion-collapse collapse" data-bs-parent="#LinkCheckAboutAccordion">
|
||||
<div id="col4" class="accordion-collapse collapse"
|
||||
data-bs-parent="#LinkCheckAboutAccordion">
|
||||
<div class="accordion-body">
|
||||
<p>
|
||||
Depending on the type of messages you are testing, opening all links on all messages
|
||||
may have undesired consequences:
|
||||
Depending on the type of messages you are testing, opening all links on all
|
||||
messages may have undesired consequences:
|
||||
</p>
|
||||
<ul>
|
||||
<li>If the message contains tracking links this may reveal your identity.</li>
|
||||
@ -366,13 +371,13 @@ export default {
|
||||
unsubscribe you.
|
||||
</li>
|
||||
<li>
|
||||
To speed up the checking process, Link check will attempt 5 URLs at a time. This
|
||||
could lead to temporary heady load on the remote server.
|
||||
To speed up the checking process, Link check will attempt 5 URLs at a time.
|
||||
This could lead to temporary heady load on the remote server.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Unless you know what messages you receive, it is advised to only run the Link check
|
||||
manually.
|
||||
Unless you know what messages you receive, it is advised to only run the Link
|
||||
check manually.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -61,16 +61,15 @@ export default {
|
||||
|
||||
scaleHTMLPreview(v) {
|
||||
if (v == 'display') {
|
||||
let self = this
|
||||
window.setTimeout(function () {
|
||||
self.resizeIFrames()
|
||||
window.setTimeout(() => {
|
||||
this.resizeIFrames()
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
hasAnyChecksEnabled: function () {
|
||||
hasAnyChecksEnabled() {
|
||||
return (mailbox.showHTMLCheck && this.message.HTML)
|
||||
|| mailbox.showLinkCheck
|
||||
|| (mailbox.showSpamCheck && mailbox.uiConfig.SpamAssassin)
|
||||
@ -78,56 +77,53 @@ export default {
|
||||
},
|
||||
|
||||
mounted() {
|
||||
let self = this
|
||||
self.canSaveTags = false
|
||||
self.messageTags = self.message.Tags
|
||||
self.renderUI()
|
||||
this.canSaveTags = false
|
||||
this.messageTags = this.message.Tags
|
||||
this.renderUI()
|
||||
|
||||
window.addEventListener("resize", self.resizeIFrames)
|
||||
window.addEventListener("resize", this.resizeIFrames)
|
||||
|
||||
let headersTab = document.getElementById('nav-headers-tab')
|
||||
headersTab.addEventListener('shown.bs.tab', function (event) {
|
||||
self.loadHeaders = true
|
||||
headersTab.addEventListener('shown.bs.tab', (event) => {
|
||||
this.loadHeaders = true
|
||||
})
|
||||
|
||||
let rawTab = document.getElementById('nav-raw-tab')
|
||||
rawTab.addEventListener('shown.bs.tab', function (event) {
|
||||
self.srcURI = self.resolve('/api/v1/message/' + self.message.ID + '/raw')
|
||||
self.resizeIFrames()
|
||||
rawTab.addEventListener('shown.bs.tab', (event) => {
|
||||
this.srcURI = this.resolve('/api/v1/message/' + this.message.ID + '/raw')
|
||||
this.resizeIFrames()
|
||||
})
|
||||
|
||||
// manually refresh tags
|
||||
self.get(self.resolve(`/api/v1/tags`), false, function (response) {
|
||||
self.availableTags = response.data
|
||||
self.$nextTick(function () {
|
||||
this.get(this.resolve(`/api/v1/tags`), false, (response) => {
|
||||
this.availableTags = response.data
|
||||
this.$nextTick(() => {
|
||||
Tags.init('select[multiple]')
|
||||
// delay tag change detection to allow Tags to load
|
||||
window.setTimeout(function () {
|
||||
self.canSaveTags = true
|
||||
window.setTimeout(() => {
|
||||
this.canSaveTags = true
|
||||
}, 200)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
isHTMLTabSelected: function () {
|
||||
isHTMLTabSelected() {
|
||||
this.showMobileButtons = this.$refs.navhtml
|
||||
&& this.$refs.navhtml.classList.contains('active')
|
||||
},
|
||||
|
||||
renderUI: function () {
|
||||
let self = this
|
||||
|
||||
renderUI() {
|
||||
// activate the first non-disabled tab
|
||||
document.querySelector('#nav-tab button:not([disabled])').click()
|
||||
document.activeElement.blur() // blur focus
|
||||
document.getElementById('message-view').scrollTop = 0
|
||||
|
||||
self.isHTMLTabSelected()
|
||||
this.isHTMLTabSelected()
|
||||
|
||||
document.querySelectorAll('button[data-bs-toggle="tab"]').forEach(function (listObj) {
|
||||
listObj.addEventListener('shown.bs.tab', function (event) {
|
||||
self.isHTMLTabSelected()
|
||||
document.querySelectorAll('button[data-bs-toggle="tab"]').forEach((listObj) => {
|
||||
listObj.addEventListener('shown.bs.tab', (event) => {
|
||||
this.isHTMLTabSelected()
|
||||
})
|
||||
})
|
||||
|
||||
@ -135,7 +131,7 @@ export default {
|
||||
[...tooltipTriggerList].map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl))
|
||||
|
||||
// delay 0.2s until vue has rendered the iframe content
|
||||
window.setTimeout(function () {
|
||||
window.setTimeout(() => {
|
||||
let p = document.getElementById('preview-html')
|
||||
if (p) {
|
||||
// make links open in new window
|
||||
@ -148,7 +144,7 @@ export default {
|
||||
anchorEl.setAttribute('target', '_blank')
|
||||
}
|
||||
}
|
||||
self.resizeIFrames()
|
||||
this.resizeIFrames()
|
||||
}
|
||||
}, 200)
|
||||
|
||||
@ -158,12 +154,12 @@ export default {
|
||||
Prism.highlightAll()
|
||||
},
|
||||
|
||||
resizeIframe: function (el) {
|
||||
resizeIframe(el) {
|
||||
let i = el.target
|
||||
i.style.height = i.contentWindow.document.body.scrollHeight + 50 + 'px'
|
||||
},
|
||||
|
||||
resizeIFrames: function () {
|
||||
resizeIFrames() {
|
||||
if (this.scaleHTMLPreview != 'display') {
|
||||
return
|
||||
}
|
||||
@ -175,7 +171,7 @@ export default {
|
||||
},
|
||||
|
||||
// set the iframe body & text colors based on current theme
|
||||
initRawIframe: function (el) {
|
||||
initRawIframe(el) {
|
||||
let bodyStyles = window.getComputedStyle(document.body, null)
|
||||
let bg = bodyStyles.getPropertyValue('background-color')
|
||||
let txt = bodyStyles.getPropertyValue('color')
|
||||
@ -189,27 +185,25 @@ export default {
|
||||
this.resizeIframe(el)
|
||||
},
|
||||
|
||||
sanitizeHTML: function (h) {
|
||||
sanitizeHTML(h) {
|
||||
// remove <base/> tag if set
|
||||
return h.replace(/<base .*>/mi, '')
|
||||
},
|
||||
|
||||
saveTags: function () {
|
||||
let self = this
|
||||
|
||||
saveTags() {
|
||||
var data = {
|
||||
IDs: [this.message.ID],
|
||||
Tags: this.messageTags
|
||||
}
|
||||
|
||||
self.put(self.resolve('/api/v1/tags'), data, function (response) {
|
||||
this.put(this.resolve('/api/v1/tags'), data, (response) => {
|
||||
window.scrollInPlace = true
|
||||
self.$emit('loadMessages')
|
||||
this.$emit('loadMessages')
|
||||
})
|
||||
},
|
||||
|
||||
// Convert plain text to HTML including anchor links
|
||||
textToHTML: function (s) {
|
||||
textToHTML(s) {
|
||||
let html = s
|
||||
|
||||
// full links with http(s)
|
||||
|
@ -46,26 +46,25 @@ export default {
|
||||
|
||||
methods: {
|
||||
// triggered manually after modal is shown
|
||||
initTags: function () {
|
||||
initTags() {
|
||||
Tags.init("select[multiple]")
|
||||
},
|
||||
|
||||
releaseMessage: function () {
|
||||
let self = this
|
||||
releaseMessage() {
|
||||
// set timeout to allow for user clicking send before the tag filter has applied the tag
|
||||
window.setTimeout(function () {
|
||||
if (!self.addresses.length) {
|
||||
window.setTimeout(() => {
|
||||
if (!this.addresses.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
let data = {
|
||||
To: self.addresses
|
||||
To: this.addresses
|
||||
}
|
||||
|
||||
self.post(self.resolve('/api/v1/message/' + self.message.ID + '/release'), data, function (response) {
|
||||
self.modal("ReleaseModal").hide()
|
||||
if (self.deleteAfterRelease) {
|
||||
self.$emit('delete')
|
||||
this.post(this.resolve('/api/v1/message/' + this.message.ID + '/release'), data, (response) => {
|
||||
this.modal("ReleaseModal").hide()
|
||||
if (this.deleteAfterRelease) {
|
||||
this.$emit('delete')
|
||||
}
|
||||
})
|
||||
}, 100)
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
<script>
|
||||
import AjaxLoader from '../AjaxLoader.vue'
|
||||
import CommonMixins from '../../mixins/CommonMixins'
|
||||
@ -23,9 +22,8 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
initScreenshot: function () {
|
||||
initScreenshot() {
|
||||
this.loading = 1
|
||||
let self = this
|
||||
// remove base tag, if set
|
||||
let h = this.message.HTML.replace(/<base .*>/mi, '')
|
||||
let proxy = this.resolve('/proxy')
|
||||
@ -38,11 +36,11 @@ export default {
|
||||
|
||||
// update any inline `url(...)` absolute links
|
||||
const urlRegex = /(url\((\'|\")?(https?:\/\/[^\)\'\"]+)(\'|\")?\))/mgi;
|
||||
h = h.replaceAll(urlRegex, function (match, p1, p2, p3) {
|
||||
h = h.replaceAll(urlRegex, (match, p1, p2, p3) => {
|
||||
if (typeof p2 === 'string') {
|
||||
return `url(${p2}${proxy}?url=` + encodeURIComponent(self.decodeEntities(p3)) + `${p2})`
|
||||
return `url(${p2}${proxy}?url=` + encodeURIComponent(this.decodeEntities(p3)) + `${p2})`
|
||||
}
|
||||
return `url(${proxy}?url=` + encodeURIComponent(self.decodeEntities(p3)) + `)`
|
||||
return `url(${proxy}?url=` + encodeURIComponent(this.decodeEntities(p3)) + `)`
|
||||
})
|
||||
|
||||
// create temporary document to manipulate
|
||||
@ -63,7 +61,7 @@ export default {
|
||||
let src = i.getAttribute('href')
|
||||
|
||||
if (src && src.match(/^https?:\/\//i) && src.indexOf(window.location.origin + window.location.pathname) !== 0) {
|
||||
i.setAttribute('href', `${proxy}?url=` + encodeURIComponent(self.decodeEntities(src)))
|
||||
i.setAttribute('href', `${proxy}?url=` + encodeURIComponent(this.decodeEntities(src)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +70,7 @@ export default {
|
||||
for (let i of images) {
|
||||
let src = i.getAttribute('src')
|
||||
if (src && src.match(/^https?:\/\//i) && src.indexOf(window.location.origin + window.location.pathname) !== 0) {
|
||||
i.setAttribute('src', `${proxy}?url=` + encodeURIComponent(self.decodeEntities(src)))
|
||||
i.setAttribute('src', `${proxy}?url=` + encodeURIComponent(this.decodeEntities(src)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +81,7 @@ export default {
|
||||
|
||||
if (src && src.match(/^https?:\/\//i) && src.indexOf(window.location.origin + window.location.pathname) !== 0) {
|
||||
// replace with proxy link
|
||||
i.setAttribute('background', `${proxy}?url=` + encodeURIComponent(self.decodeEntities(src)))
|
||||
i.setAttribute('background', `${proxy}?url=` + encodeURIComponent(this.decodeEntities(src)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +90,7 @@ export default {
|
||||
},
|
||||
|
||||
// HTML decode function
|
||||
decodeEntities: function (s) {
|
||||
decodeEntities(s) {
|
||||
let e = document.createElement('div')
|
||||
e.innerHTML = s
|
||||
let str = e.textContent
|
||||
@ -100,8 +98,7 @@ export default {
|
||||
return str
|
||||
},
|
||||
|
||||
doScreenshot: function () {
|
||||
let self = this
|
||||
doScreenshot() {
|
||||
let width = document.getElementById('message-view').getBoundingClientRect().width
|
||||
|
||||
let prev = document.getElementById('preview-html')
|
||||
@ -113,7 +110,7 @@ export default {
|
||||
width = 300
|
||||
}
|
||||
|
||||
let i = document.getElementById('screenshot-html')
|
||||
const i = document.getElementById('screenshot-html')
|
||||
|
||||
// set the iframe width
|
||||
i.style.width = width + 'px'
|
||||
@ -127,11 +124,11 @@ export default {
|
||||
width: width,
|
||||
}).then(dataUrl => {
|
||||
const link = document.createElement('a')
|
||||
link.download = self.message.ID + '.png'
|
||||
link.download = this.message.ID + '.png'
|
||||
link.href = dataUrl
|
||||
link.click()
|
||||
self.loading = 0
|
||||
self.html = false
|
||||
this.loading = 0
|
||||
this.html = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -38,41 +38,39 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
doCheck: function () {
|
||||
doCheck() {
|
||||
this.check = false
|
||||
|
||||
let self = this
|
||||
|
||||
// ignore any error, do not show loader
|
||||
axios.get(self.resolve('/api/v1/message/' + self.message.ID + '/sa-check'), null)
|
||||
.then(function (result) {
|
||||
self.check = result.data
|
||||
self.error = false
|
||||
self.setIcons()
|
||||
axios.get(this.resolve('/api/v1/message/' + this.message.ID + '/sa-check'), null)
|
||||
.then((result) => {
|
||||
this.check = result.data
|
||||
this.error = false
|
||||
this.setIcons()
|
||||
})
|
||||
.catch(function (error) {
|
||||
.catch((error) => {
|
||||
// handle error
|
||||
if (error.response && error.response.data) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
if (error.response.data.Error) {
|
||||
self.error = error.response.data.Error
|
||||
this.error = error.response.data.Error
|
||||
} else {
|
||||
self.error = error.response.data
|
||||
this.error = error.response.data
|
||||
}
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
self.error = 'Error sending data to the server. Please try again.'
|
||||
this.error = 'Error sending data to the server. Please try again.'
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
self.error = error.message
|
||||
this.error = error.message
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
badgeStyle: function (ignorePadding = false) {
|
||||
badgeStyle(ignorePadding = false) {
|
||||
let badgeStyle = 'bg-success'
|
||||
if (this.check.Error) {
|
||||
badgeStyle = 'bg-warning text-primary'
|
||||
@ -90,7 +88,7 @@ export default {
|
||||
return badgeStyle
|
||||
},
|
||||
|
||||
setIcons: function () {
|
||||
setIcons() {
|
||||
let score = this.check.Score
|
||||
if (this.check.Error && this.check.Error != '') {
|
||||
score = '!'
|
||||
@ -102,7 +100,7 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
graphSections: function () {
|
||||
graphSections() {
|
||||
let score = this.check.Score
|
||||
let p = Math.round(score / 5 * 100)
|
||||
if (p > 100) {
|
||||
@ -125,7 +123,7 @@ export default {
|
||||
]
|
||||
},
|
||||
|
||||
scoreColor: function () {
|
||||
scoreColor() {
|
||||
return this.graphSections[0].color
|
||||
},
|
||||
}
|
||||
|
Reference in New Issue
Block a user