diff --git a/NamuLink.user.js b/NamuLink.user.js index eaba448..0059bbf 100644 --- a/NamuLink.user.js +++ b/NamuLink.user.js @@ -8,7 +8,7 @@ // @downloadURL https://cdn.jsdelivr.net/gh/List-KR/NamuLink@latest/NamuLink.user.js // @license MIT // -// @version 3.8.0 +// @version 3.9.0 // @author PiQuark6046 and contributors // // @match https://namu.wiki/* @@ -21,4 +21,4 @@ // @inject-into page // ==/UserScript== // Used libraries: -(()=>{var f=class{value;next;constructor(e){this.value=e}},d=class{#e;#t;#n;constructor(){this.clear()}enqueue(e){let r=new f(e);this.#e?(this.#t.next=r,this.#t=r):(this.#e=r,this.#t=r),this.#n++}dequeue(){let e=this.#e;if(e)return this.#e=this.#e.next,this.#n--,e.value}clear(){this.#e=void 0,this.#t=void 0,this.#n=0}get size(){return this.#n}*[Symbol.iterator](){let e=this.#e;for(;e;)yield e.value,e=e.next}};var p={bind(n,e,r){return n.bind(r)}};function h(n){if(!((Number.isInteger(n)||n===Number.POSITIVE_INFINITY)&&n>0))throw new TypeError("Expected `concurrency` to be a number from 1 and up");let e=new d,r=0,t=()=>{r--,e.size>0&&e.dequeue()()},s=async(u,i,m)=>{r++;let E=(async()=>u(...m))();i(E);try{await E}catch{}t()},o=(u,i,m)=>{e.enqueue(p.bind(s.bind(void 0,u,i,m))),(async()=>(await Promise.resolve(),r0&&e.dequeue()()))()},l=(u,...i)=>new Promise(m=>{o(u,m,i)});return Object.defineProperties(l,{activeCount:{get:()=>r},pendingCount:{get:()=>e.size},clearQueue:{value(){e.clear()}}}),l}function v(n){if(!Number.isInteger(n.Count))throw new Error("MultithreadArrayOptions.Count should be an integer");if(n.Count<=0)throw new Error("MultithreadArrayOptions.Count should be greater than 0")}function y(n,e){v(e);let r=new Array(Math.ceil(n.length/e.Count));for(var t=0;tn.length?n.length:(t+1)*e.Count);return r}var a=typeof unsafeWindow<"u"?unsafeWindow:window,C=new Event("namuwikinavigationwithadvert"),w=new Event("namuwikinavigation");a.fetch=new Proxy(a.fetch,{async apply(n,e,r){let t=Reflect.apply(n,e,r);return typeof r[0]=="string"&&/^\/i\/[a-zA-Z0-9_-]{200,}/.test(r[0])&&r[1].method==="GET"&&(await(await t.then(o=>o.clone())).arrayBuffer()).byteLength>1e3&&a.dispatchEvent(C),t}});a.Array.prototype.join=new Proxy(a.Array.prototype.join,{apply(n,e,r){let t=Reflect.apply(n,e,r);return t.startsWith('noscript[data-n-head="]')&&a.dispatchEvent(w),t}});var c=[],L=n=>{c.push(...n),n.forEach(e=>{e.style.setProperty("display","none","important")})},M=()=>{console.debug("[NamuLink:index]: ShowElements:",c),c=c.filter(n=>n.parentElement!==null),c.forEach(n=>{n.style.removeProperty("display")}),c=[]},T=n=>{var e=n.filter(t=>t instanceof HTMLElement);let r=[];return e=e.filter(t=>t.innerText.length<25),e=e.filter(t=>Array.from(t.querySelectorAll("*")).filter(l=>l instanceof HTMLElement).some(l=>Number(getComputedStyle(l).getPropertyValue("margin-bottom").replace(/px$/,""))>=4)),e=e.filter(t=>Array.from(t.querySelectorAll("*")).filter(o=>o instanceof HTMLIFrameElement).length===0),e=e.filter(t=>t.querySelectorAll('span[id^="fn-"] + a[href^="#rfn-"]').length===0),e=e.filter(t=>!Array.from(t.querySelectorAll('a[rel="noopener"][target="_blank"][class] > span ~ span')).some(s=>s.innerHTML.includes("\uB098\uBB34\uB274\uC2A4"))),r.push(...e.filter(t=>Array.from(t.querySelectorAll("*")).filter(l=>l instanceof HTMLElement).filter(l=>getComputedStyle(l).getPropertyValue("animation-iteration-count")==="infinite").length>=6)),e=e.filter(t=>{let o=Array.from(t.querySelectorAll("*")).filter(l=>l instanceof HTMLElement);return o.some(l=>Number(getComputedStyle(l).getPropertyValue("margin-bottom").replace(/px$/,""))>=10)&&o.every(l=>Number(getComputedStyle(l).getPropertyValue("margin-left").replace(/px$/,""))<=10)}),r.push(...e.filter(t=>{let o=Array.from(t.querySelectorAll("*")).filter(i=>i instanceof HTMLElement),u=Array.from(t.parentElement?.querySelectorAll("*")??[]).filter(i=>i instanceof HTMLElement);return o.every(i=>!i.innerText.includes("alt='external/"))&&u.filter(i=>i.nextElementSibling===t&&!(i instanceof HTMLHeadingElement)).length>0})),r},g=async()=>{let n=[];n.push(...Array.from(a.document.querySelectorAll('div[class] div[class*=" "]:has(span ~ ul li) ~ div div[class] > div[class] div[class] ~ div[class]'))),n.push(...Array.from(a.document.querySelectorAll('div:not([class*=" "]) div[class] div[class*=" "]')));let e=[],r=h((navigator.hardwareConcurrency??4)<4?4:navigator.hardwareConcurrency),t=[];for(let s of y(n,{Count:2}))t.push(r(()=>T(s)));e=await Promise.all(t).then(s=>s.flat()),console.debug("[NamuLink:index]: HideLeftoverElement:",e),L(e)};a.addEventListener("namuwikinavigationwithadvert",g);a.addEventListener("namuwikifristvisit",g);a.addEventListener("namuwikinavigation",M);})(); +(()=>{var h=class{value;next;constructor(e){this.value=e}},d=class{#e;#t;#n;constructor(){this.clear()}enqueue(e){let l=new h(e);this.#e?(this.#t.next=l,this.#t=l):(this.#e=l,this.#t=l),this.#n++}dequeue(){let e=this.#e;if(e)return this.#e=this.#e.next,this.#n--,e.value}clear(){this.#e=void 0,this.#t=void 0,this.#n=0}get size(){return this.#n}*[Symbol.iterator](){let e=this.#e;for(;e;)yield e.value,e=e.next}};var y={bind(n,e,l){return n.bind(l)}};function f(n){if(!((Number.isInteger(n)||n===Number.POSITIVE_INFINITY)&&n>0))throw new TypeError("Expected `concurrency` to be a number from 1 and up");let e=new d,l=0,t=()=>{l--,e.size>0&&e.dequeue()()},i=async(u,s,c)=>{l++;let p=(async()=>u(...c))();s(p);try{await p}catch{}t()},o=(u,s,c)=>{e.enqueue(y.bind(i.bind(void 0,u,s,c))),(async()=>(await Promise.resolve(),l0&&e.dequeue()()))()},r=(u,...s)=>new Promise(c=>{o(u,c,s)});return Object.defineProperties(r,{activeCount:{get:()=>l},pendingCount:{get:()=>e.size},clearQueue:{value(){e.clear()}}}),r}function g(n){if(!Number.isInteger(n.Count))throw new Error("MultithreadArrayOptions.Count should be an integer");if(n.Count<=0)throw new Error("MultithreadArrayOptions.Count should be greater than 0")}function E(n,e){g(e);let l=new Array(Math.ceil(n.length/e.Count));for(var t=0;tn.length?n.length:(t+1)*e.Count);return l}var a=typeof unsafeWindow<"u"?unsafeWindow:window,C=new Event("namuwikiunloadedadvert"),T=new Event("namuwikiloadedadvert"),H=new Event("namuwikinavigation");a.fetch=new Proxy(a.fetch,{async apply(n,e,l){let t=Reflect.apply(n,e,l);return typeof l[0]=="string"&&/^\/i\/[a-zA-Z0-9_-]{200,}/.test(l[0])&&l[1].method==="GET"&&(await(await t.then(o=>o.clone())).arrayBuffer()).byteLength>1e3&&a.dispatchEvent(C),t}});a.EventTarget.prototype.addEventListener=new Proxy(a.EventTarget.prototype.addEventListener,{apply(n,e,l){return l[0]==="click"&&typeof l[1]=="function"&&l[1].toString().includes("timeStamp")&&e instanceof HTMLElement&&/.+\..+/.test(e.innerText)&&e.querySelectorAll("*").length===0&&a.dispatchEvent(T),Reflect.apply(n,e,l)}});a.Array.prototype.join=new Proxy(a.Array.prototype.join,{apply(n,e,l){let t=Reflect.apply(n,e,l);return t.startsWith('noscript[data-n-head="]')&&a.dispatchEvent(H),t}});var m=[],v=n=>{m.push(...n),n.forEach(e=>{e.style.setProperty("display","none","important")})},M=()=>{console.debug("[NamuLink:index]: ShowElements:",m),m=m.filter(n=>n.parentElement!==null),m.forEach(n=>{n.style.removeProperty("display")}),m=[]},w=n=>{var e=n.filter(t=>t instanceof HTMLElement);let l=[];return e=e.filter(t=>t.innerText.length<25),e=e.filter(t=>Array.from(t.querySelectorAll("*")).filter(r=>r instanceof HTMLElement).some(r=>Number(getComputedStyle(r).getPropertyValue("margin-bottom").replace(/px$/,""))>=4)),e=e.filter(t=>Array.from(t.querySelectorAll("*")).filter(o=>o instanceof HTMLIFrameElement).length===0),e=e.filter(t=>t.querySelectorAll('span[id^="fn-"] + a[href^="#rfn-"]').length===0),e=e.filter(t=>!Array.from(t.querySelectorAll('a[rel="noopener"][target="_blank"][class] > span ~ span')).some(i=>i.innerHTML.includes("\uB098\uBB34\uB274\uC2A4"))),l.push(...e.filter(t=>Array.from(t.querySelectorAll("*")).filter(r=>r instanceof HTMLElement).filter(r=>getComputedStyle(r).getPropertyValue("animation-iteration-count")==="infinite").length>=6)),e=e.filter(t=>{let o=Array.from(t.querySelectorAll("*")).filter(r=>r instanceof HTMLElement);return o.some(r=>Number(getComputedStyle(r).getPropertyValue("margin-bottom").replace(/px$/,""))>=10)&&o.every(r=>Number(getComputedStyle(r).getPropertyValue("margin-left").replace(/px$/,""))<=10)}),l.push(...e.filter(t=>{let o=Array.from(t.querySelectorAll("*")).filter(s=>s instanceof HTMLElement),u=Array.from(t.parentElement?.querySelectorAll("*")??[]).filter(s=>s instanceof HTMLElement);return o.every(s=>!s.innerText.includes("alt='external/"))&&u.filter(s=>s.nextElementSibling===t&&!(s instanceof HTMLHeadingElement)).length>0})),l},S=n=>{var e=n.filter(t=>t instanceof HTMLElement);let l=[];return e=e.filter(t=>t.innerText.length>45),e=e.filter(t=>Array.from(t.querySelectorAll("*")).filter(r=>r instanceof HTMLElement).some(r=>Number(getComputedStyle(r).getPropertyValue("margin-bottom").replace(/px$/,""))>=4)),e=e.filter(t=>Array.from(t.querySelectorAll("*")).filter(o=>o instanceof HTMLIFrameElement).length===0),e=e.filter(t=>t.querySelectorAll('span[id^="fn-"] + a[href^="#rfn-"]').length===0),e=e.filter(t=>!Array.from(t.querySelectorAll('a[rel="noopener"][target="_blank"][class] > span ~ span')).some(i=>i.innerHTML.includes("\uB098\uBB34\uB274\uC2A4"))),e=e.filter(t=>{let o=Array.from(t.querySelectorAll("*")).filter(r=>r instanceof HTMLElement);return o.some(r=>Number(getComputedStyle(r).getPropertyValue("margin-bottom").replace(/px$/,""))>=10)&&o.every(r=>Number(getComputedStyle(r).getPropertyValue("margin-left").replace(/px$/,""))<=10)}),e=e.filter(t=>t.querySelectorAll("div:has(a) ~ div:has(a) ~ div:has(a)").length>0),l.push(...e.filter(t=>{let o=Array.from(t.querySelectorAll("*")).filter(s=>s instanceof HTMLElement),u=Array.from(t.parentElement?.querySelectorAll("*")??[]).filter(s=>s instanceof HTMLElement);return o.every(s=>!s.innerText.includes("alt='external/"))&&u.filter(s=>s.nextElementSibling===t&&!(s instanceof HTMLHeadingElement)).length>0})),l},L=async()=>{let n=[];n.push(...Array.from(a.document.querySelectorAll('div[class] div[class*=" "]:has(span ~ ul li) ~ div div[class] > div[class] div[class] ~ div[class]'))),n.push(...Array.from(a.document.querySelectorAll('div:not([class*=" "]) div[class] div[class*=" "]')));let e=[],l=f((navigator.hardwareConcurrency??4)<4?4:navigator.hardwareConcurrency),t=[];for(let i of E(n,{Count:2}))t.push(l(()=>w(i)));e=await Promise.all(t).then(i=>i.flat()),console.debug("[NamuLink:index]: HideLeftoverElement:",e),v(e)},b=async()=>{let n=[];n.push(...Array.from(a.document.querySelectorAll('div[class] div[class*=" "]:has(span ~ ul li) ~ div div[class] > div[class] div[class] ~ div[class]'))),n.push(...Array.from(a.document.querySelectorAll('div:not([class*=" "]) div[class] div[class*=" "]')));let e=[],l=f((navigator.hardwareConcurrency??4)<4?4:navigator.hardwareConcurrency),t=[];for(let i of E(n,{Count:2}))t.push(l(()=>S(i)));e=await Promise.all(t).then(i=>i.flat()),console.debug("[NamuLink:index]: HideLeftoverElement:",e),v(e)};a.addEventListener("namuwikiloadedadvert",b);a.addEventListener("namuwikiunloadedadvert",L);a.addEventListener("namuwikifristvisit",L);a.addEventListener("namuwikinavigation",M);})(); diff --git a/package.json b/package.json index 4660282..38cc992 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "namulink", - "version": "3.8.0", + "version": "3.9.0", "description": "", "type": "module", "scripts": { diff --git a/sources/banner.txt b/sources/banner.txt index c37bc8f..9f6407c 100644 --- a/sources/banner.txt +++ b/sources/banner.txt @@ -8,7 +8,7 @@ // @downloadURL https://cdn.jsdelivr.net/gh/List-KR/NamuLink@latest/NamuLink.user.js // @license MIT // -// @version 3.8.0 +// @version 3.9.0 // @author PiQuark6046 and contributors // // @match https://namu.wiki/* diff --git a/sources/src/index.ts b/sources/src/index.ts index 9e79602..eea0fc3 100644 --- a/sources/src/index.ts +++ b/sources/src/index.ts @@ -8,7 +8,8 @@ declare const unsafeWindow: unsafeWindow // eslint-disable-next-line no-negated-condition const Win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window -const NagivationAdvertEvent = new Event('namuwikinavigationwithadvert') +const NamuWikiUnloadedAdEvent = new Event('namuwikiunloadedadvert') +const NamuWikiLoadedAdEvent = new Event('namuwikiloadedadvert') const NagivationEvent = new Event('namuwikinavigation') Win.fetch = new Proxy(Win.fetch, { @@ -17,13 +18,24 @@ Win.fetch = new Proxy(Win.fetch, { if (typeof Args[0] === 'string' && /^\/i\/[a-zA-Z0-9_-]{200,}/.test(Args[0]) && (Args[1] as RequestInit).method === 'GET') { const ResultCloned = await (Result as Promise).then(Response => Response.clone()) if ((await ResultCloned.arrayBuffer()).byteLength > 1000) { - Win.dispatchEvent(NagivationAdvertEvent) + Win.dispatchEvent(NamuWikiUnloadedAdEvent) } } return Result } }) +Win.EventTarget.prototype.addEventListener = new Proxy(Win.EventTarget.prototype.addEventListener, { + apply(Target, ThisArg, Args) { + // eslint-disable-next-line @typescript-eslint/ban-types + if (Args[0] === 'click' && typeof Args[1] === 'function' && (Args[1] as Function).toString().includes('timeStamp') + && ThisArg instanceof HTMLElement && /.+\..+/.test(ThisArg.innerText) && ThisArg.querySelectorAll('*').length === 0) { + Win.dispatchEvent(NamuWikiLoadedAdEvent) + } + return Reflect.apply(Target, ThisArg, Args) + } +}) + Win.Array.prototype.join = new Proxy(Win.Array.prototype.join, { apply(Target, ThisArg, Args) { const Result = Reflect.apply(Target, ThisArg, Args) @@ -92,6 +104,44 @@ const HideLeftoverElementNano = (ElementsInArticle: Element[]) => { return TargetedElements } +const HideAdElementNano = (ElementsInArticle: Element[]) => { + var FilteredElements = ElementsInArticle.filter(ElementInArticle => ElementInArticle instanceof HTMLElement) as HTMLElement[] + const TargetedElements: HTMLElement[] = [] + FilteredElements = FilteredElements.filter(HTMLElementInArticle => HTMLElementInArticle.innerText.length > 45) + FilteredElements = FilteredElements.filter(HTMLElementInArticle => { + const ChildElements = Array.from(HTMLElementInArticle.querySelectorAll('*')) + const ChildHTMLElements = ChildElements.filter(ChildElement => ChildElement instanceof HTMLElement) as HTMLElement[] + return ChildHTMLElements.some(ChildElement => Number(getComputedStyle(ChildElement).getPropertyValue('margin-bottom').replace(/px$/, '')) >= 4) + }) + FilteredElements = FilteredElements.filter(HTMLElementInArticle => { + const ChildElements = Array.from(HTMLElementInArticle.querySelectorAll('*')) + return ChildElements.filter(ChildElement => ChildElement instanceof HTMLIFrameElement).length === 0 + }) + FilteredElements = FilteredElements.filter(HTMLElementInArticle => { + return HTMLElementInArticle.querySelectorAll('span[id^="fn-"] + a[href^="#rfn-"]').length === 0 + }) + FilteredElements = FilteredElements.filter(HTMLElementInArticle => { + return !Array.from(HTMLElementInArticle.querySelectorAll('a[rel="noopener"][target="_blank"][class] > span ~ span')).some(HTMLElement => (HTMLElement as HTMLElement).innerHTML.includes('나무뉴스')) + }) + FilteredElements = FilteredElements.filter(HTMLElementInArticle => { + const ChildElements = Array.from(HTMLElementInArticle.querySelectorAll('*')) + const ChildHTMLElements = ChildElements.filter(ChildElement => ChildElement instanceof HTMLElement) as HTMLElement[] + return ChildHTMLElements.some(ChildElement => Number(getComputedStyle(ChildElement).getPropertyValue('margin-bottom').replace(/px$/, '')) >= 10) && + ChildHTMLElements.every(ChildElement => Number(getComputedStyle(ChildElement).getPropertyValue('margin-left').replace(/px$/, '')) <= 10) + }) + FilteredElements = FilteredElements.filter(HTMLElementInArticle => { + return HTMLElementInArticle.querySelectorAll('div:has(a) ~ div:has(a) ~ div:has(a)').length > 0 + }) + TargetedElements.push(...FilteredElements.filter(HTMLElementInArticle => { + const ChildElements = Array.from(HTMLElementInArticle.querySelectorAll('*')) + const ChildHTMLElements = ChildElements.filter(ChildElement => ChildElement instanceof HTMLElement) as HTMLElement[] + const PeerElements = Array.from(HTMLElementInArticle.parentElement?.querySelectorAll('*') ?? []) + const PeerHTMLElements = PeerElements.filter(PeerElement => PeerElement instanceof HTMLElement) as HTMLElement[] + return ChildHTMLElements.every(ChildHTMLElement => !ChildHTMLElement.innerText.includes('alt=\'external/')) && PeerHTMLElements.filter(PeerHTMLElement => PeerHTMLElement.nextElementSibling === HTMLElementInArticle && !(PeerHTMLElement instanceof HTMLHeadingElement)).length > 0 + })) + return TargetedElements +} + const HideLeftoverElement = async () => { const ElementsInArticle = [] ElementsInArticle.push(...Array.from(Win.document.querySelectorAll('div[class] div[class*=" "]:has(span ~ ul li) ~ div div[class] > div[class] div[class] ~ div[class]'))) @@ -107,6 +157,23 @@ const HideLeftoverElement = async () => { HideElements(TargetedElements) } -Win.addEventListener('namuwikinavigationwithadvert', HideLeftoverElement) +const HideAdElement = async () => { + const ElementsInArticle = [] + ElementsInArticle.push(...Array.from(Win.document.querySelectorAll('div[class] div[class*=" "]:has(span ~ ul li) ~ div div[class] > div[class] div[class] ~ div[class]'))) + ElementsInArticle.push(...Array.from(Win.document.querySelectorAll('div:not([class*=" "]) div[class] div[class*=" "]'))) + let TargetedElements: HTMLElement[] = [] + const PLimitInstance = PLimit((navigator.hardwareConcurrency ?? 4) < 4 ? 4 : navigator.hardwareConcurrency) + const PLimitJobs: Promise[] = [] + for (const ElementsInArticleChunk of SplitElementsIntoSubArrayLength(ElementsInArticle, {Count: 2})) { + PLimitJobs.push(PLimitInstance(() => HideAdElementNano(ElementsInArticleChunk))) + } + TargetedElements = await Promise.all(PLimitJobs).then(PLimitResults => PLimitResults.flat()) + console.debug('[NamuLink:index]: HideLeftoverElement:', TargetedElements) + HideElements(TargetedElements) +} + + +Win.addEventListener('namuwikiloadedadvert', HideAdElement) +Win.addEventListener('namuwikiunloadedadvert', HideLeftoverElement) Win.addEventListener('namuwikifristvisit', HideLeftoverElement) Win.addEventListener('namuwikinavigation', ShowElements)