// Want to use or contribute to this? https://github.com/Glitchii/embedbuilder
// If you found an issue, please report it, make a P.R, or use the discussion page. Thanks
var activeFields, colNum = 1, num = 0,
toRGB = (hex, reversed, integer) => {
if (reversed) return '#' + hex.match(/[\d]+/g).map(x => parseInt(x).toString(16).padStart(2, '0')).join('');
if (integer) return parseInt(hex.match(/[\d]+/g).map(x => parseInt(x).toString(16).padStart(2, '0')).join(''), 16);
if (hex.includes(',')) return hex.match(/[\d]+/g);
hex = hex.replace('#', '').match(/.{1,2}/g)
return [parseInt(hex[0], 16), parseInt(hex[1], 16), parseInt(hex[2], 16), 1];
}, json = {
content: "You can~~not~~ do `this`.```py\nAnd this.\nprint('Hi')```\n*italics* or _italics_ __*underline italics*__\n**bold** __**underline bold**__\n***bold italics*** __***underline bold italics***__\n__underline__ ~~Strikethrough~~",
embed: {
title: "Hello ~~people~~ world :wave:",
description: "You can use [links](https://discord.com) or emojis :smile: 😎\n```\nAnd also code blocks\n```",
color: 4321431,
timestamp: new Date().toISOString(),
url: "https://discord.com",
author: {
name: "Author name",
url: "https://discord.com",
icon_url: "https://unsplash.it/100"
},
thumbnail: {
url: "https://unsplash.it/200"
},
image: {
url: "https://unsplash.it/380/200"
},
footer: {
text: "Footer text",
icon_url: "https://unsplash.it/100"
},
fields: [
{
name: "Field 1, *lorem* **ipsum**, ~~dolor~~",
value: "Field value"
},
{
name: "Field 2",
value: "You can use custom emojis <:Kekwlaugh:722088222766923847>. <:GangstaBlob:742256196295065661>",
inline: false
},
{
name: "Inline field",
value: "Fields can be inline",
inline: true
},
{
name: "Inline field",
value: "*Lorem ipsum*",
inline: true
},
{
name: "Inline field",
value: "value",
inline: true
},
{
name: "Another field",
value: "> Nope, didn't forget about code blocks",
inline: false
}
]
}
};
window.onload = () => {
document.querySelectorAll('img.clickable')
.forEach(e => e.addEventListener('click', el => window.open(el.target.src)));
let editorHolder = document.querySelector('.editorHolder'),
guiParent = document.querySelector('.top'),
embedContent = document.querySelector('.messageContent'),
embedCont = document.querySelector('.messageContent + .container'),
gui = guiParent.querySelector('.gui:first-of-type');
window.editor = CodeMirror(elt => editorHolder.parentNode.replaceChild(elt, editorHolder), {
value: JSON.stringify(json, null, 4),
extraKeys: { Tab: cm => cm.replaceSelection(" ", "end") },
gutters: ["CodeMirror-foldgutter", "CodeMirror-lint-markers"],
scrollbarStyle: "overlay",
mode: "application/json",
theme: 'material-darker',
matchBrackets: true,
foldGutter: true,
lint: true,
});
editor.focus();
let notif = document.querySelector('.notification'),
url = (url) => /^(https?:)?\/\//g.exec(url) ? url : '//' + url,
makeShort = (txt, length, mediaWidth) => {
if (mediaWidth && window.matchMedia(`(max-width:${mediaWidth}px)`).matches)
return txt.length > (length - 3) ? txt.substring(0, length - 3) + '...' : txt;
return txt;
}, error = (msg, time) => {
notif.innerHTML = msg, notif.style.display = 'block';
time && setTimeout(() => notif.animate({ opacity: '0', bottom: '-50px', offset: 1 }, { easing: 'ease', duration: 500 })
.onfinish = () => notif.style.removeProperty('display'), time);
return false;
}, allGood = e => {
let str = JSON.stringify(e, null, 4), re = /("(?:icon_)?url": *")((?!\w+?:\/\/).+)"/g.exec(str);
if (e.timestamp && new Date(e.timestamp).toString() === "Invalid Date") return error('Timestamp is invalid');
if (re) { // If a URL is found without a protocol
if (!/\w+:|\/\/|^\//g.exec(re[2]) && re[2].includes('.')) {
let activeInput = document.querySelector('input[class$="link" i]:focus')
if (activeInput) {
lastPos = activeInput.selectionStart + 7;
authorLink.value = `http://${re[2]}`;
update(JSON.parse(str.replace(re[0], `${re[1]}http://${re[2]}"`)));
activeInput.setSelectionRange(lastPos, lastPos)
return true;
}
}
return error(`URL should have a protocol. Did you mean http://${makeShort(re[2], 30, 600).replace(' ', '')}?`);
}
return true;
}, markup = (txt, opts) => {
txt = txt
.replace(/\<:[^:]+:(\d+)\>/g, '
')
.replace(/\<a:[^:]+:(\d+)\>/g, '
')
.replace(/~~(.+?)~~/g, '$1')
.replace(/\*\*\*(.+?)\*\*\*/g, '$1')
.replace(/\*\*(.+?)\*\*/g, '$1')
.replace(/__(.+?)__/g, '$1')
.replace(/\*(.+?)\*/g, '$1')
.replace(/_(.+?)_/g, '$1')
if (opts.inlineBlock) txt = txt.replace(/\`([^\`]+?)\`|\`\`([^\`]+?)\`\`|\`\`\`((?:\n|.)+?)\`\`\`/g, (m, x, y, z) => x ? `${x}
` : y ? `${y}
` : z ? `${z}
` : m);
else txt = txt.replace(/\`\`\`(\w{1,15})?\n((?:\n|.)+?)\`\`\`|\`\`(.+?)\`\`(?!\`)|\`([^\`]+?)\`/g, (m, w, x, y, z) => w && x ? `
${x}
` : x ? `${x}
` : y || z ? `${y || z}
` : m);
if (opts.inEmbed) txt = txt.replace(/\[([^\[\]]+)\]\((.+?)\)/g, `$1`);
if (opts.replaceEmojis) txt = txt.replace(/(?[^>]+)(? x && emojis[x] ? emojis[x] : match);
txt = txt
.replace(/(?<=\n|^)\s*>\s+([^\n]+)/g, '')
.replace(/\n/g, '
');
return txt;
},
embed = document.querySelector('.embedGrid'),
msgEmbed = document.querySelector('.msgEmbed'),
embedTitle = document.querySelector('.embedTitle'),
embedDescription = document.querySelector('.embedDescription'),
embedAuthor = document.querySelector('.embedAuthor'),
embedFooter = document.querySelector('.embedFooter'),
embedImage = document.querySelector('.embedImage'),
embedThumbnail = document.querySelector('.embedThumbnail'),
embedFields = embed.querySelector('.embedFields'),
encodeHTML = str => str.replace(/[\u00A0-\u9999<>\&]/g, i => '' + i.charCodeAt(0) + ';'),
tstamp = stringISO => {
let date = stringISO ? new Date(stringISO) : new Date(),
dateArray = date.toLocaleString('en-US', { hour: 'numeric', hour12: true, minute: 'numeric' }),
today = new Date(),
yesterday = new Date(new Date().setDate(today.getDate() - 1));
return today.toDateString() === date.toDateString() ? `Today at ${dateArray}` :
yesterday.toDateString() === date.toDateString() ? `Yesterday at ${dateArray}` :
`${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')}/${date.getFullYear()}`;
}, display = (el, data, displayType) => {
if (data) el.innerHTML = data;
el.style.display = displayType || "unset";
}, hide = el => el.style.removeProperty('display'),
imgSrc = (elm, src, remove) => remove ? elm.style.removeProperty('content') : elm.style.content = `url(${src})`,
toObj = jsonString => JSON.parse(jsonString.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (x, y) => y ? "" : x));
buildGui = (object, opts) => {
gui.innerHTML = `
`;
let fieldsEditor = gui.querySelector('.fields ~ .edit'), addField = `
`;
if (object.embed?.fields) fieldsEditor.innerHTML = object.embed.fields.filter(f => f && typeof f === 'object').map(f => `
`).join('\n') + addField;
else fieldsEditor.innerHTML = addField;
gui.querySelectorAll('.removeBtn').forEach(e => {
e.addEventListener('click', el => {
fields = gui.querySelector('.fields ~ .edit');
let field = el.target.closest('.field');
if (field) {
let i = Array.from(fields.children).indexOf(field), jsonField = object.embed.fields[i];
if (jsonField) {
object.embed.fields.splice(i, 1);
field.remove();
update(object);
}
}
})
})
document.querySelectorAll('.gui > .item').forEach(e => {
e.addEventListener('click', el => {
let elm = (el.target.closest('.top>.gui>.item') || el.target);
if (elm.classList.contains('active')) window.getSelection().anchorNode !== elm && elm.classList.remove('active');
else {
let inlineField = elm.closest('.inlineField'),
input = elm.nextElementSibling.querySelector('input[type="text"]'),
txt = elm.nextElementSibling.querySelector('textarea');
elm.classList.add('active');
if (inlineField) inlineField.querySelector('.ttle~input').focus();
else if (input) {
input.focus();
input.selectionStart = input.selectionEnd = input.value.length;
} else if (txt) txt.focus();
elm.classList.contains('fields') && elm.scrollIntoView({ behavior: "smooth", block: "center" });
}
})
})
content = gui.querySelector('.editContent');
title = gui.querySelector('.editTitle');
authorName = gui.querySelector('.editAuthorName');
authorLink = gui.querySelector('.editAuthorLink');
desc = gui.querySelector('.editDescription');
thumbLink = gui.querySelector('.editThumbnailLink');
imgLink = gui.querySelector('.editImageLink');
footerText = gui.querySelector('.editFooterText');
footerLink = gui.querySelector('.editFooterLink');
fields = gui.querySelector('.fields ~ .edit');
document.querySelector('.addField').addEventListener('click', () => {
!json.embed && (json.embed = {});
let arr = json.embed.fields || [];
if (arr.length >= 25) return error('Cannot have more than 25 fields', 5000);
arr.push({ name: "Field name", value: "Field value", inline: false });
json.embed.fields = arr;
update(json);
buildGui(json, { newField: true, activate: document.querySelectorAll('.gui > .item.active') });
})
gui.querySelectorAll('textarea, input').forEach(e => e.addEventListener('input', el => {
let value = el.target.value, field = el.target.closest('.field');
if (field) {
let jsonField = json.embed.fields[Array.from(fields.children).indexOf(field)];
if (jsonField) {
if (el.target.type === 'text') jsonField.name = value;
else if (el.target.type === 'textarea') jsonField.value = value;
else jsonField.inline = el.target.checked;
} else {
let obj = {}
if (el.target.type === 'text') obj.name = value;
else if (el.target.type === 'textarea') obj.value = value;
else obj.inline = el.target.checked;
json.embed.fields.push(obj);
}
} else {
json.embed ??= {};
switch (el.target) {
case content: json.content = value; break;
case title: json.embed.title = value; break;
case authorName: json.embed.author ??= {}, json.embed.author.name = value; break;
case authorLink: json.embed.author ??= {}, json.embed.author.icon_url = value, imgSrc(el.target.previousElementSibling, value); break;
case desc: json.embed.description = value; break;
case thumbLink: json.embed.thumbnail ??= {}, json.embed.thumbnail.url = value, imgSrc(el.target.closest('.editIcon').querySelector('.imgParent'), value); break;
case imgLink: json.embed.image ??= {}, json.embed.image.url = value, imgSrc(el.target.closest('.editIcon').querySelector('.imgParent'), value); break;
case footerText: json.embed.footer ??= {}, json.embed.footer.text = value; break;
case footerLink: json.embed.footer ??= {}, json.embed.footer.icon_url = value, imgSrc(el.target.previousElementSibling, value); break;
}
}
update(json);
}))
if (opts?.activate) {
let elements = opts.activate;
Array.from(elements).map(el => el.className).map(clss => '.' + clss.split(' ').slice(0, 2).join('.'))
.forEach(clss => document.querySelectorAll(clss)
.forEach(e => e.classList.add('active')))
} else['.item.author', '.item.description'].forEach(clss => document.querySelector(clss).classList.add('active'));
if (opts?.newField) {
let last = fields.children[fields.childElementCount - 2], el = last.querySelector('.designerFieldName > input');
el.setSelectionRange(el.value.length, el.value.length); el.focus();
last.scrollIntoView({ behavior: "smooth", block: "center" });
}
let files = document.querySelectorAll('input[type="file"]');
files.forEach(f => f.addEventListener('change', e => {
if (f.files) {
e.target.nextElementSibling.click();
e.target.closest('.edit').querySelector('.browse').classList.add('loading');
}
}))
document.querySelectorAll('form').forEach(form => form.addEventListener('submit', e => {
e.preventDefault();
let formData = new FormData(e.target);
formData.append('file', files.files);
formData.append('datetime', '10m');
fetch('https://tempfile.site/api/files', {
method: 'POST',
body: formData,
})
.then(res => res.json())
.then(res => {
let browse = e.target.closest('.edit').querySelector('.browse');
browse.classList.remove('loading');
if (!res.ok) {
console.log(res.error);
browse.classList.add('error');
return setTimeout(() => browse.classList.remove('error'), 5000)
}
imgSrc(e.target.previousElementSibling.querySelector('.editIcon > .imgParent') || e.target.closest('.editIcon').querySelector('.imgParent'), res.link);
let input = e.target.previousElementSibling.querySelector('.editIcon > input') || e.target.previousElementSibling;
input.value = res.link;
if (input === authorLink) ((json.embed ??= {}).author ??= {}).icon_url = res.link;
else if (input === thumbLink) ((json.embed ??= {}).thumbnail ??= {}).url = res.link;
else if (input === imgLink) ((json.embed ??= {}).image ??= {}).url = res.link;
else ((json.embed ??= {}).footer ??= {}).url = res.link;
update(json);
console.info(`Image (${res.link}) will be deleted in 10 minutes. To delete it now, go to ${res.link.replace('/files', '/del')} and enter this code: ${res.authkey}`);
}).catch(err => error(`Request to tempfile.site failed with error: ${err}`, 5000))
}))
}
buildGui(json);
fields = gui.querySelector('.fields ~ .edit');
update = data => {
try {
if (!data.content) embedContent.classList.add('empty');
else {
embedContent.innerHTML = markup(encodeHTML(data.content), { replaceEmojis: true });
embedContent.classList.remove('empty');
}
if (data.embed && Object.keys(data.embed).length) {
let e = data.embed;
if (!allGood(e)) return;
if (e.title) display(embedTitle, markup(`${e.url ? '' + encodeHTML(e.title) + '' : encodeHTML(e.title)}`, { replaceEmojis: true, inlineBlock: true }));
else hide(embedTitle);
if (e.description) display(embedDescription, markup(encodeHTML(e.description), { inEmbed: true, replaceEmojis: true }));
else hide(embedDescription);
if (e.color) embed.closest('.embed').style.borderColor = encodeHTML(typeof e.color === 'number' ? '#' + e.color.toString(16).padStart(6, "0") : e.color);
else embed.closest('.embed').style.removeProperty('border-color');
if (e.author && e.author.name) display(embedAuthor, `
${e.author.icon_url ? '
' : ''}
${e.author.url ? '' + encodeHTML(e.author.name) + '' : '' + encodeHTML(e.author.name) + ''}`, 'flex');
else hide(embedAuthor);
if (e.thumbnail && e.thumbnail.url) embedThumbnail.src = encodeHTML(e.thumbnail.url), embedThumbnail.style.display = 'block';
else hide(embedThumbnail);
if (e.image && e.image.url) embedImage.src = encodeHTML(e.image.url), embedImage.style.display = 'block';
else hide(embedImage);
if (e.footer && e.footer.text) display(embedFooter, `
${e.footer.icon_url ? '' : ''}`, 'flex');
else if (e.timestamp) display(embedFooter, ``, 'flex');
else hide(embedFooter);
if (e.fields) {
embedFields.innerHTML = '';
e.fields.forEach(f => {
if (f.name && f.value) {
if (!f.inline) {
let el = embedFields.insertBefore(document.createElement('div'), null);
el.outerHTML = `
${markup(encodeHTML(f.name), { inEmbed: true, replaceEmojis: true, inlineBlock: true })}
${markup(encodeHTML(f.value), { inEmbed: true, replaceEmojis: true })}
`;
} else {
el = embedFields.insertBefore(document.createElement('div'), null);
el.outerHTML = `
${markup(encodeHTML(f.name), { inEmbed: true, replaceEmojis: true, inlineBlock: true })}
${markup(encodeHTML(f.value), { inEmbed: true, replaceEmojis: true })}
`;
colNum = (colNum === 9 ? 1 : colNum + 4);
num++;
}
}
});
colNum = 1;
let len = e.fields.filter(f => f.inline).length;
if (len === 2 || (len > 3 && len % 2 !== 0)) {
let children = Array.from(embedFields.children), arr = children.filter(x => x === children[len] || x === children[len - 1]);
arr[0] && (arr[0].style.gridColumn = '1 / 7');
arr[1] && (arr[1].style.gridColumn = '7 / 13');
}
display(embedFields, undefined, 'grid');
} else hide(embedFields);
embedCont.classList.remove('empty');
document.querySelectorAll('.markup pre > code').forEach((block) => hljs.highlightBlock(block));
notif.animate({ opacity: '0', bottom: '-50px', offset: 1 }, { easing: 'ease', duration: 500 }).onfinish = () => notif.style.removeProperty('display');
twemoji.parse(msgEmbed);
} else embedCont.classList.add('empty');
} catch (e) {
error(e);
}
}
editor.on('change', editor => {
try { update(toObj(editor.getValue())); }
catch (e) {
if (editor.getValue()) return;
embedCont.classList.add('empty');
embedContent.innerHTML = '';
}
});
let picker = new CP(document.querySelector('.picker'), state = { parent: document.querySelector('.cTop') });
picker.fire('change', toRGB('#41f097'));
let colRight = document.querySelector('.colRight'), removePicker = () => colRight.classList.remove('picking');
document.querySelector('.colBack').addEventListener('click', e => {
picker.self.remove(); removePicker();
})
picker.on('enter', () => colRight.classList.add('picking'))
picker.on('exit', removePicker);
document.querySelectorAll('.colr').forEach(e => e.addEventListener('click', el => {
el = el.target.closest('.colr') || el.target;
embed.closest('.embed').style.borderColor = el.style.backgroundColor;
json.embed && (json.embed.color = toRGB(el.style.backgroundColor, false, true));
picker.source.style.removeProperty('background');
}))
setTimeout(() => {
picker.on('change', function (r, g, b, a) {
embed.closest('.embed').style.borderColor = this.color(r, g, b);
json.embed && (json.embed.color = parseInt(this.color(r, g, b).slice(1), 16));
picker.source.style.background = this.color(r, g, b);
})
}, 1000)
document.querySelector('.timeText').innerText = tstamp();
document.querySelectorAll('.markup pre > code').forEach((block) => hljs.highlightBlock(block));
!window.navigator.userAgent.match(/Firefox\/[\d\.]+$/g) && // Firefox pushes the text up a little
document.querySelector('.botText').style.removeProperty('top');
document.querySelector('.opt.gui').addEventListener('click', () => {
json = toObj(editor.getValue() || '{}');
buildGui(json, { activate: activeFields });
document.body.classList.add('gui');
activeFields = null;
})
document.querySelector('.opt.json').addEventListener('click', () => {
editor.setValue(JSON.stringify(json, null, 4));
editor.refresh();
document.body.classList.remove('gui');
editor.focus();
activeFields = document.querySelectorAll('.gui > .item.active');
})
document.querySelector('.clear').addEventListener('click', () => {
json = {};
embed.style.removeProperty('border-color');
picker.source.style.removeProperty('background');
update(json); buildGui(json); editor.setValue(JSON.stringify(json, null, 4));
document.querySelectorAll('.gui>.item').forEach(e => e.classList.add('active'));
content.focus();
})
let colrs = document.querySelector('.colrs');
document.querySelector('.pickerToggle').addEventListener('click', () => colrs.classList.toggle('display'));
update(json);
};