2019-11-05 15:29:42 -06:00
/ * *
* Helpers
* /
2019-11-06 11:11:06 -06:00
function escape ( html , encode ) {
2019-11-05 15:29:42 -06:00
if ( encode ) {
2019-11-06 11:11:06 -06:00
if ( escape . escapeTest . test ( html ) ) {
return html . replace ( escape . escapeReplace , function ( ch ) { return escape . replacements [ ch ] ; } ) ;
2019-11-05 15:29:42 -06:00
}
} else {
2019-11-06 11:11:06 -06:00
if ( escape . escapeTestNoEncode . test ( html ) ) {
return html . replace ( escape . escapeReplaceNoEncode , function ( ch ) { return escape . replacements [ ch ] ; } ) ;
2019-11-05 15:29:42 -06:00
}
}
return html ;
}
2019-11-06 11:11:06 -06:00
escape . escapeTest = /[&<>"']/ ;
escape . escapeReplace = /[&<>"']/g ;
escape . replacements = {
'&' : '&' ,
'<' : '<' ,
'>' : '>' ,
'"' : '"' ,
"'" : '''
} ;
escape . escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/ ;
escape . escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g ;
function unescape ( html ) {
2019-11-05 15:29:42 -06:00
// explicitly match decimal, hex, and named HTML entities
return html . replace ( /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig , function ( _ , n ) {
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-11-06 11:11:06 -06:00
function edit ( regex , opt ) {
2019-11-05 15:29:42 -06:00
regex = regex . source || regex ;
opt = opt || '' ;
return {
replace : function ( name , val ) {
val = val . source || val ;
val = val . replace ( /(^|[^\[])\^/g , '$1' ) ;
regex = regex . replace ( name , val ) ;
return this ;
} ,
getRegex : function ( ) {
return new RegExp ( regex , opt ) ;
}
} ;
}
2019-11-06 11:11:06 -06:00
function cleanUrl ( sanitize , base , href ) {
2019-11-05 15:29:42 -06:00
if ( sanitize ) {
let prot ;
try {
prot = decodeURIComponent ( unescape ( href ) )
. replace ( /[^\w:]/g , '' )
. toLowerCase ( ) ;
} catch ( e ) {
return null ;
}
if ( prot . indexOf ( 'javascript:' ) === 0 || prot . indexOf ( 'vbscript:' ) === 0 || prot . indexOf ( 'data:' ) === 0 ) {
return null ;
}
}
if ( base && ! originIndependentUrl . test ( href ) ) {
href = resolveUrl ( base , href ) ;
}
try {
href = encodeURI ( href ) . replace ( /%25/g , '%' ) ;
} catch ( e ) {
return null ;
}
return href ;
}
2019-11-06 11:11:06 -06:00
function resolveUrl ( base , href ) {
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
if ( /^[^:]+:\/*[^/]*$/ . test ( base ) ) {
2019-11-06 11:11:06 -06:00
baseUrls [ ' ' + base ] = base + '/' ;
2019-11-05 15:29:42 -06:00
} else {
2019-11-06 11:11:06 -06:00
baseUrls [ ' ' + base ] = rtrim ( base , '/' , true ) ;
2019-11-05 15:29:42 -06:00
}
}
2019-11-06 11:11:06 -06:00
base = baseUrls [ ' ' + base ] ;
2019-11-05 15:29:42 -06:00
const relativeBase = base . indexOf ( ':' ) === - 1 ;
if ( href . slice ( 0 , 2 ) === '//' ) {
if ( relativeBase ) {
return href ;
}
return base . replace ( /^([^:]+:)[\s\S]*$/ , '$1' ) + href ;
} else if ( href . charAt ( 0 ) === '/' ) {
if ( relativeBase ) {
return href ;
}
return base . replace ( /^([^:]+:\/*[^/]*)[\s\S]*$/ , '$1' ) + href ;
} else {
return base + href ;
}
}
2019-11-06 11:11:06 -06:00
const baseUrls = { } ;
const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i ;
2019-11-05 15:29:42 -06:00
2019-11-06 11:11:06 -06:00
function noop ( ) { }
2019-11-05 15:29:42 -06:00
noop . exec = noop ;
2019-11-06 11:11:06 -06:00
function merge ( obj ) {
let i = 1 ,
target ,
key ;
2019-11-05 15:29:42 -06:00
for ( ; i < arguments . length ; i ++ ) {
target = arguments [ i ] ;
for ( key in target ) {
if ( Object . prototype . hasOwnProperty . call ( target , key ) ) {
obj [ key ] = target [ key ] ;
}
}
}
return obj ;
}
2019-11-06 11:11:06 -06:00
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
const row = tableRow . replace ( /\|/g , function ( 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 ;
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 ;
}
// Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
// /c*$/ is vulnerable to REDOS.
// invert: Remove suffix of non-c chars instead. Default falsey.
2019-11-06 11:11:06 -06:00
function rtrim ( str , c , invert ) {
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 ;
}
}
2019-11-06 11:11:06 -06:00
return str . substr ( 0 , l - suffLen ) ;
2019-11-05 15:29:42 -06:00
}
2019-11-06 11:11:06 -06:00
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 ;
}
2019-11-06 11:11:06 -06:00
function checkSanitizeDeprecation ( opt ) {
2019-11-05 15:29:42 -06:00
if ( opt && opt . sanitize && ! opt . silent ) {
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' ) ;
}
}
2019-11-06 11:11:06 -06:00
module . exports = {
escape ,
unescape ,
edit ,
cleanUrl ,
resolveUrl ,
noop ,
merge ,
splitCells ,
rtrim ,
findClosingBracket ,
checkSanitizeDeprecation
} ;