Files
diy-startpage/startpage.html
T

2802 lines
98 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>startpage</title>
<style>
/*
* GENERAL STATE HELPERS
*/
.hidden {
visibility: hidden;
display: none;
}
/*
* CURSOR STUFF
body {
cursor: url(""), auto;
}
.grabbable:hover {
cursor: url(""), grab;
}
.removable:hover {
cursor: url(""), crosshair;
}
*/
.movable {
position: fixed;
}
.container {
z-index: 998;
display: flex;
align-items: center;
justify-content: center;
overflow: auto;
}
.section {
margin-top: 0rem;
}
.settingsContainer {
background-color: rgba(173, 165, 165, 0.8);
z-index: 999;
user-select: none;
border: solid black 2px;
border-radius: 10px;
padding: 10px;
display: block;
top: 2rem;
align-self: flex-end;
height: 25rem;
overflow: scroll;
}
/*
* SETTINGS TABS
*/
#tabList {
display: block;
border-bottom: solid 2px black;
padding-bottom: 10px;
margin-bottom: 10px;
}
.tab {
margin-right: 2rem;
user-select: none;
text-wrap: nowrap;
}
.tab:hover {
cursor: pointer;
text-decoration: underline;
}
.tab.active {
font-weight: bold;
text-decoration: underline;
}
#ioTab {
flex-shrink: 1;
}
/*
* SETTINGS PAGES
*/
form,
#instructionsForm {
display: none;
flex-direction: column;
}
.formTitle {
margin-bottom: 1rem;
text-decoration: underline;
font-style: italic;
align-self: center;
}
label {
align-self: start;
}
input {
align-self: flex-end;
width: 100%;
}
input[type="checkbox"],
input[type="radio"] {
width: auto;
}
.expandableMenuToggle {
cursor: pointer;
padding: 0px 18px;
width: 100%;
}
.expandableMenuToggle.active, .expandableMenuToggle:hover {
background-color: rgba(173, 165, 165, 0.8);
}
.expandableMenu {
display: none;
padding: 0 18px;
border-left: 2px solid black;
}
.menuHeader {
text-align: center;
width: 100%;
background-color: rgba(173, 165, 165, 0.8);
font-style: italic;
}
.bookmarkListingContainer {
display: flex;
flex-direction: column;
}
.sectionListingContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.linkListingContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.bookmarkListingButtons {
}
.bookmarkButton {
}
.bookmarkButton:hover {
cursor: pointer;
background-color: rgba(173, 165, 165, 0.8);
}
.bookmarkDeleteButton {
}
.bookmarkDeleteButton:hover {
background-color: rgba(173, 165, 165, 0.8);
color: red;
cursor: pointer;
}
hr {
width: 75%;
}
#editToggle {
z-index: 999;
position: fixed;
font-size: 0.75rem;
}
#editToggle:hover {
cursor: pointer;
}
#imageRemovalWarning {
color: red;
size: 3rem;
font-weight: bold;
user-select: none;
z-index: 1000;
display: inline;
align-self: center;
background-color: black;
border: solid red 0.25rem;
padding: 0.25rem;
}
</style>
</head>
<body>
<div style="display: flex; flex-direction: column">
<div id="imageRemovalWarning" class="hidden">
IMAGE REMOVAL MODE ACTIVE!!
</div>
<a id="editToggle" onClick="toggleEditMode()" style="align-self: flex-end"
>edit page</a
>
<br />
<div id="settingsContainer" class="movable settingsContainer hidden">
<div id="tabList">
<span class="tab" id="containerTab">layers</span>
<span class="tab" id="imageAndWallpaperTab">images + wallpaper</span>
<span class="tab" id="audioTab">audio</span>
<span class="tab" id="globalSettingsTab">global settings</span>
<br />
<span class="tab" id="ioTab">import / export</span>
<span class="tab" id="resetTab">reset data</span>
<span class="tab" id="instructionsTab">instructions</span>
</div>
<!-- note to self:
- i should rename these so they don't end in "Form",
- rather something like "Page". to do that though i need
- to change the logic for switching tabs that has the string
- modification hardcoded
-->
<div id="instructionsForm">
<p id="helpText">
you are now editing the page!<br /><br />
this is the settings box. there are various tabs containing various
customization options for your startpage experience. you can create
and categorize bookmarks, customize the bookmark box to your taste,
change the wallpaper, and add + maniuplate any image you want!<br /><br />
<b>[ <i>let it be known</i> ]</b><br />
<i
>your changes will only be saved when you stop editing the page.
to cancel your live edits, refresh the page without hitting
save.</i
><br /><br />
<b>[ <i>bookmark tips</i> ]</b><br />
to group bookmarks together, just enter them with the same section.
bookmarks without a section will stay at the top. you can remove
individual bookmarks or whole sections, and sections can be
rearranged. there's also a button in this menu to full reset.<br /><br />
<b>[ <i>customization tips</i> ]</b><br />
everything on the screen can be dragged around and resized. left
click and drag to move, right click and drag to resize!<br /><br />
you can add as many images to the background and resize them as you
wish, but they can only be added via URL. i have provided a quick
link to a place to upload your own images if you have specific
things in mind, but you can also make a beautiful collage of random
photographs you find without using any disk space.<br /><br />
<b>[ <i>changing font</i> ]</b><br />
you can use any font that is available on
<a href="https://fonts.google.com/" target="blank">google fonts</a>.
FYI, if you have multiple fonts selected only the first one will be
loaded, and it will be the regular version (if there are variants of
the font)<br /><br />
to load a font:<br />
1.) on google fonts, click on a font you like, press the "get font"
button, and then press the "get embed code" button. <br />2.) under
the "web" tab (you are already probably on it), copy the stuff
under: "embed code in the &lt;head&gt; of your html" (it should be a
few lines starting with &lt;link&gt;)<br />3.) paste contents into
the "change font" input, found on the "container" tab of this
floating menu.<br /><br /><b>[ <i>back-up themes!</i> ]</b><br />
you can export your entire current setup to import again later (save
ur moodboards or share with friends). this is primarily useful
because everything for this page is stored as a cookie. so, if you
reset your browser history or cache, your startpage will be lost!!
unless you have it backed up 8]
<br /><br />
clicking 'export data' will fill your clipboard with your currently
saved data (make sure you've saved your changes before exporting),
simply paste the contents into a text file for safekeeping. to load
a saved configuration, just paste the exported text and hit
import.<br /><br />
if you somehow lose your bookmark box off-screen, use the "reset box
customization" options to get it back. you will lose your other
customizations tho :[<br /><br />
</p>
</div>
<form id="containerForm" onsubmit="return false">
<div id="containers">
<h2 class="menuHeader">layers</h2>
</div>
<div style="display:flex; flex-direction:row; align-items:center; justify-content: space-between; flex-wrap: wrap;">
<div>
<h2 class="menuHeader">add bookmark layer</h2>
<input
id="newTextContainerNameInput"
placeholder="optional: layer name"
type="text"
/>
<button id="newTextContainerCreateButton" onclick="createNewTextContainer(this)">
create new layer
</button>
</div>
<p>or</p>
<div>
<h2 class="menuHeader">add image to page</h2>
<input
id="newImageContainerNameInput"
placeholder="optional: image name"
type="text"
/>
<input
id="newImageContainerUrlInput"
placeholder="required: direct url to image"
type="text"
/>
<button id="newImageContainerCreateButton" onclick="createNewImageContainer(this)">
place image
</button>
</div>
</div>
</form>
<form id="imageAndWallpaperForm" onsubmit="return false">
<div class="formTitle">edit images</div>
<div>
<label> add a floating image to the page: </label><br />
<input type="text" name="url" placeholder="paste image URL" />
<span
><a
href="https://imgur.com/upload"
target="_blank"
style="color: white"
>imgur upload</a
></span
>
</div>
<button onclick="addFloatingImage()">add floating image</button><br />
<div>
<label for="imageRatioToggle">free transform mode</label>
<input type="checkbox" id="imageRatioToggle" />
</div>
<div>
<label for="imageRemovalToggle">image removal mode</label>
<input type="checkbox" id="imageRemovalToggle" />
</div>
<br />
<div class="formTitle">edit wallpaper</div>
<div>
<label> image as wallpaper: </label><br />
<input id="wallpaperUrl" placeholder="paste image URL" />
<button id="submitUrl" onclick="changeWallpaper()">
set image
</button>
<label for="wallpaperRepeatToggle"> tile? </label>
<input id="wallpaperRepeatToggle" type="checkbox" />
</div>
<div>
<label> solid color as wallpaper: </label><br />
<input type="color" id="wallpaperColorPicker" />
</div>
</form>
<form id="ioForm" onsubmit="return false">
<label>use the button below to copy your current configuration to your clipboard<br />(you should save this somewhere)</label>
<button onclick="exportData()">export data</button><br />
<input id="importDataInput" />
<button
onclick="importData()"
placeholder="paste exported data here to load"
>
import data
</button>
</form>
<form id="audioForm" onsubmit="return false">
<label
>paste a direct URL to an audio file (ending in .mp3 or .wav or
whatever) to load:</label
>
<input id="audioLinkInput" />
<button onclick="setAudioLink()">set audio</button>
<audio id="audio" controls>
<source id="audioSource" src="" type="audio/mp3" />
</audio>
<label for="autoplayAudioToggle">auto-play when page loads? </label>
<input id="autoplayAudioToggle" type="checkbox" checked />
<label
>(note: this will require white-listing this site in your browser.
you can whitelist local HTML files! the URL would be something like
"file:///C:/path/to/saved/file")</label
>
</form>
<form id="globalSettingsForm" onsubmit="return false">
<p>for reference: <a href="https://www.totallyfreecursors.com/">cursors</a></p>
<label>enter source for normal cursor: </label>
<input id="pointerCursorInput" placeholder="paste link here" />
<button onclick="setDefaultCursor()">set normal cursor</button>
<label>enter source for grabbing cursor: </label>
<input id="grabCursorInput" placeholder="paste link here" />
<button onclick="setGrabCursor()">set grabbing cursor</button>
</form>
<form id="resetForm" onsubmit="return false">
<div class="formTitle">use these buttons to reset your page</div>
<button onclick="wipeBookmarks()">wipe links</button>
<button onclick="setDefault()">set to default gunny links</button
><br />
<button onclick="wipeImages()">wipe floating images</button><br />
<button onclick="wipeWallpaper()">reset wallpaper</button><br />
<button onclick="wipeContainerSettings()">
reset box customizations</button
><br />
<button onclick="resetCursors()">reset cursors</button>
<button onclick="resetFont()">reset font</button>
</form>
</div>
</div>
</body>
<script>
// cookie holder for coordinates + size of settings menu
let settingsMenuData = {};
// cookie holder for coordinates + size of containers
let containerDataMap = new Map();
// default values for easy resetting
let defaultTextContainerSettings = {
backgroundRgba: "rgba(255,255,255,.9)",
backgroundAlpha: 100,
borderWidth: "5",
borderRadius: "0",
borderColor: "grey",
fontName: "",
fontCode: "",
sectionColor: "",
sectionSize: "16",
sectionItalic: false,
sectionBold: false,
linkColor: "",
linkSize: "16",
enableDate: true,
enableClock: true,
headerColor: "",
headerSize: "16",
headerBold: false,
headerItalic: false,
enableDivider: true,
dividerColor: "",
centerAlign: false,
shadowX: "0",
shadowY: "0",
shadowBlur: "0",
shadowRgba: "rgba(255,255,255,.90)",
};
let defaultImageContainerSettings = {
borderWidth: "0",
borderRadius: "0",
borderColor: "grey",
shadowX: "0",
shadowY: "0",
shadowBlur: "0",
shadowRgba: "rgba(255,255,255,.90)",
};
let wallpaper = "";
// local states
let editing = false;
let moving = false;
let resizing = false;
let removing = false;
let changingElement;
let lastMouseX = 0;
let lastMouseY = 0;
let justImported = false;
let activeTabId = "";
let keepImageRatio = true;
let repeatWallpaper = false;
let audioLink = "";
let autoplayAudio = true;
let numberOfTextContainers = 0;
let numberOfImageContainers = 0;
let occupiedIndices = new Map();
let cursors = {};
class Container {
id;
name;
x;
y;
height;
width;
zindex;
imageUrl;
containerSettings;
sections;
clockIntervalId;
constructor(
id,
name,
x,
y,
height,
width,
zindex,
imageUrl,
containerSettings,
sections,
) {
// check if id is already used
if (Object.keys(containerDataMap)
.indexOf(name.replace(" ", "-").toLowerCase())
!= -1
) {
alert("that name is already used, please use another");
return;
}
// ensure there are no brackets in the name
else if (name.indexOf("<") != -1 || name.indexOf(">") != -1) {
alert("no brackets in the name please");
return;
}
// if initializing saved bookmark container
if (x != undefined && y != undefined && imageUrl == undefined) {
numberOfTextContainers++;
this.name = name;
this.id = id;
this.x = x;
this.y = y;
this.height = height;
this.width = width;
this.zindex = zindex;
this.imageUrl = imageUrl;
this.containerSettings = containerSettings;
this.sections = sections;
this.initializeTextContainer();
this.createTextContainerMenuListing();
this.loadBookmarks();
this.loadBookmarkListings();
}
// if initializing saved image container
else if (x != undefined && y != undefined && imageUrl != undefined) {
numberOfImageContainers++;
this.name = name;
this.id = id;
this.x = x;
this.y = y;
this.height = height;
this.width = width;
this.zindex = zindex;
this.imageUrl = imageUrl;
this.containerSettings = containerSettings;
this.initializeImageContainer();
this.createImageContainerMenuListing();
}
// if intializing brand new bookmark container
else if (imageUrl == undefined) {
numberOfTextContainers++;
/**
todo:
remove the zindex stuff.
get rid of the separation between text/image in terms of ID
-> the function doesn't need params and should just get the index
use the id index as the key in the data map for easier zindex manipulation
make all default ids just "container-Z" regardless of type, no need to use the name in there at all
then you can get rid of the index map too, you can just check the normal data map to see if the ID is used
do you need to save the numberOfTexContainers/ImageContainers? or honestly probably not..
*/
if (name == "") {
this.name = "bookmark layer " + numberOfTextContainers;
this.id = findLowestAvailableId("text");
}
else {
this.name = name;
this.id = name.replace(" ", "-").toLowerCase();
}
// deep copy default settings
this.containerSettings = JSON.parse(JSON.stringify(defaultTextContainerSettings));
this.sections = {};
this.zindex = numberOfTextContainers + numberOfImageContainers;
this.initializeTextContainer();
this.createTextContainerMenuListing();
this.loadBookmarks();
this.loadBookmarkListings();
}
// if initializing brand new image
else {
numberOfImageContainers++;
if (name == "") {
this.name = "image " + numberOfImageContainers;
this.id = findLowestAvailableId("image");
}
else {
this.name = name;
this.id = name.replace(" ", "-").toLowerCase();
}
this.imageUrl = imageUrl;
this.zindex = numberOfTextContainers + numberOfImageContainers;
this.initializeImageContainer();
this.createImageContainerMenuListing();
}
this.applySettings();
this.addContainerEventListeners()
this.addSettingsMenuEventListeners();
// and save
containerDataMap.set(this.id, this);
}
/**
* creates image element and lets
* loadBookmarks() and applySettings() do the rest
*/
initializeImageContainer() {
// deep copy default settings
this.containerSettings = JSON.parse(JSON.stringify(defaultImageContainerSettings));
document.body.insertAdjacentHTML(
"beforeend",
`
<div style="display: inline;">
<img
class="movable userImage"
id="${this.id}"
style="z-index: ${this.zindex};"
src="${this.imageUrl}"
draggable=false
/>
</div>
`
);
// save data if not loading existing container
if (this.x == undefined && this.y == undefined) {
let newImage = document.getElementById(this.id);
this.x = newImage.offsetLeft;
this.y = newImage.offsetTop;
this.height = newImage.offsetHeight;
this.width = newImage.offsetWidth;
}
}
/**
* creates shell HTML to hold bookmarks and lets
* loadBookmarks() and applySettings() do the rest
*/
initializeTextContainer() {
// insert default container HTML
document.body.insertAdjacentHTML(
"beforeend",
`
<div class="movable container" id=${this.id}>
<div>
<div id=${this.id + "-header"}>
<span id=${this.id + "-date"}></span>
<br />
<span id=${this.id + "-clock"}></span>
</div>
<hr id=${this.id + "-divider"} />
<div id=${this.id + "-bookmarks"} ></div>
</div>
</div>
`
);
/** set up the time */
const timeFormat = new Intl.DateTimeFormat([], {
timeZone: "America/New_York",
hour12: false,
hour: "numeric",
minute: "numeric",
second: "numeric",
});
// set time immediately so it shows upon load
document.getElementById(this.id + "-clock").innerText =
timeFormat.format(new Date());
// set time on interval to continue to update
this.clockIntervalId = setInterval(() => {
document.getElementById(this.id + "-clock").innerText =
timeFormat.format(new Date());
}, 1000);
/** set up date */
const dateFormat = new Intl.DateTimeFormat([], {
weekday: "long",
day: "numeric",
month: "long",
});
document.getElementById(this.id + "-date").innerText =
dateFormat.format(new Date());
// save data if not loading existing container
if (this.x == undefined && this.y == undefined) {
let newContainer = document.getElementById(this.id);
this.x = newContainer.offsetLeft;
this.y = newContainer.offsetTop;
this.height = newContainer.offsetHeight;
this.width = newContainer.offsetWidth;
}
}
/**
* creates container options menu in page settings menu
*/
/**
* TODO:
* -> remove "images" from wallpaper tab
* -> furthermore, condense all global settings into one tab
* - including wallpaper, audio, cursor
* add toggles for each settings menu
* rename settings form div ids (see "note to self")
*/
loadBookmarkListings() {
if (Object.keys(this.sections).length == 0) {
let bookmarkListings = document.getElementById(this.id + "-bookmark-menu--listings");
bookmarkListings.innerHTML = "(no bookmarks)";
return;
}
let sectionData = Object.values(this.sections);
let containerSettings = this.containerSettings;
let bookmarkListings = document.getElementById(this.id + "-bookmark-menu--listings");
bookmarkListings.innerHTML = "";
// [re]render the section listings
for (let i = 0; i < sectionData.length; i++) {
bookmarkListings.insertAdjacentHTML(
"beforeend",
`
${i == 0 ? "" : `<br />`}
<div id="${this.id}-section-listing--${i}" class="sectionListingContainer">
<span class=${this.id + "-section"}>
${sectionData[i].label == "" ? "uncategorized links: " : "<u>section</u>: " + sectionData[i].label}
</span>
<div class="bookmarkListingButtons">
${(i > 1) ? `
<span onClick="reorderSection(this, 'up')" class="bookmarkButton">
[up]
</span>
` : ``
}
${(i > 0) && (i < sectionData.length - 1) ? `
<span onClick="reorderSection(this, 'down')" class="bookmarkButton">
[down]
</span>
` : ``
}
<span onClick="deleteSection(this)" class="bookmarkDeleteButton">[delete section]</span>
</div>
</div>
`
);
}
// [re]render the link listings
for (let s = sectionData.length - 1; s >= 0; s--) {
for (let l = sectionData[s].links.length - 1; l >= 0; l--) {
let targetSection = document.getElementById(
this.id + "-section-listing--" + s
);
targetSection.insertAdjacentHTML(
"afterend",
`
<div id="${this.id}-section-listing--${s}--${l}" class="linkListingContainer">
<span class="${this.id}-link">
${sectionData[s].links[l].label}
</span>
<div class="bookmarkListingButtons">
${l > 0 ? `
<span onClick="reorderLink(this, 'up')" class="bookmarkButton">
[up]
</span>
` : ``
}
${l < sectionData[s].links.length - 1 ? `
<span onClick="reorderLink(this, 'down')" class="bookmarkButton">
[down]
</span>
` : ``
}
<span onClick="deleteLink(this)" class="bookmarkDeleteButton">[delete link]</span>
</div>
</div>
`
);
}
}
}
createTextContainerMenuListing() {
document.getElementById("containers").insertAdjacentHTML(
"beforeend",
`
<div class="containerListing" id=${this.id + "-container-listing"}>
<p class="expandableMenuToggle active" onclick="toggleExpandableMenu(this)">- ${this.name}</p>
<div class="expandableMenu" id=${this.id + "-settings-menu"} style="display: block;">
<p class="expandableMenuToggle" onclick="toggleExpandableMenu(this)">+ ${this.name}: bookmarks</p>
<div class="expandableMenu" id=${this.id + "-bookmark-menu"} >
<p class="expandableMenuToggle" onclick="toggleExpandableMenu(this)">+ manage bookmarks / sections</p>
<div class="expandableMenu" >
<div id=${this.id + "-bookmark-menu--listings"} class="bookmarkListingContainer">
</div>
</div>
<p class="expandableMenuToggle" onclick="toggleExpandableMenu(this)">+ add new bookmarks / sections</p>
<div class="expandableMenu" id=${this.id + "-bookmark-menu--add-new"} >
<label> enter url: </label>
<input id=${this.id + "-url-input"} type="text" name="url" />
<label> enter label: </label>
<input id=${this.id + "-label-input"} type="text" name="label" />
<label> enter section (optional): </label>
<input id=${this.id + "-section-input"} type="text" name="section" />
<br /><br />
<button id=${this.id + "-add-link-button"} onclick="addLink(this)">add link</button>
</div>
</div>
<p class="expandableMenuToggle" onclick="toggleExpandableMenu(this)">+ ${this.name}: customize</p>
<div class="expandableMenu">
<p class="expandableMenuToggle" onclick="toggleExpandableMenu(this)">+ ${this.name}: header options</p>
<div class="expandableMenu">
<p class="menuHeader">date + time options</p>
<label for=${this.id + "-settings-date-toggle"}>show date?</label>
<input id=${this.id + "-settings-date-toggle"} type="checkbox" />
<br />
<label>show clock?</label>
<input id=${this.id + "-settings-clock-toggle"} type="checkbox" />
<br /><br />
<p class="menuHeader">divider options</p>
<label for=${this.id + "-settings-divider-toggle"}>show divider?</label>
<input id=${this.id + "-settings-divider-toggle"} type="checkbox" />
<br />
<label>set divider color: </label>
<input id=${this.id + "-settings-divider-color"} type="color" />
</div>
<p class="expandableMenuToggle" onclick="toggleExpandableMenu(this)">+ ${this.name}: text options</p>
<div class="expandableMenu">
<p class="menuHeader">header text options</p>
<label>set header text color: </label>
<input id=${this.id + "-settings-clock-color"} type="color" />
<br /><br />
<label>set header font size (px): </label>
<input id=${this.id + "-settings-clock-size"} />
<br />
<label for=${this.id + "-settings-clock-bold"}>bold header?</label>
<input id=${this.id + "-settings-clock-bold"} type="checkbox" />
<label for=${this.id + "-settings-clock-italic"}>italic header?</label>
<input id=${this.id + "-settings-clock-italic"} type="checkbox" />
<p class="menuHeader">section text options</p>
<label>set section size (px): </label>
<input id=${this.id + "-settings-section-size"} />
<label>set section color: </label>
<input id=${this.id + "-settings-section-color"} type="color" />
<label for="sectionBoldToggle">bold sections?</label>
<input id=${this.id + "-settings-section-bold"} type="checkbox" />
<label for="sectionItalicToggle">italic sections?</label>
<input id=${this.id + "-settings-section-italic"} type="checkbox" />
<p class="menuHeader">bookmark text options</p>
<label>set link size (px): </label>
<input id=${this.id + "-settings-link-size"} />
<label>set link color: </label>
<input id=${this.id + "-settings-link-color"} type="color" />
<p class="menuHeader">layer-wide options</p>
<label>change font (see instructions for more info):</label>
<input id=${this.id + "-settings-font-input"} />
<button id=${this.id + "-settings-set-font-button"}>set font</button>
<label>current font:</label>
<span id=${this.id + "-settings-font-name"}></span>
<br /><br />
<label>align text:</label>
<br />
<label for=${this.id + "-settings-left-align"}>left</label>
<input id=${this.id + "-settings-left-align"} type="radio" name="align" />
<label for=${this.id + "-settings-center-align"}>center</label>
<input id=${this.id + "-settings-center-align"} type="radio" name="align" />
</div>
<p class="expandableMenuToggle" onclick="toggleExpandableMenu(this)">+ ${this.name}: color + shape options</p>
<div class="expandableMenu">
<p class="menuHeader">background color</p>
<label>set background color: </label>
<input id=${this.id + "-settings-bg-color"} type="color" />
<label>set background opacity (%): </label>
<input id=${this.id + "-settings-bg-alpha"} />
<p class="menuHeader">change border + shape</p>
<label>set border color: </label>
<input id=${this.id + "-settings-border-color"} type="color" />
<label>set border width (px): </label>
<input id=${this.id + "-settings-border-width"} placeholder="0 for none" />
<label>set container roundedness (px): </label>
<input id=${this.id + "-settings-border-radius"} placeholder="0 for square" />
<p class="menuHeader">change shadow / glow</p>
shadow settings:<br />
<label>x-offset (px): </label><br />
<input id=${this.id + "-settings-shadow-x"} placeholder="all 0 for none" /><br />
<label>y-offset (px): </label><br />
<input id=${this.id + "-settings-shadow-y"} placeholder="all 0 for none" /><br />
<label>blur radius / fuzziness (px): </label><br />
<input id=${this.id + "-settings-shadow-blur"} placeholder="all 0 for none" /><br />
<label>shadow color: </label><br />
<input id=${this.id + "-settings-shadow-color"} type="color" /><br />
<label>shadow opacity:</label>
<input id=${this.id + "-settings-shadow-alpha"} />
</div>
</div>
<button id=${this.id + "--delete-button"} onclick="deleteContainer(this)">delete</button>
</div>
`
);
}
createImageContainerMenuListing() {
document.getElementById("containers").insertAdjacentHTML(
"beforeend",
`
<div class="containerListing" id=${this.id + "-container-listing"}>
<p class="expandableMenuToggle active" onclick="toggleExpandableMenu(this)">- ${this.name}</p>
<div class="expandableMenu" id=${this.id + "-settings-menu"} style="display: block;">
<p class="expandableMenuToggle" onclick="toggleExpandableMenu(this)">+ ${this.name}: color + shape options</p>
<div class="expandableMenu">
<p class="menuHeader">change border + shape</p>
<label>set border color: </label>
<input id=${this.id + "-settings-border-color"} type="color" />
<label>set border width (px): </label>
<input id=${this.id + "-settings-border-width"} placeholder="0 for none" />
<label>set container roundedness (px): </label>
<input id=${this.id + "-settings-border-radius"} placeholder="0 for square" />
<p class="menuHeader">change shadow / glow</p>
<label>x-offset (px): </label><br />
<input id=${this.id + "-settings-shadow-x"} placeholder="all 0 for none" /><br />
<label>y-offset (px): </label><br />
<input id=${this.id + "-settings-shadow-y"} placeholder="all 0 for none" /><br />
<label>blur radius / fuzziness (px): </label><br />
<input id=${this.id + "-settings-shadow-blur"} placeholder="all 0 for none" /><br />
<label>shadow color: </label><br />
<input id=${this.id + "-settings-shadow-color"} type="color" /><br />
<label>shadow opacity:</label>
<input id=${this.id + "-settings-shadow-alpha"} />
</div>
<button id=${this.id + "--delete-button"} onclick="deleteContainer(this)">delete</button>
</div>
</div>
`
);
}
/*
* applies saved cosmetic customizations to container
*/
applySettings() {
/** set options relevant to both image and text containers */
/** POSITION */
document.getElementById(this.id).style.top = this.y;
document.getElementById(this.id).style.left = this.x;
/** SIZE */
let containerSettings = this.containerSettings;
document.getElementById(this.id).style.width = this.width + "px";
// this.width - 2 * parseInt(containerSettings.borderWidth);
document.getElementById(this.id).style.height = this.height + "px";
// this.height - 2 * parseInt(containerSettings.borderWidth);
/** SHADOW / GLOW */
document.getElementById(this.id).style.boxShadow =
containerSettings.shadowX +
"px " +
containerSettings.shadowY +
"px " +
containerSettings.shadowBlur +
"px " +
containerSettings.shadowRgba;
// fill in settings ui
document.getElementById(this.id + "-settings-shadow-x").value =
containerSettings.shadowX;
document.getElementById(this.id + "-settings-shadow-y").value =
containerSettings.shadowY;
document.getElementById(this.id + "-settings-shadow-blur").value =
containerSettings.shadowBlur;
document.getElementById(this.id + "-settings-shadow-color").value =
rgbToHex(
containerSettings.shadowRgba
.replace("rgba(", "")
.slice(0, -1)
.split(",")
.splice(0, 3)
);
document.getElementById(this.id + "-settings-shadow-alpha").value =
containerSettings.shadowRgba
.replace("rgba(", "")
.slice(0, -1)
.split(",")[3] * 100;
/** BORDER */
document.getElementById(this.id).style.borderStyle = "solid";
document.getElementById(this.id).style.borderWidth =
containerSettings.borderWidth + "px";
document.getElementById(this.id).style.borderRadius =
containerSettings.borderRadius + "px";
document.getElementById(this.id).style.borderColor =
containerSettings.borderColor;
// fill-in settings ui
document.getElementById(this.id + "-settings-border-color").value =
containerSettings.borderColor;
document.getElementById(this.id + "-settings-border-width").value =
containerSettings.borderWidth;
document.getElementById(this.id + "-settings-border-radius").value =
containerSettings.borderRadius;
/** if this is an image container, all relevant settings have been applied */
if (this.imageUrl != undefined) {
return;
}
/** FONT */
if (containerSettings.fontName == "") {
document.getElementById(this.id + "-settings-font-name").innerHTML =
"system default";
} else {
document.getElementById(this.id + "-settings-font-name").innerHTML =
containerSettings.fontName;
}
if (containerSettings.fontCode != "") {
document.head.insertAdjacentHTML(
"beforeend",
containerSettings.fontCode
);
// fill in settings ui
document.getElementById(this.id).style.fontFamily =
containerSettings.fontName;
}
/** TEXT ALIGNMENT */
if (containerSettings.centerAlign) {
document.getElementById(this.id).style.textAlign = "center";
} else {
document.getElementById(this.id).style.textAlign = "left";
}
// fill-in settings ui
document.getElementById(this.id + "-settings-left-align").checked =
!containerSettings.centerAlign;
document.getElementById(this.id + "-settings-center-align").checked =
containerSettings.centerAlign;
/** BOOKMARK CUSTOMIZATION */
// apply link customization
let linkElements = document.getElementsByClassName(this.id + "-link");
for (let i = 0; i < linkElements.length; i++) {
linkElements[i].style.color = containerSettings.linkColor;
linkElements[i].style.fontSize = containerSettings.linkSize + "px";
}
// apply section customization
let sectionElements = document.getElementsByClassName(this.id + "-section");
for (let i = 0; i < sectionElements.length; i++) {
sectionElements[i].style.color = containerSettings.sectionColor;
sectionElements[i].style.fontSize = containerSettings.sectionSize + "px";
sectionElements[i].style.fontWeight = containerSettings.sectionBold ? "bold" : "normal";
sectionElements[i].style.fontStyle = containerSettings.sectionItalic ? "italic" : "normal";
}
// fill-in settings ui
document.getElementById(this.id + "-settings-section-color").value =
containerSettings.sectionColor;
document.getElementById(this.id + "-settings-section-size").value =
containerSettings.sectionSize;
document.getElementById(this.id + "-settings-section-bold").checked =
containerSettings.sectionBold;
document.getElementById(this.id + "-settings-section-italic").checked =
containerSettings.sectionItalic;
document.getElementById(this.id + "-settings-link-color").value =
containerSettings.linkColor;
document.getElementById(this.id + "-settings-link-size").value =
containerSettings.linkSize;
/** BACKGROUND COLOR */
document.getElementById(this.id).style.backgroundColor =
containerSettings.backgroundRgba;
// fill-in settings ui
document.getElementById(this.id + "-settings-bg-color").value =
rgbToHex(
containerSettings.backgroundRgba
.replace("rgba(", "")
.slice(0, -1)
.split(",")
.splice(0, 3)
);
document.getElementById(this.id + "-settings-bg-alpha").value =
containerSettings.backgroundRgba
.replace("rgba(", "")
.slice(0, -1)
.split(",")[3] * 100;
/** HEADER */
// show header elements
document.getElementById(this.id + "-date").style.display =
containerSettings.enableDate ? "inline" : "none";
document.getElementById(this.id + "-clock").style.display =
containerSettings.enableClock ? "inline" : "none";
// apply header customizations
document.getElementById(this.id + "-header").style.color =
containerSettings.headerColor;
document.getElementById(this.id + "-header").style.fontSize =
containerSettings.headerSize + "px";
document.getElementById(this.id + "-header").style.fontWeight =
containerSettings.headerBold ? "bold" : "normal";
document.getElementById(this.id + "-header").style.fontStyle =
containerSettings.headerItalic ? "italic" : "normal";
// fill-in settings ui
document.getElementById(this.id + "-settings-date-toggle").checked =
containerSettings.enableDate;
document.getElementById(this.id + "-settings-clock-toggle").checked =
containerSettings.enableClock;
document.getElementById(this.id + "-settings-clock-color").value =
containerSettings.headerColor;
document.getElementById(this.id + "-settings-clock-size").value =
containerSettings.headerSize;
document.getElementById(this.id + "-settings-clock-bold").checked =
containerSettings.headerBold;
document.getElementById(this.id + "-settings-clock-italic").checked =
containerSettings.headerItalic;
/** DIVIDER */
let divider = document.getElementById(this.id + "-divider");
if (!containerSettings.enableDivider) {
document.getElementById(this.id + "-divider").hidden = true;
// if there are links in this container, add room under the divider
if (Object.keys(this.sections).length > 0) {
document.getElementById(this.id + "-header").style.marginBottom =
"18px";
}
} else {
divider.style.color = containerSettings.dividerColor;
divider.style.backgroundColor = containerSettings.dividerColor;
divider.style.borderColor = containerSettings.dividerColor;
}
// fill-in settings ui
document.getElementById(this.id + "-settings-divider-toggle").checked =
containerSettings.enableDivider;
document.getElementById(this.id + "-settings-divider-color").value =
containerSettings.dividerColor;
}
/**
* applies event listeners for the container
*/
addContainerEventListeners() {
let container = document.getElementById(this.id);
// make element movable + resizable
container.addEventListener(
"mousedown",
mouseDownMovableElement,
true
);
// prevent context menu when resizing
container.addEventListener("contextmenu", (e) => {
e.preventDefault();
});
// stop resizing element if cursor leaves page
document.addEventListener("mouseleave", (mouseLeave) => {
if (resizing) {
resizing = false;
document.removeEventListener("mousemove", resizeElement);
storeElementData();
}
});
}
/**
* applies event listeners on container options inputs in settings menu
*/
addSettingsMenuEventListeners() {
// container border setting listeners
document
.getElementById(this.id + "-settings-border-color")
.addEventListener("input", changeContainerBorderColor, false);
document
.getElementById(this.id + "-settings-border-width")
.addEventListener("input", changeContainerBorderWidth, false);
document
.getElementById(this.id + "-settings-border-radius")
.addEventListener("input", changeContainerBorderRadius, false); // container color setting listeners
// container shadow setting listeners
document
.getElementById(this.id + "-settings-shadow-x")
.addEventListener("input", changeContainerShadow, false);
document
.getElementById(this.id + "-settings-shadow-y")
.addEventListener("input", changeContainerShadow, false);
document
.getElementById(this.id + "-settings-shadow-blur")
.addEventListener("input", changeContainerShadow, false);
document
.getElementById(this.id + "-settings-shadow-color")
.addEventListener("input", changeContainerShadow, false);
document
.getElementById(this.id + "-settings-shadow-alpha")
.addEventListener("input", changeContainerShadow, false);
/** if this is an image container, all relevant listeners have been applied */
if (this.imageUrl != undefined) {
return;
}
// conatiner background color setting listeners
document
.getElementById(this.id + "-settings-bg-color")
.addEventListener("input", changeContainerBackground, false);
document
.getElementById(this.id + "-settings-bg-alpha")
.addEventListener("input", changeContainerBackground, false);
// container text setting listeners
document
.getElementById(this.id + "-settings-set-font-button")
.addEventListener("click", changeFont, false);
document
.getElementById(this.id + "-settings-left-align")
.addEventListener("change", changeContainerAlignment, false);
document
.getElementById(this.id + "-settings-center-align")
.addEventListener("change", changeContainerAlignment, false);
// bookmark section setting listeners
document
.getElementById(this.id + "-settings-section-color")
.addEventListener("input", changeSectionColor, false);
document
.getElementById(this.id + "-settings-section-size")
.addEventListener("input", changeSectionSize, false);
document
.getElementById(this.id + "-settings-section-bold")
.addEventListener("change", toggleSectionBold, false);
document
.getElementById(this.id + "-settings-section-italic")
.addEventListener("change", toggleSectionItalic, false);
// bookmark setting listeners
document
.getElementById(this.id + "-settings-link-color")
.addEventListener("input", changeLinkColor, false);
document
.getElementById(this.id + "-settings-link-size")
.addEventListener("input", changeLinkSize, false);
// clock setting listeners
document
.getElementById(this.id + "-settings-date-toggle")
.addEventListener("input", toggleDate, false);
document
.getElementById(this.id + "-settings-clock-toggle")
.addEventListener("input", toggleClock, false);
document
.getElementById(this.id + "-settings-clock-color")
.addEventListener("input", changeHeaderColor, false);
document
.getElementById(this.id + "-settings-clock-size")
.addEventListener("input", changeHeaderSize, false);
document
.getElementById(this.id + "-settings-clock-bold")
.addEventListener("change", toggleHeaderBold, false);
document
.getElementById(this.id + "-settings-clock-italic")
.addEventListener("change", toggleHeaderItalic, false);
// container divider setting listeners
document
.getElementById(this.id + "-settings-divider-toggle")
.addEventListener("change", toggleDivider, false);
document
.getElementById(this.id + "-settings-divider-color")
.addEventListener("input", changeDividerColor, false);
}
/**
* [re]renders saved bookmark sections + links for container
*/
loadBookmarks() {
if (Object.keys(this.sections).length == 0) {
let containerBookmarks = document.getElementById(this.id + "-bookmarks");
containerBookmarks.innerHTML = "";
return;
}
let sectionData = Object.values(this.sections);
let containerSettings = this.containerSettings;
let containerBookmarks = document.getElementById(this.id + "-bookmarks");
containerBookmarks.innerHTML = "";
for (let i = 0; i < sectionData.length; i++) {
containerBookmarks.insertAdjacentHTML(
"beforeend",
`
${i == 0 ? "" : `<br />`}
<div id="${this.id}-section-${i}">
<span class=${this.id + "-section"}>
${sectionData[i].label}
</span>
</div>
`
);
}
// and then render the links
for (let s = 0; s < sectionData.length; s++) {
for (let l = 0; l < sectionData[s].links.length; l++) {
let targetSection = document.getElementById(
this.id + "-section-" + s
);
targetSection.insertAdjacentHTML(
"beforeend",
`
<div id="${this.id}-section-${s}-link-${l}" >
<a
class="${this.id}-link"
href="${sectionData[s].links[l].url}"
>
${sectionData[s].links[l].label}
</a>
</div>
`
);
}
}
// ensure smooth ux when rerendering
if (!containerSettings.enableDivider) {
document.getElementById(this.id + "-header").style.marginBottom = "18px";
}
}
}
/*************************
* constructor / init :p *
*************************/
(() => {
window.onbeforeunload = unloadPage;
/** add keybinds */
document.body.addEventListener("keydown", (e) => {
if (e.key == "e" && !editing) {
toggleEditMode();
}
if (e.key == "Escape" && editing) {
toggleEditMode();
}
});
/** set up cursors */
cursors = JSON.parse(localStorage.getItem("cursors")) || {};
if (cursors.default != undefined) {
// apply new default cursor to entire html document
document.getElementsByTagName("html")[0].style.cursor =
'url("' + cursors.default + '"), auto';
}
/** set wallpaper */
wallpaper = JSON.parse(localStorage.getItem("wallpaper")) || "";
if (wallpaper.startsWith("#")) {
document.body.style.background = wallpaper;
document.getElementById("wallpaperColorPicker").value = wallpaper;
} else {
document.body.style.backgroundImage = "url('" + wallpaper + "')";
}
repeatWallpaper =
JSON.parse(localStorage.getItem("repeatWallpaper")) || false;
if (repeatWallpaper) {
document.body.style.backgroundSize = "contain";
document.getElementById("wallpaperRepeatToggle").checked = true;
} else {
document.body.style.backgroundSize = "cover";
}
/* init audio + auto-play */
audioLink = JSON.parse(localStorage.getItem("audioLink")) || "";
document.getElementById("audioSource").src = audioLink;
autoplayAudio =
JSON.parse(localStorage.getItem("autoplayAudio")) || false;
document.getElementById("autoplayAudioToggle").checked = autoplayAudio;
if (audioLink != "" && autoplayAudio) {
document
.getElementById("audio")
.play()
.catch((error) => {
alert(
"auto-playing audio won't work unless you whitelist this page in your browser!"
);
});
}
/** (FROM ADDSETTINGSEVENTLISTENERS) local state setting input listeners */
document
.getElementById("imageRatioToggle")
.addEventListener("change", toggleImageRatio, false);
document
.getElementById("imageRemovalToggle")
.addEventListener("change", toggleImageRemoval, false);
document
.getElementById("autoplayAudioToggle")
.addEventListener("change", toggleAutoplayAudio, false);
// page wallpaper
document
.getElementById("wallpaperColorPicker")
.addEventListener("input", changeWallpaper, false);
document
/** load container data */
let occupiedIndexMapValues =
JSON.parse(localStorage.getItem("occupiedIndexMapValues")) || [];
for (let i = 0; i < occupiedIndexMapValues.length; i++) {
occupiedIndices.set(occupiedIndexMapValues[i], true);
}
let containerMapValues =
JSON.parse(localStorage.getItem("containerMapValues")) || [];
for (let i = 0; i < containerMapValues.length; i++) {
// containerDataMap.set(containerMapValues[i].id, containerMapValues[i]);
if (containerMapValues[i].id == "settingsContainer") {
continue;
}
containerDataMap.set(containerMapValues[i].id, new Container(
containerMapValues[i].id,
containerMapValues[i].name,
containerMapValues[i].x,
containerMapValues[i].y,
containerMapValues[i].height,
containerMapValues[i].width,
containerMapValues[i].zindex,
containerMapValues[i].imageUrl,
containerMapValues[i].containerSettings,
containerMapValues[i].sections
));
}
/** set up + load settings menu data */
settingsMenuData = JSON.parse(
localStorage.getItem("settingsMenuData")
) || {
x: "",
y: "",
width: "",
height: "",
};
setupContainerSettingsMenu();
// load last active settings tab user was on
activeTabId =
JSON.parse(localStorage.getItem("activeTabId")) || "instructionsTab";
document.getElementById(
activeTabId.replace("Tab", "Form")
).style.display = "flex";
document.getElementById(activeTabId).classList.add("active");
// add tab-switching listeners
let tabs = document.getElementsByClassName("tab");
for (let i = 0; i < tabs.length; i++) {
tabs[i].addEventListener("click", changeActiveTab, false);
}
})();
/**************************
* INITIALIZATION HELPERS *
**************************/
function setupContainerSettingsMenu() {
let settingsContainer = document.getElementById("settingsContainer");
if (numberOfImageContainers == 0 && numberOfTextContainers == 0) {
document.getElementById("containers").insertAdjacentHTML(
"beforeend",
`<p>(you have no layers right now)</p>`
);
}
/* set at the last saved location */
settingsContainer.style.top = settingsMenuData.y;
settingsContainer.style.left = settingsMenuData.x;
settingsContainer.style.width = settingsMenuData.width + "px";
settingsContainer.style.height = settingsMenuData.height + "px";
/* add event listeners */
// moving / resizing
settingsContainer.addEventListener(
"mousedown",
mouseDownMovableElement,
true
);
// prevent context menu when resizing
settingsContainer.addEventListener("contextmenu", (e) => {
e.preventDefault();
});
// stop resizing element if cursor leaves page
document.addEventListener("mouseleave", (mouseLeave) => {
if (resizing) {
resizing = false;
document.removeEventListener("mousemove", resizeElement);
storeElementData();
}
});
}
/** inserts saved images in their saved order (postioning done upon return)
function initializeSavedImages() {
let numImages = 1;
// render each image in the map!
imageMap.forEach((value, test, map) => {
let data = containerDataMap.get(value.id);
document.body.insertAdjacentHTML(
"beforeend",
`
<img
class="movable userImage"
id="${value.id}"
onmousedown="bringImageToTop(this)"
style="z-index: ${numImages}; width: ${data.width}; height: ${data.height};"
src="${value.url}"
draggable=false
/>
`
);
numImages++;
});
}*/
/************************
* LOCAL STATE MANAGERS *
************************/
/** primary event handler for toggling page editing */
function toggleEditMode() {
// enable editing mode (reveal hidden elements)
if (editing == false) {
editing = true;
toggleEditingElements(true);
}
// disable editing mode + save element data
else {
editing = false;
toggleEditingElements(false);
document.getElementById("editToggle").innerHTML = "saving";
// if data was just imported directly to localStorage, don't save current element states (would overwrite import)
if (justImported) {
justImported = false;
return;
}
// save state of settings menu
localStorage.setItem(
"settingsMenuData",
JSON.stringify(settingsMenuData)
);
// save states of all containers
let valueArray = [];
containerDataMap.values().forEach((value) => {
valueArray.push(value);
});
localStorage.setItem("containerMapValues", JSON.stringify(valueArray));
// save the states of occupied ID indice
valueArray = [];
occupiedIndices.keys().forEach((value) => {
valueArray.push(value);
});
localStorage.setItem("occupiedIndexMapValues", JSON.stringify(valueArray));
// save wallpaper info
localStorage.setItem("wallpaper", JSON.stringify(wallpaper));
localStorage.setItem(
"repeatWallpaper",
JSON.stringify(repeatWallpaper)
);
// save doodad + gadget cookies
localStorage.setItem("audioLink", JSON.stringify(audioLink));
localStorage.setItem("autoplayAudio", JSON.stringify(autoplayAudio));
localStorage.setItem("cursors", JSON.stringify(cursors));
// save active settings tab for next session
localStorage.setItem("activeTabId", JSON.stringify(activeTabId));
document.getElementById("editToggle").innerHTML = "edit page";
}
}
function toggleExpandableMenu(toggleButton) {
toggleButton.classList.toggle("active");
let toggleButtonLabel = toggleButton.innerText;
let menuContent = toggleButton.nextElementSibling;
if (menuContent.style.display === "block") {
menuContent.style.display = "none";
toggleButton.innerHTML = toggleButtonLabel.replace("-", "+");
} else {
menuContent.style.display = "block";
toggleButton.innerHTML = toggleButtonLabel.replace("+", "-");
}
}
function toggleImageRatio() {
keepImageRatio = !document.getElementById("imageRatioToggle").checked;
}
function toggleImageRemoval() {
removing = document.getElementById("imageRemovalToggle").checked;
let images = document.getElementsByClassName("userImage");
if (removing) {
for (let i = 0; i < images.length; i++) {
// change cursor on images to crosshair
images[i].classList.remove("grabbable");
images[i].classList.add("removable");
images[i].addEventListener(
"mousedown",
mouseDownRemovableElement,
false
);
document
.getElementById("imageRemovalWarning")
.classList.remove("hidden");
}
} else {
for (let i = 0; i < images.length; i++) {
// remove crosshair cursor from images
images[i].classList.remove("removable");
images[i].classList.add("grabbable");
images[i].removeEventListener(
"mousedown",
mouseDownRemovableElement,
false
);
document
.getElementById("imageRemovalWarning")
.classList.add("hidden");
}
}
}
/** handles the revealing/concealing of elements part of toggling edit mode */
function toggleEditingElements(show) {
const editButton = document.getElementById("editToggle");
const settingsContainer = document.getElementById("settingsContainer");
/** when editing-mode is enabled, */
if (show) {
editButton.innerHTML = "save + stop editing";
// reveal settings container
settingsContainer.classList.remove("hidden");
// change cursor on movable elements to indicate grabbable
let movableElements = document.getElementsByClassName("movable");
for (let i = 0; i < movableElements.length; i++) {
movableElements[i].classList.add("grabbable");
}
} else {
// remove grabbable cursor
let movableElements = document.getElementsByClassName("movable");
for (let i = 0; i < movableElements.length; i++) {
movableElements[i].classList.remove("grabbable");
}
// disable image removal mode active
if (removing) {
removing = false;
document.getElementById("imageRemovalToggle").checked = false;
let images = document.getElementsByClassName("userImage");
for (let i = 0; i < images.length; i++) {
images[i].classList.remove("removable");
}
document
.getElementById("imageRemovalWarning")
.classList.add("hidden");
}
// hide the settings container
settingsContainer.classList.add("hidden");
}
}
function toggleWallpaperRepeat(checkbox) {
repeatWallpaper = this.checked;
if (repeatWallpaper) {
document.body.style.backgroundSize = "contain";
} else {
document.body.style.backgroundSize = "cover";
}
}
function unloadPage() {
if (editing) {
return "you are currently editing and changes are unsaved. stay?";
}
}
function changeActiveTab(event) {
// remove 'active' state from current tab
document.getElementById(activeTabId).classList.remove("active");
document.getElementById(
activeTabId.replace("Tab", "Form")
).style.display = "none";
// set new tab
activeTabId = event.target.id;
document.getElementById(
activeTabId.replace("Tab", "Form")
).style.display = "flex";
document.getElementById(activeTabId).classList.add("active");
}
/********************************************
* DOCUMENT MANAGEMENT / MODIFICATION STUFF *
********************************************/
function createNewTextContainer() {
let containerName = document.getElementById(
"newTextContainerNameInput"
).value;
document.getElementById("newTextContainerNameInput").value = "";
let container = new Container(
undefined,
containerName,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined
);
}
function createNewImageContainer() {
let containerName = document.getElementById(
"newImageContainerNameInput"
).value;
let imageUrl = document.getElementById(
"newImageContainerUrlInput"
).value;
document.getElementById("newImageContainerNameInput").value = "";
document.getElementById("newImageContainerUrlInput").value = "";
let container = new Container(
undefined,
containerName,
undefined,
undefined,
undefined,
undefined,
undefined,
imageUrl,
undefined,
undefined
);
}
function deleteContainer(buttonPressed) {
let containerId = buttonPressed.id.split("--")[0];
let container = containerDataMap.get(containerId);
if (container.imageUrl == undefined) {
numberOfTextContainers--;
clearInterval(container.clockIntervalId);
}
else {
numberOfImageContainers--;
}
containerDataMap.delete(containerId);
occupiedIndices.delete(containerId);
document.getElementById(containerId).remove();
document.getElementById(containerId + "-container-listing").remove();
}
/**
* uses map to determine the lowest available ID index for given type, to
* prevent accidentally using the same ID number for unnamed containers twice
*/
function findLowestAvailableId(containerType) {
let id = "";
for(let index = 0; index < 200; index ++) {
id = containerType + "-" + index;
if (!occupiedIndices.has(id)) {
occupiedIndices.set(id, true);
console.log(id);
return id;
}
}
return -1;
}
/** upon click release on movable elements, store position data for when saving to localStorage when disabling edit mode */
function storeElementData() {
let borderWidth = changingElement.style.borderWidth.slice(0, -2) * 2; // slice to remove "px"
let element = document.getElementById(changingElement.id);
if (changingElement.id == "settingsContainer") {
settingsMenuData.x = element.style.left;
settingsMenuData.y = element.style.top;
settingsMenuData.width = element.offsetWidth;
settingsMenuData.height = element.offsetHeight;
}
else {
let container = containerDataMap.get(element.id);
container.x = element.style.left;
container.y = element.style.top;
container.width = element.offsetWidth - borderWidth;
container.height = element.offsetHeight - borderWidth;
}
}
function setAudioLink() {
let link = document.getElementById("audioLinkInput").value;
if (link == "") {
return;
}
audioLink = link;
}
function toggleAutoplayAudio(checkbox) {
autoplayAudio = this.checked;
}
function changeWallpaper(event) {
// if image set
if (event == undefined) {
// get background url
let url = document.getElementById("wallpaperUrl").value;
if (url == "") {
alert("must add image url to add background");
return;
}
document.body.style.backgroundImage = "url('" + url + "')";
document.getElementById("wallpaperUrl").value = "";
wallpaper = url;
}
// if solid color set
else {
document.body.style.background = event.target.value;
wallpaper = event.target.value;
}
}
function moveElement(mouseMove) {
if (!moving) {
return;
}
// calculate offset from last mouse position to current position (1 frame at a time)
let mouseOffsetX = lastMouseX - mouseMove.clientX;
let mouseOffsetY = lastMouseY - mouseMove.clientY;
// save coords of mousepos for use above ^ next frame
lastMouseX = mouseMove.clientX;
lastMouseY = mouseMove.clientY;
// move to element same distance as mouse offset
changingElement.style.top =
changingElement.offsetTop - mouseOffsetY + "px";
changingElement.style.left =
changingElement.offsetLeft - mouseOffsetX + "px";
}
function resizeElement(mouseMove) {
if (!resizing) {
return;
}
// calculate offset from last mouse position to current position (1 frame at a time)
let mouseOffsetX = lastMouseX - mouseMove.clientX;
let mouseOffsetY = lastMouseY - mouseMove.clientY;
// save coords of mousepos for use above ^ next frame
lastMouseX = mouseMove.clientX;
lastMouseY = mouseMove.clientY;
let borderWidth =
window.getComputedStyle(changingElement).borderWidth.slice(0, -2) * 2 +
window.getComputedStyle(changingElement).padding.slice(0, -2) * 2; // remove "px"
let currentWidth = changingElement.offsetWidth - borderWidth;
let currentHeight = changingElement.offsetHeight - borderWidth;
// if proportional image resizing enabled
if (changingElement.id.startsWith("img-") && keepImageRatio) {
// growing
if (mouseOffsetY > 0) {
let newWidth = currentWidth * (1 + mouseOffsetY / 100);
let newHeight = currentHeight * (1 + mouseOffsetY / 100);
// determine offset in px (to keep image center while resizing)
let offsetX = (currentWidth - newWidth) / 2.0;
let offsetY = (currentHeight - newHeight) / 2.0;
changingElement.style.width = newWidth + "px";
changingElement.style.height = newHeight + "px";
changingElement.style.left =
changingElement.offsetLeft + offsetX + "px";
changingElement.style.top =
changingElement.offsetTop + offsetY + "px";
}
// shrinking
else if (mouseOffsetY < 0) {
let newWidth = currentWidth * (1 + mouseOffsetY / 100);
let newHeight = currentHeight * (1 + mouseOffsetY / 100);
// determine offset in px (to keep image center while resizing)
let offsetX = (currentWidth - newWidth) / 2.0;
let offsetY = (currentHeight - newHeight) / 2.0;
changingElement.style.width = newWidth + "px";
changingElement.style.height = newHeight + "px";
changingElement.style.left =
changingElement.offsetLeft + offsetX + "px";
changingElement.style.top =
changingElement.offsetTop + offsetY + "px";
}
}
// if resizing container / image freely
else {
let newHeight = currentHeight - mouseOffsetY;
let newWidth = currentWidth - mouseOffsetX;
changingElement.style.height = newHeight + "px";
changingElement.style.width = newWidth + "px";
}
}
function mouseDownMovableElement(mouseDown) {
/** MOVEMENT / RESIZING HANDLERS */
if (!editing || removing) {
return;
}
changingElement = document.getElementById(mouseDown.currentTarget.id);
// save mouse position prior to drag event
lastMouseX = mouseDown.clientX;
lastMouseY = mouseDown.clientY;
// left click drag initiates movement, release on mouse up
if (mouseDown.button == 0) {
moving = true;
document.addEventListener("mousemove", moveElement);
document.addEventListener("mouseup", (mouseUp) => {
if (moving && mouseUp.button == 0) {
moving = false;
document.removeEventListener("mousemove", moveElement);
storeElementData();
}
});
}
// right click drag initiates movement, release on mouse up
if (mouseDown.button == 2) {
resizing = true;
document.addEventListener("mousemove", resizeElement);
document.addEventListener("mouseup", (mouseUp) => {
if (resizing && mouseUp.button == 2) {
resizing = false;
document.removeEventListener("mousemove", resizeElement);
storeElementData();
}
});
}
}
function setDefaultCursor() {
cursors.default = document.getElementById("pointerCursorInput").value;
// apply new default cursor to entire html document
document.getElementsByTagName("html")[0].style.cursor =
'url("' + cursors.default + '"), auto';
}
function setGrabCursor() {
cursors.grab = document.getElementById("grabCursorInput").value;
// apply new grab cursor to grabbable elements...
}
/**********************
* BOOK MARK HANDLERS *
**********************/
function addLink(containerElement) {
let containerId = containerElement.id.split("-add-link-")[0];
let container = containerDataMap.get(containerId);
// collect data from inputs
let url = document.getElementById(containerId + "-url-input").value;
let label = document.getElementById(containerId + "-label-input").value;
let section = document.getElementById(containerId + "-section-input").value;
// sanitize input
if (url.length > 0 && !url.startsWith("http")) {
url = "https://" + url; // append HTTPS to link if needed
}
if (label == "" || url == "https://") {
alert("you must specify url and label to add a bookmark");
return;
}
// store new link in container
let sectionData = Object.values(container.sections);
let sectionLabels = [];
for (let i = 0; i < sectionData.length; i++) {
sectionLabels.push(sectionData[i].label);
}
let sectionIndex = sectionLabels.indexOf(section);
// add link to existing section
if (sectionIndex != -1) {
let tempSectionLinks = Object.values(container.sections)[sectionIndex].links;
tempSectionLinks.push({
label: label,
url: url
});
}
// or create new section + add link
else {
let newSectionLinks = [];
newSectionLinks.push({
label: label,
url: url
});
// ensure that uncategorized links stay at top of container
if (section == "" && sectionData.length > 0) {
let reorderedSections = {};
reorderedSections[0] = {};
reorderedSections[0].links = newSectionLinks;
reorderedSections[0].label = section;
// move existing sections down
for (let i = 0; i < sectionData.length; i++) {
reorderedSections[i+1] = sectionData[i];
}
container.sections = reorderedSections;
}
else {
container.sections[sectionData.length] = {};
container.sections[sectionData.length].links = newSectionLinks;
container.sections[sectionData.length].label = section;
}
}
// reset inputs + render
document.getElementById(containerId + "-url-input").value = "";
document.getElementById(containerId + "-label-input").value = "";
container.loadBookmarks();
container.loadBookmarkListings();
}
function reorderSection(buttonPressed, direction) {
let temp = buttonPressed.parentElement.parentElement.id.split("--");
let container = containerDataMap.get(temp[0].split("-section-listing")[0]);
const sectionIndex = parseInt(temp[temp.length - 1]);
let copy = container.sections[sectionIndex];
if (direction == "up" && sectionIndex != 0) {
container.sections[sectionIndex] = container.sections[sectionIndex - 1];
container.sections[sectionIndex - 1] = copy;
} else if (direction == "down" && (sectionIndex + 1) != Object.keys(container.sections).length) {
container.sections[sectionIndex] = container.sections[sectionIndex + 1];
container.sections[sectionIndex + 1] = copy;
}
container.loadBookmarks();
container.loadBookmarkListings();
}
function deleteSection(buttonPressed) {
let temp = buttonPressed.parentElement.parentElement.id.split("--");
let container = containerDataMap.get(temp[0].split("-section-listing")[0]);
const sectionIndex = parseInt(temp[temp.length - 1]);
// delete section
let numSections = Object.keys(container.sections).length;
for (let i = sectionIndex; i < numSections; i++) {
container.sections[i] = container.sections[i+1];
}
// trim last section index that is no longer needed
delete container.sections[numSections - 1];
// refresh screen
container.loadBookmarks();
container.loadBookmarkListings();
}
function deleteLink(buttonPressed) {
let temp = buttonPressed.parentElement.parentElement.id.split("--");
let container = containerDataMap.get(temp[0].split("-section-listing")[0]);
const sectionIndex = parseInt(temp[temp.length - 2]);
const linkIndex = parseInt(temp[temp.length - 1]);
// delete link
container.sections[sectionIndex].links.splice(linkIndex, 1);
// refresh screen
container.loadBookmarks();
container.loadBookmarkListings();
}
function reorderLink(buttonPressed, direction) {
let temp = buttonPressed.parentElement.parentElement.id.split("--");
let container = containerDataMap.get(temp[0].split("-section-listing")[0]);
const sectionIndex = parseInt(temp[temp.length - 2]);
const linkIndex = parseInt(temp[temp.length - 1]);
let links = container.sections[sectionIndex].links;
// cut out section and re-insert into array
let link = container.sections[sectionIndex].links[linkIndex];
container.sections[sectionIndex].links.splice(linkIndex, 1);
if (direction == "up" && linkIndex != 0) {
links.splice(linkIndex - 1, 0, link);
} else if (direction == "down" && linkIndex != links.length) {
links.splice(linkIndex + 1, 0, link);
}
// refresh screen
container.loadBookmarks();
container.loadBookmarkListings();
}
/******************
* IMAGE HANDLERS *
******************/
function addFloatingImage() {
// get image url from form
const form = document.getElementById("imageAndWallpaperForm");
const formData = new FormData(form);
let url = formData.get("url");
if (url == "") {
return;
}
// save image info to local arrays (saved for real upon stopping edit)
let keys = imageMap.keys();
let numImages = 1;
for (const key of keys) {
if (key.slice(-1) > numImages) {
numImages = parseInt(key.slice(-1));
}
}
numImages++;
imageMap.set("img-" + numImages, { id: "img-" + numImages, url: url });
// render image on page
let body = document.body;
body.insertAdjacentHTML(
"beforeend",
`
<img
class="movable userImage"
id="img-${numImages}"
onmousedown="bringImageToTop(this)"
style="z-index: ${numImages};"
src="${url}"
draggable=false />
`
);
changingElement = document.getElementById("img-" + numImages);
// make image movable + resizable
changingElement.addEventListener(
"mousedown",
mouseDownMovableElement,
true
);
// prevent right click from opening context menu when resizing
changingElement.addEventListener("contextmenu", (e) => {
e.preventDefault();
});
// save image size data (don"t use parentheses when calling storeElementData so it's callback)
changingElement.onload = storeElementData;
form.reset();
}
function bringImageToTop(imageTouched) {
// when an image gets clicked or dragged, bring it to top!
if (editing) {
// copy map values to temp list
let tempValuesList = [];
let orderHelper = [];
imageMap.forEach((value, key) => {
tempValuesList.push(value);
orderHelper.push(value.id);
});
// put clicked image at end of temp array
let imageIndex = orderHelper.indexOf(imageTouched.id);
tempValuesList.splice(imageIndex, 1);
tempValuesList.push(imageMap.get(imageTouched.id));
// set new z-indexes and repopulate map based on temp array
imageMap = new Map();
for (let i = 0; i < tempValuesList.length; i++) {
let element = document.getElementById(tempValuesList[i].id);
element.style.zIndex = i.toString();
imageMap.set(tempValuesList[i].id, tempValuesList[i]);
}
}
}
function mouseDownRemovableElement(mouseDown) {
if (!removing) {
return;
}
let imageToRemove = mouseDown.currentTarget.id;
containerDataMap.delete(imageToRemove);
imageMap.delete(imageToRemove);
document.getElementById(imageToRemove).remove();
}
/************************************
* CONTAINER CUSTOMIZATION HANDLERS *
************************************/
function changeContainerBackground(colorChange) {
let containerId = colorChange.currentTarget.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.backgroundRgba = hexToRgba(
document.getElementById(containerId + "-settings-bg-color").value,
document.getElementById(containerId + "-settings-bg-alpha").value
);
document.getElementById(containerId).style.backgroundColor =
container.containerSettings.backgroundRgba;
}
function changeLinkColor(colorChange) {
let containerId = colorChange.currentTarget.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.linkColor = colorChange.target.value;
let linkElements = document.getElementsByClassName(container.id + "-link");
for (let i = 0; i < linkElements.length; i++) {
linkElements[i].style.color = container.containerSettings.linkColor;
}
}
function changeLinkSize(sizeChange) {
let containerId = sizeChange.currentTarget.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.linkSize =
sizeChange.target.value == "" ? "0" : sizeChange.target.value;
let linkElements = document.getElementsByClassName(container.id + "-link");
for (let i = 0; i < linkElements.length; i++) {
linkElements[i].style.fontSize = container.containerSettings.linkSize + "px";
}
}
function changeSectionColor(colorChange) {
let containerId = colorChange.currentTarget.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.sectionColor = colorChange.target.value;
let sectionElements = document.getElementsByClassName(container.id + "-section");
for (let i = 0; i < sectionElements.length; i++) {
sectionElements[i].style.color = container.containerSettings.sectionColor;
}
}
function changeSectionSize(sizeChange) {
let containerId = sizeChange.currentTarget.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.sectionSize =
sizeChange.target.value == "" ? "0" : sizeChange.target.value;
let sectionElements = document.getElementsByClassName(container.id + "-section");
for (let i = 0; i < sectionElements.length; i++) {
sectionElements[i].style.fontSize = container.containerSettings.sectionSize + "px";
}
}
function toggleSectionBold(checkboxChanged) {
let containerId = checkboxChanged.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
let sectionElements = document.getElementsByClassName(containerId + "-section");
for (let i = 0; i < sectionElements.length; i++) {
sectionElements[i].style.fontWeight = checkboxChanged.target.checked ? "bold" : "normal";
}
container.containerSettings.sectionBold = checkboxChanged.target.checked;
}
function toggleSectionItalic(checkboxChanged) {
let containerId = checkboxChanged.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
let sectionElements = document.getElementsByClassName(containerId + "-section");
for (let i = 0; i < sectionElements.length; i++) {
sectionElements[i].style.fontStyle = checkboxChanged.target.checked ? "italic" : "normal";
}
container.containerSettings.sectionItalic = checkboxChanged.target.checked;
}
// BORDER
function changeContainerBorderWidth(borderChange) {
let containerId = borderChange.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.borderWidth = borderChange.target.value;
document.getElementById(containerId).style.borderWidth =
container.containerSettings.borderWidth + "px";
}
function changeContainerBorderRadius(radiusChange) {
let containerId = radiusChange.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.borderRadius = radiusChange.target.value;
document.getElementById(containerId).style.borderRadius =
container.containerSettings.borderRadius + "px";
}
function changeContainerBorderColor(colorChange) {
let containerId = colorChange.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.borderColor = colorChange.target.value;
document.getElementById(containerId).style.borderColor =
container.containerSettings.borderColor;
}
// CLOCK
function toggleDate(checkboxChanged) {
let containerId = checkboxChanged.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.enableDate = checkboxChanged.target.checked;
document.getElementById(containerId + "-date").style.display =
checkboxChanged.target.checked ? "inline" : "none";
}
function toggleClock(checkboxChanged) {
let containerId = checkboxChanged.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.enableClock = checkboxChanged.target.checked;
document.getElementById(containerId + "-clock").style.display =
checkboxChanged.target.checked ? "inline" : "none";
}
function changeHeaderColor(colorChange) {
let containerId = colorChange.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.headerColor = colorChange.target.value;
document.getElementById(containerId + "-header").style.color =
container.containerSettings.headerColor;
}
function changeHeaderSize(sizeChange) {
let containerId = sizeChange.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.headerSize =
sizeChange.target.value == "" ? "0" : sizeChange.target.value;
document.getElementById(containerId + "-header").style.fontSize =
container.containerSettings.headerSize + "px";
}
function toggleHeaderBold(checkboxChanged) {
let containerId = checkboxChanged.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.headerBold = checkboxChanged.target.checked;
document.getElementById(containerId + "-header").style.fontWeight =
checkboxChanged.target.checked ? "bold" : "normal";
}
function toggleHeaderItalic(checkboxChanged) {
let containerId = checkboxChanged.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.headerItalic = checkboxChanged.target.checked;
document.getElementById(containerId + "-header").style.fontStyle =
checkboxChanged.target.checked ? "italic" : "normal";
}
// DIVIDER
function changeDividerColor(colorChange) {
let containerId = colorChange.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.dividerColor = colorChange.target.value;
let divider = document.getElementById(containerId + "-divider");
// set multiple places for compatibility
divider.style.color = container.containerSettings.dividerColor;
divider.style.backgroundColor = container.containerSettings.dividerColor;
divider.style.borderColor = container.containerSettings.dividerColor;
}
function toggleDivider(checkboxChanged) {
let containerId = checkboxChanged.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.enableDivider = checkboxChanged.target.checked;
document.getElementById(containerId + "-divider").style.display =
checkboxChanged.target.checked ? "block" : "none";
if (checkboxChanged.target.checked && Object.keys(container.sections).length > 0) {
document.getElementById(containerId + "-clock").style.marginBottom = "18px";
} else {
document.getElementById(containerId + "-clock").style.marginBottom = "0px";
}
}
// TEXT
function changeContainerAlignment(alignmentChanged) {
let containerId = alignmentChanged.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
if (document.getElementById(containerId + "-settings-left-align").checked) {
container.containerSettings.centerAlign = false;
document.getElementById(containerId).style.textAlign = "left";
} else if (document.getElementById(containerId + "-settings-center-align").checked) {
container.containerSettings.centerAlign = true;
document.getElementById(containerId).style.textAlign = "center";
}
}
function changeFont(buttonClicked) {
let containerId = buttonClicked.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
let input = document.getElementById(containerId + "-settings-font-input").value;
if (input == "") {
return;
}
// parse input to extract name of font
fontCode = input;
fontName = fontCode.slice(fontCode.indexOf("family=") + 7);
if (fontName.indexOf("&") != -1) {
fontName = fontName.slice(0, fontName.indexOf("&") + 1);
}
fontName = fontName.slice(0, fontName.indexOf(":")).replaceAll("+", " ");
// set font on container
document.head.insertAdjacentHTML("beforeend", fontCode);
document.getElementById(containerId).style.fontFamily = fontName;
document.getElementById(containerId + "-settings-font-name").innerHTML = fontName;
container.containerSettings.fontCode = fontCode;
container.containerSettings.fontName = fontName;
}
// SHADOW
function changeContainerShadow(valueChanged) {
let containerId = valueChanged.target.id.split("-settings")[0];
let container = containerDataMap.get(containerId);
container.containerSettings.shadowX =
document.getElementById(containerId + "-settings-shadow-x").value == ""
? 0
: document.getElementById(containerId + "-settings-shadow-x").value;
container.containerSettings.shadowY =
document.getElementById(containerId + "-settings-shadow-y").value == ""
? 0
: document.getElementById(containerId + "-settings-shadow-y").value;
container.containerSettings.shadowBlur =
document.getElementById(containerId + "-settings-shadow-blur").value == ""
? 0
: document.getElementById(containerId + "-settings-shadow-blur").value;
container.containerSettings.shadowRgba = hexToRgba(
document.getElementById(containerId + "-settings-shadow-color").value,
document.getElementById(containerId + "-settings-shadow-alpha").value
);
let shadow =
container.containerSettings.shadowX +
"px " +
container.containerSettings.shadowY +
"px " +
container.containerSettings.shadowBlur +
"px " +
container.containerSettings.shadowRgba;
// set shadow
document.getElementById(containerId).style.boxShadow = shadow;
}
/************************************
* RESET / IMPORT & EXPORT HANDLERS *
************************************/
function wipeBookmarks() {
// clear bookmark arrays
links = [];
sections = [];
localStorage.setItem("links", JSON.stringify(links));
localStorage.setItem("sections", JSON.stringify(sections));
if (containerSettings.enableDivider) {
document.getElementById("clock").style.marginBottom = "0px";
}
// refresh container
loadSections();
}
function wipeWallpaper() {
document.body.style.backgroundImage = "";
document.body.style.background = "";
wallpaper = "";
}
function wipeImages() {
imageMap = new Map();
// wipe images from screen
imageElements = document.getElementsByClassName("userImage");
for (let i = imageElements.length - 1; i >= 0; i--) {
imageElements[i].remove();
}
// remove image position + sizes from element data map
let tempMap = new Map();
tempMap.set(
"settingsContainer",
containerDataMap.get("settingsContainer")
);
tempMap.set("mainContainer", containerDataMap.get("mainContainer"));
containerDataMap = tempMap;
}
function wipeContainerSettings() {
containerSettings = defaultTextContainerSettings;
containerDataMap.get("mainContainer").width = "auto";
containerDataMap.get("mainContainer").height = "auto";
containerDataMap.get("mainContainer").x = "10px";
containerDataMap.get("mainContainer").y = "10px";
document.getElementById("mainContainer").style.width = "auto";
document.getElementById("mainContainer").style.height = "auto";
document.getElementById("mainContainer").style.top = "10px";
document.getElementById("mainContainer").style.left = "10px";
applySettings();
loadSections();
}
function setDefault() {
links = [
{
url: "https://www.youtube.com",
label: "youtube",
section: "video + picture",
},
{
url: "https://www.twitch.com",
label: "twitch",
section: "video + picture",
},
{
url: "https://www.instagram.com",
label: "insta",
section: "video + picture",
},
{
url: "https://www.archive.org",
label: "archive",
section: "video + picture",
},
{
url: "https://www.soundcloud.com",
label: "soundcloud",
section: "music",
},
{
url: "https://www.bandcamp.com",
label: "bandcamp",
section: "music",
},
{
url: "https://www.splice.com",
label: "splice",
section: "music",
},
{
url: "https://www.usevia.app",
label: "via",
section: "utils",
},
{
url: "https://www.distrokid.com/home",
label: "distrokid",
section: "utils",
},
];
sections = ["video + picture", "music", "utils"];
localStorage.setItem("links", JSON.stringify(links));
localStorage.setItem("sections", JSON.stringify(sections));
loadSections();
}
function resetFont() {
let oldTags = document.getElementsByTagName("link");
for (let i = 0; i < oldTags.length; i++) {
oldTags[0].remove();
}
document.body.style.fontFamily = "";
fontCode = "";
fontName = "";
}
function resetCursors() {
cursors = {};
document.getElementsByTagName("html")[0].style.cursor = "auto";
}
async function exportData() {
try {
window.focus();
await navigator.clipboard.writeText(JSON.stringify(localStorage));
} catch (error) {
console.error(error.message);
}
alert(
"export to clipboard successful. paste contents to file and keep safe!"
);
}
function importData(buttonClick) {
let data = document.getElementById("importDataInput").value;
if (data == "") {
return;
}
try {
data = JSON.parse(document.getElementById("importDataInput").value);
} catch (error) {
alert("please only enter data that was provided by the export button!");
}
for (const [key, value] of Object.entries(data)) {
localStorage.setItem(key, value);
}
alert(
"data import successful. " +
"please refresh the page!" +
"\n(you can ignore unsaved changes warning, the imported data has already been saved)"
);
justImported = true;
}
/*****************
* COLOR HELPERS *
*****************/
function hexToRgba(hex, alpha) {
const sanitizedHex = hex.replace(/^#/, "");
const parsedHex = parseInt(sanitizedHex, 16);
return (
"rgba(" +
((parsedHex >> 16) & 255) +
"," +
((parsedHex >> 8) & 255) +
"," +
(parsedHex & 255) +
"," +
alpha / 100 +
")"
);
}
function rgbToHex(rgb) {
const toHex = (c) => {
const hex = c.toString(16);
return hex.length === 1 ? "0" + hex : hex;
};
return (
"#" +
toHex(parseInt(rgb[0])) +
toHex(parseInt(rgb[1])) +
toHex(parseInt(rgb[2]))
);
}
</script>
</html>