2019-11-05 15:29:42 -06:00
/ * *
* Helpers
* /
2019-12-04 21:14:51 +01:00
const escapeTest = /[&<>"']/ ;
2022-11-20 10:05:26 -06:00
const escapeReplace = new RegExp ( escapeTest . source , 'g' ) ;
const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/ ;
const escapeReplaceNoEncode = new RegExp ( escapeTestNoEncode . source , 'g' ) ;
2019-12-04 21:14:51 +01:00
const escapeReplacements = {
'&' : '&' ,
'<' : '<' ,
'>' : '>' ,
'"' : '"' ,
"'" : '''
} ;
const getEscapeReplacement = ( ch ) => escapeReplacements [ ch ] ;
2021-11-02 07:32:17 -07:00
export function escape ( html , encode ) {
2019-11-05 15:29:42 -06:00
if ( encode ) {
2019-12-04 21:14:51 +01:00
if ( escapeTest . test ( html ) ) {
return html . replace ( escapeReplace , getEscapeReplacement ) ;
2019-11-05 15:29:42 -06:00
}
} else {
2019-12-04 21:14:51 +01:00
if ( escapeTestNoEncode . test ( html ) ) {
return html . replace ( escapeReplaceNoEncode , getEscapeReplacement ) ;
2019-11-05 15:29:42 -06:00
}
}
return html ;
}
2019-12-04 21:14:51 +01:00
const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig ;
2019-11-06 11:11:06 -06:00
2022-03-30 17:42:54 +02:00
/ * *
* @ param { string } html
* /
2021-11-02 07:32:17 -07:00
export function unescape ( html ) {
2019-11-05 15:29:42 -06:00
// explicitly match decimal, hex, and named HTML entities
2019-12-04 21:14:51 +01:00
return html . replace ( unescapeTest , ( _ , n ) => {
2019-11-05 15:29:42 -06:00
n = n . toLowerCase ( ) ;
if ( n === 'colon' ) return ':' ;
if ( n . charAt ( 0 ) === '#' ) {
return n . charAt ( 1 ) === 'x'
? String . fromCharCode ( parseInt ( n . substring ( 2 ) , 16 ) )
: String . fromCharCode ( + n . substring ( 1 ) ) ;
}
return '' ;
} ) ;
}
2019-12-04 21:14:51 +01:00
const caret = /(^|[^\[])\^/g ;
2022-03-30 17:42:54 +02:00
/ * *
* @ param { string | RegExp } regex
* @ param { string } opt
* /
2021-11-02 07:32:17 -07:00
export function edit ( regex , opt ) {
2022-03-30 17:42:54 +02:00
regex = typeof regex === 'string' ? regex : regex . source ;
2019-11-05 15:29:42 -06:00
opt = opt || '' ;
2019-11-07 12:46:32 -06:00
const obj = {
replace : ( name , val ) => {
2019-11-05 15:29:42 -06:00
val = val . source || val ;
2019-12-04 21:14:51 +01:00
val = val . replace ( caret , '$1' ) ;
2019-11-05 15:29:42 -06:00
regex = regex . replace ( name , val ) ;
2019-11-07 12:46:32 -06:00
return obj ;
2019-11-05 15:29:42 -06:00
} ,
2019-11-07 12:46:32 -06:00
getRegex : ( ) => {
2019-11-05 15:29:42 -06:00
return new RegExp ( regex , opt ) ;
}
} ;
2019-11-07 12:46:32 -06:00
return obj ;
2019-11-05 15:29:42 -06:00
}
2019-12-04 21:14:51 +01:00
const nonWordAndColonTest = /[^\w:]/g ;
const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i ;
2022-03-30 17:42:54 +02:00
/ * *
* @ param { boolean } sanitize
* @ param { string } base
* @ param { string } href
* /
2021-11-02 07:32:17 -07:00
export function cleanUrl ( sanitize , base , href ) {
2019-11-05 15:29:42 -06:00
if ( sanitize ) {
let prot ;
try {
prot = decodeURIComponent ( unescape ( href ) )
2019-12-04 21:14:51 +01:00
. replace ( nonWordAndColonTest , '' )
2019-11-05 15:29:42 -06:00
. toLowerCase ( ) ;
} catch ( e ) {
return null ;
}
if ( prot . indexOf ( 'javascript:' ) === 0 || prot . indexOf ( 'vbscript:' ) === 0 || prot . indexOf ( 'data:' ) === 0 ) {
return null ;
}
}
2019-12-04 21:14:51 +01:00
if ( base && ! originIndependentUrl . test ( href ) ) {
2019-11-05 15:29:42 -06:00
href = resolveUrl ( base , href ) ;
}
try {
href = encodeURI ( href ) . replace ( /%25/g , '%' ) ;
} catch ( e ) {
return null ;
}
return href ;
}
2019-12-04 21:14:51 +01:00
const baseUrls = { } ;
const justDomain = /^[^:]+:\/*[^/]*$/ ;
const protocol = /^([^:]+:)[\s\S]*$/ ;
const domain = /^([^:]+:\/*[^/]*)[\s\S]*$/ ;
2019-11-05 15:29:42 -06:00
2022-03-30 17:42:54 +02:00
/ * *
* @ param { string } base
* @ param { string } href
* /
2021-11-02 07:32:17 -07:00
export function resolveUrl ( base , href ) {
2019-12-04 21:14:51 +01:00
if ( ! baseUrls [ ' ' + base ] ) {
2019-11-05 15:29:42 -06:00
// we can ignore everything in base after the last slash of its path component,
// but we might need to add _that_
// https://tools.ietf.org/html/rfc3986#section-3
2019-12-04 21:14:51 +01:00
if ( justDomain . test ( base ) ) {
baseUrls [ ' ' + base ] = base + '/' ;
2019-11-05 15:29:42 -06:00
} else {
2019-12-04 21:14:51 +01:00
baseUrls [ ' ' + base ] = rtrim ( base , '/' , true ) ;
2019-11-05 15:29:42 -06:00
}
}
2019-12-04 21:14:51 +01:00
base = baseUrls [ ' ' + base ] ;
2019-11-05 15:29:42 -06:00
const relativeBase = base . indexOf ( ':' ) === - 1 ;
2019-11-07 12:46:32 -06:00
if ( href . substring ( 0 , 2 ) === '//' ) {
2019-11-05 15:29:42 -06:00
if ( relativeBase ) {
return href ;
}
2019-12-04 21:14:51 +01:00
return base . replace ( protocol , '$1' ) + href ;
2019-11-05 15:29:42 -06:00
} else if ( href . charAt ( 0 ) === '/' ) {
if ( relativeBase ) {
return href ;
}
2019-12-04 21:14:51 +01:00
return base . replace ( domain , '$1' ) + href ;
2019-11-05 15:29:42 -06:00
} else {
return base + href ;
}
}
2021-11-02 07:32:17 -07:00
export const noopTest = { exec : function noopTest ( ) { } } ;
2019-11-05 15:29:42 -06:00
2021-11-02 07:32:17 -07:00
export function splitCells ( tableRow , count ) {
2019-11-05 15:29:42 -06:00
// ensure that every cell-delimiting pipe has a space
// before it to distinguish it from an escaped pipe
2019-11-07 12:46:32 -06:00
const row = tableRow . replace ( /\|/g , ( match , offset , str ) => {
2019-11-06 11:11:06 -06:00
let escaped = false ,
curr = offset ;
while ( -- curr >= 0 && str [ curr ] === '\\' ) escaped = ! escaped ;
if ( escaped ) {
// odd number of slashes means | is escaped
// so we leave it alone
return '|' ;
} else {
// add space before unescaped |
return ' |' ;
}
} ) ,
cells = row . split ( / \|/ ) ;
2019-11-05 15:29:42 -06:00
let i = 0 ;
2021-08-02 15:12:43 -04:00
// First/last cell in a row cannot be empty if it has no leading/trailing pipe
if ( ! cells [ 0 ] . trim ( ) ) { cells . shift ( ) ; }
2022-01-26 23:07:01 -05:00
if ( cells . length > 0 && ! cells [ cells . length - 1 ] . trim ( ) ) { cells . pop ( ) ; }
2021-08-02 15:12:43 -04:00
2019-11-05 15:29:42 -06:00
if ( cells . length > count ) {
cells . splice ( count ) ;
} else {
while ( cells . length < count ) cells . push ( '' ) ;
}
for ( ; i < cells . length ; i ++ ) {
// leading or trailing whitespace is ignored per the gfm spec
cells [ i ] = cells [ i ] . trim ( ) . replace ( /\\\|/g , '|' ) ;
}
return cells ;
}
2022-03-30 17:42:54 +02:00
/ * *
* Remove trailing 'c' s . Equivalent to str . replace ( /c*$/ , '' ) .
* /c*$/ is vulnerable to REDOS .
*
* @ param { string } str
* @ param { string } c
* @ param { boolean } invert Remove suffix of non - c chars instead . Default falsey .
* /
2021-11-02 07:32:17 -07:00
export function rtrim ( str , c , invert ) {
2019-11-06 11:11:06 -06:00
const l = str . length ;
if ( l === 0 ) {
2019-11-05 15:29:42 -06:00
return '' ;
}
// Length of suffix matching the invert condition.
let suffLen = 0 ;
// Step left until we fail to match the invert condition.
2019-11-06 11:11:06 -06:00
while ( suffLen < l ) {
const currChar = str . charAt ( l - suffLen - 1 ) ;
2019-11-05 15:29:42 -06:00
if ( currChar === c && ! invert ) {
suffLen ++ ;
} else if ( currChar !== c && invert ) {
suffLen ++ ;
} else {
break ;
}
}
2022-03-19 06:11:14 +01:00
return str . slice ( 0 , l - suffLen ) ;
2019-11-05 15:29:42 -06:00
}
2021-11-02 07:32:17 -07:00
export function findClosingBracket ( str , b ) {
2019-11-05 15:29:42 -06:00
if ( str . indexOf ( b [ 1 ] ) === - 1 ) {
return - 1 ;
}
2019-11-06 11:11:06 -06:00
const l = str . length ;
let level = 0 ,
i = 0 ;
for ( ; i < l ; i ++ ) {
2019-11-05 15:29:42 -06:00
if ( str [ i ] === '\\' ) {
i ++ ;
} else if ( str [ i ] === b [ 0 ] ) {
level ++ ;
} else if ( str [ i ] === b [ 1 ] ) {
level -- ;
if ( level < 0 ) {
return i ;
}
}
}
return - 1 ;
}
2023-05-01 23:30:06 -05:00
export function checkDeprecations ( opt , callback ) {
if ( ! opt || opt . silent ) {
return ;
}
if ( callback ) {
console . warn ( 'marked(): callback is deprecated since version 5.0.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/using_pro#async' ) ;
}
if ( opt . sanitize || opt . sanitizer ) {
2019-11-05 15:29:42 -06:00
console . warn ( 'marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options' ) ;
}
2023-05-01 23:30:06 -05:00
if ( opt . highlight || opt . langPrefix ) {
console . warn ( 'marked(): highlight and langPrefix parameters are deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-highlight.' ) ;
}
if ( opt . mangle ) {
console . warn ( 'marked(): mangle parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-mangle.' ) ;
}
if ( opt . baseUrl ) {
console . warn ( 'marked(): baseUrl parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-base-url.' ) ;
}
if ( opt . smartypants ) {
console . warn ( 'marked(): smartypants parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-smartypants.' ) ;
}
if ( opt . xhtml ) {
console . warn ( 'marked(): xhtml parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-xhtml.' ) ;
}
if ( opt . headerIds || opt . headerPrefix ) {
console . warn ( 'marked(): headerIds and headerPrefix parameters are deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-gfm-heading-id.' ) ;
}
2019-11-05 15:29:42 -06:00
}
2019-11-06 11:11:06 -06:00
2020-10-21 09:56:32 -05:00
// copied from https://stackoverflow.com/a/5450113/806777
2022-03-30 17:42:54 +02:00
/ * *
* @ param { string } pattern
* @ param { number } count
* /
2021-11-02 07:32:17 -07:00
export function repeatString ( pattern , count ) {
2020-10-21 09:56:32 -05:00
if ( count < 1 ) {
return '' ;
}
let result = '' ;
while ( count > 1 ) {
if ( count & 1 ) {
result += pattern ;
}
count >>= 1 ;
pattern += pattern ;
}
return result + pattern ;
}