/** * The globally-accessible Bumble Object * * Here I can store any and all utilities without taking up space in the * gloabl namespace. */ var Bumble = {}; /** * The info panel * * The info panel is a utility that allows me to add information to a page * in such a way that it can easily be closed when the user is done reading * it. * * This aims to improve the SEO of pages by adding as much descriptive * text as I want, without harming the usability of the page. This takes into * consideration that most of my pages are, currently, JS games on canvasses. * * @param {HTMLElement} panel - An HTML element with the .infopanel class */ Bumble.InfoPanel = function(panel){ this.panel = panel; this.head = undefined; this.body = undefined; this.title = undefined this.closer = undefined; this.icons = { close: "uil-times-circle", open: "uil-info-circle" } this.isClosed = false; /** * Initialzize the info panel */ this.init = function(){ if(!this.panel){ throw new Error("Panel not found."); } this.head = this.panel.querySelector('.infopanel-head'); this.body = this.panel.querySelector('.infopanel-body'); this.title = this.head.querySelector('.infopanel-title'); this.closer = this.head.querySelector('.infopanel-closer'); this.isClosed = this.panel.classList.contains('closed'); this.closer.addEventListener("click", ()=>{ this.toggleVisibility(); }); this.toggleCloseButtonIcon(); } /** * Close the info panel */ this.close = function() { this.panel.classList.add("closed"); this.isClosed = true; } /** * Open the info panel */ this.open = function() { this.panel.classList.remove("closed") this.isClosed = false; } /** * Toggle the info panel's visibility */ this.toggleVisibility = function() { if(this.isClosed) { this.open(); } else { this.close(); } this.toggleCloseButtonIcon(); } /** * Toggle the icon of the info panel's open/close button */ this.toggleCloseButtonIcon = function() { if(this.isClosed) { this.closer.classList.remove(this.icons.close); this.closer.classList.add(this.icons.open); } else { this.closer.classList.remove(this.icons.open); this.closer.classList.add(this.icons.close); } } this.init(); } /** * A classic modal * * @param {string} DOMID - An option string of the DOM ID for premade modal */ Bumble.Modal = function(DOMID) { this.overlay = undefined; this.modal = { base: undefined, header: undefined, title: undefined, closer: undefined, body: undefined }; this.isShown = false; this.init = function(DOMID) { // Build the overlay this.overlay = document.createElement("div"); this.overlay.classList.add("modal-overlay", "hidden"); // Initialize a predefined modal if(DOMID) { const elem = document.getElementById(DOMID); if(!elem){ console.warn("No element found with ID: ", DOMID); console.warn("Creating modal"); } else { this.modal.pane = elem; this.modal.header = elem.querySelector(".modal-header"); this.modal.title = elem.querySelector(".modal-title"); this.modal.closer = elem.querySelector(".uil-times-circle"); this.modal.body = elem.querySelector(".modal-body"); } } // Build the modal from scratch if(!this.modal.pane) { this.modal.pane = document.createElement("div"); this.modal.pane.classList.add("modal"); this.modal.header = document.createElement("div"); this.modal.header.classList.add("modal-header"); this.modal.title = document.createElement("div"); this.modal.title.classList.add("modal-title"); const closerContainer = document.createElement("div"); closerContainer.classList.add("modal-closer", "text-right"); this.modal.closer = document.createElement("i"); this.modal.closer.classList.add("uil", "uil-times-circle"); closerContainer.appendChild(this.modal.closer); this.modal.header.append(this.modal.title, closerContainer); this.modal.body = document.createElement("div"); this.modal.body.classList.add("modal-body"); this.modal.pane.append(this.modal.header, this.modal.body); } // Add event listener to closer button this.modal.closer.addEventListener("click", this.hide.bind(this)); // Add modal pane to the overlay this.overlay.appendChild(this.modal.pane); // Add overlay to body document.body.appendChild(this.overlay); return; } this.SetTitle = function(title) { this.modal.title.innerText = title; } this.SetBody = function(bodyHTML) { this.modal.body.innerHTML = bodyHTML; } this.show = function() { this.isShown = true; this._toggleVisibility(); } this.hide = function() { this.isShown = false; this._toggleVisibility(); } this._toggleVisibility = function() { if(this.isShown) { this.overlay.classList.remove("hidden"); } else { this.overlay.classList.add("hidden"); } } this.init(DOMID); } /** * Store GET Parameters * * A convenient place to store and access GET parameters sent to the page. */ Bumble._GET = { /** * Loads the GET parameters into this object */ __init: function() { const params = (new URL(document.location)).searchParams.entries(); for(const entry of params) { if(entry[0] == "__init"){ continue; } this[entry[0]] = !isNaN(entry[1]) ? Number(entry[1]) : entry[1]; } } } Bumble.XFN = { me: "uil-cell", friend: "uil-user-check", colleague: "uil-books", _buildIcon: function(iconClass) { const icon = document.createElement("i"); icon.classList.add("uil", iconClass); return icon; }, init: function() { const links = document.querySelectorAll("a[rel]"); for(const link of links) { // Ignore links that only contain images if (link.childNodes.length == 1 && link.lastChild instanceof HTMLImageElement) { continue; } const rels = link.rel.split(' '); let rel = null; if(rels.indexOf("me") >= 0) { rel = this.me; } else if(rels.indexOf("friend") >= 0) { rel = this.friend; } else if(rels.indexOf("colleague") >= 0) { rel = this.colleague; } if(rel) { link.insertAdjacentElement("afterbegin", this._buildIcon(rel)); } } }, } /** * Quotes that either have a lot of meaning to me, or I just really enjoy */ Bumble.FooterQuotes = { all_quotes: [], PlaceQuote: function() { const div = document.getElementById("footer-quote"); let quote = { text: "Create a quotes.js file at static/quotes.js to add your quote to the footer.", source: "" }; if (div) { if (this.all_quotes.length) { quote = this.all_quotes[Math.floor(Math.random() * this.all_quotes.length)]; } div.innerText = quote.text; console.log(`Quote source: ${quote.source}`); } }, /** * Add a quote to the list of possible quotes to display in the footer * * @param {string} quote The quote to display in the footer * @param {string} source The source of the quote that will be logged in the browser's console */ AddQuote: function(quote, source) { this.all_quotes.push({ text: quote, source: source, }); } } function page_init() { /* Initialize info panels */ const panels = document.querySelectorAll('.infopanel'); for(const panel of panels) { panel._infoPanel = new Bumble.InfoPanel(panel); } Bumble._GET.__init(); Bumble.XFN.init(); Bumble.FooterQuotes.PlaceQuote(); } if(document.readyState != 'loading') { page_init(); } else { document.addEventListener('DOMContentLoaded', page_init); }