Commit 94d747f9 authored by Nathan Pasko's avatar Nathan Pasko
Browse files

reorganize directory to include cartridge maker and update readme

parent 49c96d1d
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="">
<meta name="description" content="" />
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<picture>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 396.6 396.6"><defs><style>.cls-1{fill:#ff0;}.cls-2{fill:blue;}</style></defs><g id="tunnel"><path d="M94.2,98.8H69.3v65.4h-36V98.8H6V63.4H94.2Z"/><path class="cls-1" d="M69.3,164.2V267.6h-36V164.2"/><path d="M241.3,267.6h77v65.3h72.3V297.5H354.3V232.1H304.4V210.6H276.7v-8.4h41.6v-38h-77V60.8h-36V112l-52-51.2V164.2h52v51.2l-52-51.2H116.7v65.6H105.9V164.2H69.3V217c0,17.2,0,49.6,42.1,49.6s41.9-32.4,41.9-49.6h0v50.6"/></g><g id="engine"><path class="cls-2" d="M126.7,310.7v.2c0,5.3,3.8,8.6,8.1,8.6a8.6,8.6,0,0,0,5.9-2.3l.6.8a9.5,9.5,0,0,1-6.5,2.5c-4.9,0-9-3.6-9-9.5s3.9-9.3,8.4-9.3,7.4,3.3,7.4,7.7v1.3Zm.1-1h13.8c0-5.2-3.1-7.1-6.4-7.1S127.3,304.8,126.8,309.7Z"/><path class="cls-2" d="M154.7,302.1h.9v3.5a8.9,8.9,0,0,1-.1,1.3h0a8,8,0,0,1,7.8-5.2c4.5,0,6.2,2.4,6.2,7.1v11.3h-1V309c0-3.5-.8-6.4-5.2-6.4a7.6,7.6,0,0,0-7.4,5.8,9.3,9.3,0,0,0-.3,2.4v9.3h-.9Z"/><path class="cls-2" d="M184.6,325.2a10.1,10.1,0,0,0,5.1,1.3c3.7,0,7-1.9,7-6.5v-2.7a6.1,6.1,0,0,1,.2-1.5h-.1a6.7,6.7,0,0,1-6.4,4.3c-4.7,0-8-3.7-8-9.4s3-9,7.8-9c3,0,5.8,1.4,6.6,4.5h.1a3.1,3.1,0,0,1-.2-1v-3.1h1V320c0,5.4-3.8,7.5-8,7.5A12.7,12.7,0,0,1,184,326Zm12.2-14.4c0-6.2-3-8.2-6.6-8.2s-6.8,3-6.8,8.1,2.8,8.4,7,8.4S196.8,317,196.8,310.8Z"/><path class="cls-2" d="M212,294.8h1v3.4h-1Zm0,7.3h1v18h-1Z"/><path class="cls-2" d="M227.3,302.1h1v3.5a4.6,4.6,0,0,1-.2,1.3h.1a8,8,0,0,1,7.8-5.2c4.5,0,6.1,2.4,6.1,7.1v11.3h-.9V309c0-3.5-.9-6.4-5.2-6.4a7.5,7.5,0,0,0-7.4,5.8,9.3,9.3,0,0,0-.3,2.4v9.3h-1Z"/><path class="cls-2" d="M255.9,310.7v.2c0,5.3,3.8,8.6,8.1,8.6a8.6,8.6,0,0,0,5.9-2.3l.6.8a9.5,9.5,0,0,1-6.5,2.5c-4.9,0-9-3.6-9-9.5s3.9-9.3,8.4-9.3,7.4,3.3,7.4,7.7v1.3Zm.1-1h13.8c0-5.2-3.1-7.1-6.4-7.1S256.5,304.8,256,309.7Z"/></g></svg> </picture>
<h1>Cartridge Maker</h1>
<h6>v.1.0</h6>
</header>
<div id="form">
<section>
<h2>Info</h2>
</section>
<section>
<label for="title">Title</label>
<input id="title" class="large" name="title" placeholder="cartridge title" required/>
</section>
<section>
<label for="display">Labels</label>
<input type="checkbox" name="display" checked />
<p>Applying the standardized labels will display the title and nav at the top of the cartridge.</p>
</section>
<section id="nav">
<label>Nav</label>
<ul>
</ul>
<button onclick="clickNewLink()">Add To Nav</button>
</section>
<section id="link-edit" class="hiding">
<h3>Link Edit <span id="current-link"></span></h3>
<label for="new-link-text">Link Text</label>
<input id="new-link-text" placeholder="my link" />
<label for="new-link-url">URL</label>
<input id="new-link-url" placeholder="https://www.com/linkurl" />
<div class="flex-row">
<button onclick="clickLinkSave()">Save Link</button>
<button onclick="clickLinkCancel()">Cancel</button>
</div>
</section>
<section>
<h2>Layers</h2>
</section>
<section id="layers">
<label>Outer / Entry</label>
<ul>
</ul>
<label>Inner / Limit</label>
<button id="new-layer-btn" onclick="clickNewLayer()">Add New Layer</button>
</section>
<section id="layer-edit" class="hiding">
<h3>Layer Edit <span id="current-index"></span></h3>
<label for="layer-id">ID</label>
<input id="layer-id" name="layer-id" placeholder="#" />
<label for="audio">Audio Path</label>
<input id="audio" name="audio" placeholder="/audio/clip4.wav" />
<label for="background">Background Color</label>
<div>
<input id="transparent" type="radio" name="transparency" value="transparent" checked />
<label for="transparent" class="inline small">Transparent</label>
</div>
<div>
<input id="choose-color" type="radio" name="transparency" value="color" />
<label for="choose-color" class="inline small">Choose Color</label>
<input id="color-picker" type="color" name="background" />
</div>
<label>Images</label>
<label class="small">Back</label>
<input id="image0" placeholder="/img/image0.png" />
<input id="image1" placeholder="/img/image1.png" />
<input id="image2" placeholder="/img/image2.png" />
<input id="image3" placeholder="/img/image3.png" />
<label class="small">Front</label>
<div class="flex-row">
<button onclick="clickLayerSave()">Save Layer</button>
<button onclick="clickLayerCancel()">Cancel</button>
</div>
</section>
<section>
<h2>Finish</h2>
</section>
<section>
<p>When you're done, click this button to export the formatted data file for your cartridge. To learn how cartridges are loaded into Tunnel, <a href="https://tunnelengine.netlify.app/manual">see the User's Manual</a>.</p>
<button id="finish-btn" onclick="clickSubmit()">
<svg class="inline-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 259.2 259.2"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path d="M129.6,0A129.6,129.6,0,1,0,259.2,129.6,129.6,129.6,0,0,0,129.6,0Zm61.8,199.6V166h-85V140c0,8.8.1,25.5-21.5,25.5S63.2,148.8,63.2,140V112.8H44.7V79.1H30.6V60.9H76V79.1H63.2v33.7H82v33.8h5.6V112.8h18.8V59.6l26.8,26.3V59.6h18.5v53.2h39.7v19.6H170v4.3h14.2v11h25.7v33.7h18.7v18.2Z"/><polygon points="133.2 112.8 106.4 112.8 133.2 139.2 133.2 112.8"/></g></g></svg>
Finish
</button>
</section>
</div>
<footer>
<p>Cartridge Maker &copy; Nathan Pasko. <a href="tunnelengine.netlify.app">Tunnel</a> is open source under the MIT License.</p>
</footer>
<script src="main.js"></script>
</body>
</html>
\ No newline at end of file
const navUl = document.querySelector('#form section#nav ul');
const linkEdit = document.getElementById('link-edit')
const linkText = document.getElementById('new-link-text');
const linkUrl = document.getElementById('new-link-url');
const layerEdit = document.getElementById('layer-edit');
const currentLinkSpan = document.getElementById('current-link');
const currentIndexSpan = document.getElementById('current-index');
const layerUl = document.querySelector('#form section#layers ul');
const layerId = document.getElementById('layer-id');
const audioPath = document.getElementById('audio');
const colorPicker = document.getElementById('color-picker');
var currentIndex = -1;
var currentLink = -1;
var info = {
title: '',
nav: [],
labels: true
};
var layers = [];
function download(content, fileName, contentType) {
var a = document.createElement('a');
var file = new Blob([content], {
type: contentType
});
a.href = URL.createObjectURL(file);
a.download = fileName;
a.click();
}
function getLinkItem(text, href, index) {
var elem = document.createElement('li');
var pText = document.createElement('p');
pText.textContent = text.length > 0 ? text : '---';
var button = document.createElement('button');
button.classList.add('delete');
button.dataset.href = href;
button.setAttribute('aria-label', `Delete ${text} Link`);
button.textContent = 'X';
button.onclick = e => clickLinkDelete(e);
pText.appendChild(button);
elem.appendChild(pText);
var edit = document.createElement('button');
edit.classList.add('edit');
edit.dataset.linkIndex = index;
edit.setAttribute('aria-label', `Edit Link ${text}`);
edit.textContent = 'Edit';
edit.onclick = e => clickLinkEdit(e);
pText.appendChild(edit);
var pHref = document.createElement('p');
var a = document.createElement('a');
a.setAttribute('href', href);
a.textContent = href;
pHref.appendChild(a);
elem.appendChild(pHref);
return elem;
}
function getLayerItem(layer, index) {
console.log('get layer item', index, layer);
var elem = document.createElement('li');
var pText = document.createElement('p');
var layerId = `${layer.id}` || '---';
pText.textContent = `[${index}] ID: ${layerId}`;
var button = document.createElement('button');
button.classList.add('delete');
button.dataset.layerIndex = index;
button.setAttribute('aria-label', `Delete Layer ${layer.id}`);
button.textContent = 'X';
button.onclick = e => clickLayerDelete(e);
pText.appendChild(button);
var edit = document.createElement('button');
edit.classList.add('edit');
edit.dataset.layerIndex = index;
edit.setAttribute('aria-label', `Edit Layer ${layer.id}`);
edit.textContent = 'Edit';
edit.onclick = e => clickLayerEdit(e);
pText.appendChild(edit);
if (index == 0) {
var control = document.createElement('span');
control.classList.add('control');
control.textContent = 'control';
pText.appendChild(control);
}
elem.appendChild(pText);
return elem;
}
function getEmptyMessage(section = 'nav') {
console.log('empty');
var p = document.createElement('p');
if (section == 'nav')
p.innerHTML = `<strong>Empty.</strong> Add links to populate cartridge nav.`;
else if (section == 'layers')
p.innerHTML = `<strong>Empty.</strong> Add data for each layer in cartridge.`;
return p;
}
function clearValue(input) {
input.value = '';
}
function removeAllChildNodes(parent) {
while (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
}
function setCurrentLink(val) {
currentLink = val;
if (currentLink >= 0) {
currentLinkSpan.textContent = `[${val}]`;
}
else {
currentLinkSpan.textContent = '';
}
}
function setCurrentIndex(val) {
currentIndex = val;
if (currentIndex >= 0) {
currentIndexSpan.textContent = `[${val}]`;
}
else {
currentIndexSpan.textContent = '';
}
}
function updateNavDisplay() {
removeAllChildNodes(navUl);
if (info.nav.length < 1) {
var li = getEmptyMessage();
navUl.appendChild(li);
}
else {
for (var i = 0; i < info.nav.length; i++) {
var li = getLinkItem(info.nav[i].text, info.nav[i].href, i);
navUl.appendChild(li);
}
}
}
function removeLink(href) {
const index = info.nav.findIndex(l => l.href == href);
if (index >= 0) {
info.nav.splice(index, 1);
updateNavDisplay();
}
else {
console.log('ERROR - link not found in array');
}
}
function updateLayerDisplay() {
console.log('update layer display', `${layers.length}ct`);
removeAllChildNodes(layerUl);
if (layers.length < 1) {
var li = getEmptyMessage('layers');
layerUl.appendChild(li);
}
else {
for (var i = 0; i < layers.length; i++) {
var li = getLayerItem(layers[i], i);
layerUl.appendChild(li);
}
}
}
function sanitizeLinkData() {
var text = linkText.value || '';
var href = linkUrl.value || '';
var linkData = {
text,
href
};
return linkData;
}
function sanitizeLayerData() {
var id = layerId.value || '';
var audio = audioPath.value || '';
var images = [];
for (var i = 0; i < 4; i++) {
const val = document.getElementById(`image${i}`).value || '';
images.push(val);
}
var layerData = {
id,
audio,
images
};
var transparency = document.querySelector('input[name="transparency"]:checked');
if (transparency.value == 'color') {
layerData['background'] = colorPicker.value;
}
return layerData;
}
function setLinkEditor() {
if (currentLink >= 0) {
const link = info.nav[currentLink];
linkText.value = link.text;
linkUrl.value = link.href;
linkEdit.classList.add('active');
}
else {
linkEdit.classList.remove('active');
}
}
function setLayerEditor() {
if (currentIndex >= 0) {
const layer = layers[currentIndex];
layerId.value = layer.id;
if (layer.audio)
audioPath.value = layer.audio;
if (layer.background)
colorPicker.value = layer.background;
for (var i = 0; i < 4; i++) {
if (layer.images && layer.images[i])
document.getElementById(`image${i}`).value = layer.images[i];
}
layerEdit.classList.add('active');
}
else {
clearValue(layerId);
clearValue(audioPath);
document.getElementById('transparent').checked = true;
document.getElementById('choose-color').checked = false;
colorPicker.value = '#000000';
for (var i = 0; i < 4; i++) {
document.getElementById(`image${i}`).value = '';
}
layerEdit.classList.remove('active');
}
}
function clickLinkSave() {
var linkData = sanitizeLinkData();
info.nav[currentLink] = linkData;
setCurrentLink(-1);
setLinkEditor();
updateNavDisplay();
}
function clickLayerSave() {
var layerData = sanitizeLayerData();
console.log(layerData);
layers[currentIndex] = layerData;
setCurrentIndex(-1);
setLayerEditor();
updateLayerDisplay();
}
function clickLinkDelete(e) {
const href = e.target.dataset.href;
console.log(href);
removeLink(href);
}
function clickNewLink() {
info.nav.push({
text: '',
href: ''
});
updateNavDisplay();
}
function clickLinkEdit(e) {
var index = e.target.dataset.linkIndex;
console.log('edit link @', index);
setCurrentLink(index);
setLinkEditor();
}
/*function clickLinkAdd() {
var link = {
text: linkText.value,
href: linkUrl.value
};
info.nav.push(link);
updateNavDisplay();
console.log('add', link);
clearValue(linkText);
clearValue(linkUrl);
}*/
function clickLinkCancel() {
setCurrentLink(-1);
setLinkEditor();
}
function clickNewLayer() {
layers.push({
id: ''
});
updateLayerDisplay();
}
function clickLayerDelete(e) {
console.log('layer delete on index', e.target.dataset.layerIndex);
layers.splice(e.target.dataset.layerIndex, 1);
updateLayerDisplay();
}
function clickLayerEdit(e) {
var index = e.target.dataset.layerIndex;
console.log('edit layer @', index);
setCurrentIndex(index);
setLayerEditor();
}
function clickLayerCancel() {
setCurrentIndex(-1);
setLayerEditor();
}
function clickSubmit() {
const cartridge = {
info,
layers
};
console.log('cart', cartridge);
const jsonCartridge = JSON.stringify(cartridge);
console.log(jsonCartridge);
download(jsonCartridge, 'jsoncart.json', 'application/json');
}
updateNavDisplay();
updateLayerDisplay();
\ No newline at end of file
:root {
--white: #fff;
--black: #111;
--blue: blue;
--yellow: yellow;
--greyTrans: #8a8a8a8a;
}
html {
background: var(--white);
color: var(--black);
font-size: 100%;
font-family: sans-serif;
}
* {
position: relative;
margin: 0;
padding: 0;
box-sizing: border-box;
}
header picture svg {
max-height: 125px;
max-width: 125px;
margin: 1rem 1rem 0;
}
footer p {
font-size: .8rem;
text-align: center;
padding: 4rem 0 12rem;
}
section {
margin: 0 auto;
max-width: 550px;
}
h1, h2, h3, h4, h5, h6, p, label {
padding: 0 1rem;
}
h2 {
max-width: 550px;
margin-top: 7rem;
}
header h6 {
padding-left: 1.2rem;
}
p, input, details {
margin: 0 auto;
max-width: 550px;
}
p {
font-weight: 200;
}
ul {
margin: .5rem 0 0;
list-style: inside;
}
ul li {
margin-bottom: .65rem;
}
ul > p {
/*padding-left: 1.85rem;*/
margin-bottom: .5rem;
background: #eee;
}
ul li p:last-child {
font-size: .85rem;
}
label {
display: block;
margin-top: .125rem;
font-size: .75rem;
font-weight: 600;
letter-spacing: .5px;
text-transform: uppercase;
}
label.small {
font-weight: 400;
}
input {
display: block;
max-height: 100px;
margin: .125rem 1rem;
padding: .5rem;
border: 2px solid var(--black);
border-radius: 0;
font-size: 1rem;
}
input.large {
font-size: 1.45rem;
}
input[type="color"] {
display: inline;
padding: 0;
}
input[type="radio"] {
display: inline;
}
button {
display: block;
margin: .25rem auto;
padding: .5rem 1rem;
color: var(--white);
background: var(--blue);
font-size: 1rem;
border: none;
border-radius: 0;
}
button:active {
background: var(--black);
}
button.delete, button.edit {
display: inline;
margin: 0 .5rem;
padding: .125rem .5rem;
}
button.edit {
padding: .125rem 1rem;
}
@media (min-width: 768px) {
.flex-row > button:not(:last-child) {
margin: 0 .5rem 0 0;
}
}
details {
width: 100%;
padding: .35rem 0 0 1rem;
}
summary {
margin-right: 1rem;
margin-bottom: .5rem;
padding: 1px;
color: var(--white);
background: var(--blue);
font-size: .75rem;
font-weight: 600;
letter-spacing: .5px;
text-transform: uppercase;
}
.flex-row {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.flex-row > * {
flex-grow: 1;
}
@media (min-width: 768px) {
.flex-row {
flex-direction: row;
}
}
.inline-icon {
height: 1.125rem;
width: 1.125rem;
margin-right: .125rem;
vertical-align: -.2rem;
}
.hiding {
pointer-events: none;
opacity: 0;
transition: opacity 1s;
}
.hiding.active {
pointer-events: all;
opacity: 1;
}
.prevent-select {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.cursor-default {
cursor: default;
}
.inline {
display: inline;
width: 0;
}
.control {
margin-left: .325rem;
font-size: .75rem;
letter-spacing: .5px;
text-transform: uppercase;
vertical-align: .125rem;
}
@keyframes rotateGradient {
0% { background-position: 0% 50%; opacity: 0;}
25% { opacity: 1; }
57% { transform: scaleY(.85); }
60% { transform: scaleY(1.1); }
68% { transform: scaleY(.9); }
85% { opacity: 1; }
100% { background-position: 100% 50%; opacity: 0 }
}
@keyframes swellFade {