2019-05-14 18:00:55 +03:00
// JS rewrite of http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/ 1.2.4.1
2016-08-27 14:54:38 +03:00
var htmLawed = module . exports =
{
_flip : function ( a )
{
var e = { } ;
for ( var i = 0 ; i < a . length ; i ++ )
e [ a [ i ] ] = true ;
return e ;
} ,
_strtr : function ( t , h )
{
for ( var i in h )
t = t . replace ( new RegExp ( i , 'g' ) , h [ i ] ) ;
return t ;
} ,
_keys : function ( h )
{
var r = [ ] ;
for ( var i in h )
r . push ( i ) ;
return r ;
} ,
2019-05-14 18:00:55 +03:00
_nonempty : function ( h )
{
for ( var i in h )
return true ;
return false ;
} ,
2016-08-27 14:54:38 +03:00
_htmlspecialchars : function ( t )
{
return t . replace ( /&/g , '&' )
. replace ( /'/g , ''' ) // '
. replace ( /"/g , '"' ) // "
. replace ( /</g , '<' )
. replace ( />/g , '>' ) ;
} ,
hl _Ids : { } ,
sanitize : function ( t , C , S )
{
if ( ! C ) C = { } ;
if ( ! S ) S = { } ;
if ( C . valid _xhtml )
{
2019-05-14 18:00:55 +03:00
C . elements = C . elements || '*-acronym-big-center-dir-font-isindex-s-strike-tt' ;
2016-08-27 14:54:38 +03:00
C . make _tag _strict = C . make _tag _strict !== undefined ? C . make _tag _strict : 2 ;
C [ 'xml:lang' ] = C [ 'xml:lang' ] !== undefined ? C [ 'xml:lang' ] : 2 ;
}
// config eles
2019-05-14 18:00:55 +03:00
var e = { 'a' : 1 , 'abbr' : 1 , 'acronym' : 1 , 'address' : 1 , 'applet' : 1 , 'area' : 1 , 'article' : 1 , 'aside' : 1 , 'audio' : 1 , 'b' : 1 , 'bdi' : 1 , 'bdo' : 1 , 'big' : 1 , 'blockquote' : 1 , 'br' : 1 , 'button' : 1 , 'canvas' : 1 , 'caption' : 1 , 'center' : 1 , 'cite' : 1 , 'code' : 1 , 'col' : 1 , 'colgroup' : 1 , 'command' : 1 , 'data' : 1 , 'datalist' : 1 , 'dd' : 1 , 'del' : 1 , 'details' : 1 , 'dfn' : 1 , 'dir' : 1 , 'div' : 1 , 'dl' : 1 , 'dt' : 1 , 'em' : 1 , 'embed' : 1 , 'fieldset' : 1 , 'figcaption' : 1 , 'figure' : 1 , 'font' : 1 , 'footer' : 1 , 'form' : 1 , 'h1' : 1 , 'h2' : 1 , 'h3' : 1 , 'h4' : 1 , 'h5' : 1 , 'h6' : 1 , 'header' : 1 , 'hgroup' : 1 , 'hr' : 1 , 'i' : 1 , 'iframe' : 1 , 'img' : 1 , 'input' : 1 , 'ins' : 1 , 'isindex' : 1 , 'kbd' : 1 , 'keygen' : 1 , 'label' : 1 , 'legend' : 1 , 'li' : 1 , 'link' : 1 , 'main' : 1 , 'map' : 1 , 'mark' : 1 , 'menu' : 1 , 'meta' : 1 , 'meter' : 1 , 'nav' : 1 , 'noscript' : 1 , 'object' : 1 , 'ol' : 1 , 'optgroup' : 1 , 'option' : 1 , 'output' : 1 , 'p' : 1 , 'param' : 1 , 'pre' : 1 , 'progress' : 1 , 'q' : 1 , 'rb' : 1 , 'rbc' : 1 , 'rp' : 1 , 'rt' : 1 , 'rtc' : 1 , 'ruby' : 1 , 's' : 1 , 'samp' : 1 , 'script' : 1 , 'section' : 1 , 'select' : 1 , 'small' : 1 , 'source' : 1 , 'span' : 1 , 'strike' : 1 , 'strong' : 1 , 'style' : 1 , 'sub' : 1 , 'summary' : 1 , 'sup' : 1 , 'table' : 1 , 'tbody' : 1 , 'td' : 1 , 'textarea' : 1 , 'tfoot' : 1 , 'th' : 1 , 'thead' : 1 , 'time' : 1 , 'tr' : 1 , 'track' : 1 , 'tt' : 1 , 'u' : 1 , 'ul' : 1 , 'var' : 1 , 'video' : 1 , 'wbr' : 1 } ;
2016-08-27 14:54:38 +03:00
if ( C . safe )
{
delete e . applet ;
2019-05-14 18:00:55 +03:00
delete e . audio ;
delete e . canvas ;
2016-08-27 14:54:38 +03:00
delete e . embed ;
delete e . iframe ;
delete e . object ;
delete e . script ;
2019-05-14 18:00:55 +03:00
delete e . video ;
2016-08-27 14:54:38 +03:00
}
var x = C . elements ? C . elements . replace ( /\s+/g , '' ) : '*' ;
var v , m , i ;
if ( x == '-*' )
e = { } ;
else if ( x . indexOf ( '*' ) < 0 )
e = htmLawed . _flip ( x . split ( /,/ ) ) ;
else if ( x . length > 1 )
{
var re = /(?:^|-|\+)[^\-+]+?(?=-|\+|$)/g ;
m = { } ;
while ( ( v = re . exec ( x ) ) )
m [ v [ 0 ] ] = true ;
for ( v in m )
{
if ( v [ 0 ] == '+' )
e [ v . substr ( 1 ) ] = true ;
else if ( v [ 0 ] == '-' && ! m [ '+' + v . substr ( 1 ) ] )
delete e [ v . substr ( 1 ) ] ;
}
}
C . elements = e ;
// config attrs
2019-05-14 18:00:55 +03:00
x = C . deny _attribute ? C . deny _attribute . replace ( /\s+/g , '' ) . toLowerCase ( ) : '' ;
x = htmLawed . _flip (
x [ 0 ] == '*'
? x . replace ( /data-/g , '/' ) . split ( '-' ) . map ( s => s . replace ( '/' , 'data-' ) )
: ( x + ( C . safe ? ',on*' : '' ) ) . split ( ',' )
) ;
2016-08-27 14:54:38 +03:00
if ( x [ 'on*' ] )
{
delete x [ 'on*' ] ;
v = { 'onblur' : 1 , 'onchange' : 1 , 'onclick' : 1 , 'ondblclick' : 1 , 'onfocus' : 1 , 'onkeydown' : 1 , 'onkeypress' : 1 , 'onkeyup' : 1 , 'onmousedown' : 1 , 'onmousemove' : 1 , 'onmouseout' : 1 , 'onmouseover' : 1 , 'onmouseup' : 1 , 'onreset' : 1 , 'onselect' : 1 , 'onsubmit' : 1 } ;
for ( i in v )
x [ i ] = true ;
}
C . deny _attribute = x ;
2019-05-14 18:00:55 +03:00
// config URLs
x = C . schemes && C . schemes . length > 2 && C . schemes . indexOf ( ':' ) > 0 ? C . schemes . toLowerCase ( ) :
'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, tel, telnet' +
( ! C . safe ? ', app, javascript; *: data, javascript, ' : '; *:' ) + 'file, http, https' ;
2016-08-27 14:54:38 +03:00
C . schemes = { } ;
m = x . replace ( /\s+/g , '' ) . split ( ';' ) ;
for ( v in m )
{
x = m [ v ] . split ( ':' , 2 ) ;
if ( x [ 1 ] )
C . schemes [ x [ 0 ] ] = htmLawed . _flip ( x [ 1 ] . split ( ',' ) ) ;
}
if ( ! C . schemes [ '*' ] )
2019-05-14 18:00:55 +03:00
{
2016-08-27 14:54:38 +03:00
C . schemes [ '*' ] = { file : 1 , http : 1 , https : 1 } ;
2019-05-14 18:00:55 +03:00
if ( ! C . safe )
{
C . schemes [ '*' ] . data = 1 ;
C . schemes [ '*' ] . javascript = 1 ;
}
}
2016-08-27 14:54:38 +03:00
if ( C . safe && ! C . schemes . style )
C . schemes . style = { '!' : 1 } ;
C . abs _url = C . abs _url !== undefined ? C . abs _url : 0 ;
if ( C . base _url === undefined || ! /^[a-zA-Z\d\.+\-]+:\/\/[^\/]+\/([\s\S]+?\/)?$/ . exec ( C . base _url ) )
C . base _url = C . abs _url = 0 ;
// config rest
C . and _mark = ! C . and _mark ? 0 : 1 ;
C . anti _link _spam = ( C . anti _link _spam !== undefined && C . anti _link _spam instanceof Array &&
C . anti _link _spam . length == 2 && ( ! C . anti _link _spam [ 0 ] || htmLawed . hl _regex ( C . anti _link _spam [ 0 ] ) ) &&
( ! C . anti _link _spam [ 1 ] || htmLawed . hl _regex ( C . anti _link _spam [ 1 ] ) ) ) ? C . anti _link _spam : 0 ;
C . anti _mail _spam = C . anti _mail _spam !== undefined ? C . anti _mail _spam : 0 ;
C . balance = C . balance !== undefined ? true && C . balance : true ;
C . cdata = C . cdata !== undefined ? C . cdata : ( ! C . safe ? 3 : 0 ) ;
C . clean _ms _char = ! C . clean _ms _char ? 0 : C . clean _ms _char ;
C . comment = C . comment !== undefined ? C . comment : ( ! C . safe ? 3 : 0 ) ;
C . css _expression = ! C . css _expression ? 0 : 1 ;
C . direct _list _nest = ! C . direct _list _nest ? 0 : 1 ;
C . hexdec _entity = C . hexdec _entity !== undefined ? C . hexdec _entity : 1 ;
C . hook = ( C . hook && typeof C . hook == 'function' ) ? C . hook : 0 ;
C . hook _tag = ( C . hook _tag && typeof C . hook _tag == 'function' ) ? C . hook _tag : 0 ;
C . keep _bad = C . keep _bad !== undefined ? C . keep _bad : 6 ;
C . lc _std _val = C . lc _std _val !== undefined ? ! ! C . lc _std _val : 1 ;
C . make _tag _strict = C . make _tag _strict !== undefined ? C . make _tag _strict : 1 ;
C . named _entity = C . named _entity !== undefined ? ! ! C . named _entity : 1 ;
C . no _deprecated _attr = C . no _deprecated _attr !== undefined ? C . no _deprecated _attr : 1 ;
C . parent = typeof C . parent == 'string' && C . parent . length > 0 ? C . parent . toLowerCase ( ) : 'body' ;
C . show _setting = C . show _setting ? C . show _setting : 0 ;
C . style _pass = ! C . style _pass ? 0 : 1 ;
C . tidy = ! C . tidy ? 0 : C . tidy ;
2019-05-14 18:00:55 +03:00
C . unique _ids = C . unique _ids !== undefined && ! /\W/u . exec ( C . unique _ids ) ? C . unique _ids : 1 ;
2016-08-27 14:54:38 +03:00
C [ 'xml:lang' ] = C [ 'xml:lang' ] !== undefined ? C [ 'xml:lang' ] : 0 ;
S = typeof S == 'object' ? S : htmLawed . hl _spec ( S ) ;
// FIXME statics
htmLawed . C = C ;
htmLawed . S = S ;
// FIXME: т.к. яваскрипт работает с unicode, вероятно t = unescape(encodeURIComponent(s))
t = t . replace ( /[\x00-\x08\x0b-\x0c\x0e-\x1f]+/g , '' ) ;
if ( C . clean _ms _char )
{
x = { "\x7f" : '' , "\x80" : '€' , "\x81" : '' , "\x83" : 'ƒ' , "\x85" : '…' , "\x86" : '†' , "\x87" : '‡' , "\x88" : 'ˆ' , "\x89" : '‰' , "\x8a" : 'Š' , "\x8b" : '‹' , "\x8c" : 'Œ' , "\x8d" : '' , "\x8e" : 'Ž' , "\x8f" : '' , "\x90" : '' , "\x95" : '•' , "\x96" : '–' , "\x97" : '—' , "\x98" : '˜' , "\x99" : '™' , "\x9a" : 'š' , "\x9b" : '›' , "\x9c" : 'œ' , "\x9d" : '' , "\x9e" : 'ž' , "\x9f" : 'Ÿ' } ;
var y ;
if ( C . clean _ms _char == 1 )
y = { "\x82" : '‚' , "\x84" : '„' , "\x91" : '‘' , "\x92" : '’' , "\x93" : '“' , "\x94" : '”' } ;
else
y = { "\x82" : '\'' , "\x84" : '"' , "\x91" : '\'' , "\x92" : '\'' , "\x93" : '"' , "\x94" : '"' } ;
for ( i in y )
x [ i ] = y [ i ] ;
t = htmLawed . _strtr ( t , x ) ;
}
if ( C . cdata || C . comment )
t = t . replace ( /<!(?:(?:--[\s\S]*?--)|(?:\[CDATA\[[\s\S]*?\]\]))>/g , htmLawed . hl _cmtcd ) ;
t = t . replace ( /&/g , '&' ) . replace ( /&([A-Za-z][A-Za-z0-9]{1,30}|#(?:[0-9]{1,8}|[Xx][0-9A-Fa-f]{1,7}));/g , htmLawed . hl _ent ) ;
if ( C . unique _ids && ! htmLawed . hl _Ids )
htmLawed . hl _Ids = { } ;
if ( C . hook )
t = C . hook ( t , C , S ) ;
// main
t = t . replace ( /<(?:(?:\s|$)|(?:[^>]*(?:>|$)))|>/gm , htmLawed . hl _tag ) ;
if ( C . balance )
t = htmLawed . hl _bal ( t , C . keep _bad , C . parent ) ;
if ( ( C . cdata || C . comment ) && t . indexOf ( "\x01" ) >= 0 )
t = htmLawed . _strtr ( t , { "\x01" : '' , "\x02" : '' , "\x03" : '&' , "\x04" : '<' , "\x05" : '>' } ) ;
if ( C . tidy )
t = htmLawed . hl _tidy ( t , C . tidy , C . parent ) ;
return t ;
} ,
hl _attrval : function ( a , t , p )
{
// check attr val against S
2019-05-14 18:00:55 +03:00
var ma = { accesskey : 1 , class : 1 , itemtype : 1 , rel : 1 } ;
var s = ma [ a ] ? ' ' : ( a == 'srcset' ? ',' : '' ) ;
2016-08-27 14:54:38 +03:00
var r = [ ] ;
var i ;
t = s ? t . split ( s ) : [ t ] ;
for ( var tk = 0 ; tk < t . length ; tk ++ )
{
var tv = t [ tk ] ;
var o = 1 ;
2019-05-14 18:00:55 +03:00
if ( tv . trim ( ) . length )
2016-08-27 14:54:38 +03:00
{
2019-05-14 18:00:55 +03:00
var l = tv . length ;
for ( var k in p )
2016-08-27 14:54:38 +03:00
{
2019-05-14 18:00:55 +03:00
var v = p [ k ] ;
switch ( k )
{
case 'maxlen' : if ( l > v ) { o = 0 ; } break ;
case 'minlen' : if ( l < v ) { o = 0 ; } break ;
case 'maxval' : if ( parseFloat ( tv ) > v ) { o = 0 ; } break ;
case 'minval' : if ( parseFloat ( tv ) < v ) { o = 0 ; } break ;
case 'match' : if ( ! v . exec ( tv ) ) { o = 0 ; } break ;
case 'nomatch' : if ( v . exec ( tv ) ) { o = 0 ; } break ;
case 'oneof' :
v = v . split ( '|' ) ;
for ( i = 0 ; i < v . length && v [ i ] != tv ; i ++ ) { }
o = ( i < v . length ) ;
break ;
case 'noneof' :
v = v . split ( '|' ) ;
for ( i = 0 ; i < v . length && v [ i ] != tv ; i ++ ) { }
o = ( i >= v . length ) ;
break ;
}
if ( ! o )
break ;
2016-08-27 14:54:38 +03:00
}
}
if ( o )
r . push ( tv ) ;
}
2019-05-14 18:00:55 +03:00
if ( s == ',' )
s = ', ' ;
2016-08-27 14:54:38 +03:00
return ( r . length > 0 ? r . join ( s ) : ( p [ 'default' ] || 0 ) ) ;
} ,
hl _bal : function ( t , keep _bad , intag )
{
2016-10-03 14:06:20 +03:00
var C = htmLawed . C ;
2016-08-27 14:54:38 +03:00
if ( keep _bad === undefined )
keep _bad = 1 ;
// balance tags
// by content
var cont = { } ;
cont . B = { 'blockquote' : 1 , 'form' : 1 , 'map' : 1 , 'noscript' : 1 } ; // Block
2019-05-14 18:00:55 +03:00
cont . E = { 'area' : 1 , 'br' : 1 , 'col' : 1 , 'command' : 1 , 'embed' : 1 , 'hr' : 1 , 'img' : 1 , 'input' : 1 , 'isindex' : 1 , 'keygen' : 1 , 'link' : 1 , 'meta' : 1 , 'param' : 1 , 'source' : 1 , 'track' : 1 , 'wbr' : 1 } ; // Empty
cont . F = { 'a' : 1 , 'article' : 1 , 'aside' : 1 , 'audio' : 1 , 'button' : 1 , 'canvas' : 1 , 'del' : 1 , 'details' : 1 , 'div' : 1 , 'dd' : 1 , 'fieldset' : 1 , 'figure' : 1 , 'footer' : 1 , 'header' : 1 , 'iframe' : 1 , 'ins' : 1 , 'li' : 1 , 'main' : 1 , 'menu' : 1 , 'nav' : 1 , 'noscript' : 1 , 'object' : 1 , 'section' : 1 , 'style' : 1 , 'td' : 1 , 'th' : 1 , 'video' : 1 } ; // Flow; later context-wise dynamic move of ins & del to cont.I
cont . I = { 'abbr' : 1 , 'acronym' : 1 , 'address' : 1 , 'b' : 1 , 'bdi' : 1 , 'bdo' : 1 , 'big' : 1 , 'caption' : 1 , 'cite' : 1 , 'code' : 1 , 'data' : 1 , 'datalist' : 1 , 'dfn' : 1 , 'dt' : 1 , 'em' : 1 , 'figcaption' : 1 , 'font' : 1 , 'h1' : 1 , 'h2' : 1 , 'h3' : 1 , 'h4' : 1 , 'h5' : 1 , 'h6' : 1 , 'hgroup' : 1 , 'i' : 1 , 'kbd' : 1 , 'label' : 1 , 'legend' : 1 , 'mark' : 1 , 'meter' : 1 , 'output' : 1 , 'p' : 1 , 'pre' : 1 , 'progress' : 1 , 'q' : 1 , 'rb' : 1 , 'rt' : 1 , 's' : 1 , 'samp' : 1 , 'small' : 1 , 'span' : 1 , 'strike' : 1 , 'strong' : 1 , 'sub' : 1 , 'summary' : 1 , 'sup' : 1 , 'time' : 1 , 'tt' : 1 , 'u' : 1 , 'var' : 1 } ; // Inline
cont . N = { 'a' : { 'a' : 1 , 'address' : 1 , 'button' : 1 , 'details' : 1 , 'embed' : 1 , 'keygen' : 1 , 'label' : 1 , 'select' : 1 , 'textarea' : 1 } , 'address' : { 'address' : 1 , 'article' : 1 , 'aside' : 1 , 'header' : 1 , 'keygen' : 1 , 'footer' : 1 , 'nav' : 1 , 'section' : 1 } , 'button' : { 'a' : 1 , 'address' : 1 , 'button' : 1 , 'details' : 1 , 'embed' : 1 , 'fieldset' : 1 , 'form' : 1 , 'iframe' : 1 , 'input' : 1 , 'keygen' : 1 , 'label' : 1 , 'select' : 1 , 'textarea' : 1 } , 'fieldset' : { 'fieldset' : 1 } , 'footer' : { 'header' : 1 , 'footer' : 1 } , 'form' : { 'form' : 1 } , 'header' : { 'header' : 1 , 'footer' : 1 } , 'label' : { 'label' : 1 } , 'main' : { 'main' : 1 } , 'meter' : { 'meter' : 1 } , 'noscript' : { 'script' : 1 } , 'pre' : { 'big' : 1 , 'font' : 1 , 'img' : 1 , 'object' : 1 , 'script' : 1 , 'small' : 1 , 'sub' : 1 , 'sup' : 1 } , 'progress' : { 'progress' : 1 } , 'rb' : { 'ruby' : 1 } , 'rt' : { 'ruby' : 1 } , 'time' : { 'time' : 1 } , } ; // Illegal
cont . S = { "colgroup" : { "col" : 1 } , "datalist" : { "option" : 1 } , "dir" : { "li" : 1 } , "dl" : { "dd" : 1 , "dt" : 1 } , "hgroup" : { "h1" : 1 , "h2" : 1 , "h3" : 1 , "h4" : 1 , "h5" : 1 , "h6" : 1 } , "menu" : { "li" : 1 } , "ol" : { "li" : 1 } , "optgroup" : { "option" : 1 } , "option" : { "#pcdata" : 1 } , "rbc" : { "rb" : 1 } , "rp" : { "#pcdata" : 1 } , "rtc" : { "rt" : 1 } , "ruby" : { "rb" : 1 , "rbc" : 1 , "rp" : 1 , "rt" : 1 , "rtc" : 1 } , "select" : { "optgroup" : 1 , "option" : 1 } , "script" : { "#pcdata" : 1 } , "table" : { "caption" : 1 , "col" : 1 , "colgroup" : 1 , "tfoot" : 1 , "tbody" : 1 , "tr" : 1 , "thead" : 1 } , "tbody" : { "tr" : 1 } , "tfoot" : { "tr" : 1 } , "textarea" : { "#pcdata" : 1 } , "thead" : { "tr" : 1 } , "tr" : { "td" : 1 , "th" : 1 } , "ul" : { "li" : 1 } } ; // Specific - immediate parent-child
2016-08-27 14:54:38 +03:00
if ( htmLawed . C . direct _list _nest )
2019-05-14 18:00:55 +03:00
{
cont . S [ 'ol' ] [ 'ol' ] = cont . S [ 'ol' ] [ 'ul' ] = cont . S [ 'ol' ] [ 'menu' ] = 1 ;
cont . S [ 'ul' ] [ 'ol' ] = cont . S [ 'ul' ] [ 'ul' ] = cont . S [ 'ul' ] [ 'menu' ] = 1 ;
cont . S [ 'menu' ] [ 'ol' ] = cont . S [ 'menu' ] [ 'ul' ] = cont . S [ 'menu' ] [ 'menu' ] = 1 ;
}
cont . O = { "address" : { "p" : 1 } , "applet" : { "param" : 1 } , "audio" : { "source" : 1 , "track" : 1 } , "blockquote" : { "script" : 1 } , "details" : { "summary" : 1 } , "fieldset" : { "legend" : 1 , "#pcdata" : 1 } , "figure" : { "figcaption" : 1 } , "form" : { "script" : 1 } , "map" : { "area" : 1 } , "object" : { "param" : 1 , "embed" : 1 } , "video" : { "source" : 1 , "track" : 1 } } ; // Other
2016-08-27 14:54:38 +03:00
cont . T = { 'colgroup' : 1 , 'dd' : 1 , 'dt' : 1 , 'li' : 1 , 'option' : 1 , 'p' : 1 , 'td' : 1 , 'tfoot' : 1 , 'th' : 1 , 'thead' : 1 , 'tr' : 1 } ; // Omitable closing
2019-05-14 18:00:55 +03:00
// block/inline type; a/ins/del both type; #pcdata: text
2016-08-27 14:54:38 +03:00
var el = { } ;
2019-05-14 18:00:55 +03:00
el . B = { "a" : 1 , "address" : 1 , "article" : 1 , "aside" : 1 , "blockquote" : 1 , "center" : 1 , "del" : 1 , "details" : 1 , "dir" : 1 , "dl" : 1 , "div" : 1 , "fieldset" : 1 , "figure" : 1 , "footer" : 1 , "form" : 1 , "ins" : 1 , "h1" : 1 , "h2" : 1 , "h3" : 1 , "h4" : 1 , "h5" : 1 , "h6" : 1 , "header" : 1 , "hr" : 1 , "isindex" : 1 , "main" : 1 , "menu" : 1 , "nav" : 1 , "noscript" : 1 , "ol" : 1 , "p" : 1 , "pre" : 1 , "section" : 1 , "style" : 1 , "table" : 1 , "ul" : 1 } ;
el . I = { "#pcdata" : 1 , "a" : 1 , "abbr" : 1 , "acronym" : 1 , "applet" : 1 , "audio" : 1 , "b" : 1 , "bdi" : 1 , "bdo" : 1 , "big" : 1 , "br" : 1 , "button" : 1 , "canvas" : 1 , "cite" : 1 , "code" : 1 , "command" : 1 , "data" : 1 , "datalist" : 1 , "del" : 1 , "dfn" : 1 , "em" : 1 , "embed" : 1 , "figcaption" : 1 , "font" : 1 , "i" : 1 , "iframe" : 1 , "img" : 1 , "input" : 1 , "ins" : 1 , "kbd" : 1 , "label" : 1 , "link" : 1 , "map" : 1 , "mark" : 1 , "meta" : 1 , "meter" : 1 , "object" : 1 , "output" : 1 , "progress" : 1 , "q" : 1 , "ruby" : 1 , "s" : 1 , "samp" : 1 , "select" : 1 , "script" : 1 , "small" : 1 , "span" : 1 , "strike" : 1 , "strong" : 1 , "sub" : 1 , "summary" : 1 , "sup" : 1 , "textarea" : 1 , "time" : 1 , "tt" : 1 , "u" : 1 , "var" : 1 , "video" : 1 , "wbr" : 1 } ;
el . N = { "a" : 1 , "address" : 1 , "article" : 1 , "aside" : 1 , "big" : 1 , "button" : 1 , "details" : 1 , "embed" : 1 , "fieldset" : 1 , "font" : 1 , "footer" : 1 , "form" : 1 , "header" : 1 , "iframe" : 1 , "img" : 1 , "input" : 1 , "keygen" : 1 , "label" : 1 , "meter" : 1 , "nav" : 1 , "object" : 1 , "progress" : 1 , "ruby" : 1 , "script" : 1 , "select" : 1 , "small" : 1 , "sub" : 1 , "sup" : 1 , "textarea" : 1 , "time" : 1 } ;
el . O = { "area" : 1 , "caption" : 1 , "col" : 1 , "colgroup" : 1 , "command" : 1 , "dd" : 1 , "dt" : 1 , "hgroup" : 1 , "keygen" : 1 , "legend" : 1 , "li" : 1 , "optgroup" : 1 , "option" : 1 , "param" : 1 , "rb" : 1 , "rbc" : 1 , "rp" : 1 , "rt" : 1 , "rtc" : 1 , "script" : 1 , "source" : 1 , "tbody" : 1 , "td" : 1 , "tfoot" : 1 , "thead" : 1 , "th" : 1 , "tr" : 1 , "track" : 1 } ;
el . F = { ... el . B , ... el . I } ;
2016-08-27 14:54:38 +03:00
function getCont ( intag )
{
var inOk = { } ;
if ( cont . S [ intag ] )
inOk = cont . S [ intag ] ;
else if ( cont . I [ intag ] )
{
inOk = el . I ;
cont . I [ 'del' ] = 1 ;
cont . I [ 'ins' ] = 1 ;
}
else if ( cont . F [ intag ] )
{
inOk = el . F ;
delete cont . I [ 'del' ] ;
delete cont . I [ 'ins' ] ;
}
else if ( cont . B [ intag ] )
{
inOk = el . B ;
delete cont . I [ 'del' ] ;
delete cont . I [ 'ins' ] ;
}
if ( cont . O [ intag ] )
inOk = { ... inOk , ... cont . O [ intag ] } ;
if ( cont . N [ intag ] )
{
inOk = { ... inOk } ;
for ( var k in cont . N [ intag ] )
delete inOk [ k ] ;
}
return inOk ;
}
// intag sets allowed child
intag = ( ( el . F [ intag ] && intag != '#pcdata' ) || el . O [ intag ] ) ? intag : 'div' ;
if ( cont . E [ intag ] )
return ( ! keep _bad ? '' : htmLawed . replace ( /</g , '<' ) . replace ( />/g , '>' ) ) ;
var inOk = getCont ( intag ) ;
var ok = { } , q = [ ] , ql ; // q = seq list of open non-empty ele
var _ob = '' ;
var r , s , e , a , x , p ;
t = t . split ( '<' ) ;
2016-10-03 14:06:20 +03:00
for ( var i = 0 ; i < t . length ; i ++ )
2016-08-27 14:54:38 +03:00
{
// get markup
r = /^(\/?)([a-z1-6]+)([^>]*)>([\s\S]*)$/ . exec ( t [ i ] ) ;
if ( ! r )
x = t [ i ] ;
else
{
//s = r[1]; e = r[2]; a = r[3]; x = r[4];
[ , s , e , a , x ] = r ; // FIXME ES6
// close tag
if ( s )
{
if ( cont . E [ e ] || ! q . filter ( x => x == e ) . length ) // FIXME ES5.1 (IE9+)
{
// Empty/unopen
}
else if ( p == e )
{
2016-10-03 14:06:20 +03:00
if ( ! cont . E [ e ] )
q . pop ( ) ;
2016-08-27 14:54:38 +03:00
_ob += '</' + e + '>' ;
e = null ;
// Last open
}
else
{
var add = '' ; // Nesting - close open tags that need to be
while ( q . length )
{
var d = q . pop ( ) ;
if ( d == e )
break ;
else
add += '</' + d + '>' ;
}
_ob += add + '</' + e + '>' ;
e = null ;
}
}
2016-10-03 14:06:20 +03:00
else if ( ! C . elements [ e ] )
{
// Forbidden tag not handled by hl_tag() - remove everything up to its end
for ( let j = i + 1 , _in = 1 ; j < t . length ; j ++ )
{
r = /^(\/?)([a-z1-6]+)([^>]*)>/ . exec ( t [ j ] ) ;
if ( r && r [ 2 ] == e )
{
_in += ( r [ 1 ] ? - 1 : 1 ) ;
}
if ( _in <= 0 )
{
t [ j ] = t [ j ] . substr ( r [ 0 ] . length ) ;
t . splice ( i , j - i ) ;
break ;
}
else if ( j == t . length - 1 )
{
t . splice ( i , t . length - i ) ;
break ;
}
}
i -- ;
continue ;
}
2016-08-27 14:54:38 +03:00
// open tag
// cont.B ele needs el.B ele as child
else if ( cont . B [ e ] && x . trim ( ) . length > 0 ) // FIXME trim
{
t [ i ] = e + a + '>' ;
t . splice ( i + 1 , 0 , 'div>' + x ) ;
2016-10-03 14:06:20 +03:00
i -- ;
2016-08-27 14:54:38 +03:00
e = x = null ;
}
2016-10-03 14:06:20 +03:00
else if ( ( ( q . length && cont . B [ p ] ) || ( cont . B [ intag ] && ! q . length ) ) && ! el . B [ e ] && ! ok [ e ] )
2016-08-27 14:54:38 +03:00
{
t . splice ( i , 0 , 'div>' ) ;
2016-10-03 14:06:20 +03:00
i -- ;
2016-08-27 14:54:38 +03:00
e = x = null ;
}
// if no open ele, intag = parent; mostly immediate parent-child relation should hold
2016-10-03 14:06:20 +03:00
else if ( ! q . length || ! el . N [ e ] || ! q . filter ( _k => cont . N [ _k ] ) . length )
2016-08-27 14:54:38 +03:00
{
if ( ! ok [ e ] )
{
2016-10-03 14:06:20 +03:00
if ( q . length && cont . T [ p ] )
2016-08-27 14:54:38 +03:00
{
_ob += '</' + q . pop ( ) + '>' ;
e = x = null ;
i -- ;
}
}
else
{
if ( ! cont . E [ e ] )
q . push ( e ) ;
_ob += '<' + e + a + '>' ;
e = null ;
}
}
// specific parent-child
else if ( cont . S [ p ] && cont . S [ p ] [ e ] )
{
if ( ! cont . E [ e ] )
q . push ( e ) ;
_ob += '<' + e + a + '>' ;
e = null ;
}
else
{
// nesting
add = '' ;
var q2 = [ ] ;
var _stop = 0 ;
for ( var k = 0 , kc = q . length ; k < kc ; k ++ )
{
d = q [ k ] ;
if ( cont . S [ d ] )
{
q2 . push ( d ) ;
continue ;
}
var ok2 = cont . I [ d ] ? el . I : el . F ;
if ( cont . O [ d ] )
ok2 = { ... ok2 , ... cont . O [ d ] } ;
if ( cont . N [ d ] )
{
ok2 = { ... ok2 } ;
for ( var _k in cont . N [ d ] )
delete ok2 [ _k ] ;
}
if ( ! ok2 [ e ] )
{
if ( ! k && ! inOk [ e ] )
{
_stop = 1 ;
break ;
}
add = '</' + d + '>' ;
while ( ++ k < kc )
add = '</' + q [ k ] + '>' + add ;
break ;
}
else
q2 . push ( d ) ;
}
if ( ! _stop )
{
q = q2 ;
if ( ! cont . E [ e ] )
q . push ( e ) ;
_ob += add + '<' + e + a + '>' ;
e = null ;
}
}
}
// allowed (ok) in parent (p)
ql = q . length ;
if ( ql )
{
p = q [ ql - 1 ] ;
ok = getCont ( p ) ;
}
else
{
ok = inOk ;
delete cont . I [ 'del' ] ;
delete cont . I [ 'ins' ] ;
}
// bad tags, & ele content
if ( e && ( keep _bad == 1 || ( ok [ '#pcdata' ] && ( keep _bad == 3 || keep _bad == 5 ) ) ) )
_ob += '<' + s + e + a + '>' ;
if ( x !== '' && x !== null )
{
if ( x . trim ( ) . length > 0 && ( ( ql && cont . B [ p ] ) || ( cont . B [ intag ] && ! ql ) ) ) // FIXME trim
_ob += '<div>' + x + '</div>' ;
else if ( keep _bad < 3 || ok [ '#pcdata' ] )
_ob += x ;
else if ( x . indexOf ( "\x02\x04" ) >= 0 )
{
x = x . split ( /(\x01\x02[^\x01\x02]+\x02\x01)+/ ) ;
for ( var _i = 0 ; _i < x . length ; _i ++ )
{
var v = x [ _i ] ;
_ob += v . substr ( 0 , 2 ) == "\x01\x02" ? v : ( keep _bad > 4 ? v . replace ( /\S+/g , '' ) : '' ) ;
}
}
else if ( keep _bad > 4 )
_ob += x . replace ( /\S+/g , '' ) ;
}
}
// end
while ( ( e = q . pop ( ) ) )
_ob += '</' + e + '>' ;
return _ob ;
} ,
hl _cmtcd : function ( t )
{
// comment/CDATA sec handler
var n = t [ 3 ] == '-' ? 'comment' : 'cdata' ;
var v = htmLawed . C [ n ] ;
if ( ! v ) return t ;
if ( v == 1 ) return '' ;
2019-05-14 18:00:55 +03:00
if ( n == 'comment' && v < 4 )
2016-08-27 14:54:38 +03:00
{
t = t . substr ( 4 , t . length - 3 - 4 ) . replace ( /--+/g , '-' ) ;
if ( t . substr ( t . length - 1 ) != ' ' )
t += ' ' ;
}
else
t = t . substr ( 1 , t . length - 2 ) ;
t = v == 2 ? htmLawed . _strtr ( t , { '&' : '&' , '<' : '<' , '>' : '>' } ) : t ;
t = ( n == 'comment' ? "\x01\x02\x04!--" + t + "--\x05\x02\x01" : "\x01\x01\x04" + t + "\x05\x01\x01" ) ;
return htmLawed . _strtr ( t , { '&' : "\x03" , '<' : "\x04" , '>' : "\x05" } ) ;
} ,
ENT : { 'fnof' : '402' , 'Alpha' : '913' , 'Beta' : '914' , 'Gamma' : '915' , 'Delta' : '916' , 'Epsilon' : '917' , 'Zeta' : '918' , 'Eta' : '919' , 'Theta' : '920' , 'Iota' : '921' , 'Kappa' : '922' , 'Lambda' : '923' , 'Mu' : '924' , 'Nu' : '925' , 'Xi' : '926' , 'Omicron' : '927' , 'Pi' : '928' , 'Rho' : '929' , 'Sigma' : '931' , 'Tau' : '932' , 'Upsilon' : '933' , 'Phi' : '934' , 'Chi' : '935' , 'Psi' : '936' , 'Omega' : '937' , 'alpha' : '945' , 'beta' : '946' , 'gamma' : '947' , 'delta' : '948' , 'epsilon' : '949' , 'zeta' : '950' , 'eta' : '951' , 'theta' : '952' , 'iota' : '953' , 'kappa' : '954' , 'lambda' : '955' , 'mu' : '956' , 'nu' : '957' , 'xi' : '958' , 'omicron' : '959' , 'pi' : '960' , 'rho' : '961' , 'sigmaf' : '962' , 'sigma' : '963' , 'tau' : '964' , 'upsilon' : '965' , 'phi' : '966' , 'chi' : '967' , 'psi' : '968' , 'omega' : '969' , 'thetasym' : '977' , 'upsih' : '978' , 'piv' : '982' , 'bull' : '8226' , 'hellip' : '8230' , 'prime' : '8242' , 'Prime' : '8243' , 'oline' : '8254' , 'frasl' : '8260' , 'weierp' : '8472' , 'image' : '8465' , 'real' : '8476' , 'trade' : '8482' , 'alefsym' : '8501' , 'larr' : '8592' , 'uarr' : '8593' , 'rarr' : '8594' , 'darr' : '8595' , 'harr' : '8596' , 'crarr' : '8629' , 'lArr' : '8656' , 'uArr' : '8657' , 'rArr' : '8658' , 'dArr' : '8659' , 'hArr' : '8660' , 'forall' : '8704' , 'part' : '8706' , 'exist' : '8707' , 'empty' : '8709' , 'nabla' : '8711' , 'isin' : '8712' , 'notin' : '8713' , 'ni' : '8715' , 'prod' : '8719' , 'sum' : '8721' , 'minus' : '8722' , 'lowast' : '8727' , 'radic' : '8730' , 'prop' : '8733' , 'infin' : '8734' , 'ang' : '8736' , 'and' : '8743' , 'or' : '8744' , 'cap' : '8745' , 'cup' : '8746' , 'int' : '8747' , 'there4' : '8756' , 'sim' : '8764' , 'cong' : '8773' , 'asymp' : '8776' , 'ne' : '8800' , 'equiv' : '8801' , 'le' : '8804' , 'ge' : '8805' , 'sub' : '8834' , 'sup' : '8835' , 'nsub' : '8836' , 'sube' : '8838' , 'supe' : '8839' , 'oplus' : '8853' , 'otimes' : '8855' , 'perp' : '8869' , 'sdot' : '8901' , 'lceil' : '8968' , 'rceil' : '8969' , 'lfloor' : '8970' , 'rfloor' : '8971' , 'lang' : '9001' , 'rang' : '9002' , 'loz' : '9674' , 'spades' : '9824' , 'clubs' : '9827' , 'hearts' : '9829' , 'diams' : '9830' , 'apos' : '39' , 'OElig' : '338' , 'oelig' : '339' , 'Scaron' : '352' , 'scaron' : '353' , 'Yuml' : '376' , 'circ' : '710' , 'tilde' : '732' , 'ensp' : '8194' , 'emsp' : '8195' , 'thinsp' : '8201' , 'zwnj' : '8204' , 'zwj' : '8205' , 'lrm' : '8206' , 'rlm' : '8207' , 'ndash' : '8211' , 'mdash' : '8212' , 'lsquo' : '8216' , 'rsquo' : '8217' , 'sbquo' : '8218' , 'ldquo' : '8220' , 'rdquo' : '8221' , 'bdquo' : '8222' , 'dagger' : '8224' , 'Dagger' : '8225' , 'permil' : '8240' , 'lsaquo' : '8249' , 'rsaquo' : '8250' , 'euro' : '8364' , 'nbsp' : '160' , 'iexcl' : '161' , 'cent' : '162' , 'pound' : '163' , 'curren' : '164' , 'yen' : '165' , 'brvbar' : '166' , 'sect' : '167' , 'uml' : '168' , 'copy' : '169' , 'ordf' : '170' , 'laquo' : '171' , 'not' : '172' , 'shy' : '173' , 'reg' : '174' , 'macr' : '175' , 'deg' : '176' , 'plusmn' : '177' , 'sup2' : '178' , 'sup3' : '179' , 'acute' : '180' , 'micro' : '181' , 'para' : '182' , 'middot' : '183' , 'cedil' : '184' , 'sup1' : '185' , 'ordm' : '186' , 'raquo' : '187' , 'frac14' : '188' , 'frac12' : '189' , 'frac34' : '190' , 'iquest' : '191' , 'Agrave' : '192' , 'Aacute' : '193' , 'Acirc' : '194' , 'Atilde' : '195' , 'Auml' : '196' , 'Aring' : '197' , 'AElig' : '198' , 'Ccedil' : '199' , 'Egrave' : '200' , 'Eacute' : '201' , 'Ecirc' : '202' , 'Euml' : '203' , 'Igrave' : '204' , 'Iacute' : '205' , 'Icirc' : '206' , 'Iuml' : '207' , 'ETH' : '208' , 'Ntilde' : '209' , 'Ograve' : '210' , 'Oacute' : '211' , 'Ocirc' : '212' , 'Otilde' : '213' , 'Ouml' : '214' , 'times' : '215' , 'Oslash' : '216' , 'Ugrave' : '217' , 'Uacute' : '218' , 'Ucirc' : '219' , 'Uuml' : '220' , 'Yacute' : '221' , 'THORN' : '222' , 'szlig' : '223' , 'agrave' : '224' , 'aacute' : '225' , 'acirc' : '226' , 'atilde' : '227' , 'auml' : '228' , 'aring' : '229' , 'aelig' : '230' , 'ccedil' : '231' , 'egrave' : '232' , 'eacute' : '233' , 'ecirc' : '234' , 'euml' : '235' , 'igrave' : '236' , 'iacute' : '237' , 'icirc' : '238' , 'iuml' : '239' , 'eth' : '240' , 'ntilde' : '241' , 'ograve' : '242' , 'oacute' : '243' , 'ocirc' : '244' , 'otilde' : '245' , 'ouml' : '246' , 'divide' : '247' , 'oslash' : '248' , 'ugrave' : '249' , 'uacute' : '250' , 'ucirc' : '251' , 'uuml' : '252' , 'yacute' : '253' , 'thorn' : '254' , 'yuml' : '255' } ,
ENT _U : { 'quot' : 1 , 'amp' : 1 , 'lt' : 1 , 'gt' : 1 } ,
hl _ent : function ( all , t )
{
// entity handler
var C = htmLawed . C ;
if ( t [ 0 ] != '#' )
{
return ( C . and _mark ? "\x06" : '&' ) + ( htmLawed . ENT _U [ t ] ? t : ( htmLawed . ENT [ t ]
? ( ! C . named _entity ? '#' + ( C . hexdec _entity > 1 ? 'x' + parseInt ( htmLawed . ENT [ t ] ) . toString ( 16 ) : htmLawed . ENT [ t ] ) : t )
: 'amp;' + t ) ) + ';' ;
}
t = t . substr ( 1 ) ;
var n = /^\d+$/ . exec ( t ) ? parseInt ( t ) : parseInt ( t . replace ( /[^a-f0-9]/gi , '' ) , 16 ) ;
if ( n < 9 || ( n > 13 && n < 32 ) || n == 11 || n == 12 || ( n > 126 && n < 160 && n != 133 ) ||
( n > 55295 && ( n < 57344 || ( n > 64975 && n < 64992 ) || n == 65534 || n == 65535 || n > 1114111 ) ) )
{
return ( C . and _mark ? "\x06" : '&' ) + "amp;#" + t + ";" ;
}
return ( C . and _mark ? "\x06" : '&' ) + '#' + ( ( ( /^\d+$/ . exec ( t ) && C . hexdec _entity < 2 ) || ! C . hexdec _entity ) ? n : 'x' + n . toString ( 16 ) ) + ';' ;
} ,
hl _prot : function ( p , c )
{
var C = htmLawed . C ;
var d = 'denied:' ;
// check URL scheme
var a = '' , b = '' ;
if ( c === undefined )
{
c = 'style' ;
b = p [ 1 ] ;
a = p [ 3 ] ;
p = p [ 2 ] . trim ( ) ;
}
c = C . schemes [ c ] || C . schemes [ '*' ] ;
if ( c [ '!' ] && p . substr ( 0 , 7 ) != d )
p = d + p ;
if ( c [ '*' ] || /^[#;?]/ . exec ( p ) || p . substr ( 0 , 7 ) == d )
return b + p + a ; // All ok, frag, query, param
var m = /^([^:?[@!$()*,=\/\'\]]+?)(:|&#(58|x3a);|%3a|\\0{0,4}3a)[\s\S]/i . exec ( p ) ; // '
if ( m && ! c [ m [ 1 ] . toLowerCase ( ) ] ) // Denied prot
return b + d + p + a ;
if ( C . abs _url )
{
if ( C . abs _url == - 1 && p . indexOf ( C . base _url ) === 0 )
{
// Make url rel
p = p . substr ( C . base _url . length ) ;
}
else if ( ! m [ 1 ] )
{
// Make url abs
if ( p . substr ( 0 , 2 ) == '//' )
p = C . base _url . substr ( 0 , C . base _url . indexOf ( ':' ) + 1 ) + p ;
else if ( p [ 0 ] == '/' )
p = C . base _url . replace ( /(^[\s\S]+?:\/\/[^\/]+)([\s\S]*)/ , '$1' ) + p ;
else if ( ! /^[\.\/]/ . exec ( p ) )
p = C . base _url + p ;
else
{
m = /^([a-zA-Z\d\-+\.]+:\/\/[^\/]+)([\s\S]*)/ . exec ( C . base _url ) ;
p = ( m [ 2 ] + p ) . replace ( /\/\.\//g , '/' ) ;
while ( /\/([^\/]{3,}|[^\/\.]+?|\.[^\/\.]|[^\/\.]\.)\/\.\.\// . exec ( p ) )
p = p . replace ( /\/([^\/]{3,}|[^\/\.]+?|\.[^\/\.]|[^\/\.]\.)\/\.\.\//g , '/' ) ;
p = m [ 1 ] + p ;
}
}
}
return b + p + a ;
} ,
hl _regex : function ( p )
{
try
{
var re = new RegExp ( p ) ;
return re && true ;
}
catch ( e )
{
}
return false ;
} ,
hl _spec : function ( t )
{
// final $spec
var s = { } ;
t = t . trim ( ) . replace ( /"(`[\s\S]|[^\"])*"/g , function ( m )
{
m = htmLawed . _strtr ( m [ 0 ] , { ';' : "\x01" , '|' : "\x02" , '~' : "\x03" , ' ' : "\x04" , ',' : "\x05" , '/' : "\x06" , '(' : "\x07" , ')' : "\x08" , '`"' : '"' } ) ;
return m . substr ( 1 , m . length - 2 ) ;
} ) . replace ( /\s+/g , '' ) ;
t = t . split ( /;/ ) ;
var e , a , _i ;
for ( var i = t . length - 1 ; i >= 0 ; i -- )
{
var w = t [ i ] ;
if ( ! w || ( e = w . indexOf ( '=' ) ) < 0 || ( a = w . substr ( e + 1 ) ) === '' )
continue ;
var x , y = { } , n = { } , p , v , m ;
a = a . split ( ',' ) ;
for ( _i = 0 ; _i < a . length ; _i ++ )
{
v = a [ _i ] ;
m = /^([a-z:\-\*]+)(?:\(([\s\S]*?)\))?/i . exec ( v ) ;
if ( ! m )
continue ;
if ( m [ 1 ] === '-*' )
{
n [ '*' ] = 1 ;
continue ;
}
x = m [ 1 ] . toLowerCase ( ) ;
if ( x [ 0 ] == '-' )
{
n [ x . substr ( 1 ) ] = 1 ;
continue ;
}
if ( ! m [ 2 ] )
{
y [ x ] = 1 ;
continue ;
}
m = m [ 2 ] . split ( '/' ) ;
for ( var _j = 0 ; _j < m . length ; _j ++ )
{
if ( ! m [ _j ] || ( p = m . indexOf ( '=' ) ) == 0 || p < 5 )
{
y [ x ] = 1 ;
continue ;
}
y [ x ] [ m [ _j ] . substr ( 0 , p ) . toLowerCase ( ) ] = htmLawed . _strtr ( m [ _j ] . substr ( p + 1 ) , { "\x01" : ';' , "\x02" : '|' , "\x03" : '~' , "\x04" : ' ' , "\x05" : ',' , "\x06" : '/' , "\x07" : '(' , "\x08" : ')' } ) ;
}
if ( y [ x ] . match && ! htmLawed . hl _regex ( y [ x ] . match ) )
delete y [ x ] . match ;
if ( y [ x ] . nomatch && ! htmLawed . hl _regex ( y [ x ] . nomatch ) )
delete y [ x ] . nomatch ;
}
2019-05-14 18:00:55 +03:00
var _y = this . _nonempty ( y ) , _n = this . _nonempty ( n ) ;
2016-08-27 14:54:38 +03:00
if ( ! _y && ! _n )
continue ;
w = w . substr ( 0 , e ) ;
for ( _i = 0 ; _i < w . length ; _i ++ )
{
v = w [ _i ] . toLowerCase ( ) ;
if ( v === '' )
continue ;
if ( _y )
2019-05-14 18:00:55 +03:00
s [ v ] = s [ v ] ? { ... s [ v ] , ... y } : y ;
2016-08-27 14:54:38 +03:00
if ( _n )
2019-05-14 18:00:55 +03:00
s [ v ] . n = s [ v ] . n ? { ... s [ v ] . n , ... n } : n ;
2016-08-27 14:54:38 +03:00
}
}
return s ;
} ,
TAG : {
2019-05-14 18:00:55 +03:00
D : { 'acronym' : 1 , 'applet' : 1 , 'big' : 1 , 'center' : 1 , 'dir' : 1 , 'font' : 1 , 'isindex' : 1 , 's' : 1 , 'strike' : 1 , 'tt' : 1 } , // Deprecated
E : { 'area' : 1 , 'br' : 1 , 'col' : 1 , 'command' : 1 , 'embed' : 1 , 'hr' : 1 , 'img' : 1 , 'input' : 1 , 'isindex' : 1 , 'keygen' : 1 , 'link' : 1 , 'meta' : 1 , 'param' : 1 , 'source' : 1 , 'track' : 1 , 'wbr' : 1 } , // Empty ele
// Ele-specific
N : {
"abbr" : {
"td" : 1 ,
"th" : 1
} ,
"accept" : {
"form" : 1 ,
"input" : 1
} ,
"accept-charset" : {
"form" : 1
} ,
"action" : {
"form" : 1
} ,
"align" : {
"applet" : 1 ,
"caption" : 1 ,
"col" : 1 ,
"colgroup" : 1 ,
"div" : 1 ,
"embed" : 1 ,
"h1" : 1 ,
"h2" : 1 ,
"h3" : 1 ,
"h4" : 1 ,
"h5" : 1 ,
"h6" : 1 ,
"hr" : 1 ,
"iframe" : 1 ,
"img" : 1 ,
"input" : 1 ,
"legend" : 1 ,
"object" : 1 ,
"p" : 1 ,
"table" : 1 ,
"tbody" : 1 ,
"td" : 1 ,
"tfoot" : 1 ,
"th" : 1 ,
"thead" : 1 ,
"tr" : 1
} ,
"allowfullscreen" : {
"iframe" : 1
} ,
"alt" : {
"applet" : 1 ,
"area" : 1 ,
"img" : 1 ,
"input" : 1
} ,
"archive" : {
"applet" : 1 ,
"object" : 1
} ,
"async" : {
"script" : 1
} ,
"autocomplete" : {
"form" : 1 ,
"input" : 1
} ,
"autofocus" : {
"button" : 1 ,
"input" : 1 ,
"keygen" : 1 ,
"select" : 1 ,
"textarea" : 1
} ,
"autoplay" : {
"audio" : 1 ,
"video" : 1
} ,
"axis" : {
"td" : 1 ,
"th" : 1
} ,
"bgcolor" : {
"embed" : 1 ,
"table" : 1 ,
"td" : 1 ,
"th" : 1 ,
"tr" : 1
} ,
"border" : {
"img" : 1 ,
"object" : 1 ,
"table" : 1
} ,
"bordercolor" : {
"table" : 1 ,
"td" : 1 ,
"tr" : 1
} ,
"cellpadding" : {
"table" : 1
} ,
"cellspacing" : {
"table" : 1
} ,
"challenge" : {
"keygen" : 1
} ,
"char" : {
"col" : 1 ,
"colgroup" : 1 ,
"tbody" : 1 ,
"td" : 1 ,
"tfoot" : 1 ,
"th" : 1 ,
"thead" : 1 ,
"tr" : 1
} ,
"charoff" : {
"col" : 1 ,
"colgroup" : 1 ,
"tbody" : 1 ,
"td" : 1 ,
"tfoot" : 1 ,
"th" : 1 ,
"thead" : 1 ,
"tr" : 1
} ,
"charset" : {
"a" : 1 ,
"script" : 1
} ,
"checked" : {
"command" : 1 ,
"input" : 1
} ,
"cite" : {
"blockquote" : 1 ,
"del" : 1 ,
"ins" : 1 ,
"q" : 1
} ,
"classid" : {
"object" : 1
} ,
"clear" : {
"br" : 1
} ,
"code" : {
"applet" : 1
} ,
"codebase" : {
"applet" : 1 ,
"object" : 1
} ,
"codetype" : {
"object" : 1
} ,
"color" : {
"font" : 1
} ,
"cols" : {
"textarea" : 1
} ,
"colspan" : {
"td" : 1 ,
"th" : 1
} ,
"compact" : {
"dir" : 1 ,
"dl" : 1 ,
"menu" : 1 ,
"ol" : 1 ,
"ul" : 1
} ,
"content" : {
"meta" : 1
} ,
"controls" : {
"audio" : 1 ,
"video" : 1
} ,
"coords" : {
"a" : 1 ,
"area" : 1
} ,
"crossorigin" : {
"img" : 1
} ,
"data" : {
"object" : 1
} ,
"datetime" : {
"del" : 1 ,
"ins" : 1 ,
"time" : 1
} ,
"declare" : {
"object" : 1
} ,
"default" : {
"track" : 1
} ,
"defer" : {
"script" : 1
} ,
"dirname" : {
"input" : 1 ,
"textarea" : 1
} ,
"disabled" : {
"button" : 1 ,
"command" : 1 ,
"fieldset" : 1 ,
"input" : 1 ,
"keygen" : 1 ,
"optgroup" : 1 ,
"option" : 1 ,
"select" : 1 ,
"textarea" : 1
} ,
"download" : {
"a" : 1
} ,
"enctype" : {
"form" : 1
} ,
"face" : {
"font" : 1
} ,
"flashvars" : {
"embed" : 1
} ,
"for" : {
"label" : 1 ,
"output" : 1
} ,
"form" : {
"button" : 1 ,
"fieldset" : 1 ,
"input" : 1 ,
"keygen" : 1 ,
"label" : 1 ,
"object" : 1 ,
"output" : 1 ,
"select" : 1 ,
"textarea" : 1
} ,
"formaction" : {
"button" : 1 ,
"input" : 1
} ,
"formenctype" : {
"button" : 1 ,
"input" : 1
} ,
"formmethod" : {
"button" : 1 ,
"input" : 1
} ,
"formnovalidate" : {
"button" : 1 ,
"input" : 1
} ,
"formtarget" : {
"button" : 1 ,
"input" : 1
} ,
"frame" : {
"table" : 1
} ,
"frameborder" : {
"iframe" : 1
} ,
"headers" : {
"td" : 1 ,
"th" : 1
} ,
"height" : {
"applet" : 1 ,
"canvas" : 1 ,
"embed" : 1 ,
"iframe" : 1 ,
"img" : 1 ,
"input" : 1 ,
"object" : 1 ,
"td" : 1 ,
"th" : 1 ,
"video" : 1
} ,
"high" : {
"meter" : 1
} ,
"href" : {
"a" : 1 ,
"area" : 1 ,
"link" : 1
} ,
"hreflang" : {
"a" : 1 ,
"area" : 1 ,
"link" : 1
} ,
"hspace" : {
"applet" : 1 ,
"embed" : 1 ,
"img" : 1 ,
"object" : 1
} ,
"icon" : {
"command" : 1
} ,
"ismap" : {
"img" : 1 ,
"input" : 1
} ,
"keyparams" : {
"keygen" : 1
} ,
"keytype" : {
"keygen" : 1
} ,
"kind" : {
"track" : 1
} ,
"label" : {
"command" : 1 ,
"menu" : 1 ,
"option" : 1 ,
"optgroup" : 1 ,
"track" : 1
} ,
"language" : {
"script" : 1
} ,
"list" : {
"input" : 1
} ,
"longdesc" : {
"img" : 1 ,
"iframe" : 1
} ,
"loop" : {
"audio" : 1 ,
"video" : 1
} ,
"low" : {
"meter" : 1
} ,
"marginheight" : {
"iframe" : 1
} ,
"marginwidth" : {
"iframe" : 1
} ,
"max" : {
"input" : 1 ,
"meter" : 1 ,
"progress" : 1
} ,
"maxlength" : {
"input" : 1 ,
"textarea" : 1
} ,
"media" : {
"a" : 1 ,
"area" : 1 ,
"link" : 1 ,
"source" : 1 ,
"style" : 1
} ,
"mediagroup" : {
"audio" : 1 ,
"video" : 1
} ,
"method" : {
"form" : 1
} ,
"min" : {
"input" : 1 ,
"meter" : 1
} ,
"model" : {
"embed" : 1
} ,
"multiple" : {
"input" : 1 ,
"select" : 1
} ,
"muted" : {
"audio" : 1 ,
"video" : 1
} ,
"name" : {
"a" : 1 ,
"applet" : 1 ,
"button" : 1 ,
"embed" : 1 ,
"fieldset" : 1 ,
"form" : 1 ,
"iframe" : 1 ,
"img" : 1 ,
"input" : 1 ,
"keygen" : 1 ,
"map" : 1 ,
"object" : 1 ,
"output" : 1 ,
"param" : 1 ,
"select" : 1 ,
"textarea" : 1
} ,
"nohref" : {
"area" : 1
} ,
"noshade" : {
"hr" : 1
} ,
"novalidate" : {
"form" : 1
} ,
"nowrap" : {
"td" : 1 ,
"th" : 1
} ,
"object" : {
"applet" : 1
} ,
"open" : {
"details" : 1
} ,
"optimum" : {
"meter" : 1
} ,
"pattern" : {
"input" : 1
} ,
"ping" : {
"a" : 1 ,
"area" : 1
} ,
"placeholder" : {
"input" : 1 ,
"textarea" : 1
} ,
"pluginspage" : {
"embed" : 1
} ,
"pluginurl" : {
"embed" : 1
} ,
"poster" : {
"video" : 1
} ,
"pqg" : {
"keygen" : 1
} ,
"preload" : {
"audio" : 1 ,
"video" : 1
} ,
"prompt" : {
"isindex" : 1
} ,
"pubdate" : {
"time" : 1
} ,
"radiogroup" : {
"command" : 1
} ,
"readonly" : {
"input" : 1 ,
"textarea" : 1
} ,
"rel" : {
"a" : 1 ,
"area" : 1 ,
"link" : 1
} ,
"required" : {
"input" : 1 ,
"select" : 1 ,
"textarea" : 1
} ,
"rev" : {
"a" : 1
} ,
"reversed" : {
"ol" : 1
} ,
"rows" : {
"textarea" : 1
} ,
"rowspan" : {
"td" : 1 ,
"th" : 1
} ,
"rules" : {
"table" : 1
} ,
"sandbox" : {
"iframe" : 1
} ,
"scope" : {
"td" : 1 ,
"th" : 1
} ,
"scoped" : {
"style" : 1
} ,
"scrolling" : {
"iframe" : 1
} ,
"seamless" : {
"iframe" : 1
} ,
"selected" : {
"option" : 1
} ,
"shape" : {
"a" : 1 ,
"area" : 1
} ,
"size" : {
"font" : 1 ,
"hr" : 1 ,
"input" : 1 ,
"select" : 1
} ,
"sizes" : {
"link" : 1
} ,
"span" : {
"col" : 1 ,
"colgroup" : 1
} ,
"src" : {
"audio" : 1 ,
"embed" : 1 ,
"iframe" : 1 ,
"img" : 1 ,
"input" : 1 ,
"script" : 1 ,
"source" : 1 ,
"track" : 1 ,
"video" : 1
} ,
"srcdoc" : {
"iframe" : 1
} ,
"srclang" : {
"track" : 1
} ,
"srcset" : {
"img" : 1
} ,
"standby" : {
"object" : 1
} ,
"start" : {
"ol" : 1
} ,
"step" : {
"input" : 1
} ,
"summary" : {
"table" : 1
} ,
"target" : {
"a" : 1 ,
"area" : 1 ,
"form" : 1
} ,
"type" : {
"a" : 1 ,
"area" : 1 ,
"button" : 1 ,
"command" : 1 ,
"embed" : 1 ,
"input" : 1 ,
"li" : 1 ,
"link" : 1 ,
"menu" : 1 ,
"object" : 1 ,
"ol" : 1 ,
"param" : 1 ,
"script" : 1 ,
"source" : 1 ,
"style" : 1 ,
"ul" : 1
} ,
"typemustmatch" : {
"object" : 1
} ,
"usemap" : {
"img" : 1 ,
"input" : 1 ,
"object" : 1
} ,
"valign" : {
"col" : 1 ,
"colgroup" : 1 ,
"tbody" : 1 ,
"td" : 1 ,
"tfoot" : 1 ,
"th" : 1 ,
"thead" : 1 ,
"tr" : 1
} ,
"value" : {
"button" : 1 ,
"data" : 1 ,
"input" : 1 ,
"li" : 1 ,
"meter" : 1 ,
"option" : 1 ,
"param" : 1 ,
"progress" : 1
} ,
"valuetype" : {
"param" : 1
} ,
"vspace" : {
"applet" : 1 ,
"embed" : 1 ,
"img" : 1 ,
"object" : 1
} ,
"width" : {
"applet" : 1 ,
"canvas" : 1 ,
"col" : 1 ,
"colgroup" : 1 ,
"embed" : 1 ,
"hr" : 1 ,
"iframe" : 1 ,
"img" : 1 ,
"input" : 1 ,
"object" : 1 ,
"pre" : 1 ,
"table" : 1 ,
"td" : 1 ,
"th" : 1 ,
"video" : 1
} ,
"wmode" : {
"embed" : 1
} ,
"wrap" : {
"textarea" : 1
}
} ,
// ARIA
NA : {
"aria-activedescendant" : 1 ,
"aria-atomic" : 1 ,
"aria-autocomplete" : 1 ,
"aria-busy" : 1 ,
"aria-checked" : 1 ,
"aria-controls" : 1 ,
"aria-describedby" : 1 ,
"aria-disabled" : 1 ,
"aria-dropeffect" : 1 ,
"aria-expanded" : 1 ,
"aria-flowto" : 1 ,
"aria-grabbed" : 1 ,
"aria-haspopup" : 1 ,
"aria-hidden" : 1 ,
"aria-invalid" : 1 ,
"aria-label" : 1 ,
"aria-labelledby" : 1 ,
"aria-level" : 1 ,
"aria-live" : 1 ,
"aria-multiline" : 1 ,
"aria-multiselectable" : 1 ,
"aria-orientation" : 1 ,
"aria-owns" : 1 ,
"aria-posinset" : 1 ,
"aria-pressed" : 1 ,
"aria-readonly" : 1 ,
"aria-relevant" : 1 ,
"aria-required" : 1 ,
"aria-selected" : 1 ,
"aria-setsize" : 1 ,
"aria-sort" : 1 ,
"aria-valuemax" : 1 ,
"aria-valuemin" : 1 ,
"aria-valuenow" : 1 ,
"aria-valuetext" : 1
2016-08-27 14:54:38 +03:00
} ,
2019-05-14 18:00:55 +03:00
// NE = Empty
NE : {
"allowfullscreen" : 1 ,
"checkbox" : 1 ,
"checked" : 1 ,
"command" : 1 ,
"compact" : 1 ,
"declare" : 1 ,
"defer" : 1 ,
"default" : 1 ,
"disabled" : 1 ,
"hidden" : 1 ,
"inert" : 1 ,
"ismap" : 1 ,
"itemscope" : 1 ,
"multiple" : 1 ,
"nohref" : 1 ,
"noresize" : 1 ,
"noshade" : 1 ,
"nowrap" : 1 ,
"open" : 1 ,
"radio" : 1 ,
"readonly" : 1 ,
"required" : 1 ,
"reversed" : 1 ,
"selected" : 1
2016-08-27 14:54:38 +03:00
} ,
2019-05-14 18:00:55 +03:00
// NO = Event
NO : {
"onabort" : 1 ,
"onblur" : 1 ,
"oncanplay" : 1 ,
"oncanplaythrough" : 1 ,
"onchange" : 1 ,
"onclick" : 1 ,
"oncontextmenu" : 1 ,
"oncopy" : 1 ,
"oncuechange" : 1 ,
"oncut" : 1 ,
"ondblclick" : 1 ,
"ondrag" : 1 ,
"ondragend" : 1 ,
"ondragenter" : 1 ,
"ondragleave" : 1 ,
"ondragover" : 1 ,
"ondragstart" : 1 ,
"ondrop" : 1 ,
"ondurationchange" : 1 ,
"onemptied" : 1 ,
"onended" : 1 ,
"onerror" : 1 ,
"onfocus" : 1 ,
"onformchange" : 1 ,
"onforminput" : 1 ,
"oninput" : 1 ,
"oninvalid" : 1 ,
"onkeydown" : 1 ,
"onkeypress" : 1 ,
"onkeyup" : 1 ,
"onload" : 1 ,
"onloadeddata" : 1 ,
"onloadedmetadata" : 1 ,
"onloadstart" : 1 ,
"onlostpointercapture" : 1 ,
"onmousedown" : 1 ,
"onmousemove" : 1 ,
"onmouseout" : 1 ,
"onmouseover" : 1 ,
"onmouseup" : 1 ,
"onmousewheel" : 1 ,
"onpaste" : 1 ,
"onpause" : 1 ,
"onplay" : 1 ,
"onplaying" : 1 ,
"onpointercancel" : 1 ,
"ongotpointercapture" : 1 ,
"onpointerdown" : 1 ,
"onpointerenter" : 1 ,
"onpointerleave" : 1 ,
"onpointermove" : 1 ,
"onpointerout" : 1 ,
"onpointerover" : 1 ,
"onpointerup" : 1 ,
"onprogress" : 1 ,
"onratechange" : 1 ,
"onreadystatechange" : 1 ,
"onreset" : 1 ,
"onsearch" : 1 ,
"onscroll" : 1 ,
"onseeked" : 1 ,
"onseeking" : 1 ,
"onselect" : 1 ,
"onshow" : 1 ,
"onstalled" : 1 ,
"onsubmit" : 1 ,
"onsuspend" : 1 ,
"ontimeupdate" : 1 ,
"ontoggle" : 1 ,
"ontouchcancel" : 1 ,
"ontouchend" : 1 ,
"ontouchmove" : 1 ,
"ontouchstart" : 1 ,
"onvolumechange" : 1 ,
"onwaiting" : 1 ,
"onwheel" : 1
2016-08-27 14:54:38 +03:00
} ,
2019-05-14 18:00:55 +03:00
// NP = Need scheme check; excludes style, on* & src
NP : {
"action" : 1 ,
"cite" : 1 ,
"classid" : 1 ,
"codebase" : 1 ,
"data" : 1 ,
"href" : 1 ,
"itemtype" : 1 ,
"longdesc" : 1 ,
"model" : 1 ,
"pluginspage" : 1 ,
"pluginurl" : 1 ,
"src" : 1 ,
"srcset" : 1 ,
"usemap" : 1
} ,
// NU = Univ & exceptions
NU : {
"accesskey" : 1 ,
"class" : 1 ,
"contenteditable" : 1 ,
"contextmenu" : 1 ,
"dir" : 1 ,
"draggable" : 1 ,
"dropzone" : 1 ,
"hidden" : 1 ,
"id" : 1 ,
"inert" : 1 ,
"itemid" : 1 ,
"itemprop" : 1 ,
"itemref" : 1 ,
"itemscope" : 1 ,
"itemtype" : 1 ,
"lang" : 1 ,
"role" : 1 ,
"spellcheck" : 1 ,
"style" : 1 ,
"tabindex" : 1 ,
"title" : 1 ,
"translate" : 1 ,
"xmlns" : 1 ,
"xml:base" : 1 ,
"xml:lang" : 1 ,
"xml:space" : 1
2016-08-27 14:54:38 +03:00
} ,
// predef attr vals for $eAL & $aNE ele
NL : {
2019-05-14 18:00:55 +03:00
"all" : 1 ,
"auto" : 1 ,
"baseline" : 1 ,
"bottom" : 1 ,
"button" : 1 ,
"captions" : 1 ,
"center" : 1 ,
"chapters" : 1 ,
"char" : 1 ,
"checkbox" : 1 ,
"circle" : 1 ,
"col" : 1 ,
"colgroup" : 1 ,
"color" : 1 ,
"cols" : 1 ,
"data" : 1 ,
"date" : 1 ,
"datetime" : 1 ,
"datetime-local" : 1 ,
"default" : 1 ,
"descriptions" : 1 ,
"email" : 1 ,
"file" : 1 ,
"get" : 1 ,
"groups" : 1 ,
"hidden" : 1 ,
"image" : 1 ,
"justify" : 1 ,
"left" : 1 ,
"ltr" : 1 ,
"metadata" : 1 ,
"middle" : 1 ,
"month" : 1 ,
"none" : 1 ,
"number" : 1 ,
"object" : 1 ,
"password" : 1 ,
"poly" : 1 ,
"post" : 1 ,
"preserve" : 1 ,
"radio" : 1 ,
"range" : 1 ,
"rect" : 1 ,
"ref" : 1 ,
"reset" : 1 ,
"right" : 1 ,
"row" : 1 ,
"rowgroup" : 1 ,
"rows" : 1 ,
"rtl" : 1 ,
"search" : 1 ,
"submit" : 1 ,
"subtitles" : 1 ,
"tel" : 1 ,
"text" : 1 ,
"time" : 1 ,
"top" : 1 ,
"url" : 1 ,
"week" : 1
2016-08-27 14:54:38 +03:00
} ,
AL : {
2019-05-14 18:00:55 +03:00
"a" : 1 ,
"area" : 1 ,
"bdo" : 1 ,
"button" : 1 ,
"col" : 1 ,
"fieldset" : 1 ,
"form" : 1 ,
"img" : 1 ,
"input" : 1 ,
"object" : 1 ,
"ol" : 1 ,
"optgroup" : 1 ,
"option" : 1 ,
"param" : 1 ,
"script" : 1 ,
"select" : 1 ,
"table" : 1 ,
"td" : 1 ,
"textarea" : 1 ,
"tfoot" : 1 ,
"th" : 1 ,
"thead" : 1 ,
"tr" : 1 ,
"track" : 1 ,
"xml:space" : 1
2016-08-27 14:54:38 +03:00
} ,
// dep attr:applicable ele
ND : {
2019-05-14 18:00:55 +03:00
"align" : {
"caption" : 1 ,
"div" : 1 ,
"h1" : 1 ,
"h2" : 1 ,
"h3" : 1 ,
"h4" : 1 ,
"h5" : 1 ,
"h6" : 1 ,
"hr" : 1 ,
"img" : 1 ,
"input" : 1 ,
"legend" : 1 ,
"object" : 1 ,
"p" : 1 ,
"table" : 1
} ,
"bgcolor" : {
"table" : 1 ,
"td" : 1 ,
"th" : 1 ,
"tr" : 1
} ,
"border" : {
"object" : 1
} ,
"bordercolor" : {
"table" : 1 ,
"td" : 1 ,
"tr" : 1
} ,
"cellspacing" : {
"table" : 1
} ,
"clear" : {
"br" : 1
} ,
"compact" : {
"dl" : 1 ,
"ol" : 1 ,
"ul" : 1
} ,
"height" : {
"td" : 1 ,
"th" : 1
} ,
"hspace" : {
"img" : 1 ,
"object" : 1
} ,
"language" : {
"script" : 1
} ,
"name" : {
"a" : 1 ,
"form" : 1 ,
"iframe" : 1 ,
"img" : 1 ,
"map" : 1
} ,
"noshade" : {
"hr" : 1
} ,
"nowrap" : {
"td" : 1 ,
"th" : 1
} ,
"size" : {
"hr" : 1
} ,
"vspace" : {
"img" : 1 ,
"object" : 1
} ,
"width" : {
"hr" : 1 ,
"pre" : 1 ,
"table" : 1 ,
"td" : 1 ,
"th" : 1
}
2016-08-27 14:54:38 +03:00
} ,
AD : {
2019-05-14 18:00:55 +03:00
"a" : 1 ,
"br" : 1 ,
"caption" : 1 ,
"div" : 1 ,
"dl" : 1 ,
"form" : 1 ,
"h1" : 1 ,
"h2" : 1 ,
"h3" : 1 ,
"h4" : 1 ,
"h5" : 1 ,
"h6" : 1 ,
"hr" : 1 ,
"iframe" : 1 ,
"img" : 1 ,
"input" : 1 ,
"legend" : 1 ,
"map" : 1 ,
"object" : 1 ,
"ol" : 1 ,
"p" : 1 ,
"pre" : 1 ,
"script" : 1 ,
"table" : 1 ,
"td" : 1 ,
"th" : 1 ,
"tr" : 1 ,
"ul" : 1
} ,
// rqd attr
AR : {
"area" : {
"alt" : "area"
} ,
"bdo" : {
"dir" : "ltr"
} ,
"command" : {
"label" : ""
} ,
"form" : {
"action" : ""
} ,
"img" : {
"src" : "" ,
"alt" : "image"
} ,
"map" : {
"name" : ""
} ,
"optgroup" : {
"label" : ""
} ,
"param" : {
"name" : ""
} ,
"style" : {
"scoped" : ""
} ,
"textarea" : {
"rows" : "10" ,
"cols" : "50"
}
2016-08-27 14:54:38 +03:00
} ,
} ,
STYLE _ENT : {
' ' : ' ' , ' ' : ' ' ,
'E' : 'e' , 'E' : 'e' , 'e' : 'e' , 'e' : 'e' ,
'X' : 'x' , 'X' : 'x' , 'x' : 'x' , 'x' : 'x' ,
'P' : 'p' , 'P' : 'p' , 'p' : 'p' , 'p' : 'p' ,
'S' : 's' , 'S' : 's' , 's' : 's' , 's' : 's' ,
'I' : 'i' , 'I' : 'i' , 'i' : 'i' , 'i' : 'i' ,
'O' : 'o' , 'O' : 'o' , 'o' : 'o' , 'o' : 'o' ,
'N' : 'n' , 'N' : 'n' , 'n' : 'n' , 'n' : 'n' ,
'U' : 'u' , 'U' : 'u' , 'u' : 'u' , 'u' : 'u' ,
'R' : 'r' , 'R' : 'r' , 'r' : 'r' , 'r' : 'r' ,
'L' : 'l' , 'L' : 'l' , 'l' : 'l' , 'l' : 'l' ,
'(' : '(' , '(' : '(' , ')' : ')' , ')' : ')' ,
':' : ':' , ':' : ':' , '"' : '"' , '"' : '"' , // FIXME PHP version has bug here: bad codes for ':'
''' : "'" , ''' : "'" , '/' : '/' , '/' : '/' ,
'*' : '*' , '*' : '*' , '\' : '\\' , '\' : '\\'
} ,
FONT _SIZE : {
'0' : 'xx-small' , '1' : 'xx-small' , '2' : 'small' , '3' : 'medium' , '4' : 'large' ,
'5' : 'x-large' , '6' : 'xx-large' , '7' : '300%' , '-1' : 'smaller' , '-2' : '60%' ,
'+1' : 'larger' , '+2' : '150%' , '+3' : '200%' , '+4' : '300%'
} ,
hl _tag : function ( t )
{
// tag/attribute handler
var C = htmLawed . C ;
var S = htmLawed . S ;
var TAG = htmLawed . TAG ;
// invalid < >
if ( t == '< ' )
return '< ' ;
if ( t == '>' )
return '>' ;
var e ;
var m = /^<(\/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?>$/m . exec ( t ) ;
if ( ! m )
return t . replace ( /</g , '<' ) . replace ( />/g , '>' ) ;
2016-10-03 14:06:20 +03:00
else if ( ! C . elements [ e = m [ 2 ] . toLowerCase ( ) ] && C . keep _bad > 0 )
{
// C.keep_bad == 0 (remove bad elements with their content) is handled by hl_bal
2016-08-27 14:54:38 +03:00
return ( C . keep _bad % 2 ) ? t . replace ( /</g , '<' ) . replace ( />/g , '>' ) : '' ;
2016-10-03 14:06:20 +03:00
}
2016-08-27 14:54:38 +03:00
// attr string
var a = m [ 3 ] . trim ( ) . replace ( /[\n\r\t]/g , ' ' ) ;
// tag transform
var tr _tag _attr = '' ;
if ( C . make _tag _strict && TAG . D [ e ] )
{
[ e , a , tr _tag _attr ] = htmLawed . hl _tag2 ( e , a , C . make _tag _strict ) ;
if ( ! e )
return ( C . keep _bad % 2 ) ? t . replace ( /</g , '<' ) . replace ( />/g , '>' ) : '' ;
}
// close tag
if ( m [ 1 ] )
return ! TAG . E [ e ] ? ( ! C . hook _tag ? '</' + e + '>' : C . hook _tag ( e ) ) : ( ( C . keep _bad % 2 ) ? t . replace ( /</g , '<' ) . replace ( />/g , '>' ) : '' ) ;
// open tag & attr
var lcase ;
if ( C . lc _std _val )
lcase = TAG . AL [ e ] ? 1 : 0 ;
var depTr = 0 ;
if ( C . no _deprecated _attr )
{
// dep attr:applicable ele
depTr = TAG . AD [ e ] ? 1 : 0 ;
}
// attr name-vals
if ( a . indexOf ( "\x01" ) >= 0 )
{
// No comment/CDATA sec
a = a . replace ( /\x01[^\x01]*\x01/g , '' ) ;
}
var mode = 0 ;
a = a . replace ( /^[ \/]+|[ \/]+$/g , '' ) ;
var aA = { } ;
var nm ;
while ( a !== '' )
{
var w = 0 ;
switch ( mode )
{
case 0 : // Name
2019-05-14 18:00:55 +03:00
m = /^[a-zA-Z][^\s=/]+/ . exec ( a ) ;
2016-08-27 14:54:38 +03:00
if ( m )
{
nm = m [ 0 ] . toLowerCase ( ) ;
w = mode = 1 ;
a = a . substr ( m [ 0 ] . length ) . replace ( /^\s+/ , '' ) ;
}
break ;
case 1 :
if ( a [ 0 ] == '=' )
{
// =
w = 1 ;
mode = 2 ;
a = a . replace ( /^[ =]+/ , '' ) ;
}
else
{
// No val
w = 1 ;
mode = 0 ;
a = a . replace ( /^\s+/ , '' ) ;
aA [ nm ] = '' ;
}
break ;
case 2 : // Val
m = /^((?:"[^\"]*")|(?:'[^\']*\')|(?:\s*[^\s"']+))([\s\S]*)/ . exec ( a ) ;
if ( m )
{
a = m [ 2 ] . replace ( /^\s+/ , '' ) ;
m = m [ 1 ] ;
w = 1 ;
mode = 0 ;
aA [ nm ] = ( m [ 0 ] == '"' || m [ 0 ] == '\'' ? m . substr ( 1 , m . length - 2 ) : m ) . replace ( /</g , '<' ) . trim ( ) ;
}
break ;
}
if ( w == 0 )
{
// Parse errs, deal with space, " & '
a = a . replace ( /^(?:"[^\"]*("|$)|'[^\']*('|$)|\S)*\s*/g , '' ) ;
mode = 0 ;
}
}
if ( mode == 1 )
aA [ nm ] = '' ;
// clean attrs
var rl = S [ e ] || { } ;
a = { } ;
var nfr = 0 ;
var k , v ;
for ( k in aA )
{
v = aA [ k ] ;
if ( ( ( C . deny _attribute [ '*' ] ? C . deny _attribute [ k ] : ! C . deny _attribute [ k ] ) &&
2019-05-14 18:00:55 +03:00
( TAG . N [ k ] && TAG . N [ k ] [ e ] ||
TAG . NU [ k ] ||
TAG . NO [ k ] && ! C . deny _attribute [ 'on*' ] ||
TAG . NA [ k ] && ! C . deny _attribute [ 'aria*' ] ||
! C . deny _attribute [ 'data*' ] && /data-((?!xml)[^:]+$)/ . exec ( k )
) && ( ! rl . n || ! rl . n [ k ] && ! rl . n [ '*' ] ) ) || rl [ k ] )
2016-08-27 14:54:38 +03:00
{
if ( TAG . NE [ k ] )
v = k ;
else if ( lcase && ( ( e != 'button' || e != 'input' ) || k == 'type' ) )
{
// Rather loose but ?not cause issues
var v2 = v . toLowerCase ( ) ;
v = TAG . NL [ v2 ] ? v2 : v ;
}
if ( k == 'style' && ! C . style _pass )
{
if ( v . indexOf ( '&#' ) >= 0 )
v = htmLawed . _strtr ( v , htmLawed . STYLE _ENT ) ;
v = v . replace ( /(url(?:\()(?: )*(?:'|"|&(?:quot|apos);)?)([\s\S]+?)((?:"|'|&(?:quot|apos);)?(?: )*(?:\)))/gi , function ( m , m1 , m2 , m3 )
{
return htmLawed . hl _prot ( [ m , m1 , m2 , m3 ] ) ;
} ) ;
v = ! C . css _expression ? v . replace ( /\\\S|(\/|(%2f))(\*|(%2a))/gi , ' ' ) . replace ( /expression/gi , ' ' ) : v ;
}
2019-05-14 18:00:55 +03:00
else if ( TAG . NP [ k ] || TAG . NO [ k ] )
2016-08-27 14:54:38 +03:00
{
2019-05-14 18:00:55 +03:00
// double-quoted char: soft-hyphen; appears here as "\xAD" or hyphen or something else depending on viewing software
v = ( v . indexOf ( '&' ) >= 0 ? v . replace ( /­|­|­/g , ' ' ) : v ) . replace ( /\xAD/g , ' ' ) ;
if ( k == 'srcset' )
{
v = v . trim ( ) . split ( /\s*,\s*/ ) . map ( v1 => (
v1 . split ( /\s+/ , 2 ) . map ( ( v2 , k2 ) => (
k2 == 0 ? htmLawed . hl _prot ( v2 , k ) : v2 . trim ( )
) ) . join ( ' ' )
) ) . join ( ', ' ) ;
}
if ( k == 'itemtype' )
{
v = v . trim ( ) . split ( /\s+/ ) . map ( v1 => htmLawed . hl _prot ( v1 , k ) ) . join ( ' ' ) ;
}
else
{
v = htmLawed . hl _prot ( v , k ) ;
}
2016-08-27 14:54:38 +03:00
if ( k == 'href' )
{
// X-spam
if ( C . anti _mail _spam && v . indexOf ( 'mailto:' ) === 0 )
v = v . replace ( /@/g , htmLawed . _htmlspecialchars ( C . anti _mail _spam ) ) ;
else if ( C . anti _link _spam )
{
var r1 = C . anti _link _spam [ 1 ] ;
if ( r1 && ( new RegExp ( r1 ) ) . exec ( v ) )
continue ;
var r0 = C . anti _link _spam [ 0 ] ;
if ( r0 && ( new RegExp ( r0 ) ) . exec ( v ) )
{
if ( a . rel )
{
if ( ! /\bnofollow\b/i . exec ( a . rel ) )
a . rel += ' nofollow' ;
}
else if ( aA [ 'rel' ] )
{
if ( ! /\bnofollow\b/i . exec ( aA [ 'rel' ] ) )
nfr = 1 ;
}
else
a . rel = 'nofollow' ;
}
}
}
}
if ( rl [ k ] && rl [ k ] instanceof Array && ( v = htmLawed . hl _attrval ( k , v , rl [ k ] ) ) === 0 )
continue ;
a [ k ] = v . replace ( /"/g , '"' ) ; // "
}
}
if ( nfr )
a . rel = a . rel ? a . rel + ' nofollow' : 'nofollow' ;
// rqd attr
if ( TAG . AR [ e ] )
{
for ( k in TAG . AR [ e ] )
if ( ! a [ k ] )
a [ k ] = TAG . AR [ e ] [ k ] || k ;
}
2019-05-14 18:00:55 +03:00
// depr attr
2016-08-27 14:54:38 +03:00
if ( depTr )
{
var c = [ ] ;
for ( k in a )
{
v = a [ k ] ;
if ( k == 'style' || ! TAG . ND [ k ] || ! TAG . ND [ k ] [ e ] )
continue ;
2019-05-14 18:00:55 +03:00
v = v . replace ( /(\\|:|;|&#)/g , '' ) ;
2016-08-27 14:54:38 +03:00
if ( k == 'align' )
{
delete a [ k ] ;
if ( e == 'img' && ( v == 'left' || v == 'right' ) )
c . push ( 'float: ' + v ) ;
else if ( ( e == 'div' || e == 'table' ) && v == 'center' )
c . push ( 'margin: auto' ) ;
else
c . push ( 'text-align: ' + v ) ;
}
else if ( k == 'bgcolor' )
c . push ( 'background-color: ' + v ) ;
else if ( k == 'border' )
c . push ( 'border: ' + v + 'px' ) ;
else if ( k == 'bordercolor' )
c . push ( 'border-color: ' + v ) ;
2019-05-14 18:00:55 +03:00
else if ( k == 'cellspacing' )
c . push ( 'border-spacing: ' + v + 'px' ) ;
2016-08-27 14:54:38 +03:00
else if ( k == 'clear' )
c . push ( 'clear: ' + ( v != 'all' ? v : 'both' ) ) ;
else if ( k == 'compact' )
c . push ( 'font-size: 85%' ) ;
else if ( k == 'height' || k == 'width' )
c . push ( k + ': ' + ( v [ 0 ] != '*' ? v + ( /^\d+$/ . exec ( v ) ? 'px' : '' ) : 'auto' ) ) ;
else if ( k == 'hspace' )
c . push ( 'margin-left: ' + v + 'px; margin-right: ' + v + 'px' ) ;
else if ( k == 'language' && ! a . type )
a . type = 'text/' + v . toLowerCase ( ) ;
else if ( k == 'name' )
{
2019-05-14 18:00:55 +03:00
if ( ! a . id && ! /\W/ . exec ( v ) )
2016-08-27 14:54:38 +03:00
a . id = v ;
if ( ! ( C . no _deprecated _attr == 2 || ( e != 'a' && e != 'map' ) ) )
{
// do not delete a[name]
continue ;
}
}
else if ( k == 'noshade' )
c . push ( 'border-style: none; border: 0; background-color: gray; color: gray' ) ;
else if ( k == 'nowrap' )
c . push ( 'white-space: nowrap' ) ;
else if ( k == 'size' )
c . push ( 'size: ' + v + 'px' ) ;
else if ( k == 'vspace' )
c . push ( 'margin-top: ' + v + 'px; margin-bottom: ' + v + 'px' ) ;
else
continue ;
delete a [ k ] ;
}
if ( c . length )
{
c = c . join ( '; ' ) ;
a . style = a . style ? a . style . replace ( /[ ;]+$/ , '' ) + '; ' + c + ';' : c + ';' ;
}
}
// unique ID
if ( C . unique _ids && a . id )
{
2019-05-14 18:00:55 +03:00
if ( /\s/ . exec ( a . id ) ||
2016-08-27 14:54:38 +03:00
htmLawed . hl _Ids [ a . id ] && C . unique _ids == 1 )
delete a . id ;
else
{
while ( htmLawed . hl _Ids [ a . id ] )
a . id = C . unique _ids + a . id ; // FIXME 1 2 3 4 ... ?
htmLawed . hl _Ids [ a . id ] = 1 ;
}
}
// xml:lang
if ( C [ 'xml:lang' ] && a . lang )
{
a [ 'xml:lang' ] = a [ 'xml:lang' ] || a [ 'lang' ] ;
if ( C [ 'xml:lang' ] == 2 )
delete a [ 'lang' ] ;
}
// for transformed tag
if ( tr _tag _attr )
a . style = a . style ? a . style . replace ( /[ ;]+$/ , '' ) + '; ' + tr _tag _attr : tr _tag _attr ;
// return with empty ele /
if ( ! C . hook _tag )
{
aA = '' ;
for ( k in a )
aA += ' ' + k + '="' + a [ k ] + '"' ;
return '<' + e + aA + ( TAG . E [ e ] ? ' /' : '' ) + '>' ;
}
return C . hook _tag ( e , a ) ;
} ,
hl _tag2 : function ( e , a , t )
{
if ( ! t )
t = 1 ;
// transform tag
if ( e == 'center' )
return [ 'div' , a , 'text-align: center;' ] ;
2019-05-14 18:00:55 +03:00
else if ( e == 'acronym' )
return [ 'abbr' , a , '' ] ;
else if ( e == 'dir' )
2016-08-27 14:54:38 +03:00
return [ 'ul' , a , '' ] ;
2019-05-14 18:00:55 +03:00
else if ( e == 'big' )
return [ 'span' , a , 'font-size: larger;' ] ;
2016-08-27 14:54:38 +03:00
else if ( e == 's' || e == 'strike' )
return [ 'span' , a , 'text-decoration: line-through;' ] ;
2019-05-14 18:00:55 +03:00
else if ( e == 'tt' )
return [ 'code' , a , '' ] ;
2016-08-27 14:54:38 +03:00
else if ( e == 'font' )
{
var a2 = '' ;
var m ;
while ( ( m = /(^|\s)(color|size)\s*=\s*('|")?([\s\S]+?)(\3|\s|$)/i . exec ( a ) ) ) // '
{
a = a . replace ( m [ 0 ] , ' ' ) ;
m [ 4 ] = m [ 4 ] . trim ( ) ;
if ( m [ 2 ] . toLowerCase ( ) == 'color' )
a2 += ' color: ' + m [ 4 ] . replace ( /"/g , "'" ) + ';' ; // '
else if ( ( m = htmLawed . FONT _SIZE [ m [ 4 ] ] ) )
a2 += ' font-size: ' + m . replace ( /"/g , "'" ) + ';' ; // '
}
while ( ( m = /(^|\s)face\s*=\s*('|")([^=]+)\2/i . exec ( a ) || /(^|\s)face\s*=(\s*)(\S+)/i . exec ( a ) ) ) // FIXME: PHP version has bug here: ("|')?
{
a = a . replace ( m [ 0 ] , ' ' ) ;
a2 += ' font-family: ' + m [ 3 ] . trim ( ) . replace ( /"/g , "'" ) + ';' ; // '
}
return [ 'span' , a , a2 . replace ( /<|^\s+/g , '' ) ] ;
}
if ( t == 2 )
return [ 0 , a , 0 ] ;
return [ e , a , '' ] ;
} ,
TIDY : {
a : { 'br' : 1 } ,
2019-05-14 18:00:55 +03:00
b : { 'button' : 1 , 'command' : 1 , 'input' : 1 , 'option' : 1 , 'param' : 1 , 'track' : 1 } ,
c : { 'audio' : 1 , 'canvas' : 1 , 'caption' : 1 , 'dd' : 1 , 'dt' : 1 , 'figcaption' : 1 , 'h1' : 1 , 'h2' : 1 , 'h3' : 1 , 'h4' : 1 , 'h5' : 1 , 'h6' : 1 , 'isindex' : 1 , 'label' : 1 , 'legend' : 1 , 'li' : 1 , 'object' : 1 , 'p' : 1 , 'pre' : 1 , 'style' : 1 , 'summary' : 1 , 'td' : 1 , 'textarea' : 1 , 'th' : 1 , 'video' : 1 } ,
d : { 'address' : 1 , 'article' : 1 , 'aside' : 1 , 'blockquote' : 1 , 'center' : 1 , 'colgroup' : 1 , 'datalist' : 1 , 'details' : 1 , 'dir' : 1 , 'div' : 1 , 'dl' : 1 , 'fieldset' : 1 , 'figure' : 1 , 'footer' : 1 , 'form' : 1 , 'header' : 1 , 'hgroup' : 1 , 'hr' : 1 , 'iframe' : 1 , 'main' : 1 , 'map' : 1 , 'menu' : 1 , 'nav' : 1 , 'noscript' : 1 , 'ol' : 1 , 'optgroup' : 1 , 'rbc' : 1 , 'rtc' : 1 , 'ruby' : 1 , 'script' : 1 , 'section' : 1 , 'select' : 1 , 'table' : 1 , 'tbody' : 1 , 'tfoot' : 1 , 'thead' : 1 , 'tr' : 1 , 'ul' : 1 } ,
2016-08-27 14:54:38 +03:00
} ,
hl _tidy : function ( t , w , p )
{
// Tidy/compact HTM
if ( ' pre,script,textarea' . indexOf ( "$p," ) >= 0 )
return t ;
2019-05-14 18:00:55 +03:00
var _repl = function ( m , m1 , m2 , m3 , m4 )
2016-08-27 14:54:38 +03:00
{
2019-05-14 18:00:55 +03:00
return m1 + htmLawed . _strtr ( m3 , { '<' : "\x01" , '>' : "\x02" , "\n" : "\x03" , "\r" : "\x04" , "\t" : "\x05" , ' ' : "\x07" } ) + m4 ;
2016-08-27 14:54:38 +03:00
} ;
t = t . replace ( /(<(!\[CDATA\[))([\s\S]+?)(\]\]>)/g , _repl )
. replace ( /(<(!--))([\s\S]+?)(-->)/g , _repl )
. replace ( /(<(pre|script|textarea)[^>]*?>)([\s\S]+?)(<\/\2>)/g , _repl )
2019-05-14 18:00:55 +03:00
. replace ( /(<\w[^>]*(?<!\/)>)\s+/g , ' $1' )
. replace ( /\s+/g , ' ' )
. replace ( /(<\w[^>]*(?<!\/)>) /g , '$1' ) ;
2016-08-27 14:54:38 +03:00
if ( w == - 1 )
return htmLawed . _strtr ( t , { "\x01" : '<' , "\x02" : '>' , "\x03" : "\n" , "\x04" : "\r" , "\x05" : "\t" , "\x07" : ' ' } ) ;
w = w . toLowerCase ( ) ;
var s = w . indexOf ( 't' ) >= 0 ? "\t" : ' ' ;
var m = /\d/ . exec ( w ) ;
s = new Array ( 1 + ( m ? m [ 0 ] : ( s == "\t" ? 1 : 2 ) ) ) . join ( s ) ; // str_repeat == new Array(n+1).join(s)
m = /[ts]([1-9])/ . exec ( w ) ;
var N = m ? m [ 1 ] : 0 ;
var T = t . split ( '<' ) ;
var X = 1 ;
var n , e , r ;
var _ob , ss = '' ;
_tidy : while ( X )
{
n = N ;
t = T ;
_ob = '' ;
if ( htmLawed . TIDY . d [ p ] )
_ob += ( ss += s ) ;
_ob += t . shift ( ) . replace ( /^\s+/ , '' ) ;
for ( var i = 0 , j = t . length ; i < j ; i ++ )
{
[ e , r ] = t [ i ] . split ( '>' ) ;
var x = e [ 0 ] == '/' ? 0 : ( e . substr ( e . length - 1 ) == '/' ? 1 : ( e [ 0 ] != '!' ? 2 : - 1 ) ) ;
var _p = e . indexOf ( ' ' ) ;
var y = ! x ? e . replace ( /^\/+/ , '' ) : ( x > 0 ? ( _p < 0 ? e : e . substr ( 0 , _p ) ) : 0 ) ;
e = '<' + e + '>' ;
if ( htmLawed . TIDY . d [ y ] )
{
if ( ! x )
{
if ( n )
{
ss = ss . substr ( 0 , ss . length - s . length ) ;
_ob += "\n" + ss + e + "\n" + ss ;
}
else
{
N ++ ;
continue _tidy ;
}
}
else
{
_ob += "\n" + ss + e + "\n" ;
_ob += ( x != 1 ? ( ss += s ) : ss ) ;
}
_ob += r ;
continue ;
}
var f = "\n" + ss ;
if ( htmLawed . TIDY . c [ y ] )
{
if ( ! x )
_ob += e + f + r ;
else
_ob += f + e + r ;
}
else if ( htmLawed . TIDY . b [ y ] )
_ob += f + e + r ;
else if ( htmLawed . TIDY . a [ y ] )
_ob += e + f + r ;
else if ( ! y )
_ob += f + e + f + r ;
else
_ob += e + r ;
}
X = 0 ;
}
t = _ob . replace ( /[\n]\s*?[\n]+/g , "\n" ) . replace ( /\n | \n/g , "\n" ) ;
var l = w . indexOf ( 'r' ) >= 0 ? ( w . indexOf ( 'n' ) >= 0 ? "\r\n" : "\r" ) : null ;
if ( l )
t = t . replace ( /\n/g , l ) ;
return htmLawed . _strtr ( t , { "\x01" : '<' , "\x02" : '>' , "\x03" : "\n" , "\x04" : "\r" , "\x05" : "\t" , "\x07" : ' ' } ) ;
} ,
hl _version : function ( )
{
2019-05-14 18:00:55 +03:00
return '1.2.4.1' ;
2016-08-27 14:54:38 +03:00
}
} ;