import type { UserSettings } from '../definitions' ; import { isIPV6 , compareIPV6 } from './ipv6' ; import { isThunderbird } from './platform' ; let anchor ; export const parsedURLCache = new Map < string , URL > ( ) ; function fixBaseURL ( $url ) { if ( ! anchor ) { anchor = document . createElement ( 'a' ) ; } anchor . href = $url ; return anchor . href ; } export function parseURL ( $url , $base = null ) { const key = `${ $url }${ $base ? `;${ $base }` : '' }` ; if ( parsedURLCache . has ( key ) ) { return parsedURLCache . get ( key ) ; } if ( $base ) { const parsedURL = new URL ( $url , fixBaseURL ( $base ) ) ; parsedURLCache . set ( key , parsedURL ) ; return parsedURL ; } const parsedURL = new URL ( fixBaseURL ( $url ) ) ; parsedURLCache . set ( $url , parsedURL ) ; return parsedURL ; } export function getAbsoluteURL ( $base , $relative ) { if ( $relative . match ( /^data\\?\:/ ) ) { return $relative ; } if ( /^\/\// . test ( $relative ) ) { return `${ location . protocol }${ $relative }` ; } const b = parseURL ( $base ) ; const a = parseURL ( $relative , b . href ) ; return a . href ; } export function isRelativeHrefOnAbsolutePath ( href ) { if ( href . startsWith ( 'data:' ) ) { return true ; } const url = parseURL ( href ) ; if ( url . protocol !== location . protocol ) { return false ; } if ( url . hostname !== location . hostname ) { return false ; } if ( url . port !== location . port ) { return false ; } return url . pathname === location . pathname ; } export function getURLHostOrProtocol ( $url ) { const url = new URL ( $url ) ; if ( url . host ) { return url . host ; } else if ( url . protocol === 'file:' ) { return url . pathname ; } return url . protocol ; } export function compareURLPatterns ( a , b ) { return a . localeCompare ( b ) ; } param url paramlist export function isURLInList ( url , list ) { for ( let i = 0 ; i < list . length ; i ++ ) { if ( isURLMatched ( url , list [ i ] ) ) { return true ; } } return false ; } param url param urlTemplate export function isURLMatched ( url , urlTemplate ) { const isFirstIPV6 = isIPV6 ( url ) ; const isSecondIPV6 = isIPV6 ( urlTemplate ) ; if ( isFirstIPV6 && isSecondIPV6 ) { return compareIPV6 ( url , urlTemplate ) ; } else if ( ! isFirstIPV6 && ! isSecondIPV6 ) { const regex = createUrlRegex ( urlTemplate ) ; return Boolean ( url . match ( regex ) ) ; } return false ; } function createUrlRegex ( urlTemplate ) { urlTemplate = urlTemplate . trim ( ) ; const exactBeginning = ( urlTemplate [ 0 ] === '^' ) ; const exactEnding = ( urlTemplate [ urlTemplate . length - 1 ] === '$' ) ; urlTemplate = ( urlTemplate . replace ( /^\^/ , '' ) . replace ( /\$$/ , '' ) . replace ( /^.*?\/{2,3}/ , '' ) . replace ( /\?.*$/ , '' ) . replace ( /\/$/ , '' ) ) ; let slashIndex ; let beforeSlash ; let afterSlash ; if ( ( slashIndex = urlTemplate . indexOf ( '/' ) ) >= 0 ) { beforeSlash = urlTemplate . substring ( 0 , slashIndex ) ; afterSlash = urlTemplate . replace ( /\$/g , '' ) . substring ( slashIndex ) ; } else { beforeSlash = urlTemplate . replace ( /\$/g , '' ) ; } let result = ( exactBeginning ? '^(.*?\\:\\/{2,3})?' : '^(.*?\\:\\/{2,3})?([^\/]*?\\.)?' ) ; const hostParts = beforeSlash . split ( '.' ) ; result += '(' ; for ( let i = 0 ; i < hostParts . length ; i ++ ) { if ( hostParts [ i ] === '*' ) { hostParts [ i ] = '[^\\.\\/]+?' ; } } result += hostParts . join ( '\\.' ) ; result += ')' ; if ( afterSlash ) { result += '(' ; result += afterSlash . replace ( '/' , '\\/' ) ; result += ')' ; } result += ( exactEnding ? '(\\/?(\\?[^\/]*?)?)$' : '(\\/?.*?)$' ) ; return new RegExp ( result , 'i' ) ; } export function isPDF ( url ) { if ( url . includes ( '.pdf' ) ) { if ( url . includes ( '?' ) ) { url = url . substring ( 0 , url . lastIndexOf ( '?' ) ) ; } if ( url . includes ( '#' ) ) { url = url . substring ( 0 , url . lastIndexOf ( '#' ) ) ; } if ( ( url . match ( /(wikipedia|wikimedia).org/i ) && url . match ( /(wikipedia|wikimedia)\.org\/.*\/[a-z]+\:[^\:\/]+\.pdf/i ) ) || ( url . match ( /timetravel\.mementoweb\.org\/reconstruct/i ) && url . match ( /\.pdf$/i ) ) ) { return false ; } if ( url . endsWith ( '.pdf' ) ) { for ( let i = url . length ; i > 0 ; i -- ) { if ( url [ i ] === '=' ) { return false ; } else if ( url [ i ] === '/' ) { return true ; } } } else { return false ; } } return false ; } export function isURLEnabled ( url , userSettings , { isProtected , isInDarkList } ) { if ( isProtected && ! userSettings . enableForProtectedPages ) { return false ; } if ( isThunderbird ) { return true ; } if ( isPDF ( url ) ) { return userSettings . enableForPDF ; } const isURLInUserList = isURLInList ( url , userSettings . siteList ) ; const isURLInEnabledList = isURLInList ( url , userSettings . siteListEnabled ) ; if ( userSettings . applyToListedOnly && ! isURLInEnabledList ) { return isURLInUserList ; } if ( isURLInEnabledList && isInDarkList ) { return true ; } return ( ! isInDarkList && ! isURLInUserList ) ; } export function isFullyQualifiedDomain ( candidate ) { return /^[a-z0-9.-]+$/ . test ( candidate ) ; }