mirror of
https://github.com/axllent/mailpit.git
synced 2025-07-05 00:48:52 +02:00
Chore: Apply linting to all JavaScript/Vue files with eslint & prettier
This commit is contained in:
@ -1,221 +1,234 @@
|
||||
<script>
|
||||
import { VcDonut } from 'vue-css-donut-chart'
|
||||
import axios from 'axios'
|
||||
import commonMixins from '../../mixins/CommonMixins'
|
||||
import { Tooltip } from 'bootstrap'
|
||||
import { VcDonut } from "vue-css-donut-chart";
|
||||
import axios from "axios";
|
||||
import commonMixins from "../../mixins/CommonMixins";
|
||||
import { Tooltip } from "bootstrap";
|
||||
import DOMPurify from "dompurify";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
message: Object,
|
||||
},
|
||||
|
||||
components: {
|
||||
VcDonut,
|
||||
},
|
||||
|
||||
emits: ["setHtmlScore", "setBadgeStyle"],
|
||||
|
||||
mixins: [commonMixins],
|
||||
|
||||
props: {
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ["setHtmlScore", "setBadgeStyle"],
|
||||
|
||||
data() {
|
||||
return {
|
||||
error: false,
|
||||
check: false,
|
||||
platforms: [],
|
||||
allPlatforms: {
|
||||
"windows": "Windows",
|
||||
windows: "Windows",
|
||||
"windows-mail": "Windows Mail",
|
||||
"outlook-com": "Outlook.com",
|
||||
"macos": "macOS",
|
||||
"ios": "iOS",
|
||||
"android": "Android",
|
||||
macos: "macOS",
|
||||
ios: "iOS",
|
||||
android: "Android",
|
||||
"desktop-webmail": "Desktop Webmail",
|
||||
"mobile-webmail": "Mobile Webmail",
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loadConfig()
|
||||
this.doCheck()
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
summary() {
|
||||
if (!this.check) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
let result = {
|
||||
const result = {
|
||||
Warnings: [],
|
||||
Total: {
|
||||
Nodes: this.check.Total.Nodes
|
||||
}
|
||||
}
|
||||
Nodes: this.check.Total.Nodes,
|
||||
},
|
||||
};
|
||||
|
||||
for (let i = 0; i < this.check.Warnings.length; i++) {
|
||||
let o = JSON.parse(JSON.stringify(this.check.Warnings[i]))
|
||||
const o = JSON.parse(JSON.stringify(this.check.Warnings[i]));
|
||||
|
||||
// for <script> test
|
||||
if (o.Results.length == 0) {
|
||||
result.Warnings.push(o)
|
||||
continue
|
||||
if (o.Results.length === 0) {
|
||||
result.Warnings.push(o);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by enabled platforms
|
||||
let results = o.Results.filter((w) => {
|
||||
return this.platforms.indexOf(w.Platform) != -1
|
||||
})
|
||||
const results = o.Results.filter((w) => {
|
||||
return this.platforms.indexOf(w.Platform) !== -1;
|
||||
});
|
||||
|
||||
if (results.length == 0) {
|
||||
continue
|
||||
if (results.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// recalculate the percentages
|
||||
let y = 0, p = 0, n = 0
|
||||
let y = 0;
|
||||
let p = 0;
|
||||
let n = 0;
|
||||
|
||||
results.forEach(function (r) {
|
||||
if (r.Support == "yes") {
|
||||
y++
|
||||
} else if (r.Support == "partial") {
|
||||
p++
|
||||
results.forEach((r) => {
|
||||
if (r.Support === "yes") {
|
||||
y++;
|
||||
} else if (r.Support === "partial") {
|
||||
p++;
|
||||
} else {
|
||||
n++
|
||||
n++;
|
||||
}
|
||||
})
|
||||
let total = y + p + n
|
||||
o.Results = results
|
||||
});
|
||||
const total = y + p + n;
|
||||
o.Results = results;
|
||||
o.Score = {
|
||||
Found: o.Score.Found,
|
||||
Supported: y / total * 100,
|
||||
Partial: p / total * 100,
|
||||
Unsupported: n / total * 100
|
||||
}
|
||||
Supported: (y / total) * 100,
|
||||
Partial: (p / total) * 100,
|
||||
Unsupported: (n / total) * 100,
|
||||
};
|
||||
|
||||
result.Warnings.push(o)
|
||||
result.Warnings.push(o);
|
||||
}
|
||||
|
||||
let maxPartial = 0, maxUnsupported = 0
|
||||
let maxPartial = 0;
|
||||
let maxUnsupported = 0;
|
||||
result.Warnings.forEach((w) => {
|
||||
let scoreWeight = 1
|
||||
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
|
||||
scoreWeight = w.Score.Found / result.Total.Nodes
|
||||
scoreWeight = w.Score.Found / result.Total.Nodes;
|
||||
}
|
||||
|
||||
// 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 (this.isPseudoClassOrAtRule(w.Title)) {
|
||||
scoreWeight = 0.05
|
||||
w.PseudoClassOrAtRule = true
|
||||
scoreWeight = 0.05;
|
||||
w.PseudoClassOrAtRule = true;
|
||||
}
|
||||
|
||||
let scorePartial = w.Score.Partial * scoreWeight
|
||||
let scoreUnsupported = w.Score.Unsupported * scoreWeight
|
||||
const scorePartial = w.Score.Partial * scoreWeight;
|
||||
const scoreUnsupported = w.Score.Unsupported * scoreWeight;
|
||||
if (scorePartial > maxPartial) {
|
||||
maxPartial = scorePartial
|
||||
maxPartial = scorePartial;
|
||||
}
|
||||
if (scoreUnsupported > maxUnsupported) {
|
||||
maxUnsupported = scoreUnsupported
|
||||
maxUnsupported = scoreUnsupported;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// sort warnings by final score
|
||||
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
|
||||
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 (this.isPseudoClassOrAtRule(a.Title)) {
|
||||
aWeight = 0.05
|
||||
aWeight = 0.05;
|
||||
}
|
||||
|
||||
if (this.isPseudoClassOrAtRule(b.Title)) {
|
||||
bWeight = 0.05
|
||||
bWeight = 0.05;
|
||||
}
|
||||
|
||||
return (a.Score.Unsupported + a.Score.Partial) * aWeight < (b.Score.Unsupported + b.Score.Partial) * bWeight
|
||||
})
|
||||
return (
|
||||
(a.Score.Unsupported + a.Score.Partial) * aWeight <
|
||||
(b.Score.Unsupported + b.Score.Partial) * bWeight
|
||||
);
|
||||
});
|
||||
|
||||
result.Total.Supported = 100 - maxPartial - maxUnsupported
|
||||
result.Total.Partial = maxPartial
|
||||
result.Total.Unsupported = maxUnsupported
|
||||
result.Total.Supported = 100 - maxPartial - maxUnsupported;
|
||||
result.Total.Partial = maxPartial;
|
||||
result.Total.Unsupported = maxUnsupported;
|
||||
|
||||
this.$emit('setHtmlScore', result.Total.Supported)
|
||||
this.$emit("setHtmlScore", result.Total.Supported);
|
||||
|
||||
return result
|
||||
return result;
|
||||
},
|
||||
|
||||
graphSections() {
|
||||
let s = Math.round(this.summary.Total.Supported)
|
||||
let p = Math.round(this.summary.Total.Partial)
|
||||
let u = 100 - s - p
|
||||
const s = Math.round(this.summary.Total.Supported);
|
||||
const p = Math.round(this.summary.Total.Partial);
|
||||
const u = 100 - s - p;
|
||||
return [
|
||||
{
|
||||
label: this.round2dm(this.summary.Total.Supported) + '% supported',
|
||||
label: this.round2dm(this.summary.Total.Supported) + "% supported",
|
||||
value: s,
|
||||
color: '#198754'
|
||||
color: "#198754",
|
||||
},
|
||||
{
|
||||
label: this.round2dm(this.summary.Total.Partial) + '% partially supported',
|
||||
label: this.round2dm(this.summary.Total.Partial) + "% partially supported",
|
||||
value: p,
|
||||
color: '#ffc107'
|
||||
color: "#ffc107",
|
||||
},
|
||||
{
|
||||
label: this.round2dm(this.summary.Total.Unsupported) + '% not supported',
|
||||
label: this.round2dm(this.summary.Total.Unsupported) + "% not supported",
|
||||
value: u,
|
||||
color: '#dc3545'
|
||||
}
|
||||
]
|
||||
color: "#dc3545",
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
// colors depend on both varying unsupported & partially unsupported percentages
|
||||
scoreColor() {
|
||||
if (this.summary.Total.Unsupported < 5 && this.summary.Total.Partial < 10) {
|
||||
this.$emit('setBadgeStyle', 'bg-success')
|
||||
return 'text-success'
|
||||
this.$emit("setBadgeStyle", "bg-success");
|
||||
return "text-success";
|
||||
} else if (this.summary.Total.Unsupported < 10 && this.summary.Total.Partial < 15) {
|
||||
this.$emit('setBadgeStyle', 'bg-warning text-primary')
|
||||
return 'text-warning'
|
||||
this.$emit("setBadgeStyle", "bg-warning text-primary");
|
||||
return "text-warning";
|
||||
}
|
||||
|
||||
this.$emit('setBadgeStyle', 'bg-danger')
|
||||
return 'text-danger'
|
||||
}
|
||||
this.$emit("setBadgeStyle", "bg-danger");
|
||||
return "text-danger";
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
message: {
|
||||
handler() {
|
||||
this.$emit('setHtmlScore', false)
|
||||
this.doCheck()
|
||||
this.$emit("setHtmlScore", false);
|
||||
this.doCheck();
|
||||
},
|
||||
deep: true
|
||||
deep: true,
|
||||
},
|
||||
platforms(v) {
|
||||
localStorage.setItem('html-check-platforms', JSON.stringify(v))
|
||||
localStorage.setItem("html-check-platforms", JSON.stringify(v));
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loadConfig();
|
||||
this.doCheck();
|
||||
},
|
||||
|
||||
methods: {
|
||||
doCheck() {
|
||||
this.check = false
|
||||
this.check = false;
|
||||
|
||||
if (this.message.HTML == "") {
|
||||
return
|
||||
if (this.message.HTML === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore any error, do not show loader
|
||||
axios.get(this.resolve('/api/v1/message/' + this.message.ID + '/html-check'), null)
|
||||
axios
|
||||
.get(this.resolve("/api/v1/message/" + this.message.ID + "/html-check"), null)
|
||||
.then((result) => {
|
||||
this.check = result.data
|
||||
this.error = false
|
||||
this.check = result.data;
|
||||
this.error = false;
|
||||
|
||||
// set tooltips
|
||||
window.setTimeout(() => {
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
[...tooltipTriggerList].map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl))
|
||||
}, 500)
|
||||
[...tooltipTriggerList].map((tooltipTriggerEl) => new Tooltip(tooltipTriggerEl));
|
||||
}, 500);
|
||||
})
|
||||
.catch((error) => {
|
||||
// handle error
|
||||
@ -223,68 +236,72 @@ export default {
|
||||
// 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) {
|
||||
this.error = error.response.data.Error
|
||||
this.error = error.response.data.Error;
|
||||
} else {
|
||||
this.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
|
||||
this.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
|
||||
this.error = error.message
|
||||
this.error = error.message;
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
loadConfig() {
|
||||
let platforms = localStorage.getItem('html-check-platforms')
|
||||
const platforms = localStorage.getItem("html-check-platforms");
|
||||
if (platforms) {
|
||||
try {
|
||||
this.platforms = JSON.parse(platforms)
|
||||
} catch (e) {
|
||||
}
|
||||
this.platforms = JSON.parse(platforms);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// set all options
|
||||
if (this.platforms.length == 0) {
|
||||
this.platforms = Object.keys(this.allPlatforms)
|
||||
if (this.platforms.length === 0) {
|
||||
this.platforms = Object.keys(this.allPlatforms);
|
||||
}
|
||||
},
|
||||
|
||||
// return a platform's families (email clients)
|
||||
families(k) {
|
||||
if (this.check.Platforms[k]) {
|
||||
return this.check.Platforms[k]
|
||||
return this.check.Platforms[k];
|
||||
}
|
||||
|
||||
return []
|
||||
return [];
|
||||
},
|
||||
|
||||
// return whether the test string is a pseudo class (:<test>) or at rule (@<test>)
|
||||
isPseudoClassOrAtRule(t) {
|
||||
return t.match(/^(:|@)/)
|
||||
return t.match(/^(:|@)/);
|
||||
},
|
||||
|
||||
round(v) {
|
||||
return Math.round(v)
|
||||
return Math.round(v);
|
||||
},
|
||||
|
||||
round2dm(v) {
|
||||
return Math.round(v * 100) / 100
|
||||
return Math.round(v * 100) / 100;
|
||||
},
|
||||
|
||||
scrollToWarnings() {
|
||||
if (!this.$refs.warnings) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
this.$refs.warnings.scrollIntoView({ behavior: "smooth" })
|
||||
this.$refs.warnings.scrollIntoView({ behavior: "smooth" });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize HTML to prevent XSS
|
||||
sanitizeHTML(html) {
|
||||
return DOMPurify.sanitize(html);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -299,39 +316,50 @@ export default {
|
||||
<div class="mt-5 mb-3">
|
||||
<div class="row w-100">
|
||||
<div class="col-md-8">
|
||||
<vc-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">
|
||||
<vc-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>
|
||||
<div class="text-body">
|
||||
support
|
||||
</div>
|
||||
<div class="text-body">support</div>
|
||||
<template #legend>
|
||||
<p class="my-3 small mb-1 text-center" @click="scrollToWarnings">
|
||||
<span class="text-nowrap">
|
||||
<i class="bi bi-circle-fill text-success"></i>
|
||||
{{ round2dm(summary.Total.Supported) }}% supported
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="text-nowrap">
|
||||
<i class="bi bi-circle-fill text-warning"></i>
|
||||
{{ round2dm(summary.Total.Partial) }}% partially supported
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="text-nowrap">
|
||||
<i class="bi bi-circle-fill text-danger"></i>
|
||||
{{ round2dm(summary.Total.Unsupported) }}% not supported
|
||||
</span>
|
||||
</p>
|
||||
<p class="small text-muted">
|
||||
calculated from {{ formatNumber(check.Total.Tests) }} tests
|
||||
</p>
|
||||
<p class="small text-muted">calculated from {{ formatNumber(check.Total.Tests) }} tests</p>
|
||||
</template>
|
||||
</vc-donut>
|
||||
|
||||
<div class="input-group justify-content-center mb-3">
|
||||
<button class="btn btn-outline-secondary" data-bs-toggle="modal"
|
||||
data-bs-target="#AboutHTMLCheckResults">
|
||||
<button
|
||||
class="btn btn-outline-secondary"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#AboutHTMLCheckResults"
|
||||
>
|
||||
<i class="bi bi-info-circle-fill"></i>
|
||||
Help
|
||||
</button>
|
||||
@ -339,12 +367,24 @@ export default {
|
||||
</div>
|
||||
<div class="col-md">
|
||||
<h2 class="h5 mb-3">Tested platforms:</h2>
|
||||
<div class="form-check form-switch" v-for="p, k in allPlatforms">
|
||||
<input class="form-check-input" type="checkbox" role="switch" :value="k" v-model="platforms"
|
||||
:aria-label="p" :id="'Check_' + k">
|
||||
<label class="form-check-label" :for="'Check_' + k"
|
||||
:class="platforms.indexOf(k) !== -1 ? '' : 'text-muted'" :title="families(k).join(', ')"
|
||||
data-bs-toggle="tooltip" :data-bs-title="families(k).join(', ')">
|
||||
<div v-for="(p, k) in allPlatforms" :key="'check_' + k" class="form-check form-switch">
|
||||
<input
|
||||
:id="'Check_' + k"
|
||||
v-model="platforms"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
:value="k"
|
||||
:aria-label="p"
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
:for="'Check_' + k"
|
||||
:class="platforms.indexOf(k) !== -1 ? '' : 'text-muted'"
|
||||
:title="families(k).join(', ')"
|
||||
data-bs-toggle="tooltip"
|
||||
:data-bs-title="families(k).join(', ')"
|
||||
>
|
||||
{{ p }}
|
||||
</label>
|
||||
</div>
|
||||
@ -356,45 +396,72 @@ export default {
|
||||
<h4 ref="warnings" class="h5 mt-4">
|
||||
{{ summary.Warnings.length }} Warnings from {{ formatNumber(summary.Total.Nodes) }} HTML nodes:
|
||||
</h4>
|
||||
<div class="accordion" id="warnings">
|
||||
<div class="accordion-item" v-for="warning in summary.Warnings">
|
||||
<div id="warnings" class="accordion">
|
||||
<div v-for="(warning, i) in summary.Warnings" :key="'warning_' + i" class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
:data-bs-target="'#' + warning.Slug" aria-expanded="false" :aria-controls="warning.Slug">
|
||||
<button
|
||||
class="accordion-button collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
:data-bs-target="'#' + warning.Slug"
|
||||
aria-expanded="false"
|
||||
:aria-controls="warning.Slug"
|
||||
>
|
||||
<div class="row w-100 w-lg-75">
|
||||
<div class="col-sm">
|
||||
{{ warning.Title }}
|
||||
<span class="ms-2 small badge text-bg-secondary" title="Test category">
|
||||
{{ warning.Category }}
|
||||
</span>
|
||||
<span class="ms-2 small badge text-bg-light"
|
||||
title="The number of times this was detected">
|
||||
<span
|
||||
class="ms-2 small badge text-bg-light"
|
||||
title="The number of times this was detected"
|
||||
>
|
||||
x {{ warning.Score.Found }}
|
||||
</span>
|
||||
</div>
|
||||
<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">
|
||||
<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"
|
||||
>
|
||||
<div class="progress-bar bg-success">
|
||||
{{ round(warning.Score.Supported) + '%' }}
|
||||
{{ round(warning.Score.Supported) + "%" }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress" role="progressbar" aria-label="Partial"
|
||||
:aria-valuenow="warning.Score.Partial" aria-valuemin="0" aria-valuemax="100"
|
||||
:style="{ width: warning.Score.Partial + '%' }" title="Partial support">
|
||||
<div
|
||||
class="progress"
|
||||
role="progressbar"
|
||||
aria-label="Partial"
|
||||
:aria-valuenow="warning.Score.Partial"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
:style="{ width: warning.Score.Partial + '%' }"
|
||||
title="Partial support"
|
||||
>
|
||||
<div class="progress-bar progress-bar-striped bg-warning text-dark">
|
||||
{{ round(warning.Score.Partial) + '%' }}
|
||||
{{ round(warning.Score.Partial) + "%" }}
|
||||
</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">
|
||||
<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"
|
||||
>
|
||||
<div class="progress-bar bg-danger">
|
||||
{{ round(warning.Score.Unsupported) + '%' }}
|
||||
{{ round(warning.Score.Unsupported) + "%" }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -404,28 +471,45 @@ export default {
|
||||
</h2>
|
||||
<div :id="warning.Slug" class="accordion-collapse collapse" data-bs-parent="#warnings">
|
||||
<div class="accordion-body">
|
||||
<p v-if="warning.Description != '' || warning.PseudoClassOrAtRule">
|
||||
<p v-if="warning.Description !== '' || warning.PseudoClassOrAtRule">
|
||||
<span v-if="warning.PseudoClassOrAtRule" class="d-block alert alert-warning mb-2">
|
||||
<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.
|
||||
<template v-if="warning.Score.Found === 1">property</template>
|
||||
<template v-else>properties</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>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<span
|
||||
v-if="warning.Description !== ''"
|
||||
class="me-2"
|
||||
v-html="sanitizeHTML(warning.Description)"
|
||||
></span>
|
||||
<!-- -eslint-disable vue/no-v-html -->
|
||||
</p>
|
||||
|
||||
<template v-if="warning.Results.length">
|
||||
<h3 class="h6">Clients with partial or no support:</h3>
|
||||
<p>
|
||||
<small v-for="warning in warning.Results" class="text-nowrap d-inline-block me-4">
|
||||
<i class="bi bi-circle-fill"
|
||||
:class="warning.Support == 'no' ? 'text-danger' : 'text-warning'"
|
||||
:title="warning.Support == 'no' ? 'Not supported' : 'Partially supported'"></i>
|
||||
{{ warning.Name }}
|
||||
<span class="badge text-bg-secondary" v-if="warning.NoteNumber != ''"
|
||||
title="See notes">
|
||||
{{ warning.NoteNumber }}
|
||||
<small
|
||||
v-for="(warningRes, wi) in warning.Results"
|
||||
:key="'warning_results_' + wi"
|
||||
class="text-nowrap d-inline-block me-4"
|
||||
>
|
||||
<i
|
||||
class="bi bi-circle-fill"
|
||||
:class="warningRes.Support === 'no' ? 'text-danger' : 'text-warning'"
|
||||
:title="
|
||||
warningRes.Support === 'no' ? 'Not supported' : 'Partially supported'
|
||||
"
|
||||
></i>
|
||||
{{ warningRes.Name }}
|
||||
<span
|
||||
v-if="warningRes.NoteNumber !== ''"
|
||||
class="badge text-bg-secondary"
|
||||
title="See notes"
|
||||
>
|
||||
{{ warningRes.NoteNumber }}
|
||||
</span>
|
||||
</small>
|
||||
</p>
|
||||
@ -433,17 +517,21 @@ export default {
|
||||
|
||||
<div v-if="Object.keys(warning.NotesByNumber).length" class="mt-3">
|
||||
<h3 class="h6">Notes:</h3>
|
||||
<div v-for="n, i in warning.NotesByNumber" class="small row my-2">
|
||||
<div
|
||||
v-for="(n, ni) in warning.NotesByNumber"
|
||||
:key="'warning_notes' + ni"
|
||||
class="small row my-2"
|
||||
>
|
||||
<div class="col-auto pe-0">
|
||||
<span class="badge text-bg-secondary">
|
||||
{{ i }}
|
||||
{{ ni }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col" v-html="n"></div>
|
||||
<div class="col" v-html="sanitizeHTML(n)"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="small mt-3 mb-0" v-if="warning.URL">
|
||||
<p v-if="warning.URL" class="small mt-3 mb-0">
|
||||
<a :href="warning.URL" target="_blank">Online reference</a>
|
||||
</p>
|
||||
</div>
|
||||
@ -452,30 +540,44 @@ export default {
|
||||
</div>
|
||||
|
||||
<p class="text-center text-muted small mt-4">
|
||||
Scores based on <b>{{ check.Total.Tests }}</b> tests of HTML and CSS properties using
|
||||
compatibility data from <a href="https://www.caniemail.com/" target="_blank">caniemail.com</a>.
|
||||
Scores based on <b>{{ check.Total.Tests }}</b> tests of HTML and CSS properties using compatibility data
|
||||
from <a href="https://www.caniemail.com/" target="_blank">caniemail.com</a>.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<div class="modal fade" id="AboutHTMLCheckResults" tabindex="-1" aria-labelledby="AboutHTMLCheckResultsLabel"
|
||||
aria-hidden="true">
|
||||
<div
|
||||
id="AboutHTMLCheckResults"
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
aria-labelledby="AboutHTMLCheckResultsLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="AboutHTMLCheckResultsLabel">About HTML check</h1>
|
||||
<h1 id="AboutHTMLCheckResultsLabel" class="modal-title fs-5">About HTML check</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="accordion" id="HTMLCheckAboutAccordion">
|
||||
<div id="HTMLCheckAboutAccordion" class="accordion">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#col1" aria-expanded="false" aria-controls="col1">
|
||||
<button
|
||||
class="accordion-button collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#col1"
|
||||
aria-expanded="false"
|
||||
aria-controls="col1"
|
||||
>
|
||||
What is HTML check?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="col1" class="accordion-collapse collapse"
|
||||
data-bs-parent="#HTMLCheckAboutAccordion">
|
||||
<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
|
||||
@ -485,13 +587,22 @@ export default {
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#col2" aria-expanded="false" aria-controls="col2">
|
||||
<button
|
||||
class="accordion-button collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#col2"
|
||||
aria-expanded="false"
|
||||
aria-controls="col2"
|
||||
>
|
||||
How does it work?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="col2" class="accordion-collapse collapse"
|
||||
data-bs-parent="#HTMLCheckAboutAccordion">
|
||||
<div
|
||||
id="col2"
|
||||
class="accordion-collapse collapse"
|
||||
data-bs-parent="#HTMLCheckAboutAccordion"
|
||||
>
|
||||
<div class="accordion-body">
|
||||
<p>
|
||||
Internally the original HTML message is run against
|
||||
@ -504,10 +615,11 @@ export default {
|
||||
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>
|
||||
<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>
|
||||
@ -528,13 +640,22 @@ export default {
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#col3" aria-expanded="false" aria-controls="col3">
|
||||
<button
|
||||
class="accordion-button collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#col3"
|
||||
aria-expanded="false"
|
||||
aria-controls="col3"
|
||||
>
|
||||
Is the final score accurate?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="col3" class="accordion-collapse collapse"
|
||||
data-bs-parent="#HTMLCheckAboutAccordion">
|
||||
<div
|
||||
id="col3"
|
||||
class="accordion-collapse collapse"
|
||||
data-bs-parent="#HTMLCheckAboutAccordion"
|
||||
>
|
||||
<div class="accordion-body">
|
||||
<p>
|
||||
There are many ways to define "accurate", and how one should calculate the
|
||||
@ -578,13 +699,22 @@ export default {
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#col4" aria-expanded="false" aria-controls="col4">
|
||||
<button
|
||||
class="accordion-button collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#col4"
|
||||
aria-expanded="false"
|
||||
aria-controls="col4"
|
||||
>
|
||||
What about invalid HTML?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="col4" class="accordion-collapse collapse"
|
||||
data-bs-parent="#HTMLCheckAboutAccordion">
|
||||
<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
|
||||
@ -592,7 +722,6 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
Reference in New Issue
Block a user