|
@@ -17,7 +17,7 @@
|
|
// @license AGPL-3.0-only
|
|
// @license AGPL-3.0-only
|
|
// @author Sv443
|
|
// @author Sv443
|
|
// @copyright Sv443 (https://github.com/Sv443)
|
|
// @copyright Sv443 (https://github.com/Sv443)
|
|
-// @icon https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/images/logo/logo_dev_48.png
|
|
|
|
|
|
+// @icon https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/images/logo/logo_dev_48.png
|
|
// @match https://music.youtube.com/*
|
|
// @match https://music.youtube.com/*
|
|
// @match https://www.youtube.com/*
|
|
// @match https://www.youtube.com/*
|
|
// @run-at document-start
|
|
// @run-at document-start
|
|
@@ -33,49 +33,49 @@
|
|
// @grant GM.openInTab
|
|
// @grant GM.openInTab
|
|
// @grant unsafeWindow
|
|
// @grant unsafeWindow
|
|
// @noframes
|
|
// @noframes
|
|
-// @resource css-bundle https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/dist/BetterYTM.css
|
|
|
|
-// @resource css-above_queue_btns https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/style/aboveQueueBtns.css
|
|
|
|
-// @resource css-anchor_improvements https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/style/anchorImprovements.css
|
|
|
|
-// @resource css-fix_hdr https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/style/fixHDR.css
|
|
|
|
-// @resource css-fix_spacing https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/style/fixSpacing.css
|
|
|
|
-// @resource css-show_votes https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/style/showVotes.css
|
|
|
|
-// @resource css-vol_slider_size https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/style/volSliderSize.css
|
|
|
|
-// @resource doc-changelog https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/changelog.md
|
|
|
|
-// @resource icon-advanced_mode https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/plus_circle_small.svg
|
|
|
|
-// @resource icon-arrow_down https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/arrow_down.svg
|
|
|
|
-// @resource icon-auto_like_enabled https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/auto_like_enabled.svg
|
|
|
|
-// @resource icon-auto_like https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/auto_like.svg
|
|
|
|
-// @resource icon-clear_list https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/clear_list.svg
|
|
|
|
-// @resource icon-delete https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/delete.svg
|
|
|
|
-// @resource icon-edit https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/edit.svg
|
|
|
|
-// @resource icon-error https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/error.svg
|
|
|
|
-// @resource icon-experimental https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/beaker_small.svg
|
|
|
|
-// @resource icon-globe_small https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/globe_small.svg
|
|
|
|
-// @resource icon-globe https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/globe.svg
|
|
|
|
-// @resource icon-help https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/help.svg
|
|
|
|
-// @resource icon-image_filled https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/image_filled.svg
|
|
|
|
-// @resource icon-image https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/image.svg
|
|
|
|
-// @resource icon-link https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/link.svg
|
|
|
|
-// @resource icon-lyrics https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/lyrics.svg
|
|
|
|
-// @resource icon-reload https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/refresh.svg
|
|
|
|
-// @resource icon-skip_to https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/skip_to.svg
|
|
|
|
-// @resource icon-spinner https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/icons/spinner.svg
|
|
|
|
-// @resource img-close https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/images/close.png
|
|
|
|
-// @resource img-discord https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/images/external/discord.png
|
|
|
|
-// @resource img-github https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/images/external/github.png
|
|
|
|
-// @resource img-greasyfork https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/images/external/greasyfork.png
|
|
|
|
-// @resource img-logo_dev https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/images/logo/logo_dev_48.png
|
|
|
|
-// @resource img-logo https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/images/logo/logo_48.png
|
|
|
|
-// @resource img-openuserjs https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/images/external/openuserjs.png
|
|
|
|
-// @resource trans-de_DE https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/translations/de_DE.json
|
|
|
|
-// @resource trans-en_US https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/translations/en_US.json
|
|
|
|
-// @resource trans-en_UK https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/translations/en_UK.json
|
|
|
|
-// @resource trans-es_ES https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/translations/es_ES.json
|
|
|
|
-// @resource trans-fr_FR https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/translations/fr_FR.json
|
|
|
|
-// @resource trans-hi_IN https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/translations/hi_IN.json
|
|
|
|
-// @resource trans-ja_JA https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/translations/ja_JA.json
|
|
|
|
-// @resource trans-pt_BR https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/translations/pt_BR.json
|
|
|
|
-// @resource trans-zh_CN https://raw.githubusercontent.com/Sv443/BetterYTM/f91767c2/assets/translations/zh_CN.json
|
|
|
|
|
|
+// @resource css-bundle https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/dist/BetterYTM.css
|
|
|
|
+// @resource css-above_queue_btns https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/style/aboveQueueBtns.css
|
|
|
|
+// @resource css-anchor_improvements https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/style/anchorImprovements.css
|
|
|
|
+// @resource css-fix_hdr https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/style/fixHDR.css
|
|
|
|
+// @resource css-fix_spacing https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/style/fixSpacing.css
|
|
|
|
+// @resource css-show_votes https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/style/showVotes.css
|
|
|
|
+// @resource css-vol_slider_size https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/style/volSliderSize.css
|
|
|
|
+// @resource doc-changelog https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/changelog.md
|
|
|
|
+// @resource icon-advanced_mode https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/plus_circle_small.svg
|
|
|
|
+// @resource icon-arrow_down https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/arrow_down.svg
|
|
|
|
+// @resource icon-auto_like_enabled https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/auto_like_enabled.svg
|
|
|
|
+// @resource icon-auto_like https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/auto_like.svg
|
|
|
|
+// @resource icon-clear_list https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/clear_list.svg
|
|
|
|
+// @resource icon-delete https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/delete.svg
|
|
|
|
+// @resource icon-edit https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/edit.svg
|
|
|
|
+// @resource icon-error https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/error.svg
|
|
|
|
+// @resource icon-experimental https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/beaker_small.svg
|
|
|
|
+// @resource icon-globe_small https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/globe_small.svg
|
|
|
|
+// @resource icon-globe https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/globe.svg
|
|
|
|
+// @resource icon-help https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/help.svg
|
|
|
|
+// @resource icon-image_filled https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/image_filled.svg
|
|
|
|
+// @resource icon-image https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/image.svg
|
|
|
|
+// @resource icon-link https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/link.svg
|
|
|
|
+// @resource icon-lyrics https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/lyrics.svg
|
|
|
|
+// @resource icon-reload https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/refresh.svg
|
|
|
|
+// @resource icon-skip_to https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/skip_to.svg
|
|
|
|
+// @resource icon-spinner https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/icons/spinner.svg
|
|
|
|
+// @resource img-close https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/images/close.png
|
|
|
|
+// @resource img-discord https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/images/external/discord.png
|
|
|
|
+// @resource img-github https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/images/external/github.png
|
|
|
|
+// @resource img-greasyfork https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/images/external/greasyfork.png
|
|
|
|
+// @resource img-logo_dev https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/images/logo/logo_dev_48.png
|
|
|
|
+// @resource img-logo https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/images/logo/logo_48.png
|
|
|
|
+// @resource img-openuserjs https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/images/external/openuserjs.png
|
|
|
|
+// @resource trans-de_DE https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/translations/de_DE.json
|
|
|
|
+// @resource trans-en_US https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/translations/en_US.json
|
|
|
|
+// @resource trans-en_UK https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/translations/en_UK.json
|
|
|
|
+// @resource trans-es_ES https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/translations/es_ES.json
|
|
|
|
+// @resource trans-fr_FR https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/translations/fr_FR.json
|
|
|
|
+// @resource trans-hi_IN https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/translations/hi_IN.json
|
|
|
|
+// @resource trans-ja_JA https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/translations/ja_JA.json
|
|
|
|
+// @resource trans-pt_BR https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/translations/pt_BR.json
|
|
|
|
+// @resource trans-zh_CN https://raw.githubusercontent.com/Sv443/BetterYTM/308d1e4c/assets/translations/zh_CN.json
|
|
// @require https://cdn.jsdelivr.net/npm/@sv443-network/[email protected]/dist/index.global.js
|
|
// @require https://cdn.jsdelivr.net/npm/@sv443-network/[email protected]/dist/index.global.js
|
|
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/fuse.basic.js
|
|
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/fuse.basic.js
|
|
// @require https://cdn.jsdelivr.net/npm/[email protected]/lib/marked.umd.js
|
|
// @require https://cdn.jsdelivr.net/npm/[email protected]/lib/marked.umd.js
|
|
@@ -183,10 +183,12 @@ var PluginIntent;
|
|
PluginIntent[PluginIntent["WriteTranslations"] = 8] = "WriteTranslations";
|
|
PluginIntent[PluginIntent["WriteTranslations"] = 8] = "WriteTranslations";
|
|
/** Plugin can create modal dialogs */
|
|
/** Plugin can create modal dialogs */
|
|
PluginIntent[PluginIntent["CreateModalDialogs"] = 16] = "CreateModalDialogs";
|
|
PluginIntent[PluginIntent["CreateModalDialogs"] = 16] = "CreateModalDialogs";
|
|
|
|
+ /** Plugin can read and write auto-like data */
|
|
|
|
+ PluginIntent[PluginIntent["ReadAndWriteAutoLikeData"] = 32] = "ReadAndWriteAutoLikeData";
|
|
})(PluginIntent || (PluginIntent = {}));const modeRaw = "development";
|
|
})(PluginIntent || (PluginIntent = {}));const modeRaw = "development";
|
|
const branchRaw = "develop";
|
|
const branchRaw = "develop";
|
|
const hostRaw = "github";
|
|
const hostRaw = "github";
|
|
-const buildNumberRaw = "f91767c2";
|
|
|
|
|
|
+const buildNumberRaw = "308d1e4c";
|
|
/** The mode in which the script was built (production or development) */
|
|
/** The mode in which the script was built (production or development) */
|
|
const mode = (modeRaw.match(/^#{{.+}}$/) ? "production" : modeRaw);
|
|
const mode = (modeRaw.match(/^#{{.+}}$/) ? "production" : modeRaw);
|
|
/** The branch to use in various URLs that point to the GitHub repo */
|
|
/** The branch to use in various URLs that point to the GitHub repo */
|
|
@@ -591,7 +593,7 @@ class BytmDialog extends NanoEmitter {
|
|
*/
|
|
*/
|
|
open(e) {
|
|
open(e) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
- var _a;
|
|
|
|
|
|
+ var _a, _b, _c;
|
|
e === null || e === void 0 ? void 0 : e.preventDefault();
|
|
e === null || e === void 0 ? void 0 : e.preventDefault();
|
|
e === null || e === void 0 ? void 0 : e.stopImmediatePropagation();
|
|
e === null || e === void 0 ? void 0 : e.stopImmediatePropagation();
|
|
if (this.isOpen())
|
|
if (this.isOpen())
|
|
@@ -601,8 +603,6 @@ class BytmDialog extends NanoEmitter {
|
|
throw new Error(`A dialog with the same ID of '${this.id}' already exists and is open!`);
|
|
throw new Error(`A dialog with the same ID of '${this.id}' already exists and is open!`);
|
|
if (!this.isMounted())
|
|
if (!this.isMounted())
|
|
yield this.mount();
|
|
yield this.mount();
|
|
- document.body.classList.add("bytm-disable-scroll");
|
|
|
|
- (_a = document.querySelector(getDomain() === "ytm" ? "ytmusic-app" : "ytd-app")) === null || _a === void 0 ? void 0 : _a.setAttribute("inert", "true");
|
|
|
|
const dialogBg = document.querySelector(`#bytm-${this.id}-dialog-bg`);
|
|
const dialogBg = document.querySelector(`#bytm-${this.id}-dialog-bg`);
|
|
if (!dialogBg)
|
|
if (!dialogBg)
|
|
return warn(`Couldn't find background element for dialog with ID '${this.id}'`);
|
|
return warn(`Couldn't find background element for dialog with ID '${this.id}'`);
|
|
@@ -611,6 +611,19 @@ class BytmDialog extends NanoEmitter {
|
|
dialogBg.inert = false;
|
|
dialogBg.inert = false;
|
|
currentDialogId = this.id;
|
|
currentDialogId = this.id;
|
|
openDialogs.unshift(this.id);
|
|
openDialogs.unshift(this.id);
|
|
|
|
+ // make sure all other dialogs are inert
|
|
|
|
+ for (const dialogId of openDialogs) {
|
|
|
|
+ if (dialogId !== this.id) {
|
|
|
|
+ // special treatment for the old config menu, as always
|
|
|
|
+ if (dialogId === "cfg-menu")
|
|
|
|
+ (_a = document.querySelector("#bytm-cfg-menu-bg")) === null || _a === void 0 ? void 0 : _a.setAttribute("inert", "true");
|
|
|
|
+ else
|
|
|
|
+ (_b = document.querySelector(`#bytm-${dialogId}-dialog-bg`)) === null || _b === void 0 ? void 0 : _b.setAttribute("inert", "true");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // make sure body is inert and scroll is locked
|
|
|
|
+ document.body.classList.add("bytm-disable-scroll");
|
|
|
|
+ (_c = document.querySelector(getDomain() === "ytm" ? "ytmusic-app" : "ytd-app")) === null || _c === void 0 ? void 0 : _c.setAttribute("inert", "true");
|
|
this.events.emit("open");
|
|
this.events.emit("open");
|
|
emitInterface("bytm:dialogOpened", this);
|
|
emitInterface("bytm:dialogOpened", this);
|
|
emitInterface(`bytm:dialogOpened:${this.id}`, this);
|
|
emitInterface(`bytm:dialogOpened:${this.id}`, this);
|
|
@@ -619,7 +632,7 @@ class BytmDialog extends NanoEmitter {
|
|
}
|
|
}
|
|
/** Closes the dialog - prevents default action and immediate propagation of the passed event */
|
|
/** Closes the dialog - prevents default action and immediate propagation of the passed event */
|
|
close(e) {
|
|
close(e) {
|
|
- var _a;
|
|
|
|
|
|
+ var _a, _b, _c, _d;
|
|
e === null || e === void 0 ? void 0 : e.preventDefault();
|
|
e === null || e === void 0 ? void 0 : e.preventDefault();
|
|
e === null || e === void 0 ? void 0 : e.stopImmediatePropagation();
|
|
e === null || e === void 0 ? void 0 : e.stopImmediatePropagation();
|
|
if (!this.isOpen())
|
|
if (!this.isOpen())
|
|
@@ -631,14 +644,24 @@ class BytmDialog extends NanoEmitter {
|
|
dialogBg.style.visibility = "hidden";
|
|
dialogBg.style.visibility = "hidden";
|
|
dialogBg.style.display = "none";
|
|
dialogBg.style.display = "none";
|
|
dialogBg.inert = true;
|
|
dialogBg.inert = true;
|
|
- if (BytmDialog.getCurrentDialogId() === this.id)
|
|
|
|
- currentDialogId = null;
|
|
|
|
openDialogs.splice(openDialogs.indexOf(this.id), 1);
|
|
openDialogs.splice(openDialogs.indexOf(this.id), 1);
|
|
|
|
+ currentDialogId = (_a = openDialogs[0]) !== null && _a !== void 0 ? _a : null;
|
|
|
|
+ // make sure the new top-most dialog is not inert
|
|
|
|
+ if (currentDialogId) {
|
|
|
|
+ // special treatment for the old config menu, as always
|
|
|
|
+ if (currentDialogId === "cfg-menu")
|
|
|
|
+ (_b = document.querySelector("#bytm-cfg-menu-bg")) === null || _b === void 0 ? void 0 : _b.removeAttribute("inert");
|
|
|
|
+ else
|
|
|
|
+ (_c = document.querySelector(`#bytm-${currentDialogId}-dialog-bg`)) === null || _c === void 0 ? void 0 : _c.removeAttribute("inert");
|
|
|
|
+ }
|
|
|
|
+ // remove the scroll lock and inert attribute on the body if no dialogs are open
|
|
if (openDialogs.length === 0) {
|
|
if (openDialogs.length === 0) {
|
|
document.body.classList.remove("bytm-disable-scroll");
|
|
document.body.classList.remove("bytm-disable-scroll");
|
|
- (_a = document.querySelector(getDomain() === "ytm" ? "ytmusic-app" : "ytd-app")) === null || _a === void 0 ? void 0 : _a.removeAttribute("inert");
|
|
|
|
|
|
+ (_d = document.querySelector(getDomain() === "ytm" ? "ytmusic-app" : "ytd-app")) === null || _d === void 0 ? void 0 : _d.removeAttribute("inert");
|
|
}
|
|
}
|
|
this.events.emit("close");
|
|
this.events.emit("close");
|
|
|
|
+ emitInterface("bytm:dialogClosed", this);
|
|
|
|
+ emitInterface(`bytm:dialogClosed:${this.id}`, this);
|
|
if (this.options.destroyOnClose)
|
|
if (this.options.destroyOnClose)
|
|
this.destroy();
|
|
this.destroy();
|
|
// don't destroy *and* unmount at the same time
|
|
// don't destroy *and* unmount at the same time
|
|
@@ -1261,7 +1284,46 @@ function createToggleInput(_a) {
|
|
labelEl && labelPos === "right" && wrapperEl.appendChild(labelEl);
|
|
labelEl && labelPos === "right" && wrapperEl.appendChild(labelEl);
|
|
return wrapperEl;
|
|
return wrapperEl;
|
|
});
|
|
});
|
|
|
|
+}/** Generic dialog for importing and exporting any string of data */
|
|
|
|
+class ImportExportDialog extends BytmDialog {
|
|
|
|
+ constructor(options) {
|
|
|
|
+ super(Object.assign({ renderHeader: () => ImportExportDialog.renderHeader(options), renderBody: () => ImportExportDialog.renderBody(options), renderFooter: () => ImportExportDialog.renderFooter(options), closeOnBgClick: true, closeOnEscPress: true, closeBtnEnabled: true, destroyOnClose: true, small: true }, options));
|
|
|
|
+ }
|
|
|
|
+ static renderHeader(_options) {
|
|
|
|
+ return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
+ // TODO:
|
|
|
|
+ // render header with trKeyTitle
|
|
|
|
+ return document.createElement("div");
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ static renderBody(_opts) {
|
|
|
|
+ return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
+ // TODO:
|
|
|
|
+ // two horizontal tabs:
|
|
|
|
+ // - export:
|
|
|
|
+ // - description element with trKeyDescExport
|
|
|
|
+ // - textarea with data, if dataHidden is true, show a button to reveal it
|
|
|
|
+ // - button to copy the data to clipboard
|
|
|
|
+ // - import:
|
|
|
|
+ // - description element with trKeyDescImport
|
|
|
|
+ // - textarea for user to paste data, if dataHidden is true, use password masking
|
|
|
|
+ return document.createElement("div");
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ static renderFooter(_options) {
|
|
|
|
+ return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
+ // TODO:
|
|
|
|
+ // - when export:
|
|
|
|
+ // - copy button
|
|
|
|
+ // - on click, copy exportData to clipboard
|
|
|
|
+ // - on shift-click, copy exportDataSpecial to clipboard, fall back to exportData
|
|
|
|
+ // - when import:
|
|
|
|
+ // - import button
|
|
|
|
+ return document.createElement("div");
|
|
|
|
+ });
|
|
|
|
+ }
|
|
}let autoLikeDialog = null;
|
|
}let autoLikeDialog = null;
|
|
|
|
+let autoLikeImExDialog = null;
|
|
/** Creates and/or returns the import dialog */
|
|
/** Creates and/or returns the import dialog */
|
|
function getAutoLikeDialog() {
|
|
function getAutoLikeDialog() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1269,7 +1331,7 @@ function getAutoLikeDialog() {
|
|
yield initAutoLikeStore();
|
|
yield initAutoLikeStore();
|
|
autoLikeDialog = new BytmDialog({
|
|
autoLikeDialog = new BytmDialog({
|
|
id: "auto-like-channels",
|
|
id: "auto-like-channels",
|
|
- width: 600,
|
|
|
|
|
|
+ width: 700,
|
|
height: 1000,
|
|
height: 1000,
|
|
closeBtnEnabled: true,
|
|
closeBtnEnabled: true,
|
|
closeOnBgClick: true,
|
|
closeOnBgClick: true,
|
|
@@ -1281,14 +1343,50 @@ function getAutoLikeDialog() {
|
|
renderFooter: renderFooter$3,
|
|
renderFooter: renderFooter$3,
|
|
});
|
|
});
|
|
siteEvents.on("autoLikeChannelsUpdated", () => __awaiter(this, void 0, void 0, function* () {
|
|
siteEvents.on("autoLikeChannelsUpdated", () => __awaiter(this, void 0, void 0, function* () {
|
|
|
|
+ if (autoLikeImExDialog === null || autoLikeImExDialog === void 0 ? void 0 : autoLikeImExDialog.isOpen())
|
|
|
|
+ autoLikeImExDialog.unmount();
|
|
if (autoLikeDialog === null || autoLikeDialog === void 0 ? void 0 : autoLikeDialog.isOpen()) {
|
|
if (autoLikeDialog === null || autoLikeDialog === void 0 ? void 0 : autoLikeDialog.isOpen()) {
|
|
- autoLikeDialog.close();
|
|
|
|
autoLikeDialog.unmount();
|
|
autoLikeDialog.unmount();
|
|
yield autoLikeDialog.open();
|
|
yield autoLikeDialog.open();
|
|
log("Auto-like channels updated, refreshed dialog");
|
|
log("Auto-like channels updated, refreshed dialog");
|
|
}
|
|
}
|
|
}));
|
|
}));
|
|
}
|
|
}
|
|
|
|
+ if (!autoLikeImExDialog) {
|
|
|
|
+ autoLikeImExDialog = new ImportExportDialog({
|
|
|
|
+ id: "auto-like-channels-import-export",
|
|
|
|
+ width: 600,
|
|
|
|
+ height: 500,
|
|
|
|
+ // try to compress the data if possible
|
|
|
|
+ exportData: () => __awaiter(this, void 0, void 0, function* () {
|
|
|
|
+ return (yield compressionSupported())
|
|
|
|
+ ? yield UserUtils.compress(JSON.stringify(autoLikeStore.getData()), compressionFormat, "string")
|
|
|
|
+ : JSON.stringify(autoLikeStore.getData());
|
|
|
|
+ }),
|
|
|
|
+ // copy plain when shift-clicking the copy button
|
|
|
|
+ exportDataSpecial: () => JSON.stringify(autoLikeStore.getData()),
|
|
|
|
+ onImport: (data) => __awaiter(this, void 0, void 0, function* () {
|
|
|
|
+ try {
|
|
|
|
+ const parsed = yield tryToDecompressAndParse(data);
|
|
|
|
+ if (!parsed)
|
|
|
|
+ throw new Error("No valid data found in the imported string");
|
|
|
|
+ if (!parsed || typeof parsed !== "object")
|
|
|
|
+ return alert(t("import_error_invalid"));
|
|
|
|
+ if (!parsed.channels || typeof parsed.channels !== "object" || Object.keys(parsed.channels).length === 0)
|
|
|
|
+ return alert(t("import_error_no_data"));
|
|
|
|
+ yield autoLikeStore.setData(parsed);
|
|
|
|
+ siteEvents.emit("autoLikeChannelsUpdated");
|
|
|
|
+ }
|
|
|
|
+ catch (err) {
|
|
|
|
+ error("Couldn't import auto-like channels data:", err);
|
|
|
|
+ }
|
|
|
|
+ }),
|
|
|
|
+ trKeyTitle: "auto_like_export_import_title",
|
|
|
|
+ trKeyDescImport: "auto_like_import_desc",
|
|
|
|
+ trKeyDescExport: "auto_like_export_desc",
|
|
|
|
+ dataHidden: false,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
return autoLikeDialog;
|
|
return autoLikeDialog;
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -1417,7 +1515,9 @@ function renderFooter$3() {
|
|
return wrapperEl;
|
|
return wrapperEl;
|
|
}
|
|
}
|
|
function openImportExportAutoLikeChannelsDialog() {
|
|
function openImportExportAutoLikeChannelsDialog() {
|
|
- alert("TODO: ImportExportDialog stuff");
|
|
|
|
|
|
+ return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
+ yield (autoLikeImExDialog === null || autoLikeImExDialog === void 0 ? void 0 : autoLikeImExDialog.open());
|
|
|
|
+ });
|
|
}
|
|
}
|
|
//#region add prompt
|
|
//#region add prompt
|
|
function addAutoLikeEntryPrompts() {
|
|
function addAutoLikeEntryPrompts() {
|
|
@@ -1748,23 +1848,8 @@ function renderFooter$1() {
|
|
if (!textAreaElem)
|
|
if (!textAreaElem)
|
|
return warn("Couldn't find import menu textarea element");
|
|
return warn("Couldn't find import menu textarea element");
|
|
try {
|
|
try {
|
|
- /** Tries to parse an uncompressed or compressed input string as a JSON object */
|
|
|
|
- const decode = (input) => __awaiter(this, void 0, void 0, function* () {
|
|
|
|
- try {
|
|
|
|
- return JSON.parse(input);
|
|
|
|
- }
|
|
|
|
- catch (_a) {
|
|
|
|
- try {
|
|
|
|
- return JSON.parse(yield UserUtils.decompress(input, compressionFormat, "string"));
|
|
|
|
- }
|
|
|
|
- catch (err) {
|
|
|
|
- warn("Couldn't import configuration:", err);
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- const parsed = yield decode(textAreaElem.value.trim());
|
|
|
|
- if (typeof parsed !== "object")
|
|
|
|
|
|
+ const parsed = yield tryToDecompressAndParse(textAreaElem.value.trim());
|
|
|
|
+ if (!parsed || typeof parsed !== "object")
|
|
return alert(t("import_error_invalid"));
|
|
return alert(t("import_error_invalid"));
|
|
if (typeof parsed.formatVersion !== "number")
|
|
if (typeof parsed.formatVersion !== "number")
|
|
return alert(t("import_error_no_format_version"));
|
|
return alert(t("import_error_no_format_version"));
|
|
@@ -2092,7 +2177,7 @@ function renderBody$1(_a) {
|
|
return wrapperEl;
|
|
return wrapperEl;
|
|
});
|
|
});
|
|
}//#region create menu
|
|
}//#region create menu
|
|
-let isCfgMenuAdded = false;
|
|
|
|
|
|
+let isCfgMenuMounted = false;
|
|
let isCfgMenuOpen = false;
|
|
let isCfgMenuOpen = false;
|
|
/** Threshold in pixels from the top of the options container that dictates for how long the scroll indicator is shown */
|
|
/** Threshold in pixels from the top of the options container that dictates for how long the scroll indicator is shown */
|
|
const scrollIndicatorOffsetThreshold = 30;
|
|
const scrollIndicatorOffsetThreshold = 30;
|
|
@@ -2107,12 +2192,12 @@ let hiddenCopiedTxtTimeout;
|
|
* Adds an element to open the BetterYTM menu
|
|
* Adds an element to open the BetterYTM menu
|
|
* @deprecated to be replaced with new menu - see https://github.com/Sv443/BetterYTM/issues/23
|
|
* @deprecated to be replaced with new menu - see https://github.com/Sv443/BetterYTM/issues/23
|
|
*/
|
|
*/
|
|
-function addCfgMenu() {
|
|
|
|
|
|
+function mountCfgMenu() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
var _a, _b, _c, _d;
|
|
var _a, _b, _c, _d;
|
|
- if (isCfgMenuAdded)
|
|
|
|
|
|
+ if (isCfgMenuMounted)
|
|
return;
|
|
return;
|
|
- isCfgMenuAdded = true;
|
|
|
|
|
|
+ isCfgMenuMounted = true;
|
|
initLocale = getFeature("locale");
|
|
initLocale = getFeature("locale");
|
|
initConfig$1 = getFeatures();
|
|
initConfig$1 = getFeatures();
|
|
const initLangReloadText = t("lang_changed_prompt_reload");
|
|
const initLangReloadText = t("lang_changed_prompt_reload");
|
|
@@ -2129,7 +2214,7 @@ function addCfgMenu() {
|
|
closeCfgMenu(e);
|
|
closeCfgMenu(e);
|
|
});
|
|
});
|
|
document.body.addEventListener("keydown", (e) => {
|
|
document.body.addEventListener("keydown", (e) => {
|
|
- if (isCfgMenuOpen && e.key === "Escape" && !BytmDialog.getCurrentDialogId())
|
|
|
|
|
|
+ if (isCfgMenuOpen && e.key === "Escape" && BytmDialog.getCurrentDialogId() === "cfg-menu")
|
|
closeCfgMenu(e);
|
|
closeCfgMenu(e);
|
|
});
|
|
});
|
|
const menuContainer = document.createElement("div");
|
|
const menuContainer = document.createElement("div");
|
|
@@ -2702,8 +2787,8 @@ function addCfgMenu() {
|
|
return;
|
|
return;
|
|
closeCfgMenu();
|
|
closeCfgMenu();
|
|
bgElem.remove();
|
|
bgElem.remove();
|
|
- isCfgMenuAdded = false;
|
|
|
|
- yield addCfgMenu();
|
|
|
|
|
|
+ isCfgMenuMounted = false;
|
|
|
|
+ yield mountCfgMenu();
|
|
yield openCfgMenu();
|
|
yield openCfgMenu();
|
|
}));
|
|
}));
|
|
});
|
|
});
|
|
@@ -2724,7 +2809,8 @@ function closeCfgMenu(evt, enableScroll = true) {
|
|
clearTimeout(hiddenCopiedTxtTimeout);
|
|
clearTimeout(hiddenCopiedTxtTimeout);
|
|
openDialogs.splice(openDialogs.indexOf("cfg-menu"), 1);
|
|
openDialogs.splice(openDialogs.indexOf("cfg-menu"), 1);
|
|
setCurrentDialogId((_b = openDialogs === null || openDialogs === void 0 ? void 0 : openDialogs[0]) !== null && _b !== void 0 ? _b : null);
|
|
setCurrentDialogId((_b = openDialogs === null || openDialogs === void 0 ? void 0 : openDialogs[0]) !== null && _b !== void 0 ? _b : null);
|
|
- siteEvents.emit("cfgMenuClosed");
|
|
|
|
|
|
+ emitInterface("bytm:dialogClosed", this);
|
|
|
|
+ emitInterface("bytm:dialogClosed:cfg-menu", this);
|
|
if (!menuBg)
|
|
if (!menuBg)
|
|
return warn("Couldn't close config menu because background element couldn't be found. The config menu is considered closed but might still be open. In this case please reload the page. If the issue persists, please create an issue on GitHub.");
|
|
return warn("Couldn't close config menu because background element couldn't be found. The config menu is considered closed but might still be open. In this case please reload the page. If the issue persists, please create an issue on GitHub.");
|
|
(_c = menuBg.querySelectorAll(".bytm-ftconf-adv-copy-hint")) === null || _c === void 0 ? void 0 : _c.forEach((el) => el.style.display = "none");
|
|
(_c = menuBg.querySelectorAll(".bytm-ftconf-adv-copy-hint")) === null || _c === void 0 ? void 0 : _c.forEach((el) => el.style.display = "none");
|
|
@@ -2735,21 +2821,23 @@ function closeCfgMenu(evt, enableScroll = true) {
|
|
function openCfgMenu() {
|
|
function openCfgMenu() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
var _a;
|
|
var _a;
|
|
- if (!isCfgMenuAdded)
|
|
|
|
- yield addCfgMenu();
|
|
|
|
|
|
+ if (!isCfgMenuMounted)
|
|
|
|
+ yield mountCfgMenu();
|
|
if (isCfgMenuOpen)
|
|
if (isCfgMenuOpen)
|
|
return;
|
|
return;
|
|
isCfgMenuOpen = true;
|
|
isCfgMenuOpen = true;
|
|
document.body.classList.add("bytm-disable-scroll");
|
|
document.body.classList.add("bytm-disable-scroll");
|
|
(_a = document.querySelector(getDomain() === "ytm" ? "ytmusic-app" : "ytd-app")) === null || _a === void 0 ? void 0 : _a.setAttribute("inert", "true");
|
|
(_a = document.querySelector(getDomain() === "ytm" ? "ytmusic-app" : "ytd-app")) === null || _a === void 0 ? void 0 : _a.setAttribute("inert", "true");
|
|
const menuBg = document.querySelector("#bytm-cfg-menu-bg");
|
|
const menuBg = document.querySelector("#bytm-cfg-menu-bg");
|
|
- if (!menuBg)
|
|
|
|
- return;
|
|
|
|
- menuBg.style.visibility = "visible";
|
|
|
|
- menuBg.style.display = "block";
|
|
|
|
setCurrentDialogId("cfg-menu");
|
|
setCurrentDialogId("cfg-menu");
|
|
openDialogs.unshift("cfg-menu");
|
|
openDialogs.unshift("cfg-menu");
|
|
|
|
+ emitInterface("bytm:dialogOpened", this);
|
|
|
|
+ emitInterface("bytm:dialogOpened:cfg-menu", this);
|
|
checkToggleScrollIndicator();
|
|
checkToggleScrollIndicator();
|
|
|
|
+ if (!menuBg)
|
|
|
|
+ return warn("Couldn't open config menu because background element couldn't be found. The config menu is considered open but might still be closed. In this case please reload the page. If the issue persists, please create an issue on GitHub.");
|
|
|
|
+ menuBg.style.visibility = "visible";
|
|
|
|
+ menuBg.style.display = "block";
|
|
});
|
|
});
|
|
}
|
|
}
|
|
//#region chk scroll indicator
|
|
//#region chk scroll indicator
|
|
@@ -5870,35 +5958,43 @@ function clearConfig() {
|
|
}const { getUnsafeWindow, randomId } = UserUtils__namespace;
|
|
}const { getUnsafeWindow, randomId } = UserUtils__namespace;
|
|
/** All functions that can be called on the BYTM interface using `unsafeWindow.BYTM.functionName();` (or `const { functionName } = unsafeWindow.BYTM;`) */
|
|
/** All functions that can be called on the BYTM interface using `unsafeWindow.BYTM.functionName();` (or `const { functionName } = unsafeWindow.BYTM;`) */
|
|
const globalFuncs = {
|
|
const globalFuncs = {
|
|
- // meta
|
|
|
|
|
|
+ // meta:
|
|
registerPlugin,
|
|
registerPlugin,
|
|
- getPluginInfo,
|
|
|
|
- // utils
|
|
|
|
- addSelectorListener,
|
|
|
|
|
|
+ /**/ getPluginInfo,
|
|
|
|
+ // bytm-specific:
|
|
getResourceUrl,
|
|
getResourceUrl,
|
|
getSessionId,
|
|
getSessionId,
|
|
|
|
+ // dom:
|
|
|
|
+ addSelectorListener,
|
|
|
|
+ onInteraction,
|
|
getVideoTime,
|
|
getVideoTime,
|
|
- setLocale: setLocaleInterface,
|
|
|
|
|
|
+ getThumbnailUrl,
|
|
|
|
+ getBestThumbnailUrl,
|
|
|
|
+ // translations:
|
|
|
|
+ /**/ setLocale: setLocaleInterface,
|
|
getLocale,
|
|
getLocale,
|
|
hasKey,
|
|
hasKey,
|
|
hasKeyFor,
|
|
hasKeyFor,
|
|
t,
|
|
t,
|
|
tp,
|
|
tp,
|
|
- getFeatures: getFeaturesInterface,
|
|
|
|
- saveFeatures: saveFeaturesInterface,
|
|
|
|
|
|
+ // feature config:
|
|
|
|
+ /**/ getFeatures: getFeaturesInterface,
|
|
|
|
+ /**/ saveFeatures: saveFeaturesInterface,
|
|
|
|
+ // lyrics:
|
|
fetchLyricsUrlTop,
|
|
fetchLyricsUrlTop,
|
|
getLyricsCacheEntry,
|
|
getLyricsCacheEntry,
|
|
sanitizeArtists,
|
|
sanitizeArtists,
|
|
sanitizeSong,
|
|
sanitizeSong,
|
|
- onInteraction,
|
|
|
|
- getThumbnailUrl,
|
|
|
|
- getBestThumbnailUrl,
|
|
|
|
|
|
+ // auto-like:
|
|
|
|
+ /**/ getAutoLikeData: getAutoLikeDataInterface,
|
|
|
|
+ /**/ saveAutoLikeData: saveAutoLikeDataInterface,
|
|
|
|
+ // components:
|
|
createHotkeyInput,
|
|
createHotkeyInput,
|
|
createToggleInput,
|
|
createToggleInput,
|
|
createCircularBtn,
|
|
createCircularBtn,
|
|
|
|
+ createRipple,
|
|
showToast,
|
|
showToast,
|
|
showIconToast,
|
|
showIconToast,
|
|
- createRipple,
|
|
|
|
};
|
|
};
|
|
/** Initializes the BYTM interface */
|
|
/** Initializes the BYTM interface */
|
|
function initInterface() {
|
|
function initInterface() {
|
|
@@ -5950,6 +6046,7 @@ function initPlugins() {
|
|
registeredPlugins.set(key, { def, events });
|
|
registeredPlugins.set(key, { def, events });
|
|
queuedPlugins.delete(key);
|
|
queuedPlugins.delete(key);
|
|
emitOnPlugins("pluginRegistered", (d) => sameDef(d, def), pluginDefToInfo(def));
|
|
emitOnPlugins("pluginRegistered", (d) => sameDef(d, def), pluginDefToInfo(def));
|
|
|
|
+ info(`Initialized plugin '${getPluginKey(def)}'`, LogLevel.Info);
|
|
}
|
|
}
|
|
catch (err) {
|
|
catch (err) {
|
|
error(`Failed to initialize plugin '${getPluginKey(def)}':`, err);
|
|
error(`Failed to initialize plugin '${getPluginKey(def)}':`, err);
|
|
@@ -6038,7 +6135,10 @@ function registerPlugin(def) {
|
|
/** Checks whether the passed token is a valid auth token for any registered plugin and returns the plugin ID, else returns undefined */
|
|
/** Checks whether the passed token is a valid auth token for any registered plugin and returns the plugin ID, else returns undefined */
|
|
function resolveToken(token) {
|
|
function resolveToken(token) {
|
|
var _a, _b;
|
|
var _a, _b;
|
|
- return token ? (_b = (_a = [...registeredPluginTokens.entries()].find(([, v]) => v === token)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : undefined : undefined;
|
|
|
|
|
|
+ return typeof token === "string" && token.length > 0
|
|
|
|
+ ? (_b = (_a = [...registeredPluginTokens.entries()]
|
|
|
|
+ .find(([, t]) => token === t)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : undefined
|
|
|
|
+ : undefined;
|
|
}
|
|
}
|
|
//#region proxy funcs
|
|
//#region proxy funcs
|
|
/**
|
|
/**
|
|
@@ -6075,6 +6175,24 @@ function saveFeaturesInterface(token, features) {
|
|
if (resolveToken(token) === undefined)
|
|
if (resolveToken(token) === undefined)
|
|
return;
|
|
return;
|
|
setFeatures(features);
|
|
setFeatures(features);
|
|
|
|
+}
|
|
|
|
+/**
|
|
|
|
+ * Returns the auto-like data.
|
|
|
|
+ * This is an authenticated function so you must pass the session- and plugin-unique token, retreived at registration.
|
|
|
|
+ */
|
|
|
|
+function getAutoLikeDataInterface(token) {
|
|
|
|
+ if (resolveToken(token) === undefined)
|
|
|
|
+ return;
|
|
|
|
+ return autoLikeStore.getData();
|
|
|
|
+}
|
|
|
|
+/**
|
|
|
|
+ * Saves new auto-like data, synchronously to the in-memory cache and asynchronously to the persistent storage.
|
|
|
|
+ * This is an authenticated function so you must pass the session- and plugin-unique token, retreived at registration.
|
|
|
|
+ */
|
|
|
|
+function saveAutoLikeDataInterface(token, data) {
|
|
|
|
+ if (resolveToken(token) === undefined)
|
|
|
|
+ return;
|
|
|
|
+ return autoLikeStore.setData(data);
|
|
}//#region globals
|
|
}//#region globals
|
|
/** Options that are applied to every SelectorObserver instance */
|
|
/** Options that are applied to every SelectorObserver instance */
|
|
const defaultObserverOptions = {
|
|
const defaultObserverOptions = {
|
|
@@ -6604,6 +6722,23 @@ function openInTab(href, background = false) {
|
|
window.open(href, "_blank", "noopener noreferrer");
|
|
window.open(href, "_blank", "noopener noreferrer");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+/** Tries to parse an uncompressed or compressed input string as a JSON object */
|
|
|
|
+function tryToDecompressAndParse(input) {
|
|
|
|
+ return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
+ try {
|
|
|
|
+ return JSON.parse(input);
|
|
|
|
+ }
|
|
|
|
+ catch (_a) {
|
|
|
|
+ try {
|
|
|
|
+ return JSON.parse(yield UserUtils.decompress(input, compressionFormat, "string"));
|
|
|
|
+ }
|
|
|
|
+ catch (err) {
|
|
|
|
+ error("Couldn't decompress and parse data due to an error:", err);
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+}
|
|
//#region resources
|
|
//#region resources
|
|
/**
|
|
/**
|
|
* Returns the URL of a resource by its name, as defined in `assets/resources.json`, from GM resource cache - [see GM.getResourceUrl docs](https://wiki.greasespot.net/GM.getResourceUrl)
|
|
* Returns the URL of a resource by its name, as defined in `assets/resources.json`, from GM resource cache - [see GM.getResourceUrl docs](https://wiki.greasespot.net/GM.getResourceUrl)
|