Merge pull request #11 from DenverCoder1/demo-site

pull/13/head
Jonah Lawrence 3 years ago committed by GitHub
commit 57e4703edf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,312 @@
html {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
*,
*::before,
*::after {
-webkit-box-sizing: inherit;
-moz-box-sizing: inherit;
box-sizing: inherit;
}
:root {
--background: #eee;
--card-background: white;
--text: #1a1a1a;
--border: #ccc;
--stroke: #a9a9a9;
--blue-light: #2196f3;
--blue-dark: #1e88e5;
--button-outline: black;
--red: #ff6464;
--red-alt: #e45353;
}
[data-theme="dark"] {
--background: #090d13;
--card-background: #0d1117;
--text: #efefef;
--border: #2a2e34;
--stroke: #737373;
--blue-light: #1976d2;
--blue-dark: #1565c0;
--button-outline: black;
--red: #ff6464;
--red-alt: #e45353;
}
body {
background: var(--background);
font-family: "Open Sans", Roboto, Arial, sans-serif;
padding-top: 10px;
color: var(--text);
}
.github {
text-align: center;
margin-bottom: 12px;
}
.github a {
color: var(--text);
}
@media only screen and (max-width: 340px) {
.github a {
display: none;
}
.github iframe {
margin-left: 0 !important;
}
}
.container {
margin: auto;
max-width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.properties,
.output {
max-width: 550px;
margin: 10px;
background: var(--card-background);
padding: 25px;
padding-top: 0;
border: 1px solid var(--border);
border-radius: 6px;
}
@media only screen and (max-width: 1024px) {
.properties,
.output {
width: 100%;
}
}
h1 {
text-align: center;
}
h2 {
border-bottom: 1px solid var(--stroke);
}
:not(.btn):focus {
outline: var(--blue-light) auto 2px;
}
.btn {
max-width: 100%;
background-color: var(--blue-light);
color: white;
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-family: inherit;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
transition: 0.2s ease-in-out;
}
.btn:focus {
outline: var(--button-outline) auto 2px;
}
.btn:not(:disabled):hover {
background-color: var(--blue-dark);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
}
.btn:disabled {
opacity: 0.8;
box-shadow: none;
cursor: not-allowed;
}
.parameters {
margin: auto;
display: grid;
align-items: center;
justify-content: start;
text-align: left;
grid-gap: 8px;
}
.two-columns {
grid-template-columns: auto 1fr;
}
.three-columns {
grid-template-columns: auto 1fr auto;
}
.parameters .btn {
margin-top: 8px;
}
.parameters input[type="text"],
.parameters input[type="number"],
.parameters input.jscolor,
.parameters select {
padding: 10px 14px;
display: inline-block;
border: 1px solid var(--border);
border-radius: 6px;
box-sizing: border-box;
font-family: inherit;
background: var(--card-background);
width: 100%;
color: inherit;
}
.param.inline {
max-width: 54px;
padding: 10px 6px !important;
}
.parameters input.jscolor {
font-size: 12px;
max-width: 218px;
}
@media only screen and (max-width: 1024px) {
.parameters input.jscolor {
max-width: none;
}
}
.parameters select {
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml;utf8,<svg fill='black' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
background-repeat: no-repeat;
background-position-x: 100%;
background-position-y: 5px;
}
[data-theme="dark"] .parameters select {
background-image: url("data:image/svg+xml;utf8,<svg fill='white' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
}
span[title="required"] {
color: var(--red);
}
input:invalid {
outline: 2px var(--red) auto;
}
input:focus:invalid {
outline: none;
box-shadow: 0 0 0 1px var(--blue-light), 0 0 0 3px var(--red);
}
.delete-line.btn {
margin: 0;
background: inherit;
color: inherit;
font-size: 22px;
padding: 0;
height: 40px;
width: 40px;
background: var(--card-background);
color: var(--red-alt);
box-shadow: none;
}
.delete-line.btn:not(:disabled):hover {
box-shadow: 0 1px 3px rgb(0 0 0 / 12%), 0 1px 2px rgb(0 0 0 / 24%);
background: #e4535314;
}
.delete-line.btn:disabled {
color: var(--stroke);
}
.add-line.btn {
width: 100px;
font-size: 1em;
padding: 6px 0;
margin-top: 0.8em;
}
.output img {
max-width: 100%;
}
.output img.outlined {
border: 1px dashed var(--red);
border-radius: 2px;
}
.output .md {
background: var(--border);
border-radius: 6px;
padding: 12px 16px;
word-break: break-all;
}
.output .btn {
margin-top: 16px;
}
label.show-border {
display: block;
margin-top: 8px;
}
/* tooltips */
.btn.tooltip {
display: flex;
justify-content: center;
align-items: center;
}
/* tooltip bubble */
.btn.tooltip:before {
content: attr(title);
position: absolute;
transform: translateY(-45px);
height: auto;
width: auto;
background: #4a4a4afa;
border-radius: 4px;
color: white;
line-height: 30px;
font-size: 1em;
padding: 0 12px;
pointer-events: none;
opacity: 0;
}
/* tooltip bottom triangle */
.btn.tooltip:after {
content: "";
position: absolute;
transform: translateY(-27px);
border-style: solid;
border-color: #4a4a4afa transparent transparent transparent;
pointer-events: none;
opacity: 0;
}
/* show tooltip on hover */
.btn.tooltip[title]:hover:before,
.btn.tooltip[title]:hover:after,
.btn.tooltip:disabled:hover:before,
.btn.tooltip:disabled:hover:after {
transition: 0.2s ease-in opacity;
opacity: 1;
}
.btn.tooltip:disabled:before {
content: "You must first input valid text.";
}

@ -0,0 +1,28 @@
a.darkmode {
position: fixed;
top: 2em;
right: 2em;
color: var(--text);
background: var(--background);
height: 3em;
width: 3em;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
border: 2px solid var(--border);
box-shadow: 0 0 3px rgb(0 0 0 / 12%), 0 1px 2px rgb(0 0 0 / 24%);
transition: 0.2s ease-in box-shadow;
}
a.darkmode:hover {
box-shadow: 0 0 6px rgb(0 0 0 / 16%), 0 3px 6px rgb(0 0 0 / 23%);
}
@media only screen and (max-width: 600px) {
a.darkmode {
top: unset;
bottom: 1em;
right: 1em;
}
}

@ -0,0 +1,107 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-48CYVH0XEF"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-48CYVH0XEF');
</script>
<title>Readme Typing SVG - Demo Site</title>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap">
<link href="https://css.gg/css?=|moon|sun" rel="stylesheet">
<link rel="stylesheet" href="./css/style.css">
<link rel="stylesheet" href="./css/toggle-dark.css">
<script type="text/javascript" src="./js/script.js" defer></script>
<script type="text/javascript" src="./js/toggle-dark.js" defer></script>
<script type="text/javascript" src="./js/jscolor.min.js" defer></script>
</head>
<body <?php echo $_COOKIE["darkmode"] == "on" ? 'data-theme="dark"' : ""; ?>>
<h1>⌨️ Readme Typing SVG</h1>
<div class="github">
<!-- GitHub Sponsors -->
<iframe src="https://github.com/sponsors/DenverCoder1/button" title="Sponsor DenverCoder1" height="32"
width="116" style="border: 0; margin-left: 12px;"></iframe>
<!-- GitHub Logo -->
<a href="https://git.io/typing-svg" title="GitHub" style="text-decoration: none;">
<svg stroke="#0000" fill="currentColor" stroke-width="0" viewBox="0 0 448 512" height="32px" width="32px"
xmlns="http://www.w3.org/2000/svg">
<path
d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zM277.3 415.7c-8.4 1.5-11.5-3.7-11.5-8 0-5.4.2-33 .2-55.3 0-15.6-5.2-25.5-11.3-30.7 37-4.1 76-9.2 76-73.1 0-18.2-6.5-27.3-17.1-39 1.7-4.3 7.4-22-1.7-45-13.9-4.3-45.7 17.9-45.7 17.9-13.2-3.7-27.5-5.6-41.6-5.6-14.1 0-28.4 1.9-41.6 5.6 0 0-31.8-22.2-45.7-17.9-9.1 22.9-3.5 40.6-1.7 45-10.6 11.7-15.6 20.8-15.6 39 0 63.6 37.3 69 74.3 73.1-4.8 4.3-9.1 11.7-10.6 22.3-9.5 4.3-33.8 11.7-48.3-13.9-9.1-15.8-25.5-17.1-25.5-17.1-16.2-.2-1.1 10.2-1.1 10.2 10.8 5 18.4 24.2 18.4 24.2 9.7 29.7 56.1 19.7 56.1 19.7 0 13.9.2 36.5.2 40.6 0 4.3-3 9.5-11.5 8-66-22.1-112.2-84.9-112.2-158.3 0-91.8 70.2-161.5 162-161.5S388 165.6 388 257.4c.1 73.4-44.7 136.3-110.7 158.3zm-98.1-61.1c-1.9.4-3.7-.4-3.9-1.7-.2-1.5 1.1-2.8 3-3.2 1.9-.2 3.7.6 3.9 1.9.3 1.3-1 2.6-3 3zm-9.5-.9c0 1.3-1.5 2.4-3.5 2.4-2.2.2-3.7-.9-3.7-2.4 0-1.3 1.5-2.4 3.5-2.4 1.9-.2 3.7.9 3.7 2.4zm-13.7-1.1c-.4 1.3-2.4 1.9-4.1 1.3-1.9-.4-3.2-1.9-2.8-3.2.4-1.3 2.4-1.9 4.1-1.5 2 .6 3.3 2.1 2.8 3.4zm-12.3-5.4c-.9 1.1-2.8.9-4.3-.6-1.5-1.3-1.9-3.2-.9-4.1.9-1.1 2.8-.9 4.3.6 1.3 1.3 1.8 3.3.9 4.1zm-9.1-9.1c-.9.6-2.6 0-3.7-1.5s-1.1-3.2 0-3.9c1.1-.9 2.8-.2 3.7 1.3 1.1 1.5 1.1 3.3 0 4.1zm-6.5-9.7c-.9.9-2.4.4-3.5-.6-1.1-1.3-1.3-2.8-.4-3.5.9-.9 2.4-.4 3.5.6 1.1 1.3 1.3 2.8.4 3.5zm-6.7-7.4c-.4.9-1.7 1.1-2.8.4-1.3-.6-1.9-1.7-1.5-2.6.4-.6 1.5-.9 2.8-.4 1.3.7 1.9 1.8 1.5 2.6z">
</path>
</svg></a>
<!-- GitHub Star -->
<iframe
src="https://ghbtns.com/github-btn.html?user=DenverCoder1&repo=readme-typing-svg&type=star&count=true&size=large"
width="140" height="30" title="GitHub stars" style="border: 0;"></iframe>
</div>
<div class="container">
<div class="properties">
<h2>Add your text</h2>
<form class="parameters three-columns lines"><!-- Lines are added in JavaScript --></form>
<button class="add-line btn" onclick="return preview.addLine();">+ Add line</button>
<h2>Options</h2>
<form class="parameters two-columns options">
<label for="font">Font</label>
<input class="param" type="text" id="font" name="font" placeholder="JetBrains Mono" value="JetBrains Mono"
pattern="^[A-Za-z0-9\- ]*$" title="Font from Google Fonts. Only letters, numbers, and spaces.">
<label for="color">Font color</label>
<input class="param jscolor jscolor-active" id="color" name="background"
data-jscolor="{ format: 'hexa' }" value="#36BCF7">
<label for="size">Font size</label>
<input class="param" type="number" id="size" name="size" placeholder="20" value="20">
<label for="center">Centered</label>
<select class="param" id="center" name="center" value="false">
<option>false</option>
<option>true</option>
</select>
<label for="dimensions">W ✕ H</label>
<span id="dimensions">
<input class="param inline" type="number" id="width" name="width" placeholder="400" value="400">
<label></label>
<input class="param inline" type="number" id="height" name="height" placeholder="50" value="50">
</span>
</form>
</div>
<div class="output">
<h2>Preview</h2>
<img alt="Readme Typing SVG" src="/?lines=The+five+boxing+wizards+jump+quickly" />
<label class="show-border">
<input type="checkbox">
Show border
</label>
<h2>Markdown</h2>
<div class="md">
<code></code>
</div>
<button class="copy-button btn tooltip" onclick="clipboard.copy(this);" onmouseout="tooltip.reset(this);" disabled>
Copy To Clipboard
</button>
</div>
</div>
<a href="javascript:toggleTheme()" class="darkmode" title="toggle dark mode">
<i class="<?php echo $_COOKIE["darkmode"] == "on" ? 'gg-sun' : "gg-moon"; ?>"></i>
</a>
</body>
</html>

File diff suppressed because one or more lines are too long

@ -0,0 +1,203 @@
let preview = {
// default values
defaults: {
font: "JetBrains Mono",
color: "36BCF7",
size: "20",
center: "false",
width: "400",
height: "50"
},
dummyText: [
"The five boxing wizards jump quickly",
"How vexingly quick daft zebras jump",
"Quick fox jumps nightly above wizard",
"Sphinx of black quartz, judge my vow",
"Waltz, bad nymph, for quick jigs vex",
"Glib jocks quiz nymph to vex dwarf",
"Jived fox nymph grabs quick waltz",
],
// update the preview
update: function () {
const copyButton = document.querySelector(".copy-button");
// get parameter values from all .param elements
const params = Array.from(document.querySelectorAll(".param:not([data-index])")).reduce(
(acc, next) => {
// copy accumulator into local object
let obj = acc;
let value = next.value;
// remove hash from any colors and remove "FF" if full opacity
value = value.replace(/#([A-Fa-f0-9]{6})(F|f){2}$/, "$1");
// add value to reduction accumulator
obj[next.id] = value;
return obj;
}, {}
);
const lineInputs = Array.from(document.querySelectorAll(".param[data-index]"));
// disable copy button if any line contains semicolon
if (lineInputs.some((el) => el.value.indexOf(";") >= 0)) {
return copyButton.disabled = "true";
}
// add lines to parameters
params.lines = lineInputs
.map((el) => el.value) // get values
.filter((val) => val.length) // skip blank entries
.join(";"); // join lines with ';' delimiter
// function to URI encode string but keep semicolons as ';' and spaces as '+'
const encode = (str) => {
return encodeURIComponent(str).replace(/%3B/g, ";").replace(/%20/g, "+")
};
// convert parameters to query string
const query = Object.keys(params)
.filter((key) => params[key] !== this.defaults[key]) // skip if default value
.map((key) => encode(key) + "=" + encode(params[key])) // encode keys and values
.join("&"); // join lines with '&' delimiter
// generate links and markdown
const imageURL = `${window.location.origin}?${query}`;
const demoImageURL = `/?${query}`;
const repoLink = "https://git.io/typing-svg";
const md = `[![Typing SVG](${imageURL})](${repoLink})`;
// don't update if nothing has changed
const mdElement = document.querySelector(".md code");
if (mdElement.innerText === md) {
return;
}
// update image preview
document.querySelector(".output img").src = demoImageURL;
// update markdown
mdElement.innerText = md;
// disable copy button if no lines are filled in
copyButton.disabled = !params.lines.length;
},
addLine: function () {
const parent = document.querySelector(".lines");
const index = parent.querySelectorAll("input").length + 1;
// label
const label = document.createElement("label");
label.innerText = `Line ${index}`;
label.setAttribute("for", `line-${index}`);
label.dataset.index = index;
// line input box
const input = document.createElement("input");
input.className = "param";
input.type = "text";
input.id = `line-${index}`;
input.name = `line-${index}`;
input.placeholder = "Enter text here";
input.value = this.dummyText[(index - 1) % this.dummyText.length];
input.pattern = "^[^;]*$";
input.title = "Text cannot contain semicolons";
input.dataset.index = index;
// removal button
const deleteButton = document.createElement("button");
deleteButton.className = "delete-line btn";
deleteButton.setAttribute(
"onclick",
"return preview.removeLine(this.dataset.index);"
);
deleteButton.innerHTML = '<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
parent.appendChild(label);
parent.appendChild(input);
parent.appendChild(deleteButton);
// disable button if only 1
parent.querySelector(".delete-line.btn").disabled = index == 1;
// update and exit
this.update();
return false;
},
removeLine: function (index) {
index = Number(index);
const parent = document.querySelector(".lines");
// remove all elements for given property
parent
.querySelectorAll(`[data-index="${index}"]`)
.forEach((el) => {
parent.removeChild(el);
});
// update index numbers
const labels = parent.querySelectorAll("label");
labels
.forEach((label) => {
const labelIndex = Number(label.dataset.index);
if (labelIndex > index) {
label.dataset.index = labelIndex - 1;
label.setAttribute("for", `line-${labelIndex - 1}`);
label.innerText = `Line ${labelIndex - 1}`;
}
});
const inputs = parent.querySelectorAll(".param");
inputs
.forEach((input) => {
const inputIndex = Number(input.dataset.index);
if (inputIndex > index) {
input.dataset.index = inputIndex - 1;
input.setAttribute("id", `line-${inputIndex - 1}`);
input.setAttribute("name", `line-${inputIndex - 1}`);
}
});
const buttons = parent.querySelectorAll(".delete-line.btn");
buttons
.forEach((button) => {
const buttonIndex = Number(button.dataset.index);
if (buttonIndex > index) {
button.dataset.index = buttonIndex - 1;
}
});
// disable button if only 1
buttons[0].disabled = buttons.length == 1;
// update and exit
this.update();
return false;
},
};
let clipboard = {
copy: function (el) {
// create input box to copy from
const input = document.createElement("input");
input.value = document.querySelector(".md code").innerText;
document.body.appendChild(input);
// select all
input.select();
input.setSelectionRange(0, 99999);
// copy
document.execCommand("copy");
// remove input box
input.parentElement.removeChild(input);
// set tooltip text
el.title = "Copied!";
},
};
let tooltip = {
reset: function (el) {
// remove tooltip text
el.removeAttribute("title");
},
};
// refresh preview on interactions with the page
document.addEventListener("keyup", () => preview.update(), false);
document.addEventListener("click", () => preview.update(), false);
// checkbox listener
document.querySelector(".show-border input").addEventListener("change", function () {
const img = document.querySelector(".output img");
this.checked ? img.classList.add("outlined") : img.classList.remove("outlined");
});
// when the page loads
window.addEventListener(
"load",
() => {
// add first line
preview.addLine();
preview.update();
},
false
);

@ -0,0 +1,22 @@
function toggleTheme() {
const icon = document.querySelector(".darkmode i");
/* dark mode on */
if (document.body.getAttribute("data-theme") !== "dark") {
icon.className = "gg-sun";
setCookie("darkmode", "on", 9999);
document.body.setAttribute("data-theme", "dark");
}
/* dark mode off */
else {
icon.className = "gg-moon";
setCookie("darkmode", "off", 9999);
document.body.removeAttribute("data-theme");
}
}
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
var expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}

@ -4,6 +4,7 @@ require '../vendor/autoload.php';
// set content type
header("Content-type: image/svg+xml");
// set cache to refresh once per day
$timestamp = gmdate("D, d M Y 23:59:00") . " GMT";
header("Expires: $timestamp");
@ -11,6 +12,12 @@ header("Last-Modified: $timestamp");
header("Pragma: no-cache");
header("Cache-Control: no-cache, must-revalidate");
// redirect to demo site if no text is given
if (!isset($_REQUEST["lines"])) {
header('Location: demo/');
exit;
}
try {
// create renderer model
$model = new RendererModel("templates/main.php", $_REQUEST);

Loading…
Cancel
Save