JSFiddle - React, Tailwind, and code Playground
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"><p>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">this website shows the formatting</a>. Note that the letters are case sensitive.</p></td>
</tr>
<tr>
<th>Message Format:</th>
<td>
<textarea type="text" id="messageFormat" name="messageFormat" oninput="expandTextarea(this)" 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"></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 here." oninput="expandTextarea(this)"></textarea>
...
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;
}
th[colspan="2"] {
padding: 5px 0;
font-size: 24px;
}
th:not([colspan="2"]) {
text-align: left;
vertical-align: top;
width: 14em;
}
tr th:first-child, tr td:first-child {
padding-left: 10px;
}
tr th:last-child, tr td:last-child {
padding-right: 10px;
}
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;
}
table {
width: 100%;
border-collapse: collapse;
}
input, button, textarea {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
ol {
margin: 0;
padding-left: 0;
list-style-position: outside;
}
input, textarea {
width: 100%;
}
li span {
display: block;
overflow: hidden;
}
li button {
float: right;
padding: 0;
width: 1.65em;
height: 1.65em;
}
textarea {
resize: vertical;
}
.controls input {
display: inline-block;
width: 100%;
}
JavaScript
//const regex = /\b%(\w)([0-9]{1,2})\b/g
const regex = /(^|\s)%([a-zA-Z])([0-9]{1,2})\b/g
const dateTimesE = document.getElementById('datesTimes')
const dateRowTemplate = document.getElementById('dateRow')
const messageFormatE = document.getElementById('messageFormat')
const messageOutputE = document.getElementById('messageOutput')
// https://stackoverflow.com/a/1353711
//const isValidDate = d => d instanceof Date && !isNaN(d)
const isInvalidDate = d => !(d instanceof Date && !isNaN(d))
const addListItem = () => dateTimesE.appendChild(dateRowTemplate.content.cloneNode(true))
const removeListItem = e => e.closest('li').remove()
function expandTextarea(e) {
e.style.height = ''
e.style.height = `${e.scrollHeight + 5}px`
}
function submitPost() {
try {
const messageFormat = messageFormatE.value
//TODO: Add more trimming to ease validation.
const dates = Array.from(document.getElementsByName('date')).map(e => new Date(e.value.replace(' at ', ' ')))
//const dates = Array.from(document.getElementsByName('date')).map(e => e.value.trim()).filter(v => v.length > 0).map(v => new Date(v.replace(' at ', ' ')))
//if (dates.some(isInvalidDate)) throw ['One or more inputted dates are invalid.', dates]
const unixes = dates.map(d => Math.round(+d / 1000))
const formattedMessage = messageFormat.replaceAll(regex, (m, o, timeType, unixIndex) => {
//if (typeof unixes[--unixIndex] === 'undefined') return m
if (isInvalidDate(dates[--unixIndex])) return m
return `${o}<t:${unixes[unixIndex]}:${timeType}>`
})
messageOutputE.value = formattedMessage
expandTextarea(messageOutputE)
}
catch (e) {
if (Array.isArray(e)) {
alert(e.shift())
console.log(e)
}
else alert(e)
}
finally {
return false
}
}
addListItem()
addListItem()