Format Messages with Dates
by MegaScience
HTML
<form id="messageFormatter" name="messageFormatter" method="post" onsubmit="return submitPost()">
<table>
<tr>
<th colspan="2">Format Messages with Dates</th>
</tr>
<tr>
<td colspan="2">Use this to format messages. Format is in the form <code>%L#</code>, where <code>%</code> identifies the replacement, <code>L</code> is any letter for the format type, and <code>#</code> is any number between <code>1</code> and <code>100</code> matching the number adjacent to a date in the below list. For example, typing <code>Hello %F1!</code> with the date <code>July 1, 2021 at 6:00 AM PDT</code> in slot 1 will result in the message <code>Hello <t:1625144400:F>!</code>, which displays in English language in EDT as <code>Hello Thursday, July 1, 2021 9:00 AM!</code> For more information, <a href="https://c.r74n.com/discord/formatting" target="_blank">this website shows the formatting</a>. Note that the letters are case sensitive.</td>
</tr>
<tr>
<th>Message Format:</th>
<td>
<textarea type="text" id="messageFormat" name="messageFormat" oninput="expandTextarea(this)" onblur="storeData()" required></textarea>
</td>
</tr>
<tr>
<th>Dates/Times:</th>
<td>
<ol id="datesTimes">
<li><button type="button" tabindex="-1" onclick="addListItem()">+</button><span><input type="text" name="date" placeholder="Example: July 11, 2021, 12:00 PM PDT" onblur="processDate(this) && storeData()"></span></li>
</ol>
</td>
</tr>
<tr>
<td class="controls" colspan="2">
<input type="submit" name="submit" value="Submit" />
</td>
</tr>
<tr>
<td colspan="2">
<textarea type="text" id="messageOutput" name="messageOutput" placeholder="Formatted message appears...
CSS
html, body {
display: flex;
justify-content: center;
align-items: center;
background-color: #ccccc4;
min-height: 100%;
margin: 0;
font-family: arial, sans-serif;
font-size: 14px;
}
form {
/*max-width: 960px;*/
max-width: 880px;
margin: 5px auto;
min-height: calc(100% - 35px);
background-color: #FFF;
border: 1px solid black;
border-radius: 25px;
padding-bottom: 25px;
/*max-height: 100vh;*/
max-height: 50%;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
table {
width: 100%;
border-collapse: collapse;
}
th[colspan="2"] {
padding: 5px 0;
font-size: 24px;
}
th:not([colspan="2"]) {
text-align: left;
vertical-align: top;
width: 14em;
}
td, ol#datesTimes {
overflow: visible;
}
tr :is(th, td) {
--tr-padding: 10px;
&:first-child {
padding-left: var(--tr-padding);
}
&:last-child {
padding-right: var(--tr-padding);
}
}
textarea {
resize: vertical;
overflow-y: auto;
}
input, textarea {
width: 100%;
}
input, button, textarea {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
input[name="date"][data-epoch="NaN"i] {
background-color: rgba(255, 170, 170, 100);
}
ol {
margin: 0;
padding-left: 0;
list-style-position: outside;
overflow-y: auto;
}
li span {
display: block;
overflow: hidden;
}
li button {
float: right;
padding: 0;
width: 1.65em;
height: 1.65em;
}
.controls input {
display: inline-block;
width: 100%;
}
JavaScript
class dataObject {
constructor({ messageFormat = '', epoches = [], messageOutput = '' } = { messageFormat: '', epoches: [], messageOutput: '' }) {
this.messageFormat = isString(messageFormat) ? messageFormat : ''
//this.epoches = Array.isArray(epoches) && epoches.every(v => Array.isArray(v) && v.length == 2 && v.every(isString) && regex.numberOrNaNOnly.test(v[1])) ? epoches : []
this.epoches = Array.isArray(epoches) ? epoches.map(v => Array.isArray(v) && v.length == 2 && v.every(isString) && regex.numberOrNaNOnly.test(v[1]) ? v : ['', 'NaN']) : []
this.messageOutput = isString(messageOutput) ? messageOutput : ''
}
}
const lsTag = 'MessageFormatter'
//const regex = /\b%(\w)([0-9]{1,2})\b/g
//const regex = /(^|\s)%([a-zA-Z])([0-9]{1,2})\b/g
const regex = {
tags: /(^|\s)%([a-zA-Z])([0-9]{1,2})\b/g,
ordinalIndicator: /\b([0-9]{1,2})(?:st|nd|rd|th)\b/i,
leadingDay: /^(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday), /i,
hourOnly: /(^|\s)([0-9]{1,2}) ?(AM|PM)\b/i,
UTF8: /%([0-9A-F]{2})/g,
numberOrNaNOnly: /^(-?\d+|NaN)$/i,
abbreviations: /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec|Mon|Tue|Wed|Thu|Fri|Sat|Sun)\./i,
ampm: /\b(A|P)\.?(M)(?:\.|\b)?/i
}
const dates = document.getElementsByName('date')
const dateTimesE = document.getElementById('datesTimes')
const dateRowTemplate = document.getElementById('dateRow')
const messageFormatE = document.getElementById('messageFormat')
const messageOutputE = document.getElementById('messageOutput')
let saveInterval
const isString = s => typeof s === 'string' || s instanceof String
// https://stackoverflow.com/a/1353711
//const isValidDate = d => d instanceof Date && !isNaN(d)
//const isInvalidDate = d => !(d instanceof Date && !isNaN(d))
const addListItem = () => {
const n = dateRowTemplate.content.cloneNode(true)
dateTimesE.appendChild(n)
return n
}
const removeListItem = e => e.parentNode.remove()
const processDate = e =>...