From 6a19102b8290eabf9e697eb0c975ab247df6960b Mon Sep 17 00:00:00 2001 From: Robert Clarke Date: Wed, 8 May 2019 11:40:39 +0100 Subject: [PATCH] if a theme is no longer available, js will now fall back to previous or default theme --- README.md | 10 ++-- javascript/src/zulma_switchcss.js | 81 ++++++++++++++++++++++++++++--- static/js/zulma_switchcss.js | 2 +- static/js/zulma_switchcss.js.map | 2 +- 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 432fd87..a601615 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ That's it! No more configuration should be required, however head to the [Option ### Sources All the source javascript files live in `javascript/src`. This is a list of the javascript files, their purpose, and their sources. All files are prefixed with `zulma_` to avoid any name clashes. -- `zulma_search.js` - Used when a user types into the search box on the navbar (if enabled). The search is shamefully stolen from [Zola's site](https://github.com/getzola/zola/blob/6100a43/docs/static/search.js). Thanks, Vincent! +- `zulma_search.js` - Used when a user types into the search box on the navbar (if enabled). Taken from [Zola's site](https://github.com/getzola/zola/blob/6100a43/docs/static/search.js). - `zulma_navbar.js` - Used for the mobile navbar toggle. Taken from the [bulma template](https://github.com/dansup/bulma-templates/blob/6263eb7/js/bulma.js) at Bulmaswatch -- `zulma_switchcss.js` - Used for swapping themes. Created by me. +- `zulma_switchcss.js` - Used for swapping themes (if enabled). Created by me. ### Building These are transpiled by babel, minified by webpack, sourcemaps are generated and then everything placed in `static/js`. The repo already contains the transpiled and minified files along with their corrosponding sourcemaps so you don't need to do anything to use these. If you would prefer to build it yourself, feel free to inspect the js files and then run the build process yourself (please ensure that you have [node, npm](https://nodejs.org/en/) and [yarn](https://yarnpkg.com/lang/en/) installed.): @@ -181,13 +181,15 @@ zulma_theme = "darkly" Additionally, in extra, you can also set the `zulma_allow_theme_selection` boolean. Setting this to `true` will allow a menu in the footer to allow users to select their own theme. This option will store their theme choice in their localstorage and apply it on every page, assuming `zulma_allow_theme_selection` is still true. This requires javascript to be enabled to function; if the page detects javascript is disabled on the clients machine, it will hide itself. +Each theme contains the entirety of Bulma, and will weigh in at ~180kb. If you're running on a server severely limited on space, then I'd recommend you delete each theme you're not using, either from the source or from `/public`. Obviously, doing this will cause `zulma_allow_theme_selection` to work improperly, so make sure you either override `extra.zulma_themes` in `config.toml` to only show themes you have left or to not enable this option at all. + ```toml [extra] zulma_allow_theme_selection = true ``` ## Original -This template is based on the [blog template](https://dansup.github.io/bulma-templates/templates/blog.html) over at [Free Bulma Templates](https://dansup.github.io/bulma-templates/). All themes were taken from [Bulmaswatch](https://jenil.github.io/bulmaswatch/). The code behind from adapted from the [after-dark](https://github.com/getzola/after-dark/blob/master/README.md) zola template. +This template is based on the [blog template](https://dansup.github.io/bulma-templates/templates/blog.html) over at [Free Bulma Templates](https://dansup.github.io/bulma-templates/). All themes were taken from [Bulmaswatch](https://jenil.github.io/bulmaswatch/). The code behind from originally adapted from the [after-dark](https://github.com/getzola/after-dark/blob/master/README.md) zola template. ## Known Bugs -If user theme swapping is enabled and the user selects a theme different to the default, a slight delay will be introduced in page rendering as the css gets swapped out and in by the javascript. This is better than the alternative FOUC or flashes of the old theme, but still annoying. I don't know any way around this, but with browser caching it should be fast enough to not cause serious issues. \ No newline at end of file +- If user theme swapping is enabled and the user selects a theme different to the default, a slight delay will be introduced in page rendering as the css gets swapped out and in by the javascript. This is better than the alternative FOUC or flashes of the old theme, but still annoying. I don't know any way around this, but with browser caching it should be fast enough to not cause serious issues. \ No newline at end of file diff --git a/javascript/src/zulma_switchcss.js b/javascript/src/zulma_switchcss.js index be471e4..f6f5d95 100644 --- a/javascript/src/zulma_switchcss.js +++ b/javascript/src/zulma_switchcss.js @@ -6,6 +6,7 @@ //Variables let link = null; + let previousLink = null; let theme = localStorage.getItem(THEME_KEY); //Private Methods @@ -16,21 +17,27 @@ fileref.rel = "stylesheet"; fileref.type = "text/css"; fileref.href = `/${themeName}.css`; + fileref.id = themeName; //append it to the head link = document.getElementsByTagName("head")[0].appendChild(fileref); //when it's loaded, call onLinkLoad link.addEventListener('load', onLinkLoad); + //if it errors, call onLinkError + link.addEventListener('error', onLinkError); //if this is the first load of the page, remove the current stylesheet early to avoid flash of wrongly styled content if (firstLoad) { + //keep the old link in case something goes wrong + previousLink = document.querySelectorAll(`.${STYLESHEET_CLASSNAME}`)[0]; removeStylesheets(); } saveTheme(themeName); }; + /* Removes all current stylesheets on the page */ function removeStylesheets() { document.querySelectorAll(`.${STYLESHEET_CLASSNAME}`).forEach((el) => { el.remove(); @@ -39,11 +46,35 @@ /* The function called when the css has finished loading */ function onLinkLoad() { + //remove event listeners link.removeEventListener('load', onLinkLoad); + link.removeEventListener('error', onLinkError); //remove the previous stylesheet(s) removeStylesheets(); //add stylesheet class link.className += STYLESHEET_CLASSNAME; + //everything is good, so we don't need this + previousLink = null; + //make body visible again if it was hidden + showBody(); + }; + + function onLinkError() { + //remove event listeners + link.removeEventListener('load', onLinkLoad); + link.removeEventListener('error', onLinkError); + //remove theme from localstorage + clearTheme(); + //remove theme from dropdown list + removeFromThemeSelect(link.id); + //remove link from page + link.remove(); + //re-add the previous stylesheet (if any) + if (previousLink) { + document.getElementsByTagName("head")[0].appendChild(previousLink); + } + //set the theme select to the previous stylesheet + setThemeSelect(document.querySelectorAll(`.${STYLESHEET_CLASSNAME}`)[0].id) //make body visible again if it was hidden showBody(); }; @@ -53,6 +84,11 @@ localStorage.setItem(THEME_KEY, themeName); }; + /* Clears the current theme in localstorage */ + function clearTheme() { + localStorage.removeItem(THEME_KEY); + }; + /* Hides the body of the page */ function hideBody() { var head = document.getElementsByTagName('head')[0]; @@ -76,6 +112,43 @@ css.remove(); }; + /* Sets the theme selection to the given theme */ + function setThemeSelect(theme) { + //get all select options + let elements = document.querySelectorAll('#theme-select>option'); + //if there are elements, the page is loaded and continue + if (elements.length) { + elements.forEach(element => { + if (element.value === theme) { + element.selected = 'selected'; + } + }); + } else { + //if there are no elements, the page is not yet loaded; wait for loaded event and try again. + window.addEventListener('DOMContentLoaded', () => { + setThemeSelect(theme) + }); + } + } + + function removeFromThemeSelect(theme) { + //get all select options + let elements = document.querySelectorAll('#theme-select>option'); + //if there are elements, the page is loaded + if (elements.length) { + elements.forEach(element => { + if (element.value === theme) { + element.remove(); + } + }); + } else { + //if there are no elements, the page is not yet loaded; wait for loaded event and try again. + window.addEventListener('DOMContentLoaded', () => { + removeFromThemeSelect(theme) + }); + } + } + //Public Methods switch_css.init = function () { //if user has selected and theme and it is not the current theme @@ -85,13 +158,7 @@ //change the theme changeTheme(theme, true); //when the DOM is loaded, change the select to their current choice - window.addEventListener('DOMContentLoaded', () => { - document.querySelectorAll('#theme-select>option').forEach(element => { - if (element.value === theme) { - element.selected = 'selected'; - } - }); - }); + setThemeSelect(theme); } //when the DOM is loaded, set the dropdown to trigger the theme change window.addEventListener('DOMContentLoaded', () => { diff --git a/static/js/zulma_switchcss.js b/static/js/zulma_switchcss.js index 6919d63..feeb920 100644 --- a/static/js/zulma_switchcss.js +++ b/static/js/zulma_switchcss.js @@ -1,2 +1,2 @@ -!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=2)}({2:function(e,t){!function(e){var t="ZULMA_THEME",n="stop-blink",o="stylesheet",r=null,c=localStorage.getItem(t);function i(e,n){var o=document.createElement("link");o.rel="stylesheet",o.type="text/css",o.href="/".concat(e,".css"),(r=document.getElementsByTagName("head")[0].appendChild(o)).addEventListener("load",u),n&&l(),function(e){localStorage.setItem(t,e)}(e)}function l(){document.querySelectorAll(".".concat(o)).forEach(function(e){e.remove()})}function u(){var e;r.removeEventListener("load",u),l(),r.className+=o,(e=document.getElementById(n))&&e.remove()}e.init=function(){var e,t;c&&!document.getElementById(c)&&(e=document.getElementsByTagName("head")[0],(t=document.createElement("style")).id=n,t.setAttribute("type","text/css"),t.styleSheet?t.styleSheet.cssText=css:t.appendChild(document.createTextNode("body{visibility:hidden;}")),e.appendChild(t),i(c,!0),window.addEventListener("DOMContentLoaded",function(){document.querySelectorAll("#theme-select>option").forEach(function(e){e.value===c&&(e.selected="selected")})})),window.addEventListener("DOMContentLoaded",function(){document.getElementById("theme-select").onchange=function(){i(this.value)}})}}(switch_css=window.switch_css||{}),switch_css.init()}}); +!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=2)}({2:function(e,t){!function(e){var t="ZULMA_THEME",n="stop-blink",o="stylesheet",r=null,c=null,l=localStorage.getItem(t);function i(e,n){var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.href="/".concat(e,".css"),l.id=e,(r=document.getElementsByTagName("head")[0].appendChild(l)).addEventListener("load",d),r.addEventListener("error",a),n&&(c=document.querySelectorAll(".".concat(o))[0],u()),function(e){localStorage.setItem(t,e)}(e)}function u(){document.querySelectorAll(".".concat(o)).forEach(function(e){e.remove()})}function d(){r.removeEventListener("load",d),r.removeEventListener("error",a),u(),r.className+=o,c=null,s()}function a(){r.removeEventListener("load",d),r.removeEventListener("error",a),localStorage.removeItem(t),function e(t){var n=document.querySelectorAll("#theme-select>option");n.length?n.forEach(function(e){e.value===t&&e.remove()}):window.addEventListener("DOMContentLoaded",function(){e(t)})}(r.id),r.remove(),c&&document.getElementsByTagName("head")[0].appendChild(c),f(document.querySelectorAll(".".concat(o))[0].id),s()}function s(){var e=document.getElementById(n);e&&e.remove()}function f(e){var t=document.querySelectorAll("#theme-select>option");t.length?t.forEach(function(t){t.value===e&&(t.selected="selected")}):window.addEventListener("DOMContentLoaded",function(){f(e)})}e.init=function(){var e,t;l&&!document.getElementById(l)&&(e=document.getElementsByTagName("head")[0],(t=document.createElement("style")).id=n,t.setAttribute("type","text/css"),t.styleSheet?t.styleSheet.cssText=css:t.appendChild(document.createTextNode("body{visibility:hidden;}")),e.appendChild(t),i(l,!0),f(l)),window.addEventListener("DOMContentLoaded",function(){document.getElementById("theme-select").onchange=function(){i(this.value)}})}}(switch_css=window.switch_css||{}),switch_css.init()}}); //# sourceMappingURL=zulma_switchcss.js.map \ No newline at end of file diff --git a/static/js/zulma_switchcss.js.map b/static/js/zulma_switchcss.js.map index f1a9ea2..0f2355c 100644 --- a/static/js/zulma_switchcss.js.map +++ b/static/js/zulma_switchcss.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/zulma_switchcss.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","switch_css","THEME_KEY","STOP_BLINK_CSS_ID","STYLESHEET_CLASSNAME","link","theme","localStorage","getItem","changeTheme","themeName","firstLoad","fileref","document","createElement","rel","type","href","concat","getElementsByTagName","appendChild","addEventListener","onLinkLoad","removeStylesheets","setItem","saveTheme","querySelectorAll","forEach","el","remove","css","removeEventListener","className","getElementById","init","head","style","id","setAttribute","styleSheet","cssText","createTextNode","window","element","selected","onchange","this"],"mappings":"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","file":"zulma_switchcss.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 2);\n","(function (switch_css) {\r\n //Constants\r\n const THEME_KEY = \"ZULMA_THEME\";\r\n const STOP_BLINK_CSS_ID = \"stop-blink\";\r\n const STYLESHEET_CLASSNAME = \"stylesheet\"\r\n\r\n //Variables\r\n let link = null;\r\n let theme = localStorage.getItem(THEME_KEY);\r\n\r\n //Private Methods\r\n /* Called when the theme is changed */\r\n function changeTheme(themeName, firstLoad) {\r\n //create the css link element\r\n var fileref = document.createElement(\"link\");\r\n fileref.rel = \"stylesheet\";\r\n fileref.type = \"text/css\";\r\n fileref.href = `/${themeName}.css`;\r\n\r\n //append it to the head\r\n link = document.getElementsByTagName(\"head\")[0].appendChild(fileref);\r\n\r\n //when it's loaded, call onLinkLoad\r\n link.addEventListener('load', onLinkLoad);\r\n\r\n //if this is the first load of the page, remove the current stylesheet early to avoid flash of wrongly styled content\r\n if (firstLoad) {\r\n removeStylesheets();\r\n }\r\n\r\n saveTheme(themeName);\r\n };\r\n\r\n function removeStylesheets() {\r\n document.querySelectorAll(`.${STYLESHEET_CLASSNAME}`).forEach((el) => {\r\n el.remove();\r\n });\r\n }\r\n\r\n /* The function called when the css has finished loading */\r\n function onLinkLoad() {\r\n link.removeEventListener('load', onLinkLoad);\r\n //remove the previous stylesheet(s)\r\n removeStylesheets();\r\n //add stylesheet class\r\n link.className += STYLESHEET_CLASSNAME;\r\n //make body visible again if it was hidden\r\n showBody();\r\n };\r\n\r\n /* Saves the current theme in localstorage */\r\n function saveTheme(themeName) {\r\n localStorage.setItem(THEME_KEY, themeName);\r\n };\r\n\r\n /* Hides the body of the page */\r\n function hideBody() {\r\n var head = document.getElementsByTagName('head')[0];\r\n var style = document.createElement('style');\r\n\r\n style.id = STOP_BLINK_CSS_ID;\r\n style.setAttribute('type', 'text/css');\r\n\r\n if (style.styleSheet) {\r\n style.styleSheet.cssText = css;\r\n } else {\r\n style.appendChild(document.createTextNode('body{visibility:hidden;}'));\r\n }\r\n head.appendChild(style);\r\n };\r\n\r\n /* Shows the body of the page */\r\n function showBody() {\r\n let css = document.getElementById(STOP_BLINK_CSS_ID);\r\n if (css)\r\n css.remove();\r\n };\r\n\r\n //Public Methods\r\n switch_css.init = function () {\r\n //if user has selected and theme and it is not the current theme\r\n if (theme && !document.getElementById(theme)) {\r\n //hide the body to stop FOUC\r\n hideBody();\r\n //change the theme\r\n changeTheme(theme, true);\r\n //when the DOM is loaded, change the select to their current choice\r\n window.addEventListener('DOMContentLoaded', () => {\r\n document.querySelectorAll('#theme-select>option').forEach(element => {\r\n if (element.value === theme) {\r\n element.selected = 'selected';\r\n }\r\n });\r\n });\r\n }\r\n //when the DOM is loaded, set the dropdown to trigger the theme change\r\n window.addEventListener('DOMContentLoaded', () => {\r\n document.getElementById('theme-select').onchange = function () {\r\n changeTheme(this.value);\r\n }\r\n });\r\n }\r\n}(switch_css = window.switch_css || {}));\r\n\r\nswitch_css.init();"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/zulma_switchcss.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","switch_css","THEME_KEY","STOP_BLINK_CSS_ID","STYLESHEET_CLASSNAME","link","previousLink","theme","localStorage","getItem","changeTheme","themeName","firstLoad","fileref","document","createElement","rel","type","href","concat","id","getElementsByTagName","appendChild","addEventListener","onLinkLoad","onLinkError","querySelectorAll","removeStylesheets","setItem","saveTheme","forEach","el","remove","removeEventListener","className","showBody","removeItem","removeFromThemeSelect","elements","length","element","window","setThemeSelect","css","getElementById","selected","init","head","style","setAttribute","styleSheet","cssText","createTextNode","onchange","this"],"mappings":"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","file":"zulma_switchcss.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 2);\n","(function (switch_css) {\r\n //Constants\r\n const THEME_KEY = \"ZULMA_THEME\";\r\n const STOP_BLINK_CSS_ID = \"stop-blink\";\r\n const STYLESHEET_CLASSNAME = \"stylesheet\"\r\n\r\n //Variables\r\n let link = null;\r\n let previousLink = null;\r\n let theme = localStorage.getItem(THEME_KEY);\r\n\r\n //Private Methods\r\n /* Called when the theme is changed */\r\n function changeTheme(themeName, firstLoad) {\r\n //create the css link element\r\n var fileref = document.createElement(\"link\");\r\n fileref.rel = \"stylesheet\";\r\n fileref.type = \"text/css\";\r\n fileref.href = `/${themeName}.css`;\r\n fileref.id = themeName;\r\n\r\n //append it to the head\r\n link = document.getElementsByTagName(\"head\")[0].appendChild(fileref);\r\n\r\n //when it's loaded, call onLinkLoad\r\n link.addEventListener('load', onLinkLoad);\r\n //if it errors, call onLinkError\r\n link.addEventListener('error', onLinkError);\r\n\r\n //if this is the first load of the page, remove the current stylesheet early to avoid flash of wrongly styled content\r\n if (firstLoad) {\r\n //keep the old link in case something goes wrong\r\n previousLink = document.querySelectorAll(`.${STYLESHEET_CLASSNAME}`)[0];\r\n removeStylesheets();\r\n }\r\n\r\n saveTheme(themeName);\r\n };\r\n\r\n /* Removes all current stylesheets on the page */\r\n function removeStylesheets() {\r\n document.querySelectorAll(`.${STYLESHEET_CLASSNAME}`).forEach((el) => {\r\n el.remove();\r\n });\r\n }\r\n\r\n /* The function called when the css has finished loading */\r\n function onLinkLoad() {\r\n //remove event listeners\r\n link.removeEventListener('load', onLinkLoad);\r\n link.removeEventListener('error', onLinkError);\r\n //remove the previous stylesheet(s)\r\n removeStylesheets();\r\n //add stylesheet class\r\n link.className += STYLESHEET_CLASSNAME;\r\n //everything is good, so we don't need this\r\n previousLink = null;\r\n //make body visible again if it was hidden\r\n showBody();\r\n };\r\n\r\n function onLinkError() {\r\n //remove event listeners\r\n link.removeEventListener('load', onLinkLoad);\r\n link.removeEventListener('error', onLinkError);\r\n //remove theme from localstorage\r\n clearTheme();\r\n //remove theme from dropdown list\r\n removeFromThemeSelect(link.id);\r\n //remove link from page\r\n link.remove();\r\n //re-add the previous stylesheet (if any)\r\n if (previousLink) {\r\n document.getElementsByTagName(\"head\")[0].appendChild(previousLink);\r\n }\r\n //set the theme select to the previous stylesheet\r\n setThemeSelect(document.querySelectorAll(`.${STYLESHEET_CLASSNAME}`)[0].id)\r\n //make body visible again if it was hidden\r\n showBody();\r\n };\r\n\r\n /* Saves the current theme in localstorage */\r\n function saveTheme(themeName) {\r\n localStorage.setItem(THEME_KEY, themeName);\r\n };\r\n\r\n /* Clears the current theme in localstorage */\r\n function clearTheme() {\r\n localStorage.removeItem(THEME_KEY);\r\n };\r\n\r\n /* Hides the body of the page */\r\n function hideBody() {\r\n var head = document.getElementsByTagName('head')[0];\r\n var style = document.createElement('style');\r\n\r\n style.id = STOP_BLINK_CSS_ID;\r\n style.setAttribute('type', 'text/css');\r\n\r\n if (style.styleSheet) {\r\n style.styleSheet.cssText = css;\r\n } else {\r\n style.appendChild(document.createTextNode('body{visibility:hidden;}'));\r\n }\r\n head.appendChild(style);\r\n };\r\n\r\n /* Shows the body of the page */\r\n function showBody() {\r\n let css = document.getElementById(STOP_BLINK_CSS_ID);\r\n if (css)\r\n css.remove();\r\n };\r\n\r\n /* Sets the theme selection to the given theme */\r\n function setThemeSelect(theme) {\r\n //get all select options\r\n let elements = document.querySelectorAll('#theme-select>option');\r\n //if there are elements, the page is loaded and continue\r\n if (elements.length) {\r\n elements.forEach(element => {\r\n if (element.value === theme) {\r\n element.selected = 'selected';\r\n }\r\n });\r\n } else {\r\n //if there are no elements, the page is not yet loaded; wait for loaded event and try again.\r\n window.addEventListener('DOMContentLoaded', () => {\r\n setThemeSelect(theme)\r\n });\r\n }\r\n }\r\n\r\n function removeFromThemeSelect(theme) {\r\n //get all select options\r\n let elements = document.querySelectorAll('#theme-select>option');\r\n //if there are elements, the page is loaded\r\n if (elements.length) {\r\n elements.forEach(element => {\r\n if (element.value === theme) {\r\n element.remove();\r\n }\r\n });\r\n } else {\r\n //if there are no elements, the page is not yet loaded; wait for loaded event and try again.\r\n window.addEventListener('DOMContentLoaded', () => {\r\n removeFromThemeSelect(theme)\r\n });\r\n }\r\n }\r\n\r\n //Public Methods\r\n switch_css.init = function () {\r\n //if user has selected and theme and it is not the current theme\r\n if (theme && !document.getElementById(theme)) {\r\n //hide the body to stop FOUC\r\n hideBody();\r\n //change the theme\r\n changeTheme(theme, true);\r\n //when the DOM is loaded, change the select to their current choice\r\n setThemeSelect(theme);\r\n }\r\n //when the DOM is loaded, set the dropdown to trigger the theme change\r\n window.addEventListener('DOMContentLoaded', () => {\r\n document.getElementById('theme-select').onchange = function () {\r\n changeTheme(this.value);\r\n }\r\n });\r\n }\r\n}(switch_css = window.switch_css || {}));\r\n\r\nswitch_css.init();"],"sourceRoot":""} \ No newline at end of file