diff --git a/src/userscripts/manga-reading/manga-reading.user.ts b/src/userscripts/manga-reading/manga-reading.user.ts index 8d6a3e5..f6474bc 100644 --- a/src/userscripts/manga-reading/manga-reading.user.ts +++ b/src/userscripts/manga-reading/manga-reading.user.ts @@ -2,7 +2,7 @@ // @name manga reading // @namespace https://userscripts.skaarup.dev // @homepageURL https://userscripts.skaarup.dev -// @version 3.1 +// @version 3.2 // @author nws // @description Adds nearly complete keyboard navigation and cleans up the user interface of manganato // @updateURL https://userscripts.skaarup.dev/scripts/manga-reading.user.js @@ -57,6 +57,8 @@ import mangaReadingNotificationContainer from './templates/manga-reading-notific import mangaReadingNotification from './templates/manga-reading-notification.html'; async function initMangaReading() { + const logName = `${GM.info.script.name} -`; + function disableScrolling() { document.documentElement.style.top = `${-document.documentElement.scrollTop}px`; document.documentElement.style.position = 'fixed'; @@ -74,10 +76,10 @@ async function initMangaReading() { document.documentElement.scrollTop = scrollTop; document.body.scrollTop = scrollTop; } - function loadStyle(name: string, inline: boolean, shadowRoot: ShadowRoot | null) { + function loadStyle(name: string, inline: boolean, shadowRoot: ShadowRoot) { const data = GM_getResourceText(name); if (!data) { - console.warn(`Resource ${name} not found`); + console.warn(`${logName} Resource ${name} not found`); return; } @@ -88,7 +90,7 @@ async function initMangaReading() { style.innerHTML = data; if (inline) document.head.append(style); - else shadowRoot?.appendChild(style); + else shadowRoot.appendChild(style); } function getMr() { @@ -128,19 +130,20 @@ async function initMangaReading() { ); const outerFrame = document.createElement('mr-config-container'); + if (!outerFrame.shadowRoot) throw new Error('Shadow root not found'); const shadowRoot = outerFrame.shadowRoot; - if (!shadowRoot) return; loadStyle('stylesheet', false, shadowRoot); const frame = shadowRoot.children[0]; - if (!frame) return; + if (!frame) throw new Error('Frame not found'); const backdrop = frame.querySelector('[data-mr-backdrop]'); - if (!backdrop) return; + if (!backdrop) throw new Error('Backdrop not found'); backdrop.onclick = closeConfig; const subConfigTarget = frame.querySelector('[data-mr-sub-config-target]'); + if (!subConfigTarget) throw new Error('Sub config target not found'); const closeConfigButtons = frame.querySelectorAll('[data-mr-close]'); for (let i = 0; i < closeConfigButtons.length; i++) { @@ -173,7 +176,7 @@ async function initMangaReading() { callback }; - ui.subConfigTarget?.appendChild(container); + ui.subConfigTarget.appendChild(container); } function unregisterConfig(info: vm_infoType) { @@ -200,7 +203,7 @@ async function initMangaReading() { disableScrolling(); - shadowRoot?.querySelector('input')?.focus(); + shadowRoot.querySelector('input')?.focus(); } function closeConfig() { @@ -242,9 +245,9 @@ async function initMangaReading() { } function attachFocusEvent() { - console.log(`${GM.info.script.name} - Attaching focus event...`); - ui.frame?.addEventListener('keydown', keydownEventListener as any); - console.log(`${GM.info.script.name} - Attached focus events.`); + console.log(`${logName} Attaching focus event...`); + ui.frame.addEventListener('keydown', keydownEventListener as any); + console.log(`${logName} Attached focus events.`); } function noModifier(e: KeyboardEvent) { @@ -263,19 +266,6 @@ async function initMangaReading() { return e.altKey && !(e.ctrlKey || e.shiftKey); } - async function globalShortcuts(event: KeyboardEvent) { - switch (true) { - case event.code === 'Backslash' && ctrlModifier(event): - // setDebugging(!configGlobals.debugging); - // ui.debuggingCheckbox.checked = configGlobals.debugging; - return true; - case event.code === 'Semicolon' && ctrlModifier(event): - // reloadResources(); - return true; - } - return false; - } - async function configOpenShortcuts(event: KeyboardEvent) { if (event.code === 'Escape' && noModifier(event)) { closeConfig(); @@ -316,9 +306,9 @@ async function initMangaReading() { } function attachShortcutEvents() { - console.log(`${GM.info.script.name} - Attaching shortcut events...`); + console.log(`${logName} Attaching shortcut events...`); document.addEventListener('keyup', keyupEventListener); - console.log(`${GM.info.script.name} - Attached shortcut events.`); + console.log(`${logName} Attached shortcut events.`); } function registerKeyUp(shortcutType: shortcutType, ...shortcuts: shortcut[]) { @@ -337,42 +327,37 @@ async function initMangaReading() { } function registerKeyUps() { - const namePrefix = 'nwsLib'; - registerKeyUp('Global', { - name: `${namePrefix} - global`, - callback: globalShortcuts - }); registerKeyUp('ConfigOpen', { - name: `${namePrefix} - config open`, + name: `${logName} config open`, callback: configOpenShortcuts }); registerKeyUp('ConfigClosed', { - name: `${namePrefix} - config closed`, + name: `${logName} config closed`, callback: configClosedShortcuts }); } async function onInit(callback: () => Promise, postCallback: () => Promise) { try { - console.log(`${GM.info.script.name} - Loading...`); + console.log(`${logName} Loading...`); const id = GM.registerMenuCommand(`Configure ${GM.info.script.name}`, () => { openConfig(); }); registerKeyUps(); attachFocusEvent(); attachShortcutEvents(); - console.log(`${GM.info.script.name} - Loaded.`); + console.log(`${logName} Loaded.`); await callback(); if (postCallback !== undefined) { await postCallback(); } } catch (e) { - console.error(`${GM.info.script.name} - Error:`, e); + console.error(`${logName} Error:`, e); } } async function init(callback: () => Promise, postCallback: () => Promise) { - console.log(`${GM.info.script.name} - Initializing...`); + console.log(`${logName} Initializing...`); switch (document.readyState) { case 'complete': await onInit(callback, postCallback); @@ -412,7 +397,6 @@ async function initMangaReading() { }; } const mr = getMr(); - if (!mr) return; const globals: manga_reading.globalsType = { nextUrl: '', @@ -461,22 +445,23 @@ async function initMangaReading() { notificationList.style.setProperty('--z-index', '999999999'); function initToastContainer() { - console.log(`${GM.info.script.name} - Initializing Toast Container.`); - if (!mr) return; + console.log(`${logName} Initializing Toast Container.`); for (let i = 0; i < mr.shadowRoot.children.length; i++) { const element = mr.shadowRoot.children[i]; - if (element?.tagName.toLowerCase() === 'style') { + if (!element) continue; + + if (element.tagName.toLowerCase() === 'style') { shadowRoot.appendChild(element.cloneNode(true)); } } - console.log(`${GM.info.script.name} - Initialized Toast Container.`); + console.log(`${logName} Initialized Toast Container.`); } function insertToastContainer() { - console.log(`${GM.info.script.name} - Inserting Toast Container.`); + console.log(`${logName} Inserting Toast Container.`); document.body.appendChild(outerFrame); - console.log(`${GM.info.script.name} - Inserted Toast Container.`); + console.log(`${logName} Inserted Toast Container.`); } const notificationTemp = document.createElement('div'); @@ -489,21 +474,39 @@ async function initMangaReading() { const notificationButton = notificationButtonTemp.children[0] as HTMLDivElement; const temp = document.createElement('div'); - temp.innerHTML = atob(mangaReadingConfig); const configElement = temp.children[0] as HTMLDivElement; + const ptApiUrl = configElement.querySelector('[data-mr-pt-api-url]'); + if (!ptApiUrl) throw new Error('PT API URL not found'); + const ptApiBearerToken = configElement.querySelector( + '[data-mr-pt-api-bearer-token]' + ); + if (!ptApiBearerToken) throw new Error('PT API Bearer Token not found'); + const titleList = configElement.querySelector('[data-mr-title-list]'); + if (!titleList) throw new Error('Title List not found'); + const subTitle = configElement.querySelector('[data-mr-title-current]'); + if (!subTitle) throw new Error('Subtitle not found'); + const btnAdd = configElement.querySelector('[data-mr-title-current-add]'); + if (!btnAdd) throw new Error('Add button not found'); + const btnRemove = configElement.querySelector( + '[data-mr-title-current-remove]' + ); + if (!btnRemove) throw new Error('Remove button not found'); + const btnSave = configElement.querySelector('[data-mr-title-list-save]'); + if (!btnSave) throw new Error('Save button not found'); + const btnReset = configElement.querySelector('[data-mr-title-list-reset]'); + if (!btnReset) throw new Error('Reset button not found'); + const ui = { - ptApiUrl: configElement.querySelector('[data-mr-pt-api-url]'), - ptApiBearerToken: configElement.querySelector( - '[data-mr-pt-api-bearer-token]' - ), - titleList: configElement.querySelector('[data-mr-title-list]'), - subTitle: configElement.querySelector('[data-mr-title-current]'), - btnAdd: configElement.querySelector('[data-mr-title-current-add]'), - btnRemove: configElement.querySelector('[data-mr-title-current-remove]'), - btnSave: configElement.querySelector('[data-mr-title-list-save]'), - btnReset: configElement.querySelector('[data-mr-title-list-reset]') + ptApiUrl, + ptApiBearerToken, + titleList, + subTitle, + btnAdd, + btnRemove, + btnSave, + btnReset }; function escapeRegExp(input: string) { @@ -513,13 +516,12 @@ async function initMangaReading() { window.location = url as string & Location; } function setTitleList() { - if (!ui.titleList) return; ui.titleList.value = globals.titleList.join('\r\n'); } function setPTUi() { - if (ui.ptApiUrl) ui.ptApiUrl.value = globals.ptApi.url; - if (ui.ptApiBearerToken) ui.ptApiBearerToken.value = globals.ptApi.bearerToken; + ui.ptApiUrl.value = globals.ptApi.url; + ui.ptApiBearerToken.value = globals.ptApi.bearerToken; } function atNeither() { @@ -565,6 +567,7 @@ async function initMangaReading() { for (let i = 0; i < images.length; i++) { const image = images[i]; if (!image) continue; + image.style.width = `${(pageWidth / image.width) * 100}%`; } @@ -575,8 +578,9 @@ async function initMangaReading() { for (let i = 0; i < images.length; i++) { const image = images[i]; if (!image) continue; + image.style.width = `${(pageWidth / image.width) * 100}%`; - image.style.width = `${(window?.visualViewport?.height ?? 1 / image.height) * 100}%`; + image.style.width = `${(window.visualViewport?.height ?? 1 / image.height) * 100}%`; } return; @@ -585,22 +589,17 @@ async function initMangaReading() { for (let i = 0; i < images.length; i++) { const image = images[i]; if (!image) continue; + image.style.width = image.style.width = `${input}%`; } } function setSubTitle() { - if (!ui.subTitle) return; ui.subTitle.innerText = atChapterOrManga() ? globals.currentTitle : 'No title'; } function registerConfig() { setSubTitle(); - if (!mr) return; - if (!ui.btnRemove) return; - if (!ui.btnAdd) return; - if (!ui.btnReset) return; - if (!ui.btnSave) return; ui.btnRemove.onclick = removeTitle; ui.btnRemove.disabled = atNeither(); @@ -609,7 +608,6 @@ async function initMangaReading() { ui.btnAdd.disabled = atNeither(); ui.btnReset.onclick = () => { - if (!ui.titleList) return; ui.titleList.value = globals.titleList.join('\r\n'); }; @@ -622,28 +620,26 @@ async function initMangaReading() { setTitleList(); setPTUi(); - if (!ui.btnRemove) return; - if (!ui.btnAdd) return; - ui.btnRemove.disabled = atNeither(); ui.btnAdd.disabled = atNeither(); }); } function addTitle() { - const trimmedValue = ui.titleList?.value.trim() ?? ''; + const trimmedValue = ui.titleList.value.trim(); const taTitleListValue = trimmedValue.split(/\r?\n/); - if (taTitleListValue.includes(globals.currentTitle)) return; + if (taTitleListValue.includes(globals.currentTitle)) { + return; + } const curTAVal = trimmedValue.length > 0 ? `${trimmedValue}\r\n` : ''; - if (!ui.titleList) return; ui.titleList.value = curTAVal + globals.currentTitle; } async function saveTitles() { - globals.titleList = [...new Set(ui.titleList?.value.trim().split(/\r?\n/).sort())]; + globals.titleList = [...new Set(ui.titleList.value.trim().split(/\r?\n/).sort())]; await GM.setValue(key.titleList, JSON.stringify(globals.titleList)); if (atChapter()) { @@ -652,15 +648,13 @@ async function initMangaReading() { } async function savePtApi() { - globals.ptApi.url = ui.ptApiUrl?.value.trim() ?? ''; - globals.ptApi.bearerToken = ui.ptApiBearerToken?.value.trim() ?? ''; + globals.ptApi.url = ui.ptApiUrl.value.trim(); + globals.ptApi.bearerToken = ui.ptApiBearerToken.value.trim(); await GM.setValue(key.ptAPi, JSON.stringify(globals.ptApi)); } function removeTitle() { - if (!ui.titleList) return; - - const curTAVal = ui.titleList.value.trim() ?? ''; + const curTAVal = ui.titleList.value.trim(); const regex = new RegExp(`${escapeRegExp(globals.currentTitle)}\\r?\\n?`, 'gi'); ui.titleList.value = curTAVal.replace(regex, ''); } @@ -679,6 +673,7 @@ async function initMangaReading() { const latestChapterLink = latestChapter?.querySelector('& > span > a'); if (!latestChapterLink) return; + setLocation(latestChapterLink.href); } @@ -695,7 +690,7 @@ async function initMangaReading() { } function findUrls() { - console.log(`${GM.info.script.name} - Finding URLs...`); + console.log(`${logName} Finding URLs...`); const links = document.querySelectorAll('div.breadcrumb > p > span > a'); const titleLink = links[1]; @@ -705,7 +700,7 @@ async function initMangaReading() { setSubTitle(); if (!atChapter() || titleLink === undefined) { - console.log(`${GM.info.script.name} - Found URLs.`); + console.log(`${logName} Found URLs.`); return; } @@ -729,39 +724,39 @@ async function initMangaReading() { globals.prevUrl = titleLink.href; } - console.log(`${GM.info.script.name} - Found URLs.`); + console.log(`${logName} Found URLs.`); } async function loadTitleList() { - console.log(`${GM.info.script.name} - Loading title list...`); + console.log(`${logName} Loading title list...`); const value = await GM.getValue(key.titleList, JSON.stringify({ titleList: [] })); if (typeof value !== 'string') { - console.error('Invalid titleList value:', value); + console.error(`${logName} Invalid titleList value: ${value}`); globals.titleList = []; return; } globals.titleList = JSON.parse(value); - console.log(`${GM.info.script.name} - Loaded title list.`); + console.log(`${logName} Loaded title list.`); } async function loadPtApi() { - console.log(`${GM.info.script.name} - Loading progress tracker api...`); + console.log(`${logName} Loading progress tracker api...`); const value = await GM.getValue(key.ptAPi, JSON.stringify({ url: '', bearerToken: '' })); if (typeof value !== 'string') { - console.error('Invalid ptApi value:', value); + console.error(`${logName} Invalid ptApi value: ${value}`); globals.ptApi = { url: '', bearerToken: '' }; return; } globals.ptApi = JSON.parse(value); - console.log(`${GM.info.script.name} - Loaded progress tracker api.`); + console.log(`${logName} Loaded progress tracker api.`); } function manganatoSiteOverrides() { @@ -793,9 +788,9 @@ async function initMangaReading() { } function siteOverrides() { - console.log(`${GM.info.script.name} - Applying site overrides...`); + console.log(`${logName} Applying site overrides...`); manganatoSiteOverrides(); - console.log(`${GM.info.script.name} - Applied site overrides.`); + console.log(`${logName} Applied site overrides.`); } function setAt() { @@ -804,7 +799,7 @@ async function initMangaReading() { const atChapter = new RegExp(/\/manga\/[\w.\-~%]+\/chapter-[\d.-]+/).test(path); const atManga = new RegExp(/\/manga\/[\w.\-~%]+$/).test(path); - console.log(`${GM.info.script.name} - Setting at...`, { atChapter, atManga }); + console.log(`${logName} Setting at...`, { atChapter, atManga }); if (atChapter) globals.at = 'chapter'; else if (atManga) globals.at = 'manga'; @@ -820,7 +815,6 @@ async function initMangaReading() { '.panel_page_number > .group_page > a.page_select' ); - console.log('pageSelected', pageSelected); if (!pageSelected) return; const previous = pageSelected.previousElementSibling as HTMLAnchorElement | null; @@ -960,13 +954,13 @@ async function initMangaReading() { }); if (!response.ok) { - console.error('Failed to fetch bookmarks'); + console.error(`${logName} Failed to fetch bookmarks`); return []; } return response.json(); } catch (e) { - console.error('Failed to fetch bookmarks'); + console.error(`${logName} Failed to fetch bookmarks`); console.error(e); return []; } @@ -980,12 +974,12 @@ async function initMangaReading() { }); if (!response.ok) { - console.error('Failed to delete bookmark'); + console.error(`${logName} Failed to delete bookmark`); return false; } return true; } catch (e) { - console.error('Failed to delete bookmarks'); + console.error(`${logName} Failed to delete bookmarks`); console.error(e); return false; } @@ -996,7 +990,7 @@ async function initMangaReading() { body: { name: string; href: string } ) { try { - console.log(`${GM.info.script.name} - Creating or updating bookmark`, options.input, body); + console.log(`${logName} Creating or updating bookmark`, options.input, body); const response = await nwsFetch(options.input, { method: 'PUT', body: JSON.stringify(body), @@ -1004,7 +998,7 @@ async function initMangaReading() { }); if (!response.ok) { - console.error(`${GM.info.script.name} - Failed to create or update bookmark`); + console.error(`${logName} Failed to create or update bookmark`); return { success: false, created: false }; } @@ -1013,12 +1007,12 @@ async function initMangaReading() { json.success = true; return json; } catch (e) { - console.error(`${GM.info.script.name} - Failed to parse response`); + console.error(`${logName} Failed to parse response`); console.error(e); return { success: false, created: false }; } } catch (e) { - console.error(`${GM.info.script.name} - Failed to create or update bookmarks`); + console.error(`${logName} Failed to create or update bookmarks`); console.error(e); return { success: false, created: false }; } @@ -1036,12 +1030,12 @@ async function initMangaReading() { }); if (!response.ok) { - console.error(`${GM.info.script.name} - Failed to check bookmark`); + console.error(`${logName} Failed to check bookmark`); return false; } return true; } catch (e) { - console.error(`${GM.info.script.name} - Failed to check bookmark`); + console.error(`${logName} Failed to check bookmark`); console.error(e); return false; } @@ -1099,7 +1093,7 @@ async function initMangaReading() { const content = notification.querySelector('[data-content]'); if (title === null || description === null || content === null) { - console.error(`${GM.info.script.name} - Notification elements not found.`); + console.error(`${logName} Notification elements not found.`); return; } @@ -1362,28 +1356,25 @@ async function initMangaReading() { } function registerKeyUps() { - mr?.shortcut.keyUp.register('ConfigClosed', { - name: `${GM.info.script.name} - config closed`, + mr.shortcut.keyUp.register('ConfigClosed', { + name: `${logName} config closed`, callback: configClosedShortcuts }); } async function checkFirstRun() { - console.log(`${GM.info.script.name} - First run check...`); + console.log(`${logName} First run check...`); const value = await GM.getValue(key.firstRun, true); if (value) { - console.log(`${GM.info.script.name} - First run detected.`); + console.log(`${logName} First run detected.`); GM.setValue(key.firstRun, false); - GM.notification( - 'First run setup complete', - `${GM.info.script.name} - ${GM.info.script.name}` - ); + GM.notification('First run setup complete', `${logName} ${GM.info.script.name}`); } - console.log(`${GM.info.script.name} - First run checked.`); + console.log(`${logName} First run checked.`); } async function onInit() { - console.log(`${GM.info.script.name} - ${GM.info.script.name} - Loading...`); + console.log(`${logName} ${GM.info.script.name} - Loading...`); await checkFirstRun(); registerKeyUps(); setAt(); @@ -1394,19 +1385,27 @@ async function initMangaReading() { removeMargins(); } siteOverrides(); - console.log(`${GM.info.script.name} - ${GM.info.script.name} - Loaded.`); + console.log(`${logName} ${GM.info.script.name} - Loaded.`); } async function postInit() { - console.log(`${GM.info.script.name} - ${GM.info.script.name} - Post Loading...`); + console.log(`${logName} ${GM.info.script.name} - Post Loading...`); initToastContainer(); insertToastContainer(); loadStyle('stylesheet', false, shadowRoot); loadStyle('overrides', true, shadowRoot); - console.log(`${GM.info.script.name} - ${GM.info.script.name} - Post Loaded.`); + console.log(`${logName} ${GM.info.script.name} - Post Loaded.`); } registerConfig(); mr.init(onInit, postInit); } -initMangaReading(); +async function mangaReadingSafeStart() { + try { + await initMangaReading(); + } catch (e) { + console.error(`Error initializing userscript - ${GM.info.script.name}:\n`, e); + } +} + +mangaReadingSafeStart();