Bumble.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /**
  2. * The globally-accessible Bumble Object
  3. *
  4. * Here I can store any and all utilities without taking up space in the
  5. * gloabl namespace.
  6. */
  7. var Bumble = {};
  8. /**
  9. * The info panel
  10. *
  11. * The info panel is a utility that allows me to add information to a page
  12. * in such a way that it can easily be closed when the user is done reading
  13. * it.
  14. *
  15. * This aims to improve the SEO of pages by adding as much descriptive
  16. * text as I want, without harming the usability of the page. This takes into
  17. * consideration that most of my pages are, currently, JS games on canvasses.
  18. *
  19. * @param {HTMLElement} panel - An HTML element with the .infopanel class
  20. */
  21. Bumble.InfoPanel = function(panel){
  22. this.panel = panel;
  23. this.head = undefined;
  24. this.body = undefined;
  25. this.title = undefined
  26. this.closer = undefined;
  27. this.icons = {
  28. close: "uil-times-circle",
  29. open: "uil-info-circle"
  30. }
  31. this.isClosed = false;
  32. /**
  33. * Initialzize the info panel
  34. */
  35. this.init = function(){
  36. if(!this.panel){ throw new Error("Panel not found."); }
  37. this.head = this.panel.querySelector('.infopanel-head');
  38. this.body = this.panel.querySelector('.infopanel-body');
  39. this.title = this.head.querySelector('.infopanel-title');
  40. this.closer = this.head.querySelector('.infopanel-closer');
  41. this.isClosed = this.panel.classList.contains('closed');
  42. this.closer.addEventListener("click", ()=>{ this.toggleVisibility(); });
  43. this.toggleCloseButtonIcon();
  44. }
  45. /**
  46. * Close the info panel
  47. */
  48. this.close = function() {
  49. this.panel.classList.add("closed");
  50. this.isClosed = true;
  51. }
  52. /**
  53. * Open the info panel
  54. */
  55. this.open = function() {
  56. this.panel.classList.remove("closed")
  57. this.isClosed = false;
  58. }
  59. /**
  60. * Toggle the info panel's visibility
  61. */
  62. this.toggleVisibility = function() {
  63. if(this.isClosed) { this.open(); }
  64. else { this.close(); }
  65. this.toggleCloseButtonIcon();
  66. }
  67. /**
  68. * Toggle the icon of the info panel's open/close button
  69. */
  70. this.toggleCloseButtonIcon = function() {
  71. if(this.isClosed) {
  72. this.closer.classList.remove(this.icons.close);
  73. this.closer.classList.add(this.icons.open);
  74. }
  75. else {
  76. this.closer.classList.remove(this.icons.open);
  77. this.closer.classList.add(this.icons.close);
  78. }
  79. }
  80. this.init();
  81. }
  82. /**
  83. * A classic modal
  84. *
  85. * @param {string} DOMID - An option string of the DOM ID for premade modal
  86. */
  87. Bumble.Modal = function(DOMID) {
  88. this.overlay = undefined;
  89. this.modal = {
  90. base: undefined,
  91. header: undefined,
  92. title: undefined,
  93. closer: undefined,
  94. body: undefined
  95. };
  96. this.isShown = false;
  97. this.init = function(DOMID) {
  98. // Build the overlay
  99. this.overlay = document.createElement("div");
  100. this.overlay.classList.add("modal-overlay", "hidden");
  101. // Initialize a predefined modal
  102. if(DOMID) {
  103. const elem = document.getElementById(DOMID);
  104. if(!elem){
  105. console.warn("No element found with ID: ", DOMID);
  106. console.warn("Creating modal");
  107. }
  108. else {
  109. this.modal.pane = elem;
  110. this.modal.header = elem.querySelector(".modal-header");
  111. this.modal.title = elem.querySelector(".modal-title");
  112. this.modal.closer = elem.querySelector(".uil-times-circle");
  113. this.modal.body = elem.querySelector(".modal-body");
  114. }
  115. }
  116. // Build the modal from scratch
  117. if(!this.modal.pane) {
  118. this.modal.pane = document.createElement("div");
  119. this.modal.pane.classList.add("modal");
  120. this.modal.header = document.createElement("div");
  121. this.modal.header.classList.add("modal-header");
  122. this.modal.title = document.createElement("div");
  123. this.modal.title.classList.add("modal-title");
  124. const closerContainer = document.createElement("div");
  125. closerContainer.classList.add("modal-closer", "text-right");
  126. this.modal.closer = document.createElement("i");
  127. this.modal.closer.classList.add("uil", "uil-times-circle");
  128. closerContainer.appendChild(this.modal.closer);
  129. this.modal.header.append(this.modal.title, closerContainer);
  130. this.modal.body = document.createElement("div");
  131. this.modal.body.classList.add("modal-body");
  132. this.modal.pane.append(this.modal.header, this.modal.body);
  133. }
  134. // Add event listener to closer button
  135. this.modal.closer.addEventListener("click", this.hide.bind(this));
  136. // Add modal pane to the overlay
  137. this.overlay.appendChild(this.modal.pane);
  138. // Add overlay to body
  139. document.body.appendChild(this.overlay);
  140. return;
  141. }
  142. this.SetTitle = function(title) {
  143. this.modal.title.innerText = title;
  144. }
  145. this.SetBody = function(bodyHTML) {
  146. this.modal.body.innerHTML = bodyHTML;
  147. }
  148. this.show = function() {
  149. this.isShown = true;
  150. this._toggleVisibility();
  151. }
  152. this.hide = function() {
  153. this.isShown = false;
  154. this._toggleVisibility();
  155. }
  156. this._toggleVisibility = function() {
  157. if(this.isShown) {
  158. this.overlay.classList.remove("hidden");
  159. }
  160. else {
  161. this.overlay.classList.add("hidden");
  162. }
  163. }
  164. this.init(DOMID);
  165. }
  166. /**
  167. * Store GET Parameters
  168. *
  169. * A convenient place to store and access GET parameters sent to the page.
  170. */
  171. Bumble._GET = {
  172. /**
  173. * Loads the GET parameters into this object
  174. */
  175. __init: function() {
  176. const params = (new URL(document.location)).searchParams.entries();
  177. for(const entry of params) {
  178. if(entry[0] == "__init"){ continue; }
  179. this[entry[0]] = !isNaN(entry[1]) ? Number(entry[1]) : entry[1];
  180. }
  181. }
  182. }
  183. Bumble.XFN = {
  184. me: "uil-cell",
  185. friend: "uil-user-check",
  186. colleague: "uil-books",
  187. _buildIcon: function(iconClass) {
  188. const icon = document.createElement("i");
  189. icon.classList.add("uil", iconClass);
  190. return icon;
  191. },
  192. init: function() {
  193. const links = document.querySelectorAll("a[rel]");
  194. for(const link of links) {
  195. // Ignore links that only contain images
  196. if (link.childNodes.length == 1 && link.lastChild instanceof HTMLImageElement) { continue; }
  197. const rels = link.rel.split(' ');
  198. let rel = null;
  199. if(rels.indexOf("me") >= 0) {
  200. rel = this.me;
  201. }
  202. else if(rels.indexOf("friend") >= 0) {
  203. rel = this.friend;
  204. }
  205. else if(rels.indexOf("colleague") >= 0) {
  206. rel = this.colleague;
  207. }
  208. if(rel) {
  209. link.insertAdjacentElement("afterbegin", this._buildIcon(rel));
  210. }
  211. }
  212. },
  213. }
  214. /**
  215. * Quotes that either have a lot of meaning to me, or I just really enjoy
  216. */
  217. Bumble.FooterQuotes = {
  218. all_quotes: [],
  219. PlaceQuote: function() {
  220. const div = document.getElementById("footer-quote");
  221. let quote = {
  222. text: "Create a quotes.js file at static/quotes.js to add your quote to the footer.",
  223. source: ""
  224. };
  225. if (div) {
  226. if (this.all_quotes.length) {
  227. quote = this.all_quotes[Math.floor(Math.random() * this.all_quotes.length)];
  228. }
  229. div.innerText = quote.text;
  230. console.log(`Quote source: ${quote.source}`);
  231. }
  232. },
  233. /**
  234. * Add a quote to the list of possible quotes to display in the footer
  235. *
  236. * @param {string} quote The quote to display in the footer
  237. * @param {string} source The source of the quote that will be logged in the browser's console
  238. */
  239. AddQuote: function(quote, source) {
  240. this.all_quotes.push({
  241. text: quote,
  242. source: source,
  243. });
  244. }
  245. }
  246. function page_init() {
  247. /* Initialize info panels */
  248. const panels = document.querySelectorAll('.infopanel');
  249. for(const panel of panels) {
  250. panel._infoPanel = new Bumble.InfoPanel(panel);
  251. }
  252. Bumble._GET.__init();
  253. Bumble.XFN.init();
  254. Bumble.FooterQuotes.PlaceQuote();
  255. }
  256. if(document.readyState != 'loading') {
  257. page_init();
  258. }
  259. else {
  260. document.addEventListener('DOMContentLoaded', page_init);
  261. }