mirror of
https://github.com/danbulant/dribbblish-dynamic-theme
synced 2026-05-26 13:31:45 +00:00
127 lines
53 KiB
JavaScript
127 lines
53 KiB
JavaScript
/*
|
|
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
|
|
* This devtool is neither made for production nor for readable output files.
|
|
* It uses "eval()" calls to create a separate source file in the browser devtools.
|
|
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
|
* or disable the default devtool with "devtool: false".
|
|
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
|
*/
|
|
/******/ (() => {
|
|
// webpackBootstrap
|
|
/******/ var __webpack_modules__ = {
|
|
/***/ "./src/styles/main.scss":
|
|
/*!******************************!*\
|
|
!*** ./src/styles/main.scss ***!
|
|
\******************************/
|
|
/***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
"use strict";
|
|
eval('__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (__webpack_require__.p + "user.css");\n\n//# sourceURL=webpack:///./src/styles/main.scss?');
|
|
|
|
/***/
|
|
},
|
|
|
|
/***/ "./src/js/main.js":
|
|
/*!************************!*\
|
|
!*** ./src/js/main.js ***!
|
|
\************************/
|
|
/***/ () => {
|
|
eval('// Hide popover message\r\n// document.getElementById("popover-container").style.height = 0;\r\nclass ConfigMenu {\r\n /**\r\n * @typedef {Object} DribbblishConfigItem\r\n * @property {"checkbox" | "select" | "button" | "slider" | "number" | "text" | "time" | "color"} type\r\n * @property {String|DribbblishConfigArea} [area={name: "Main Settings", order: 0}]\r\n * @property {any} [data={}]\r\n * @property {Number} [order=0] order < 0 = Higher up | order > 0 = Lower Down\r\n * @property {String} key\r\n * @property {String} name\r\n * @property {String} [description=""]\r\n * @property {any} [defaultValue]\r\n * @property {Boolean} [hidden=false]\r\n * @property {Boolean} [insertOnTop=false]\r\n * @property {Boolean} [fireInitialChange=true]\r\n * @property {showChildren} [showChildren]\r\n * @property {onAppended} [onAppended]\r\n * @property {onChange} [onChange]\r\n * @property {DribbblishConfigItem[]} [children=[]]\r\n * @property {String} [childOf=null] key of parent (set automatically)\r\n */\r\n\r\n /**\r\n * @typedef DribbblishConfigArea\r\n * @property {String} name\r\n * @property {Number} [order=0] order < 0 = Higher up | order > 0 = Lower Down\r\n */\r\n\r\n /**\r\n * @callback showChildren\r\n * @param {any} value\r\n * @returns {Boolean | String[]}\r\n */\r\n\r\n /**\r\n * @callback onAppended\r\n * @returns {void}\r\n */\r\n\r\n /**\r\n * @callback onChange\r\n * @param {any} value\r\n * @returns {void}\r\n */\r\n\r\n /** @type {Object.<string, DribbblishConfigItem>} */\r\n #config;\r\n\r\n constructor() {\r\n this.#config = {};\r\n this.configButton = new Spicetify.Menu.Item("Dribbblish Settings", false, () => DribbblishShared.config.open());\r\n this.configButton.register();\r\n\r\n const container = document.createElement("div");\r\n container.id = "dribbblish-config";\r\n container.innerHTML = /* html */ `\r\n <div class="dribbblish-config-container">\r\n <button aria-label="Close" class="dribbblish-config-close main-trackCreditsModal-closeBtn">\r\n <svg width="18" height="18" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M31.098 29.794L16.955 15.65 31.097 1.51 29.683.093 15.54 14.237 1.4.094-.016 1.508 14.126 15.65-.016 29.795l1.414 1.414L15.54 17.065l14.144 14.143" fill="currentColor" fill-rule="evenodd"></path></svg>\r\n </button>\r\n <h1>Dribbblish Settings</h1>\r\n <div class="dribbblish-config-areas"></div>\r\n </div>\r\n <div class="dribbblish-config-backdrop"></div>\r\n `;\r\n\r\n document.body.appendChild(container);\r\n document.querySelector(".dribbblish-config-close").addEventListener("click", () => DribbblishShared.config.close());\r\n document.querySelector(".dribbblish-config-backdrop").addEventListener("click", () => DribbblishShared.config.close());\r\n }\r\n\r\n open() {\r\n document.getElementById("dribbblish-config").setAttribute("active", "");\r\n }\r\n\r\n close() {\r\n document.getElementById("dribbblish-config").removeAttribute("active");\r\n }\r\n\r\n /**\r\n * @private\r\n * @param {DribbblishConfigItem} options\r\n */\r\n addInputHTML(options) {\r\n this.registerArea(options.area);\r\n const parent = document.querySelector(`.dribbblish-config-area[name="${options.area.name}"] .dribbblish-config-area-items`);\r\n\r\n const elem = document.createElement("div");\r\n elem.style.order = options.order;\r\n elem.classList.add("dribbblish-config-item");\r\n elem.setAttribute("key", options.key);\r\n elem.setAttribute("type", options.type);\r\n elem.setAttribute("hidden", options.hidden);\r\n if (options.childOf) elem.setAttribute("parent", options.childOf);\r\n elem.innerHTML = /* html */ `\r\n <h2 class="x-settings-title main-type-cello${!options.description ? " no-desc" : ""}" as="h2">${options.name}</h2>\r\n <label class="main-type-mesto">${options.description.replace(/\\n/g, "<br>")}</label>\r\n <label class="x-toggle-wrapper x-settings-secondColumn">\r\n ${options.input}\r\n </label>\r\n `;\r\n\r\n if (options.insertOnTop && parent.children.length > 0) {\r\n parent.insertBefore(elem, parent.children[0]);\r\n } else {\r\n parent.appendChild(elem);\r\n }\r\n }\r\n\r\n /**\r\n * @param {DribbblishConfigItem} options\r\n */\r\n register(options) {\r\n /** @type {DribbblishConfigItem} */\r\n const defaultOptions = {\r\n hidden: false,\r\n area: "Main Settings",\r\n order: 0,\r\n data: {},\r\n name: "",\r\n description: "",\r\n insertOnTop: false,\r\n fireInitialChange: true,\r\n showChildren: () => true,\r\n onAppended: () => {},\r\n onChange: () => {},\r\n children: [],\r\n childOf: null\r\n };\r\n // Set Defaults\r\n options = { ...defaultOptions, ...options };\r\n if (typeof options.area == "string") options.area = { name: options.area, order: 0 };\r\n options.description = options.description\r\n .split("\\n")\r\n .filter((line) => line.trim() != "")\r\n .map((line) => line.trim())\r\n .join("\\n");\r\n options._onChange = options.onChange;\r\n options.onChange = (val) => {\r\n options._onChange(val);\r\n const show = options.showChildren(val);\r\n options.children.forEach((child) => this.setHidden(child.key, Array.isArray(show) ? !show.includes(child.key) : !show));\r\n };\r\n options.children = options.children.map((child) => {\r\n return { ...child, area: options.area, childOf: options.key };\r\n });\r\n\r\n this.#config[options.key] = options;\r\n this.#config[options.key].value = localStorage.getItem(`dribbblish:config:${options.key}`) ?? JSON.stringify(options.defaultValue);\r\n\r\n if (options.type == "checkbox") {\r\n const input = /* html */ `\r\n <input id="dribbblish-config-input-${options.key}" class="x-toggle-input" type="checkbox"${this.get(options.key) ? " checked" : ""}>\r\n <span class="x-toggle-indicatorWrapper">\r\n <span class="x-toggle-indicator"></span>\r\n </span>\r\n `;\r\n this.addInputHTML({ ...options, input });\r\n\r\n document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("change", (e) => {\r\n this.set(options.key, e.target.checked);\r\n options.onChange(this.get(options.key));\r\n });\r\n } else if (options.type == "select") {\r\n // Validate\r\n const val = this.get(options.key);\r\n if (val < 0 || val > options.data.length - 1) this.set(options.key);\r\n\r\n const input = /* html */ `\r\n <select class="main-dropDown-dropDown" id="dribbblish-config-input-${options.key}">\r\n ${options.data.map((option, i) => `<option value="${i}"${this.get(options.key) == i ? " selected" : ""}>${option}</option>`).join("")}\r\n </select>\r\n `;\r\n this.addInputHTML({ ...options, input });\r\n\r\n document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("change", (e) => {\r\n this.set(options.key, Number(e.target.value));\r\n options.onChange(this.get(options.key));\r\n });\r\n } else if (options.type == "button") {\r\n options.fireInitialChange = false;\r\n if (typeof options.data != "string") options.data = options.name;\r\n\r\n const input = /* html */ `\r\n <button class="main-buttons-button main-button-primary" type="button" id="dribbblish-config-input-${options.key}">\r\n <div class="x-settings-buttonContainer">\r\n <span>${options.data}</span>\r\n </div>\r\n </button>\r\n `;\r\n this.addInputHTML({ ...options, input });\r\n\r\n document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("click", (e) => {\r\n options.onChange(true);\r\n });\r\n } else if (options.type == "number") {\r\n // Validate\r\n if (options.defaultValue == null) options.defaultValue = 0;\r\n const val = this.get(options.key);\r\n if (options.data.min != null && val < options.data.min) this.set(options.key, options.data.min);\r\n if (options.data.max != null && val > options.data.max) this.set(options.key, options.data.max);\r\n\r\n const input = /* html */ `\r\n <input type="number" id="dribbblish-config-input-${options.key}" value="${this.get(options.key)}">\r\n `;\r\n this.addInputHTML({ ...options, input });\r\n\r\n // Prevent inputting +, - and e. Why is it even possible in the first place?\r\n document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("keypress", (e) => {\r\n if (["+", "-", "e"].includes(e.key)) e.preventDefault();\r\n });\r\n\r\n document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("input", (e) => {\r\n if (options.data.min != null && e.target.value < options.data.min) e.target.value = options.data.min;\r\n if (options.data.max != null && e.target.value > options.data.max) e.target.value = options.data.max;\r\n\r\n this.set(options.key, Number(e.target.value));\r\n options.onChange(this.get(options.key));\r\n });\r\n } else if (options.type == "text") {\r\n if (options.defaultValue == null) options.defaultValue = "";\r\n\r\n const input = /* html */ `\r\n <input type="text" id="dribbblish-config-input-${options.key}" value="${this.get(options.key)}">\r\n `;\r\n this.addInputHTML({ ...options, input });\r\n\r\n document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("input", (e) => {\r\n // TODO: maybe add an validation function via `data.validate`\r\n this.set(options.key, e.target.value);\r\n options.onChange(this.get(options.key));\r\n });\r\n } else if (options.type == "slider") {\r\n // Validate\r\n if (options.defaultValue == null) options.defaultValue = 0;\r\n const val = this.get(options.key);\r\n if (options.data.min != null && val < options.data.min) this.set(options.key, options.data.min);\r\n if (options.data.max != null && val > options.data.max) this.set(options.key, options.data.max);\r\n\r\n const input = /* html */ `\r\n <input\r\n type="range"\r\n id="dribbblish-config-input-${options.key}"\r\n name="${options.name}"\r\n min="${options.data?.min ?? "0"}"\r\n max="${options.data?.max ?? "100"}"\r\n step="${options.data?.step ?? "1"}"\r\n value="${this.get(options.key)}"\r\n tooltip="${this.get(options.key)}${options.data?.suffix ?? ""}"\r\n >\r\n `;\r\n this.addInputHTML({ ...options, input });\r\n\r\n document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("input", (e) => {\r\n document.getElementById(`dribbblish-config-input-${options.key}`).setAttribute("tooltip", `${e.target.value}${options.data?.suffix ?? ""}`);\r\n document.getElementById(`dribbblish-config-input-${options.key}`).setAttribute("value", e.target.value);\r\n this.set(options.key, Number(e.target.value));\r\n options.onChange(this.get(options.key));\r\n });\r\n } else if (options.type == "time") {\r\n // Validate\r\n if (options.defaultValue == null) options.defaultValue = "00:00";\r\n const input = /* html */ `\r\n <input type="time" id="dribbblish-config-input-${options.key}" name="${options.name}" value="${this.get(options.key)}">\r\n `;\r\n this.addInputHTML({ ...options, input });\r\n\r\n document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("input", (e) => {\r\n document.getElementById(`dribbblish-config-input-${options.key}`).setAttribute("value", e.target.value);\r\n this.set(options.key, e.target.value);\r\n options.onChange(this.get(options.key));\r\n });\r\n } else if (options.type == "color") {\r\n // Validate\r\n if (options.defaultValue == null) options.defaultValue = "#000000";\r\n const input = /* html */ `\r\n <input type="color" id="dribbblish-config-input-${options.key}" name="${options.name}" value="${this.get(options.key)}">\r\n `;\r\n this.addInputHTML({ ...options, input });\r\n\r\n document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("input", (e) => {\r\n this.set(options.key, e.target.value);\r\n options.onChange(this.get(options.key));\r\n });\r\n } else {\r\n throw new Error(`Config Type "${options.type}" invalid`);\r\n }\r\n\r\n options.children.forEach((child) => this.register(child));\r\n\r\n options.onAppended();\r\n if (options.fireInitialChange) options.onChange(this.get(options.key));\r\n }\r\n\r\n /**\r\n * @param {DribbblishConfigArea} area\r\n */\r\n registerArea(area) {\r\n if (!document.querySelector(`.dribbblish-config-area[name="${area.name}"]`)) {\r\n const areaElem = document.createElement("div");\r\n areaElem.classList.add("dribbblish-config-area");\r\n areaElem.style.order = area.order;\r\n const uncollapsedAreas = JSON.parse(localStorage.getItem("dribbblish:config-areas:uncollapsed") ?? "[]");\r\n if (!uncollapsedAreas.includes(area.name)) areaElem.toggleAttribute("collapsed");\r\n areaElem.setAttribute("name", area.name);\r\n areaElem.innerHTML = /* html */ `\r\n <h2 class="dribbblish-config-area-header">\r\n ${area.name}\r\n <svg height="24" width="24" viewBox="0 0 24 24" class="main-topBar-icon"><polyline points="16 4 7 12 16 20" fill="none" stroke="currentColor"></polyline></svg>\r\n </h2>\r\n <div class="dribbblish-config-area-items"></div>\r\n `;\r\n document.querySelector(".dribbblish-config-areas").appendChild(areaElem);\r\n areaElem.querySelector("h2").addEventListener("click", () => {\r\n areaElem.toggleAttribute("collapsed");\r\n let uncollapsedAreas = JSON.parse(localStorage.getItem("dribbblish:config-areas:uncollapsed") ?? "[]");\r\n if (areaElem.hasAttribute("collapsed")) {\r\n uncollapsedAreas = uncollapsedAreas.filter((areaName) => areaName != area.name);\r\n } else {\r\n uncollapsedAreas.push(area.name);\r\n }\r\n localStorage.setItem("dribbblish:config-areas:uncollapsed", JSON.stringify(uncollapsedAreas));\r\n });\r\n }\r\n }\r\n\r\n /**\r\n *\r\n * @param {String} key\r\n * @param {any} defaultValueOverride\r\n * @returns {any}\r\n */\r\n get(key, defaultValueOverride) {\r\n const val = JSON.parse(this.#config[key].value ?? null); // Turn undefined into null because `JSON.parse()` dosen\'t like undefined\r\n if (val == null) return defaultValueOverride ?? this.#config[key].defaultValue;\r\n return val;\r\n }\r\n\r\n /**\r\n *\r\n * @param {String} key\r\n * @param {any} val\r\n */\r\n set(key, val) {\r\n this.#config[key].value = JSON.stringify(val);\r\n localStorage.setItem(`dribbblish:config:${key}`, JSON.stringify(val));\r\n }\r\n\r\n /**\r\n *\r\n * @param {String} key\r\n * @param {Boolean} hidden\r\n */\r\n setHidden(key, hidden) {\r\n this.#config[key].hidden = hidden;\r\n document.querySelector(`.dribbblish-config-item[key="${key}"]`).setAttribute("hidden", hidden);\r\n }\r\n\r\n getOptions(key) {\r\n return this.#config[key];\r\n }\r\n}\r\n\r\nclass _DribbblishShared {\r\n constructor() {\r\n this.config = new ConfigMenu();\r\n }\r\n}\r\nconst DribbblishShared = new _DribbblishShared();\r\n\r\nDribbblishShared.config.register({\r\n type: "checkbox",\r\n key: "rightBigCover",\r\n name: "Right expanded cover",\r\n description: "Have the expanded cover Image on the right instead of on the left",\r\n defaultValue: true,\r\n onChange: (val) => {\r\n if (val) {\r\n document.documentElement.classList.add("right-expanded-cover");\r\n } else {\r\n document.documentElement.classList.remove("right-expanded-cover");\r\n }\r\n }\r\n});\r\n\r\nDribbblishShared.config.register({\r\n type: "checkbox",\r\n key: "roundSidebarIcons",\r\n name: "Round Sidebar Icons",\r\n description: "If the Sidebar Icons should be round instead of square",\r\n defaultValue: false,\r\n onChange: (val) => document.documentElement.style.setProperty("--sidebar-icons-border-radius", val ? "50%" : "var(--image-radius)")\r\n});\r\n\r\nDribbblishShared.config.register({\r\n area: "Animations & Transitions",\r\n type: "checkbox",\r\n key: "sidebarHoverAnimation",\r\n name: "Sidebar Hover Animation",\r\n description: "If the Sidebar Icons should have an animated background on hover",\r\n defaultValue: true,\r\n onChange: (val) => document.documentElement.style.setProperty("--sidebar-icons-hover-animation", val ? "1" : "0")\r\n});\r\n\r\nwaitForElement(["#main"], () => {\r\n DribbblishShared.config.register({\r\n type: "select",\r\n data: ["None", "None (With Top Padding)", "Solid", "Transparent"],\r\n key: "winTopBar",\r\n name: "Windows Top Bar",\r\n description: "Have different top Bars (or none at all)",\r\n defaultValue: 0,\r\n onChange: (val) => {\r\n switch (val) {\r\n case 0:\r\n document.getElementById("main").setAttribute("top-bar", "none");\r\n break;\r\n case 1:\r\n document.getElementById("main").setAttribute("top-bar", "none-padding");\r\n break;\r\n case 2:\r\n document.getElementById("main").setAttribute("top-bar", "solid");\r\n break;\r\n case 3:\r\n document.getElementById("main").setAttribute("top-bar", "transparent");\r\n break;\r\n }\r\n }\r\n });\r\n\r\n DribbblishShared.config.register({\r\n type: "select",\r\n data: ["Dribbblish", "Spotify"],\r\n key: "playerControlsStyle",\r\n name: "Player Controls Style",\r\n description: "Style of the Player Controls. Selecting Spotify basically changes Play / Pause back to the center",\r\n defaultValue: 0,\r\n onChange: (val) => {\r\n switch (val) {\r\n case 0:\r\n document.getElementById("main").setAttribute("player-controls", "dribbblish");\r\n break;\r\n case 1:\r\n document.getElementById("main").setAttribute("player-controls", "spotify");\r\n break;\r\n }\r\n }\r\n });\r\n\r\n DribbblishShared.config.register({\r\n area: "Ads",\r\n type: "checkbox",\r\n key: "hideAds",\r\n name: "Hide Ads",\r\n description: `Hide ads / premium features (see: <a href="https://github.com/Daksh777/SpotifyNoPremium">SpotifyNoPremium</a>)`,\r\n defaultValue: false,\r\n onAppended: () => {\r\n document.styleSheets[0].insertRule(/* css */ `\r\n /* Remove upgrade button*/\r\n #main[hide-ads] .main-topBar-UpgradeButton {\r\n display: none\r\n }\r\n `);\r\n document.styleSheets[0].insertRule(/* css */ `\r\n /* Remove upgrade to premium button in user menu */\r\n #main[hide-ads] .main-contextMenu-menuItemButton[href="https://www.spotify.com/premium/"] {\r\n display: none\r\n }\r\n `);\r\n document.styleSheets[0].insertRule(/* css */ `\r\n /* Remove ad placeholder in main screen */\r\n #main[hide-ads] .main-leaderboardComponent-container {\r\n display: none\r\n }\r\n `);\r\n },\r\n onChange: (val) => document.getElementById("main").toggleAttribute("hide-ads", val)\r\n });\r\n});\r\n\r\nfunction waitForElement(els, func, timeout = 100) {\r\n const queries = els.map((el) => document.querySelector(el));\r\n if (queries.every((a) => a)) {\r\n func(queries);\r\n } else if (timeout > 0) {\r\n setTimeout(waitForElement, 300, els, func, --timeout);\r\n }\r\n}\r\n\r\nwaitForElement([`.main-rootlist-rootlistPlaylistsScrollNode ul[tabindex="0"]`, `.main-rootlist-rootlistPlaylistsScrollNode ul[tabindex="0"] li`], ([root, firstItem]) => {\r\n const listElem = firstItem.parentElement;\r\n root.classList.add("dribs-playlist-list");\r\n\r\n /** Replace Playlist name with their pictures */\r\n function loadPlaylistImage() {\r\n for (const item of listElem.children) {\r\n let link = item.querySelector("a");\r\n if (!link) continue;\r\n\r\n let [_, app, uid] = link.pathname.split("/");\r\n let uri;\r\n if (app === "playlist") {\r\n uri = Spicetify.URI.playlistV2URI(uid);\r\n } else if (app === "folder") {\r\n const base64 = localStorage.getItem("dribbblish:folder-image:" + uid);\r\n let img = link.querySelector("img");\r\n if (!img) {\r\n img = document.createElement("img");\r\n img.classList.add("playlist-picture");\r\n link.prepend(img);\r\n }\r\n img.src = base64 || "/images/tracklist-row-song-fallback.svg";\r\n continue;\r\n }\r\n\r\n Spicetify.CosmosAsync.get(`sp://core-playlist/v1/playlist/${uri.toURI()}/metadata`, { policy: { picture: true } }).then((res) => {\r\n const meta = res.metadata;\r\n let img = link.querySelector("img");\r\n if (!img) {\r\n img = document.createElement("img");\r\n img.classList.add("playlist-picture");\r\n link.prepend(img);\r\n }\r\n img.src = meta.picture || "/images/tracklist-row-song-fallback.svg";\r\n });\r\n }\r\n }\r\n\r\n DribbblishShared.loadPlaylistImage = loadPlaylistImage;\r\n loadPlaylistImage();\r\n\r\n new MutationObserver(loadPlaylistImage).observe(listElem, { childList: true });\r\n});\r\n\r\nwaitForElement([".main-rootlist-rootlist", ".main-rootlist-wrapper > :nth-child(2) > :first-child", "#spicetify-show-list"], ([rootlist]) => {\r\n function checkSidebarPlaylistScroll() {\r\n const topDist = rootlist.getBoundingClientRect().top - document.querySelector("#spicetify-show-list:not(:empty), .main-rootlist-wrapper > :nth-child(2) > :first-child").getBoundingClientRect().top;\r\n const bottomDist = document.querySelector(".main-rootlist-wrapper > :nth-child(2) > :last-child").getBoundingClientRect().bottom - rootlist.getBoundingClientRect().bottom;\r\n\r\n rootlist.classList.remove("no-top-shadow", "no-bottom-shadow");\r\n if (topDist < 10) rootlist.classList.add("no-top-shadow");\r\n if (bottomDist < 10) rootlist.classList.add("no-bottom-shadow");\r\n }\r\n checkSidebarPlaylistScroll();\r\n\r\n // Use Interval because scrolling takes a while and getBoundingClientRect() gets position at the moment of calling, so the interval keeps calling for 1s\r\n let c = 0;\r\n let interval;\r\n rootlist.addEventListener("wheel", () => {\r\n checkSidebarPlaylistScroll();\r\n c = 0;\r\n if (interval == null)\r\n interval = setInterval(() => {\r\n if (c > 20) {\r\n clearInterval(interval);\r\n interval = null;\r\n return;\r\n }\r\n\r\n checkSidebarPlaylistScroll();\r\n c++;\r\n }, 50);\r\n });\r\n});\r\n\r\nwaitForElement([".Root__main-view"], ([mainView]) => {\r\n const shadow = document.createElement("div");\r\n shadow.id = "dribbblish-back-shadow";\r\n mainView.prepend(shadow);\r\n});\r\n\r\nwaitForElement([".Root__nav-bar .LayoutResizer__input, .Root__nav-bar .LayoutResizer__resize-bar input"], ([resizer]) => {\r\n const observer = new MutationObserver(updateVariable);\r\n observer.observe(resizer, { attributes: true, attributeFilter: ["value"] });\r\n function updateVariable() {\r\n let value = resizer.value;\r\n if (value < 121) {\r\n value = 72;\r\n document.documentElement.classList.add("sidebar-hide-text");\r\n } else {\r\n document.documentElement.classList.remove("sidebar-hide-text");\r\n }\r\n document.documentElement.style.setProperty("--sidebar-width", value + "px");\r\n }\r\n updateVariable();\r\n});\r\n\r\nwaitForElement([".Root__main-view .os-resize-observer-host"], ([resizeHost]) => {\r\n const observer = new ResizeObserver(updateVariable);\r\n observer.observe(resizeHost);\r\n function updateVariable([event]) {\r\n document.documentElement.style.setProperty("--main-view-width", event.contentRect.width + "px");\r\n document.documentElement.style.setProperty("--main-view-height", event.contentRect.height + "px");\r\n if (event.contentRect.width < 700) {\r\n document.documentElement.classList.add("minimal-player");\r\n } else {\r\n document.documentElement.classList.remove("minimal-player");\r\n }\r\n if (event.contentRect.width < 550) {\r\n document.documentElement.classList.add("extra-minimal-player");\r\n } else {\r\n document.documentElement.classList.remove("extra-minimal-player");\r\n }\r\n }\r\n});\r\n\r\n(function Dribbblish() {\r\n const progBar = document.querySelector(".playback-bar");\r\n const root = document.querySelector(".Root");\r\n\r\n if (!Spicetify.Player.origin || !progBar || !root) {\r\n setTimeout(Dribbblish, 300);\r\n return;\r\n }\r\n\r\n const progKnob = progBar.querySelector(".progress-bar__slider");\r\n\r\n const tooltip = document.createElement("div");\r\n tooltip.className = "prog-tooltip";\r\n progKnob.append(tooltip);\r\n\r\n function updateProgTime(timeOverride) {\r\n const newText = Spicetify.Player.formatTime(timeOverride || Spicetify.Player.getProgress()) + " / " + Spicetify.Player.formatTime(Spicetify.Player.getDuration());\r\n // To reduce DOM Updates when the Song is Paused\r\n if (tooltip.innerText != newText) tooltip.innerText = newText;\r\n }\r\n const knobPosObserver = new MutationObserver((muts) => {\r\n const progressPercentage = Number(getComputedStyle(document.querySelector(".progress-bar")).getPropertyValue("--progress-bar-transform").replace("%", "")) / 100;\r\n updateProgTime(Spicetify.Player.getDuration() * progressPercentage);\r\n });\r\n knobPosObserver.observe(document.querySelector(".progress-bar"), {\r\n attributes: true,\r\n attributeFilter: ["style"]\r\n });\r\n Spicetify.Player.addEventListener("songchange", () => updateProgTime());\r\n updateProgTime();\r\n\r\n Spicetify.CosmosAsync.sub("sp://connect/v1", (state) => {\r\n const isExternal = state.devices.some((a) => a.is_active);\r\n if (isExternal) {\r\n root.classList.add("is-connectBarVisible");\r\n } else {\r\n root.classList.remove("is-connectBarVisible");\r\n }\r\n });\r\n\r\n const filePickerForm = document.createElement("form");\r\n filePickerForm.setAttribute("aria-hidden", true);\r\n filePickerForm.innerHTML = \'<input type="file" class="hidden-visually" />\';\r\n document.body.appendChild(filePickerForm);\r\n /** @type {HTMLInputElement} */\r\n const filePickerInput = filePickerForm.childNodes[0];\r\n filePickerInput.accept = ["image/jpeg", "image/apng", "image/avif", "image/gif", "image/png", "image/svg+xml", "image/webp"].join(",");\r\n\r\n filePickerInput.onchange = () => {\r\n if (!filePickerInput.files.length) return;\r\n\r\n const file = filePickerInput.files[0];\r\n const reader = new FileReader();\r\n reader.onload = (event) => {\r\n const result = event.target.result;\r\n const id = Spicetify.URI.from(filePickerInput.uri).id;\r\n try {\r\n localStorage.setItem("dribbblish:folder-image:" + id, result);\r\n } catch {\r\n Spicetify.showNotification("File too large");\r\n }\r\n DribbblishShared.loadPlaylistImage?.call();\r\n };\r\n reader.readAsDataURL(file);\r\n };\r\n\r\n new Spicetify.ContextMenu.Item(\r\n "Remove folder image",\r\n ([uri]) => {\r\n const id = Spicetify.URI.from(uri).id;\r\n localStorage.removeItem("dribbblish:folder-image:" + id);\r\n DribbblishShared.loadPlaylistImage?.call();\r\n },\r\n ([uri]) => Spicetify.URI.isFolder(uri),\r\n "x"\r\n ).register();\r\n new Spicetify.ContextMenu.Item(\r\n "Choose folder image",\r\n ([uri]) => {\r\n filePickerInput.uri = uri;\r\n filePickerForm.reset();\r\n filePickerInput.click();\r\n },\r\n ([uri]) => Spicetify.URI.isFolder(uri),\r\n "edit"\r\n ).register();\r\n})();\r\n\r\nlet current = "2.6.0";\r\n\r\n/* Config settings */\r\n\r\nDribbblishShared.config.register({\r\n area: "Animations & Transitions",\r\n type: "slider",\r\n key: "fadeDuration",\r\n name: "Color Fade Duration",\r\n description: "Select the duration of the color fading transition",\r\n defaultValue: 0.5,\r\n data: {\r\n min: 0,\r\n max: 10,\r\n step: 0.1,\r\n suffix: "s"\r\n },\r\n onChange: (val) => document.documentElement.style.setProperty("--song-transition-speed", val + "s")\r\n});\r\n\r\n// waitForElement because Spicetify is not initialized at startup\r\nwaitForElement(["#main"], () => {\r\n DribbblishShared.config.register({\r\n area: { name: "About", order: 999 },\r\n type: "button",\r\n key: "aboutDribbblish",\r\n name: "Info",\r\n description: `\r\n OS: ${capitalizeFirstLetter(Spicetify.Platform.PlatformData.os_name)} v${Spicetify.Platform.PlatformData.os_version}\r\n Spotify: v${Spicetify.Platform.PlatformData.event_sender_context_information?.client_version_string ?? Spicetify.Platform.PlatformData.client_version_triple}\r\n Dribbblish: v${current}\r\n `,\r\n data: "Copy",\r\n onChange: (val) => {\r\n copyToClipboard(DribbblishShared.config.getOptions("aboutDribbblish").description);\r\n Spicetify.showNotification("Copied Versions");\r\n }\r\n });\r\n});\r\n\r\nfunction capitalizeFirstLetter(string) {\r\n return string.charAt(0).toUpperCase() + string.slice(1);\r\n}\r\n\r\nfunction copyToClipboard(text) {\r\n var input = document.createElement("textarea");\r\n input.style.display = "fixed";\r\n input.innerHTML = text;\r\n document.body.appendChild(input);\r\n input.select();\r\n var result = document.execCommand("copy");\r\n document.body.removeChild(input);\r\n return result;\r\n}\r\n\r\n/* js */\r\nfunction getAlbumInfo(uri) {\r\n return Spicetify.CosmosAsync.get(`hm://album/v1/album-app/album/${uri}/desktop`);\r\n}\r\n\r\nfunction isLight(hex) {\r\n var [r, g, b] = hexToRgb(hex).map(Number);\r\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\r\n return brightness > 128;\r\n}\r\n\r\nfunction hexToRgb(hex) {\r\n var bigint = parseInt(hex.replace("#", ""), 16);\r\n var r = (bigint >> 16) & 255;\r\n var g = (bigint >> 8) & 255;\r\n var b = bigint & 255;\r\n return [r, g, b];\r\n}\r\n\r\nfunction rgbToHex([r, g, b]) {\r\n const rgb = (r << 16) | (g << 8) | (b << 0);\r\n return "#" + (0x1000000 + rgb).toString(16).slice(1);\r\n}\r\n\r\nconst LightenDarkenColor = (h, p) =>\r\n "#" +\r\n [1, 3, 5]\r\n .map((s) => parseInt(h.substr(s, 2), 16))\r\n .map((c) => parseInt((c * (100 + p)) / 100))\r\n .map((c) => (c < 255 ? c : 255))\r\n .map((c) => c.toString(16).padStart(2, "0"))\r\n .join("");\r\n\r\nfunction rgbToHsl([r, g, b]) {\r\n (r /= 255), (g /= 255), (b /= 255);\r\n var max = Math.max(r, g, b),\r\n min = Math.min(r, g, b);\r\n var h,\r\n s,\r\n l = (max + min) / 2;\r\n if (max == min) {\r\n h = s = 0; // achromatic\r\n } else {\r\n var d = max - min;\r\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\r\n switch (max) {\r\n case r:\r\n h = (g - b) / d + (g < b ? 6 : 0);\r\n break;\r\n case g:\r\n h = (b - r) / d + 2;\r\n break;\r\n case b:\r\n h = (r - g) / d + 4;\r\n break;\r\n }\r\n h /= 6;\r\n }\r\n return [h, s, l];\r\n}\r\n\r\nfunction hslToRgb([h, s, l]) {\r\n var r, g, b;\r\n if (s == 0) {\r\n r = g = b = l; // achromatic\r\n } else {\r\n function hue2rgb(p, q, t) {\r\n if (t < 0) t += 1;\r\n if (t > 1) t -= 1;\r\n if (t < 1 / 6) return p + (q - p) * 6 * t;\r\n if (t < 1 / 2) return q;\r\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\r\n return p;\r\n }\r\n var q = l < 0.5 ? l * (1 + s) : l + s - l * s;\r\n var p = 2 * l - q;\r\n r = hue2rgb(p, q, h + 1 / 3);\r\n g = hue2rgb(p, q, h);\r\n b = hue2rgb(p, q, h - 1 / 3);\r\n }\r\n return [r * 255, g * 255, b * 255];\r\n}\r\n\r\nfunction setLightness(hex, lightness) {\r\n hsl = rgbToHsl(hexToRgb(hex));\r\n hsl[2] = lightness;\r\n return rgbToHex(hslToRgb(hsl));\r\n}\r\n\r\nfunction parseComputedStyleColor(col) {\r\n if (col.startsWith("#")) return col;\r\n if (col.startsWith("rgb("))\r\n return rgbToHex(\r\n col\r\n .replace(/rgb|(|)/g, "")\r\n .split(",")\r\n .map((part) => Number(part.trim()))\r\n );\r\n}\r\n\r\n// `parseComputedStyleColor()` beacuse "--spice-sidebar" is `rgb()`\r\nlet textColor = parseComputedStyleColor(getComputedStyle(document.documentElement).getPropertyValue("--spice-text"));\r\nlet textColorBg = parseComputedStyleColor(getComputedStyle(document.documentElement).getPropertyValue("--spice-main"));\r\nlet sidebarColor = parseComputedStyleColor(getComputedStyle(document.documentElement).getPropertyValue("--spice-sidebar"));\r\n\r\nfunction setRootColor(name, colHex) {\r\n let root = document.documentElement;\r\n if (root === null) return;\r\n root.style.setProperty("--spice-" + name, colHex);\r\n root.style.setProperty("--spice-rgb-" + name, hexToRgb(colHex).join(","));\r\n}\r\n\r\nfunction toggleDark(setDark) {\r\n if (setDark === undefined) setDark = isLight(textColorBg);\r\n\r\n document.documentElement.style.setProperty("--is_light", setDark ? 0 : 1);\r\n textColorBg = setDark ? "#0A0A0A" : "#FAFAFA";\r\n\r\n setRootColor("main", textColorBg);\r\n setRootColor("player", textColorBg);\r\n setRootColor("card", setDark ? "#040404" : "#ECECEC");\r\n setRootColor("subtext", setDark ? "#EAEAEA" : "#3D3D3D");\r\n setRootColor("notification", setDark ? "#303030" : "#DDDDDD");\r\n\r\n updateColors(textColor, sidebarColor, false);\r\n}\r\n\r\nfunction checkDarkLightMode(colors) {\r\n const theme = DribbblishShared.config.get("theme");\r\n if (theme == 2) {\r\n // Based on Time\r\n const start = 60 * parseInt(DribbblishShared.config.get("darkModeOnTime").split(":")[0]) + parseInt(DribbblishShared.config.get("darkModeOnTime").split(":")[1]);\r\n const end = 60 * parseInt(DribbblishShared.config.get("darkModeOffTime").split(":")[0]) + parseInt(DribbblishShared.config.get("darkModeOffTime").split(":")[1]);\r\n\r\n const now = new Date();\r\n const time = 60 * now.getHours() + now.getMinutes();\r\n\r\n if (end < start) dark = start <= time || time < end;\r\n else dark = start <= time && time < end;\r\n toggleDark(dark);\r\n } else if (theme == 3) {\r\n // Based on Color\r\n if (colors && colors.length > 0) toggleDark(isLight(colors[0]));\r\n }\r\n}\r\n// Run every Minute to check time and set dark / light mode\r\nsetInterval(checkDarkLightMode, 60000);\r\n\r\nDribbblishShared.config.register({\r\n area: "Theme",\r\n type: "checkbox",\r\n key: "dynamicColors",\r\n name: "Dynamic",\r\n description: "If the Theme\'s Color should be extracted from Albumart",\r\n defaultValue: true,\r\n onChange: (val) => updateColors(),\r\n showChildren: (val) => !val,\r\n children: [\r\n {\r\n type: "color",\r\n key: "colorOverride",\r\n name: "Color",\r\n description: "The Color of the Theme",\r\n defaultValue: "#1ed760",\r\n fireInitialChange: false,\r\n onChange: (val) => updateColors()\r\n }\r\n ]\r\n});\r\n\r\nDribbblishShared.config.register({\r\n area: "Theme",\r\n type: "select",\r\n data: ["Dark", "Light", "Based on Time", "Based on Color"],\r\n key: "theme",\r\n name: "Theme",\r\n description: "Select Dark / Bright mode",\r\n defaultValue: 0,\r\n showChildren: (val) => {\r\n if (val == 2) return ["darkModeOnTime", "darkModeOffTime"];\r\n //if (val == 3) return [""];\r\n return false;\r\n },\r\n onChange: (val) => {\r\n switch (val) {\r\n case 0:\r\n toggleDark(true);\r\n break;\r\n case 1:\r\n toggleDark(false);\r\n break;\r\n case 2:\r\n checkDarkLightMode();\r\n break;\r\n case 3:\r\n checkDarkLightMode();\r\n break;\r\n }\r\n },\r\n children: [\r\n {\r\n type: "time",\r\n key: "darkModeOnTime",\r\n name: "Dark Mode On Time",\r\n description: "Beginning of Dark mode time",\r\n defaultValue: "20:00",\r\n fireInitialChange: false,\r\n onChange: checkDarkLightMode\r\n },\r\n {\r\n type: "time",\r\n key: "darkModeOffTime",\r\n name: "Dark Mode Off Time",\r\n description: "End of Dark mode time",\r\n defaultValue: "06:00",\r\n fireInitialChange: false,\r\n onChange: checkDarkLightMode\r\n }\r\n ]\r\n});\r\n\r\nvar currentColor;\r\nvar currentSideColor;\r\n\r\nfunction updateColors(textColHex, sideColHex, checkDarkMode = true) {\r\n if (textColHex && sideColHex) {\r\n currentColor = textColHex;\r\n currentSideColor = sideColHex;\r\n } else {\r\n if (!(currentColor && currentSideColor)) return; // If `updateColors()` is called early these vars are undefined and would break\r\n textColHex = currentColor;\r\n sideColHex = currentSideColor;\r\n }\r\n\r\n if (!DribbblishShared.config.get("dynamicColors")) {\r\n const col = DribbblishShared.config.get("colorOverride");\r\n textColHex = col;\r\n sideColHex = col;\r\n }\r\n\r\n let isLightBg = isLight(textColorBg);\r\n if (isLightBg) textColHex = LightenDarkenColor(textColHex, -15); // vibrant color is always too bright for white bg mode\r\n\r\n let darkColHex = LightenDarkenColor(textColHex, isLightBg ? 12 : -20);\r\n let darkerColHex = LightenDarkenColor(textColHex, isLightBg ? 30 : -40);\r\n let buttonBgColHex = setLightness(textColHex, isLightBg ? 0.9 : 0.14);\r\n setRootColor("text", textColHex);\r\n setRootColor("button", darkerColHex);\r\n setRootColor("button-active", darkColHex);\r\n setRootColor("selected-row", darkerColHex);\r\n setRootColor("tab-active", buttonBgColHex);\r\n setRootColor("button-disabled", buttonBgColHex);\r\n setRootColor("sidebar", sideColHex);\r\n\r\n if (checkDarkMode) checkDarkLightMode([textColHex, sideColHex]);\r\n}\r\n\r\nlet nearArtistSpanText = "";\r\nlet coverListenerInstalled = true;\r\nasync function songchange() {\r\n try {\r\n // warning popup\r\n if (Spicetify.Platform.PlatformData.client_version_triple < "1.1.68") Spicetify.showNotification(`Your version of Spotify ${Spicetify.Platform.PlatformData.client_version_triple}) is un-supported`);\r\n } catch (err) {\r\n console.error(err);\r\n }\r\n\r\n let album_uri = Spicetify.Player.data.track.metadata.album_uri;\r\n let bgImage = Spicetify.Player.data.track.metadata.image_url;\r\n if (bgImage === undefined) {\r\n bgImage = "/images/tracklist-row-song-fallback.svg";\r\n textColor = "#509bf5";\r\n updateColors(textColor, textColor);\r\n coverListenerInstalled = false;\r\n }\r\n if (!coverListenerInstalled) hookCoverChange(true);\r\n\r\n if (album_uri !== undefined && !album_uri.includes("spotify:show")) {\r\n const albumInfo = await getAlbumInfo(album_uri.replace("spotify:album:", ""));\r\n\r\n let album_date = new Date(albumInfo.year, (albumInfo.month || 1) - 1, albumInfo.day || 0);\r\n let recent_date = new Date();\r\n recent_date.setMonth(recent_date.getMonth() - 6);\r\n album_date = album_date.toLocaleString("default", album_date > recent_date ? { year: "numeric", month: "short" } : { year: "numeric" });\r\n album_link = \'<a title="\' + Spicetify.Player.data.track.metadata.album_title + \'" href="\' + album_uri + \'" data-uri="\' + album_uri + \'" data-interaction-target="album-name" class="tl-cell__content">\' + Spicetify.Player.data.track.metadata.album_title + "</a>";\r\n\r\n nearArtistSpanText = album_link + " • " + album_date;\r\n } else if (Spicetify.Player.data.track.uri.includes("spotify:episode")) {\r\n // podcast\r\n bgImage = bgImage.replace("spotify:image:", "https://i.scdn.co/image/");\r\n nearArtistSpanText = Spicetify.Player.data.track.metadata.album_title;\r\n } else if (Spicetify.Player.data.track.metadata.is_local == "true") {\r\n // local file\r\n nearArtistSpanText = Spicetify.Player.data.track.metadata.album_title;\r\n } else if (Spicetify.Player.data.track.provider == "ad") {\r\n // ad\r\n nearArtistSpanText = "advertisement";\r\n coverListenerInstalled = false;\r\n return;\r\n } else {\r\n // When clicking a song from the homepage, songChange is fired with half empty metadata\r\n // todo: retry only once?\r\n setTimeout(songchange, 200);\r\n }\r\n\r\n if (document.querySelector("#main-trackInfo-year") === null) {\r\n waitForElement([".main-trackInfo-container"], (queries) => {\r\n nearArtistSpan = document.createElement("div");\r\n nearArtistSpan.id = "main-trackInfo-year";\r\n nearArtistSpan.classList.add("main-trackInfo-artists", "ellipsis-one-line", "main-type-finale");\r\n nearArtistSpan.innerHTML = nearArtistSpanText;\r\n queries[0].append(nearArtistSpan);\r\n });\r\n } else {\r\n nearArtistSpan.innerHTML = nearArtistSpanText;\r\n }\r\n document.documentElement.style.setProperty("--image_url", \'url("\' + bgImage + \'")\');\r\n}\r\n\r\nSpicetify.Player.addEventListener("songchange", songchange);\r\n\r\nfunction pickCoverColor(img) {\r\n if (!img.currentSrc.startsWith("spotify:")) return;\r\n var swatches = new Vibrant(img, 5).swatches();\r\n lightCols = ["Vibrant", "DarkVibrant", "Muted", "LightVibrant"];\r\n darkCols = ["Vibrant", "LightVibrant", "Muted", "DarkVibrant"];\r\n\r\n mainCols = isLight(textColorBg) ? lightCols : darkCols;\r\n textColor = "#509bf5";\r\n for (var col in mainCols)\r\n if (swatches[mainCols[col]]) {\r\n textColor = swatches[mainCols[col]].getHex();\r\n break;\r\n }\r\n\r\n sidebarColor = "#509bf5";\r\n for (var col in lightCols)\r\n if (swatches[lightCols[col]]) {\r\n sidebarColor = swatches[lightCols[col]].getHex();\r\n break;\r\n }\r\n updateColors(textColor, sidebarColor);\r\n}\r\n\r\nwaitForElement([".main-nowPlayingBar-left"], (queries) => {\r\n var observer = new MutationObserver(function (mutations) {\r\n mutations.forEach(function (mutation) {\r\n if (mutation.removedNodes.length > 0) coverListenerInstalled = false;\r\n });\r\n });\r\n observer.observe(queries[0], { childList: true });\r\n});\r\n\r\nfunction hookCoverChange(pick) {\r\n waitForElement([".cover-art-image"], (queries) => {\r\n coverListenerInstalled = true;\r\n if (pick && queries[0].complete && queries[0].naturalHeight !== 0) pickCoverColor(queries[0]);\r\n queries[0].addEventListener("load", function () {\r\n try {\r\n pickCoverColor(queries[0]);\r\n } catch (error) {\r\n console.error(error);\r\n setTimeout(pickCoverColor, 300, queries[0]);\r\n }\r\n });\r\n });\r\n}\r\n\r\nhookCoverChange(false);\r\n\r\n(function Startup() {\r\n if (!Spicetify.showNotification) {\r\n setTimeout(Startup, 300);\r\n return;\r\n }\r\n // Check latest release\r\n fetch("https://api.github.com/repos/JulienMaille/dribbblish-dynamic-theme/releases/latest")\r\n .then((response) => {\r\n return response.json();\r\n })\r\n .then((data) => {\r\n if (data.tag_name > current) {\r\n upd = document.createElement("div");\r\n upd.innerText = `Theme UPD v${data.tag_name} avail.`;\r\n upd.classList.add("ellipsis-one-line", "main-type-finale");\r\n upd.setAttribute("title", `Changes: ${data.name}`);\r\n upd.style.setProperty("color", "var(--spice-button-active)");\r\n document.querySelector(".main-userWidget-box").append(upd);\r\n document.querySelector(".main-userWidget-box").classList.add("update-avail");\r\n new Spicetify.Menu.Item("Update Dribbblish", false, () => window.open("https://github.com/JulienMaille/dribbblish-dynamic-theme/blob/main/README.md#install--update", "_blank")).register();\r\n }\r\n })\r\n .catch((err) => {\r\n // Do something for an error here\r\n console.error(err);\r\n });\r\n})();\r\n\r\ndocument.documentElement.style.setProperty("--warning_message", " ");\r\n\n\n//# sourceURL=webpack:///./src/js/main.js?');
|
|
|
|
/***/
|
|
}
|
|
|
|
/******/
|
|
};
|
|
/************************************************************************/
|
|
/******/ // The require scope
|
|
/******/ var __webpack_require__ = {};
|
|
/******/
|
|
/************************************************************************/
|
|
/******/ /* webpack/runtime/define property getters */
|
|
/******/ (() => {
|
|
/******/ // define getter functions for harmony exports
|
|
/******/ __webpack_require__.d = (exports, definition) => {
|
|
/******/ for (var key in definition) {
|
|
/******/ if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
/******/
|
|
}
|
|
/******/
|
|
}
|
|
/******/
|
|
};
|
|
/******/
|
|
})();
|
|
/******/
|
|
/******/ /* webpack/runtime/global */
|
|
/******/ (() => {
|
|
/******/ __webpack_require__.g = (function () {
|
|
/******/ if (typeof globalThis === "object") return globalThis;
|
|
/******/ try {
|
|
/******/ return this || new Function("return this")();
|
|
/******/
|
|
} catch (e) {
|
|
/******/ if (typeof window === "object") return window;
|
|
/******/
|
|
}
|
|
/******/
|
|
})();
|
|
/******/
|
|
})();
|
|
/******/
|
|
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
/******/ (() => {
|
|
/******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
|
|
/******/
|
|
})();
|
|
/******/
|
|
/******/ /* webpack/runtime/make namespace object */
|
|
/******/ (() => {
|
|
/******/ // define __esModule on exports
|
|
/******/ __webpack_require__.r = (exports) => {
|
|
/******/ if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
|
|
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
/******/
|
|
}
|
|
/******/ Object.defineProperty(exports, "__esModule", { value: true });
|
|
/******/
|
|
};
|
|
/******/
|
|
})();
|
|
/******/
|
|
/******/ /* webpack/runtime/publicPath */
|
|
/******/ (() => {
|
|
/******/ var scriptUrl;
|
|
/******/ if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + "";
|
|
/******/ var document = __webpack_require__.g.document;
|
|
/******/ if (!scriptUrl && document) {
|
|
/******/ if (document.currentScript) /******/ scriptUrl = document.currentScript.src;
|
|
/******/ if (!scriptUrl) {
|
|
/******/ var scripts = document.getElementsByTagName("script");
|
|
/******/ if (scripts.length) scriptUrl = scripts[scripts.length - 1].src;
|
|
/******/
|
|
}
|
|
/******/
|
|
}
|
|
/******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
|
|
/******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
|
|
/******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
|
|
/******/ scriptUrl = scriptUrl
|
|
.replace(/#.*$/, "")
|
|
.replace(/\?.*$/, "")
|
|
.replace(/\/[^\/]+$/, "/");
|
|
/******/ __webpack_require__.p = scriptUrl;
|
|
/******/
|
|
})();
|
|
/******/
|
|
/************************************************************************/
|
|
/******/
|
|
/******/ // startup
|
|
/******/ // Load entry module and return exports
|
|
/******/ // This entry module can't be inlined because the eval devtool is used.
|
|
/******/ __webpack_modules__["./src/js/main.js"](0, {}, __webpack_require__);
|
|
/******/ var __webpack_exports__ = {};
|
|
/******/ __webpack_modules__["./src/styles/main.scss"](0, __webpack_exports__, __webpack_require__);
|
|
/******/
|
|
/******/
|
|
})();
|