/* tslint:disable:quotemark */ import * as _ from 'lodash'; import * as urlHelper from 'url'; import { domain as extractDomain } from '../../bbcode/core'; // tslint:disable-next-line:ban-ts-ignore // @ts-ignore // tslint:disable-next-line:no-submodule-imports ban-ts-ignore match-default-export-name import processorScript from '!!raw-loader!./assets/browser.processor.raw.js'; export interface DomMutator { match: string | RegExp; injectJs: string; eventName: string; urlMutator?(url: string): string; } // tslint:disable-next-line:max-line-length const imgurOuterStyle = 'z-index: 1000000; position: absolute; bottom: 0.75rem; right: 0.75rem; background: rgba(0, 128, 0, 0.8); border: 2px solid rgba(144, 238, 144, 0.5); width: 3rem; height: 3rem; font-size: 15pt; font-weight: normal; color: white; border-radius: 3rem; margin: 0; font-family: Helvetica,Arial,sans-serif; box-shadow: 2px 2px 2px rgba(0,0,0,0.5)'; // tslint:disable-next-line:max-line-length const imgurInnerStyle = 'position: absolute; top: 50%; left: 50%; transform: translateY(-50%) translateX(-50%); text-shadow: 1px 1px 2px rgba(0,0,0,0.4);'; export interface DomMutatorScripts { processor: string; } export class ImageDomMutator { // tslint:disable: prefer-function-over-method private hostMutators: Record = {}; private regexMutators: DomMutator[] = []; private debug: boolean; private scripts: DomMutatorScripts = { processor: '' }; constructor(debug: boolean) { // this.debug = debug; this.debug = debug || true; } setDebug(debug: boolean): void { this.debug = debug; } mutateUrl(url: string): string { const mutator = this.matchMutator(url); if ((!mutator) || (!mutator.urlMutator)) return url; return mutator.urlMutator(url); } getMutatorJsForSite(url: string, eventName: string): string | undefined { let mutator = this.matchMutator(url); if (!mutator) { mutator = this.hostMutators['default']; } if ((!mutator) || (mutator.eventName !== eventName)) return; // console.log(`Mutator match: ${mutator.match}`, (mutator === this.hostMutators['default']), url); return this.wrapJs(mutator.injectJs) + this.getReShowMutator(); } matchMutator(url: string): DomMutator | undefined { if (url === 'about:blank') { return this.hostMutators['about:blank']; } const urlDomain = extractDomain(url); if (!urlDomain) return; if (urlDomain in this.hostMutators) return this.hostMutators[urlDomain]; return _.find( this.regexMutators, (m: DomMutator) => { const match = m.match; return (match instanceof RegExp) ? (urlDomain.match(match) !== null) : (match === urlDomain); } ); } protected wrapJs(mutatorJs: string): string { return `(() => { try { ${mutatorJs} } catch (err) { console.error('Mutator error', err); } })();`; } protected add( domain: string | RegExp, mutatorJs: string, urlMutator?: (url: string) => string, eventName: string = 'update-target-url' ): void { if (domain instanceof RegExp) { this.regexMutators.push( { match: domain, injectJs: mutatorJs, urlMutator, eventName } ); return; } this.hostMutators[domain] = { match: domain, injectJs: mutatorJs, urlMutator, eventName }; } protected async loadScripts(): Promise { this.scripts = { processor: processorScript }; } async init(): Promise { await this.loadScripts(); /* tslint:disable max-line-length */ this.add('default', this.getBaseJsMutatorScript(['.content video', '.content img', '#video, video', '#image, img'])); this.add('about:blank', ''); this.add('e621.net', this.getBaseJsMutatorScript(['video', '#image'])); this.add('e-hentai.org', this.getBaseJsMutatorScript(['video', '#img'])); this.add('gelbooru.com', this.getBaseJsMutatorScript(['video.gelcomVPlayer', '.post-view video', '.contain-push video', '#image'])); this.add('gyazo.com', this.getBaseJsMutatorScript(['.image-view video', '.image-view img'])); this.add('chan.sankakucomplex.com', this.getBaseJsMutatorScript(['video', '#image'])); this.add('danbooru.donmai.us', this.getBaseJsMutatorScript(['video', '#image'])); this.add('gfycat.com', this.getBaseJsMutatorScript(['video'], true, [], true)); this.add('gfycatporn.com', this.getBaseJsMutatorScript(['video'], true, [], true)); this.add('youtube.com', this.getBaseJsMutatorScript(['video']), undefined, 'dom-ready'); this.add('instantfap.com', this.getBaseJsMutatorScript(['#post video', '#post img'])); this.add('webmshare.com', this.getBaseJsMutatorScript(['video'])); this.add('vimeo.com', this.getBaseJsMutatorScript(['#video, video', '#image, img'])); this.add('sex.com', this.getBaseJsMutatorScript(['.image_frame video', '.image_frame img']), undefined, 'dom-ready'); // this.add('redirect.media.tumblr.com', this.getBaseJsMutatorScript(['picture video', 'picture img'])); this.add(/^[a-zA-Z0-9-]+\.media\.tumblr\.com$/, this.getBaseJsMutatorScript(['.photoset video', '.photoset img', 'img:not([role="img"]):not([alt="Avatar"])', '#base-container video', '#base-container img', 'picture video', 'picture img', 'video', 'img']), undefined, 'dom-ready'); this.add(/^[a-zA-Z0-9-]+\.tumblr\.com$/, this.getBaseJsMutatorScript(['.photoset iframe', '.photoset video', '.photoset img', 'img:not([role="img"]):not([alt="Avatar"])', 'picture video', 'picture img', 'video', 'img']), undefined, 'dom-ready'); this.add('postimg.cc', this.getBaseJsMutatorScript(['video', '#main-image'])); this.add('gifsauce.com', this.getBaseJsMutatorScript(['video'])); // this.add('motherless.com', this.getBaseJsMutatorScript(['.content video', '.content img'])); this.add(/^media[0-9]\.giphy\.com$/, this.getBaseJsMutatorScript(['video', 'img[alt]'])); this.add('giphy.com', this.getBaseJsMutatorScript(['video', 'a > div > img'])); this.add(/^media[0-9]\.tenor\.com$/, this.getBaseJsMutatorScript(['#view .file video', '#view .file img'])); this.add('tenor.com', this.getBaseJsMutatorScript(['#view video', '#view img'])); this.add('hypnohub.net', this.getBaseJsMutatorScript(['video', '#image', 'img'])); this.add('derpibooru.org', this.getBaseJsMutatorScript(['video', '#image-display', 'img'])); this.add('sexbot.gallery', this.getBaseJsMutatorScript(['video.hero', 'video'])); this.add('imagefap.com', this.getBaseJsMutatorScript(['.image-wrapper img', 'video', 'img'])); this.add('myhentaicomics.com', this.getBaseJsMutatorScript(['#entire_image img', 'video', 'img'])); this.add('redgifs.com', this.getBaseJsMutatorScript(['video'], true, [], false, true)); this.add('furaffinity.net', this.getBaseJsMutatorScript(['#submissionImg', 'video', 'img'])); this.add('rule34.paheal.net', this.getBaseJsMutatorScript(['#main_image', 'video', 'img'])); this.add('xhamster.com', this.getBaseJsMutatorScript(['#photo_slider video', '#photo_slider img', 'video', 'img'])); this.add('shadbase.com', this.getBaseJsMutatorScript(['#comic video', '#comic img', 'video', 'img'])); this.add('instagram.com', this.getBaseJsMutatorScript(['article video', 'article img', 'video', 'img'])); this.add('rule34video.com', this.getBaseJsMutatorScript(['video'], true, [], false, true)); this.add( 'pornhub.com', this.getBaseJsMutatorScript([/*'#__flistCore', '#player', */ '#photoImageSection img', 'video', 'img', '#player'], false) ); this.add( 'gifmixxx.com', ` const bgImage = document.querySelector('.gif.fit'); const bgImageStyle = bgImage.style.backgroundImage; ${this.getBaseJsMutatorScript(['.gif.fit', '.gif', 'video', 'img'])}; bgImage.style.backgroundImage = bgImageStyle; bgImage.style.backgroundSize = 'contain'; bgImage.style.backgroundRepeat = 'no-repeat'; bgImage.style.color = 'transparent'; ` ); this.add( 'i.imgur.com', ` const imageCount = (new URL(window.location.href)).searchParams.get('flist_gallery_image_count'); ${this.getBaseJsMutatorScript(['video', 'img'])} if(imageCount > 1) { ${this.injectHtmlJs(`
`)} const imageCountEl = document.getElementById('imageCountInner'); if (imageCountEl) { imageCountEl.innerHTML = '' + (imageCount); } } ` ); this.add( 'imgur.com', ` const imageCount = $('.post-container video, .post-container img').length; ${this.getBaseJsMutatorScript(['.post-container video', '.post-container img'], true)} if(imageCount > 1) $('#flistWrapper').append('
+' + (imageCount - 1) + '
'); ` ); this.add( 'rule34.xxx', `${this.getBaseJsMutatorScript(['video', '#image'])} const content = document.querySelector('#content'); if (content) content.remove(); `, undefined, 'dom-ready' ); this.add( 'hentai-foundry.com', this.getBaseJsMutatorScript(['#picBox video', '#picBox img']), (url: string): string => { const u = urlHelper.parse(url, true); if (!u) return url; // tslint:disable-next-line no-any (u.query as any).enterAgree = 1; u.search = null; return urlHelper.format(u); } ); this.add( 'twitter.com', ` const finalizer = (counter) => { if (counter <= 0) { return; } setTimeout( () => { const e = document.querySelector('#flistWrapper img'); if (e) { const src = e.getAttribute('src'); if (src) { e.setAttribute('src', src.replace(/name\=[a-z0-9\-\_]+/, 'name=large')); } } const v = document.querySelector('#flistWrapper video'); if (v) { v.play(); } finalizer(counter - 1); }, 100 ); }; const scheduler = () => { setTimeout( () => { // skip content warning document.querySelectorAll('article article div[tabindex="0"] *').forEach(e => e.click()); if (!document.querySelector('article video, div[aria-label="Image"] img')) { console.log('NOT FOUND'); scheduler(); return; } ${this.getBaseJsMutatorScript(['article video', 'div[aria-label=\'Image\'] img'])} finalizer(25); }, 200 ); }; scheduler(); ` ); } protected getBaseJsMutatorScript(elSelector: string[], skipElementRemove: boolean = false, safeTags: string[] = [], delayPreprocess: boolean = false, scheduled: boolean = false): string { const js = this.scripts.processor; // ./assets/browser.processor.raw.js const settings = { skipElementRemove, safeTags, delayPreprocess, selectors: elSelector, schedule: scheduled, debug: this.debug }; const settingsJson = JSON.stringify(settings, null, 0); return js.replace(/\/\* ## SETTINGS_START[^]*SETTINGS_END ## \*\//m, `this.settings = ${settingsJson};`); } getErrorMutator(code: number, description: string): string { const errorHtml = `

${code}

${description}

`; return this.injectHtmlJs(errorHtml); } protected injectHtmlJs(html: string): string { return this.wrapJs(` const range = document.createRange(); range.selectNode(document.body); const error = range.createContextualFragment(\`${html}\`); document.body.appendChild(error); `); } getHideMutator(): string { return this.injectHtmlJs(`
`) + this.wrapJs( ` window.__flistUnhide = () => { const elements = document.querySelectorAll('#flistHider'); if (elements) { elements.forEach( (el) => el.remove() ); } }; ` ); } getReShowMutator(): string { return this.wrapJs( ` const elements = document.querySelectorAll('#flistHider'); if (elements) { elements.forEach( (el) => el.remove() ); } ` ); } }