|
|
@ -1,5 +1,5 @@
|
|
|
|
const preview = {
|
|
|
|
const preview = {
|
|
|
|
// default values
|
|
|
|
// default values for Readme Typing SVG parameters
|
|
|
|
defaults: {
|
|
|
|
defaults: {
|
|
|
|
font: "monospace",
|
|
|
|
font: "monospace",
|
|
|
|
weight: "400",
|
|
|
|
weight: "400",
|
|
|
@ -18,6 +18,12 @@ const preview = {
|
|
|
|
random: "false",
|
|
|
|
random: "false",
|
|
|
|
separator: ";",
|
|
|
|
separator: ";",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// input field initial values that differ from the defaults
|
|
|
|
|
|
|
|
overrides: {
|
|
|
|
|
|
|
|
font: "Fira Code",
|
|
|
|
|
|
|
|
pause: "1000",
|
|
|
|
|
|
|
|
width: "435",
|
|
|
|
|
|
|
|
},
|
|
|
|
// dummy text for default line values
|
|
|
|
// dummy text for default line values
|
|
|
|
dummyText: [
|
|
|
|
dummyText: [
|
|
|
|
"The five boxing wizards jump quickly",
|
|
|
|
"The five boxing wizards jump quickly",
|
|
|
@ -30,11 +36,11 @@ const preview = {
|
|
|
|
],
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Update the preview image and markdown
|
|
|
|
* Get the current parameters from the form
|
|
|
|
|
|
|
|
* @returns {object} The current parameters
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
update() {
|
|
|
|
getParams() {
|
|
|
|
const copyButtons = document.querySelectorAll(".copy-button");
|
|
|
|
// get all parameters from the .param fields
|
|
|
|
// get parameter values from all .param elements
|
|
|
|
|
|
|
|
const params = Array.from(document.querySelectorAll(".param:not([data-index])")).reduce((acc, next) => {
|
|
|
|
const params = Array.from(document.querySelectorAll(".param:not([data-index])")).reduce((acc, next) => {
|
|
|
|
// copy accumulator into local object
|
|
|
|
// copy accumulator into local object
|
|
|
|
let obj = acc;
|
|
|
|
let obj = acc;
|
|
|
@ -72,14 +78,20 @@ const preview = {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
params.lines = mergeLines(lineInputs, params.separator);
|
|
|
|
params.lines = mergeLines(lineInputs, params.separator);
|
|
|
|
// function to URI encode string but keep semicolons as ';' and spaces as '+'
|
|
|
|
return params;
|
|
|
|
const encode = (str) => {
|
|
|
|
},
|
|
|
|
return encodeURIComponent(str).replace(/%3B/g, ";").replace(/%20/g, "+");
|
|
|
|
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Update the preview image and markdown
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
update() {
|
|
|
|
|
|
|
|
const copyButtons = document.querySelectorAll(".copy-button");
|
|
|
|
|
|
|
|
// get parameter values
|
|
|
|
|
|
|
|
const params = this.getParams();
|
|
|
|
// convert parameters to query string
|
|
|
|
// convert parameters to query string
|
|
|
|
const query = Object.keys(params)
|
|
|
|
const query = Object.keys(params)
|
|
|
|
.filter((key) => params[key] !== this.defaults[key]) // skip if default value
|
|
|
|
.filter((key) => params[key] !== this.defaults[key]) // skip if default value
|
|
|
|
.map((key) => encode(key) + "=" + encode(params[key])) // encode keys and values
|
|
|
|
.map((key) => this.customEncode(key) + "=" + this.customEncode(params[key])) // encode keys and values
|
|
|
|
.join("&"); // join lines with '&' delimiter
|
|
|
|
.join("&"); // join lines with '&' delimiter
|
|
|
|
// generate links and markdown
|
|
|
|
// generate links and markdown
|
|
|
|
const imageURL = `${window.location.origin}?${query}`;
|
|
|
|
const imageURL = `${window.location.origin}?${query}`;
|
|
|
@ -102,44 +114,58 @@ const preview = {
|
|
|
|
htmlElement.innerText = html;
|
|
|
|
htmlElement.innerText = html;
|
|
|
|
// disable copy button if no lines are filled in
|
|
|
|
// disable copy button if no lines are filled in
|
|
|
|
copyButtons.forEach((el) => (el.disabled = !params.lines.length));
|
|
|
|
copyButtons.forEach((el) => (el.disabled = !params.lines.length));
|
|
|
|
|
|
|
|
// update URL to match parameters
|
|
|
|
|
|
|
|
this.openPermalink();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Add a new line to the input fields
|
|
|
|
* Encode a string for use in a URL but keep semicolons as ';' and spaces as '+'
|
|
|
|
|
|
|
|
* @param {string} str The string to encode
|
|
|
|
|
|
|
|
* @returns The encoded string
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
customEncode(str) {
|
|
|
|
|
|
|
|
return encodeURIComponent(str).replace(/%3B/g, ";").replace(/%20/g, "+");
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Add new line input fields
|
|
|
|
|
|
|
|
* @param {number} count The number of lines to add
|
|
|
|
* @returns {false} Always returns false to prevent form submission
|
|
|
|
* @returns {false} Always returns false to prevent form submission
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
addLine() {
|
|
|
|
addLines(count) {
|
|
|
|
const parent = document.querySelector(".lines");
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
|
|
const index = parent.querySelectorAll("input").length + 1;
|
|
|
|
const parent = document.querySelector(".lines");
|
|
|
|
// label
|
|
|
|
const index = parent.querySelectorAll("input").length + 1;
|
|
|
|
const label = document.createElement("label");
|
|
|
|
// label
|
|
|
|
label.innerText = `Line ${index}`;
|
|
|
|
const label = document.createElement("label");
|
|
|
|
label.setAttribute("for", `line-${index}`);
|
|
|
|
label.innerText = `Line ${index}`;
|
|
|
|
label.dataset.index = index;
|
|
|
|
label.setAttribute("for", `line-${index}`);
|
|
|
|
// line input box
|
|
|
|
label.dataset.index = index;
|
|
|
|
const input = document.createElement("input");
|
|
|
|
// line input box
|
|
|
|
input.className = "param";
|
|
|
|
const input = document.createElement("input");
|
|
|
|
input.type = "text";
|
|
|
|
input.className = "param";
|
|
|
|
input.id = `line-${index}`;
|
|
|
|
input.type = "text";
|
|
|
|
input.name = `line-${index}`;
|
|
|
|
input.id = `line-${index}`;
|
|
|
|
input.placeholder = "Enter text here";
|
|
|
|
input.name = `line-${index}`;
|
|
|
|
input.value = this.dummyText[(index - 1) % this.dummyText.length];
|
|
|
|
input.placeholder = "Enter text here";
|
|
|
|
input.dataset.index = index;
|
|
|
|
input.value = this.dummyText[(index - 1) % this.dummyText.length];
|
|
|
|
// removal button
|
|
|
|
input.dataset.index = index;
|
|
|
|
const deleteButton = document.createElement("button");
|
|
|
|
// removal button
|
|
|
|
deleteButton.className = "delete-line btn";
|
|
|
|
const deleteButton = document.createElement("button");
|
|
|
|
deleteButton.setAttribute("onclick", "return preview.removeLine(this.dataset.index);");
|
|
|
|
deleteButton.className = "delete-line btn";
|
|
|
|
deleteButton.innerHTML =
|
|
|
|
deleteButton.setAttribute("onclick", "return preview.removeLine(this.dataset.index);");
|
|
|
|
'<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024" height="0.85em" width="0.85em" xmlns="http://www.w3.org/2000/svg"> <path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"> </path> </svg>';
|
|
|
|
deleteButton.innerHTML =
|
|
|
|
deleteButton.dataset.index = index;
|
|
|
|
'<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024" height="0.85em" width="0.85em" xmlns="http://www.w3.org/2000/svg"> <path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"> </path> </svg>';
|
|
|
|
|
|
|
|
deleteButton.dataset.index = index;
|
|
|
|
|
|
|
|
|
|
|
|
// add elements
|
|
|
|
// add elements
|
|
|
|
parent.appendChild(label);
|
|
|
|
parent.appendChild(label);
|
|
|
|
parent.appendChild(input);
|
|
|
|
parent.appendChild(input);
|
|
|
|
parent.appendChild(deleteButton);
|
|
|
|
parent.appendChild(deleteButton);
|
|
|
|
|
|
|
|
|
|
|
|
// disable button if only 1
|
|
|
|
// disable button if only 1
|
|
|
|
parent.querySelector(".delete-line.btn").disabled = index == 1;
|
|
|
|
parent.querySelector(".delete-line.btn").disabled = index == 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// update and exit
|
|
|
|
// update and exit
|
|
|
|
this.update();
|
|
|
|
this.update();
|
|
|
@ -147,7 +173,7 @@ const preview = {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Remove a line from the input fields
|
|
|
|
* Remove a line input field
|
|
|
|
* @param {number} index The index of the line to remove
|
|
|
|
* @param {number} index The index of the line to remove
|
|
|
|
* @returns {false} Always returns false to prevent form submission
|
|
|
|
* @returns {false} Always returns false to prevent form submission
|
|
|
|
*/
|
|
|
|
*/
|
|
|
@ -192,19 +218,14 @@ const preview = {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Reset all input fields to default values
|
|
|
|
* Reset all input fields to their initial values
|
|
|
|
* @returns {false} Always returns false to prevent form submission
|
|
|
|
* @returns {false} Always returns false to prevent form submission
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
reset() {
|
|
|
|
reset() {
|
|
|
|
const overrides = {
|
|
|
|
|
|
|
|
font: "Fira Code",
|
|
|
|
|
|
|
|
pause: "1000",
|
|
|
|
|
|
|
|
width: "435",
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// reset all inputs
|
|
|
|
// reset all inputs
|
|
|
|
const inputs = document.querySelectorAll(".param");
|
|
|
|
const inputs = document.querySelectorAll(".param");
|
|
|
|
inputs.forEach((input) => {
|
|
|
|
inputs.forEach((input) => {
|
|
|
|
let value = overrides[input.name] || this.defaults[input.name];
|
|
|
|
let value = this.overrides[input.name] || this.defaults[input.name];
|
|
|
|
if (value) {
|
|
|
|
if (value) {
|
|
|
|
if (["color", "background"].includes(input.name)) {
|
|
|
|
if (["color", "background"].includes(input.name)) {
|
|
|
|
input.jscolor.fromString(value);
|
|
|
|
input.jscolor.fromString(value);
|
|
|
@ -214,20 +235,89 @@ const preview = {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Get the current parameters in a permalink format
|
|
|
|
|
|
|
|
* @returns {string} The permalink URL
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
getPermalink() {
|
|
|
|
|
|
|
|
// get parameters from form
|
|
|
|
|
|
|
|
const params = this.getParams();
|
|
|
|
|
|
|
|
// convert parameters to query string
|
|
|
|
|
|
|
|
const defaultInputs = { ...this.defaults, ...this.overrides };
|
|
|
|
|
|
|
|
defaultInputs.lines = this.dummyText[0];
|
|
|
|
|
|
|
|
const query = Object.keys(params)
|
|
|
|
|
|
|
|
.filter((key) => params[key] !== defaultInputs[key]) // skip if default value
|
|
|
|
|
|
|
|
.map((key) => this.customEncode(key) + "=" + this.customEncode(params[key])) // encode keys and values
|
|
|
|
|
|
|
|
.join("&"); // join lines with '&' delimiter
|
|
|
|
|
|
|
|
// return permalink
|
|
|
|
|
|
|
|
return `${window.location.origin}${window.location.pathname}` + (query ? `?${query}` : "");
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Save the current parameters to the URL
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
openPermalink() {
|
|
|
|
|
|
|
|
window.history.replaceState({}, "", this.getPermalink());
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Restore the last saved parameters from the URL
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
restore() {
|
|
|
|
|
|
|
|
// get parameters from URL
|
|
|
|
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
|
|
|
|
const params = { ...this.defaults, ...this.overrides, ...Object.fromEntries(urlParams) };
|
|
|
|
|
|
|
|
// set all parameters
|
|
|
|
|
|
|
|
const inputs = document.querySelectorAll(".param");
|
|
|
|
|
|
|
|
inputs.forEach((input) => {
|
|
|
|
|
|
|
|
let value = params[input.name];
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
|
|
|
if (["color", "background"].includes(input.name)) {
|
|
|
|
|
|
|
|
input.jscolor.fromString(value);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
input.value = value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// add lines
|
|
|
|
|
|
|
|
const lines = params.lines || this.dummyText[0];
|
|
|
|
|
|
|
|
const lineInputs = lines.split(params.separator);
|
|
|
|
|
|
|
|
this.addLines(lineInputs.length);
|
|
|
|
|
|
|
|
lineInputs.forEach((line, index) => {
|
|
|
|
|
|
|
|
document.querySelector(`#line-${index + 1}`).value = line;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const clipboard = {
|
|
|
|
const clipboard = {
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Copy the text from a code block to the clipboard
|
|
|
|
* Copy text to the clipboard
|
|
|
|
* @param {HTMLElement} el The element that was clicked
|
|
|
|
* @param {HTMLElement} btn The element that was clicked
|
|
|
|
|
|
|
|
* @param {String} text The text to copy
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
copy(el) {
|
|
|
|
copy(btn, text) {
|
|
|
|
const textToCopy = el.parentElement.querySelector("code").innerText;
|
|
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
|
|
navigator.clipboard.writeText(textToCopy).then(() => {
|
|
|
|
|
|
|
|
// set tooltip text
|
|
|
|
// set tooltip text
|
|
|
|
el.title = "Copied!";
|
|
|
|
btn.title = "Copied!";
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Copy the text from a code block to the clipboard
|
|
|
|
|
|
|
|
* @param {HTMLElement} btn The element that was clicked
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
copyCode(btn) {
|
|
|
|
|
|
|
|
this.copy(btn, btn.parentElement.querySelector("code").innerText);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Copy the permalink to the clipboard
|
|
|
|
|
|
|
|
* @param {HTMLElement} btn The element that was clicked
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
copyPermalink(btn) {
|
|
|
|
|
|
|
|
this.copy(btn, preview.getPermalink());
|
|
|
|
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const tooltip = {
|
|
|
|
const tooltip = {
|
|
|
@ -255,9 +345,8 @@ document.querySelector(".show-border input").addEventListener("change", function
|
|
|
|
window.addEventListener(
|
|
|
|
window.addEventListener(
|
|
|
|
"load",
|
|
|
|
"load",
|
|
|
|
() => {
|
|
|
|
() => {
|
|
|
|
// add first line
|
|
|
|
preview.restore(); // restore parameters
|
|
|
|
preview.addLine();
|
|
|
|
preview.update(); // update preview
|
|
|
|
preview.update();
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false
|
|
|
|
false
|
|
|
|
);
|
|
|
|
);
|
|
|
|