2019-10-25 12:38:07 -04:00
/ * !
CSSLint v1 . 0.4
Copyright ( c ) 2016 Nicole Sullivan and Nicholas C . Zakas . All rights reserved .
Permission is hereby granted , free of charge , to any person obtaining a copy
of this software and associated documentation files ( the 'Software' ) , to deal
in the Software without restriction , including without limitation the rights
to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
copies of the Software , and to permit persons to whom the Software is
furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED 'AS IS' , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE .
* /
var CSSLint = ( function ( ) {
var module = module || { } ,
exports = exports || { } ;
/ * !
Parser - Lib
Copyright ( c ) 2009 - 2016 Nicholas C . Zakas . All rights reserved .
Permission is hereby granted , free of charge , to any person obtaining a copy
of this software and associated documentation files ( the "Software" ) , to deal
in the Software without restriction , including without limitation the rights
to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
copies of the Software , and to permit persons to whom the Software is
furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE .
* /
/* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
var parserlib = ( function ( ) {
var require ;
require = ( function e ( t , n , r ) { function s ( o , u ) { if ( ! n [ o ] ) { if ( ! t [ o ] ) { var a = typeof require == "function" && require ; if ( ! u && a ) return a ( o , ! 0 ) ; if ( i ) return i ( o , ! 0 ) ; var f = new Error ( "Cannot find module '" + o + "'" ) ; throw f . code = "MODULE_NOT_FOUND" , f } var l = n [ o ] = { exports : { } } ; t [ o ] [ 0 ] . call ( l . exports , function ( e ) { var n = t [ o ] [ 1 ] [ e ] ; return s ( n ? n : e ) } , l , l . exports , e , t , n , r ) } return n [ o ] . exports } var i = typeof require == "function" && require ; for ( var o = 0 ; o < r . length ; o ++ ) s ( r [ o ] ) ; return s } ) ( { 1 : [ function ( require , module , exports ) {
"use strict" ;
/* exported Colors */
var Colors = module . exports = {
_ _proto _ _ : null ,
aliceblue : "#f0f8ff" ,
antiquewhite : "#faebd7" ,
aqua : "#00ffff" ,
aquamarine : "#7fffd4" ,
azure : "#f0ffff" ,
beige : "#f5f5dc" ,
bisque : "#ffe4c4" ,
black : "#000000" ,
blanchedalmond : "#ffebcd" ,
blue : "#0000ff" ,
blueviolet : "#8a2be2" ,
brown : "#a52a2a" ,
burlywood : "#deb887" ,
cadetblue : "#5f9ea0" ,
chartreuse : "#7fff00" ,
chocolate : "#d2691e" ,
coral : "#ff7f50" ,
cornflowerblue : "#6495ed" ,
cornsilk : "#fff8dc" ,
crimson : "#dc143c" ,
cyan : "#00ffff" ,
darkblue : "#00008b" ,
darkcyan : "#008b8b" ,
darkgoldenrod : "#b8860b" ,
darkgray : "#a9a9a9" ,
darkgrey : "#a9a9a9" ,
darkgreen : "#006400" ,
darkkhaki : "#bdb76b" ,
darkmagenta : "#8b008b" ,
darkolivegreen : "#556b2f" ,
darkorange : "#ff8c00" ,
darkorchid : "#9932cc" ,
darkred : "#8b0000" ,
darksalmon : "#e9967a" ,
darkseagreen : "#8fbc8f" ,
darkslateblue : "#483d8b" ,
darkslategray : "#2f4f4f" ,
darkslategrey : "#2f4f4f" ,
darkturquoise : "#00ced1" ,
darkviolet : "#9400d3" ,
deeppink : "#ff1493" ,
deepskyblue : "#00bfff" ,
dimgray : "#696969" ,
dimgrey : "#696969" ,
dodgerblue : "#1e90ff" ,
firebrick : "#b22222" ,
floralwhite : "#fffaf0" ,
forestgreen : "#228b22" ,
fuchsia : "#ff00ff" ,
gainsboro : "#dcdcdc" ,
ghostwhite : "#f8f8ff" ,
gold : "#ffd700" ,
goldenrod : "#daa520" ,
gray : "#808080" ,
grey : "#808080" ,
green : "#008000" ,
greenyellow : "#adff2f" ,
honeydew : "#f0fff0" ,
hotpink : "#ff69b4" ,
indianred : "#cd5c5c" ,
indigo : "#4b0082" ,
ivory : "#fffff0" ,
khaki : "#f0e68c" ,
lavender : "#e6e6fa" ,
lavenderblush : "#fff0f5" ,
lawngreen : "#7cfc00" ,
lemonchiffon : "#fffacd" ,
lightblue : "#add8e6" ,
lightcoral : "#f08080" ,
lightcyan : "#e0ffff" ,
lightgoldenrodyellow : "#fafad2" ,
lightgray : "#d3d3d3" ,
lightgrey : "#d3d3d3" ,
lightgreen : "#90ee90" ,
lightpink : "#ffb6c1" ,
lightsalmon : "#ffa07a" ,
lightseagreen : "#20b2aa" ,
lightskyblue : "#87cefa" ,
lightslategray : "#778899" ,
lightslategrey : "#778899" ,
lightsteelblue : "#b0c4de" ,
lightyellow : "#ffffe0" ,
lime : "#00ff00" ,
limegreen : "#32cd32" ,
linen : "#faf0e6" ,
magenta : "#ff00ff" ,
maroon : "#800000" ,
mediumaquamarine : "#66cdaa" ,
mediumblue : "#0000cd" ,
mediumorchid : "#ba55d3" ,
mediumpurple : "#9370d8" ,
mediumseagreen : "#3cb371" ,
mediumslateblue : "#7b68ee" ,
mediumspringgreen : "#00fa9a" ,
mediumturquoise : "#48d1cc" ,
mediumvioletred : "#c71585" ,
midnightblue : "#191970" ,
mintcream : "#f5fffa" ,
mistyrose : "#ffe4e1" ,
moccasin : "#ffe4b5" ,
navajowhite : "#ffdead" ,
navy : "#000080" ,
oldlace : "#fdf5e6" ,
olive : "#808000" ,
olivedrab : "#6b8e23" ,
orange : "#ffa500" ,
orangered : "#ff4500" ,
orchid : "#da70d6" ,
palegoldenrod : "#eee8aa" ,
palegreen : "#98fb98" ,
paleturquoise : "#afeeee" ,
palevioletred : "#d87093" ,
papayawhip : "#ffefd5" ,
peachpuff : "#ffdab9" ,
peru : "#cd853f" ,
pink : "#ffc0cb" ,
plum : "#dda0dd" ,
powderblue : "#b0e0e6" ,
purple : "#800080" ,
red : "#ff0000" ,
rosybrown : "#bc8f8f" ,
royalblue : "#4169e1" ,
saddlebrown : "#8b4513" ,
salmon : "#fa8072" ,
sandybrown : "#f4a460" ,
seagreen : "#2e8b57" ,
seashell : "#fff5ee" ,
sienna : "#a0522d" ,
silver : "#c0c0c0" ,
skyblue : "#87ceeb" ,
slateblue : "#6a5acd" ,
slategray : "#708090" ,
slategrey : "#708090" ,
snow : "#fffafa" ,
springgreen : "#00ff7f" ,
steelblue : "#4682b4" ,
tan : "#d2b48c" ,
teal : "#008080" ,
thistle : "#d8bfd8" ,
tomato : "#ff6347" ,
turquoise : "#40e0d0" ,
violet : "#ee82ee" ,
wheat : "#f5deb3" ,
white : "#ffffff" ,
whitesmoke : "#f5f5f5" ,
yellow : "#ffff00" ,
yellowgreen : "#9acd32" ,
//'currentColor' color keyword https://www.w3.org/TR/css3-color/#currentcolor
currentColor : "The value of the 'color' property." ,
//CSS2 system colors https://www.w3.org/TR/css3-color/#css2-system
activeBorder : "Active window border." ,
activecaption : "Active window caption." ,
appworkspace : "Background color of multiple document interface." ,
background : "Desktop background." ,
buttonface : "The face background color for 3-D elements that appear 3-D due to one layer of surrounding border." ,
buttonhighlight : "The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border." ,
buttonshadow : "The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border." ,
buttontext : "Text on push buttons." ,
captiontext : "Text in caption, size box, and scrollbar arrow box." ,
graytext : "Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color." ,
greytext : "Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color." ,
highlight : "Item(s) selected in a control." ,
highlighttext : "Text of item(s) selected in a control." ,
inactiveborder : "Inactive window border." ,
inactivecaption : "Inactive window caption." ,
inactivecaptiontext : "Color of text in an inactive caption." ,
infobackground : "Background color for tooltip controls." ,
infotext : "Text color for tooltip controls." ,
menu : "Menu background." ,
menutext : "Text in menus." ,
scrollbar : "Scroll bar gray area." ,
threeddarkshadow : "The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border." ,
threedface : "The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border." ,
threedhighlight : "The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border." ,
threedlightshadow : "The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border." ,
threedshadow : "The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border." ,
window : "Window background." ,
windowframe : "Window frame." ,
windowtext : "Text in windows."
} ;
} , { } ] , 2 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = Combinator ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Parser = require ( "./Parser" ) ;
/ * *
* Represents a selector combinator ( whitespace , + , > ) .
* @ namespace parserlib . css
* @ class Combinator
* @ extends parserlib . util . SyntaxUnit
* @ constructor
* @ param { String } text The text representation of the unit .
* @ param { int } line The line of text on which the unit resides .
* @ param { int } col The column of text on which the unit resides .
* /
function Combinator ( text , line , col ) {
SyntaxUnit . call ( this , text , line , col , Parser . COMBINATOR _TYPE ) ;
/ * *
* The type of modifier .
* @ type String
* @ property type
* /
this . type = "unknown" ;
//pretty simple
if ( /^\s+$/ . test ( text ) ) {
this . type = "descendant" ;
} else if ( text === ">" ) {
this . type = "child" ;
} else if ( text === "+" ) {
this . type = "adjacent-sibling" ;
} else if ( text === "~" ) {
this . type = "sibling" ;
}
}
Combinator . prototype = new SyntaxUnit ( ) ;
Combinator . prototype . constructor = Combinator ;
} , { "../util/SyntaxUnit" : 26 , "./Parser" : 6 } ] , 3 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = Matcher ;
var StringReader = require ( "../util/StringReader" ) ;
var SyntaxError = require ( "../util/SyntaxError" ) ;
/ * *
* This class implements a combinator library for matcher functions .
* The combinators are described at :
* https : //developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#Component_value_combinators
* /
function Matcher ( matchFunc , toString ) {
this . match = function ( expression ) {
// Save/restore marks to ensure that failed matches always restore
// the original location in the expression.
var result ;
expression . mark ( ) ;
result = matchFunc ( expression ) ;
if ( result ) {
expression . drop ( ) ;
} else {
expression . restore ( ) ;
}
return result ;
} ;
this . toString = typeof toString === "function" ? toString : function ( ) {
return toString ;
} ;
}
/** Precedence table of combinators. */
Matcher . prec = {
MOD : 5 ,
SEQ : 4 ,
ANDAND : 3 ,
OROR : 2 ,
ALT : 1
} ;
/** Simple recursive-descent grammar to build matchers from strings. */
Matcher . parse = function ( str ) {
var reader , eat , expr , oror , andand , seq , mod , term , result ;
reader = new StringReader ( str ) ;
eat = function ( matcher ) {
var result = reader . readMatch ( matcher ) ;
if ( result === null ) {
throw new SyntaxError (
"Expected " + matcher , reader . getLine ( ) , reader . getCol ( ) ) ;
}
return result ;
} ;
expr = function ( ) {
// expr = oror (" | " oror)*
var m = [ oror ( ) ] ;
while ( reader . readMatch ( " | " ) !== null ) {
m . push ( oror ( ) ) ;
}
return m . length === 1 ? m [ 0 ] : Matcher . alt . apply ( Matcher , m ) ;
} ;
oror = function ( ) {
// oror = andand ( " || " andand)*
var m = [ andand ( ) ] ;
while ( reader . readMatch ( " || " ) !== null ) {
m . push ( andand ( ) ) ;
}
return m . length === 1 ? m [ 0 ] : Matcher . oror . apply ( Matcher , m ) ;
} ;
andand = function ( ) {
// andand = seq ( " && " seq)*
var m = [ seq ( ) ] ;
while ( reader . readMatch ( " && " ) !== null ) {
m . push ( seq ( ) ) ;
}
return m . length === 1 ? m [ 0 ] : Matcher . andand . apply ( Matcher , m ) ;
} ;
seq = function ( ) {
// seq = mod ( " " mod)*
var m = [ mod ( ) ] ;
while ( reader . readMatch ( /^ (?![&|\]])/ ) !== null ) {
m . push ( mod ( ) ) ;
}
return m . length === 1 ? m [ 0 ] : Matcher . seq . apply ( Matcher , m ) ;
} ;
mod = function ( ) {
// mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )?
var m = term ( ) ;
if ( reader . readMatch ( "?" ) !== null ) {
return m . question ( ) ;
} else if ( reader . readMatch ( "*" ) !== null ) {
return m . star ( ) ;
} else if ( reader . readMatch ( "+" ) !== null ) {
return m . plus ( ) ;
} else if ( reader . readMatch ( "#" ) !== null ) {
return m . hash ( ) ;
} else if ( reader . readMatch ( /^\{\s*/ ) !== null ) {
var min = eat ( /^\d+/ ) ;
eat ( /^\s*,\s*/ ) ;
var max = eat ( /^\d+/ ) ;
eat ( /^\s*\}/ ) ;
return m . braces ( + min , + max ) ;
}
return m ;
} ;
term = function ( ) {
// term = <nt> | literal | "[ " expression " ]"
if ( reader . readMatch ( "[ " ) !== null ) {
var m = expr ( ) ;
eat ( " ]" ) ;
return m ;
}
return Matcher . fromType ( eat ( /^[^ ?*+#{]+/ ) ) ;
} ;
result = expr ( ) ;
if ( ! reader . eof ( ) ) {
throw new SyntaxError (
"Expected end of string" , reader . getLine ( ) , reader . getCol ( ) ) ;
}
return result ;
} ;
/ * *
* Convert a string to a matcher ( parsing simple alternations ) ,
* or do nothing if the argument is already a matcher .
* /
Matcher . cast = function ( m ) {
if ( m instanceof Matcher ) {
return m ;
}
return Matcher . parse ( m ) ;
} ;
/ * *
* Create a matcher for a single type .
* /
Matcher . fromType = function ( type ) {
// Late require of ValidationTypes to break a dependency cycle.
var ValidationTypes = require ( "./ValidationTypes" ) ;
return new Matcher ( function ( expression ) {
return expression . hasNext ( ) && ValidationTypes . isType ( expression , type ) ;
} , type ) ;
} ;
/ * *
* Create a matcher for one or more juxtaposed words , which all must
* occur , in the given order .
* /
Matcher . seq = function ( ) {
var ms = Array . prototype . slice . call ( arguments ) . map ( Matcher . cast ) ;
if ( ms . length === 1 ) {
return ms [ 0 ] ;
}
return new Matcher ( function ( expression ) {
var i , result = true ;
for ( i = 0 ; result && i < ms . length ; i ++ ) {
result = ms [ i ] . match ( expression ) ;
}
return result ;
} , function ( prec ) {
var p = Matcher . prec . SEQ ;
var s = ms . map ( function ( m ) {
return m . toString ( p ) ;
} ) . join ( " " ) ;
if ( prec > p ) {
s = "[ " + s + " ]" ;
}
return s ;
} ) ;
} ;
/ * *
* Create a matcher for one or more alternatives , where exactly one
* must occur .
* /
Matcher . alt = function ( ) {
var ms = Array . prototype . slice . call ( arguments ) . map ( Matcher . cast ) ;
if ( ms . length === 1 ) {
return ms [ 0 ] ;
}
return new Matcher ( function ( expression ) {
var i , result = false ;
for ( i = 0 ; ! result && i < ms . length ; i ++ ) {
result = ms [ i ] . match ( expression ) ;
}
return result ;
} , function ( prec ) {
var p = Matcher . prec . ALT ;
var s = ms . map ( function ( m ) {
return m . toString ( p ) ;
} ) . join ( " | " ) ;
if ( prec > p ) {
s = "[ " + s + " ]" ;
}
return s ;
} ) ;
} ;
/ * *
* Create a matcher for two or more options . This implements the
* double bar ( || ) and double ampersand ( && ) operators , as well as
* variants of && where some of the alternatives are optional .
* This will backtrack through even successful matches to try to
* maximize the number of items matched .
* /
Matcher . many = function ( required ) {
var ms = Array . prototype . slice . call ( arguments , 1 ) . reduce ( function ( acc , v ) {
if ( v . expand ) {
// Insert all of the options for the given complex rule as
// individual options.
var ValidationTypes = require ( "./ValidationTypes" ) ;
acc . push . apply ( acc , ValidationTypes . complex [ v . expand ] . options ) ;
} else {
acc . push ( Matcher . cast ( v ) ) ;
}
return acc ;
} , [ ] ) ;
if ( required === true ) {
required = ms . map ( function ( ) {
return true ;
} ) ;
}
var result = new Matcher ( function ( expression ) {
var seen = [ ] , max = 0 , pass = 0 ;
var success = function ( matchCount ) {
if ( pass === 0 ) {
max = Math . max ( matchCount , max ) ;
return matchCount === ms . length ;
} else {
return matchCount === max ;
}
} ;
var tryMatch = function ( matchCount ) {
for ( var i = 0 ; i < ms . length ; i ++ ) {
if ( seen [ i ] ) {
continue ;
}
expression . mark ( ) ;
if ( ms [ i ] . match ( expression ) ) {
seen [ i ] = true ;
// Increase matchCount iff this was a required element
// (or if all the elements are optional)
if ( tryMatch ( matchCount + ( ( required === false || required [ i ] ) ? 1 : 0 ) ) ) {
expression . drop ( ) ;
return true ;
}
// Backtrack: try *not* matching using this rule, and
// let's see if it leads to a better overall match.
expression . restore ( ) ;
seen [ i ] = false ;
} else {
expression . drop ( ) ;
}
}
return success ( matchCount ) ;
} ;
if ( ! tryMatch ( 0 ) ) {
// Couldn't get a complete match, retrace our steps to make the
// match with the maximum # of required elements.
pass ++ ;
tryMatch ( 0 ) ;
}
if ( required === false ) {
return max > 0 ;
}
// Use finer-grained specification of which matchers are required.
for ( var i = 0 ; i < ms . length ; i ++ ) {
if ( required [ i ] && ! seen [ i ] ) {
return false ;
}
}
return true ;
} , function ( prec ) {
var p = required === false ? Matcher . prec . OROR : Matcher . prec . ANDAND ;
var s = ms . map ( function ( m , i ) {
if ( required !== false && ! required [ i ] ) {
return m . toString ( Matcher . prec . MOD ) + "?" ;
}
return m . toString ( p ) ;
} ) . join ( required === false ? " || " : " && " ) ;
if ( prec > p ) {
s = "[ " + s + " ]" ;
}
return s ;
} ) ;
result . options = ms ;
return result ;
} ;
/ * *
* Create a matcher for two or more options , where all options are
* mandatory but they may appear in any order .
* /
Matcher . andand = function ( ) {
var args = Array . prototype . slice . call ( arguments ) ;
args . unshift ( true ) ;
return Matcher . many . apply ( Matcher , args ) ;
} ;
/ * *
* Create a matcher for two or more options , where options are
* optional and may appear in any order , but at least one must be
* present .
* /
Matcher . oror = function ( ) {
var args = Array . prototype . slice . call ( arguments ) ;
args . unshift ( false ) ;
return Matcher . many . apply ( Matcher , args ) ;
} ;
/** Instance methods on Matchers. */
Matcher . prototype = {
constructor : Matcher ,
// These are expected to be overridden in every instance.
match : function ( ) { throw new Error ( "unimplemented" ) ; } ,
toString : function ( ) { throw new Error ( "unimplemented" ) ; } ,
// This returns a standalone function to do the matching.
func : function ( ) { return this . match . bind ( this ) ; } ,
// Basic combinators
then : function ( m ) { return Matcher . seq ( this , m ) ; } ,
or : function ( m ) { return Matcher . alt ( this , m ) ; } ,
andand : function ( m ) { return Matcher . many ( true , this , m ) ; } ,
oror : function ( m ) { return Matcher . many ( false , this , m ) ; } ,
// Component value multipliers
star : function ( ) { return this . braces ( 0 , Infinity , "*" ) ; } ,
plus : function ( ) { return this . braces ( 1 , Infinity , "+" ) ; } ,
question : function ( ) { return this . braces ( 0 , 1 , "?" ) ; } ,
hash : function ( ) {
return this . braces ( 1 , Infinity , "#" , Matcher . cast ( "," ) ) ;
} ,
braces : function ( min , max , marker , optSep ) {
var m1 = this , m2 = optSep ? optSep . then ( this ) : this ;
if ( ! marker ) {
marker = "{" + min + "," + max + "}" ;
}
return new Matcher ( function ( expression ) {
var result = true , i ;
for ( i = 0 ; i < max ; i ++ ) {
if ( i > 0 && optSep ) {
result = m2 . match ( expression ) ;
} else {
result = m1 . match ( expression ) ;
}
if ( ! result ) {
break ;
}
}
return i >= min ;
} , function ( ) {
return m1 . toString ( Matcher . prec . MOD ) + marker ;
} ) ;
}
} ;
} , { "../util/StringReader" : 24 , "../util/SyntaxError" : 25 , "./ValidationTypes" : 21 } ] , 4 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = MediaFeature ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Parser = require ( "./Parser" ) ;
/ * *
* Represents a media feature , such as max - width : 500.
* @ namespace parserlib . css
* @ class MediaFeature
* @ extends parserlib . util . SyntaxUnit
* @ constructor
* @ param { SyntaxUnit } name The name of the feature .
* @ param { SyntaxUnit } value The value of the feature or null if none .
* /
function MediaFeature ( name , value ) {
SyntaxUnit . call ( this , "(" + name + ( value !== null ? ":" + value : "" ) + ")" , name . startLine , name . startCol , Parser . MEDIA _FEATURE _TYPE ) ;
/ * *
* The name of the media feature
* @ type String
* @ property name
* /
this . name = name ;
/ * *
* The value for the feature or null if there is none .
* @ type SyntaxUnit
* @ property value
* /
this . value = value ;
}
MediaFeature . prototype = new SyntaxUnit ( ) ;
MediaFeature . prototype . constructor = MediaFeature ;
} , { "../util/SyntaxUnit" : 26 , "./Parser" : 6 } ] , 5 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = MediaQuery ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Parser = require ( "./Parser" ) ;
/ * *
* Represents an individual media query .
* @ namespace parserlib . css
* @ class MediaQuery
* @ extends parserlib . util . SyntaxUnit
* @ constructor
* @ param { String } modifier The modifier "not" or "only" ( or null ) .
* @ param { String } mediaType The type of media ( i . e . , "print" ) .
* @ param { Array } parts Array of selectors parts making up this selector .
* @ param { int } line The line of text on which the unit resides .
* @ param { int } col The column of text on which the unit resides .
* /
function MediaQuery ( modifier , mediaType , features , line , col ) {
SyntaxUnit . call ( this , ( modifier ? modifier + " " : "" ) + ( mediaType ? mediaType : "" ) + ( mediaType && features . length > 0 ? " and " : "" ) + features . join ( " and " ) , line , col , Parser . MEDIA _QUERY _TYPE ) ;
/ * *
* The media modifier ( "not" or "only" )
* @ type String
* @ property modifier
* /
this . modifier = modifier ;
/ * *
* The mediaType ( i . e . , "print" )
* @ type String
* @ property mediaType
* /
this . mediaType = mediaType ;
/ * *
* The parts that make up the selector .
* @ type Array
* @ property features
* /
this . features = features ;
}
MediaQuery . prototype = new SyntaxUnit ( ) ;
MediaQuery . prototype . constructor = MediaQuery ;
} , { "../util/SyntaxUnit" : 26 , "./Parser" : 6 } ] , 6 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = Parser ;
var EventTarget = require ( "../util/EventTarget" ) ;
var SyntaxError = require ( "../util/SyntaxError" ) ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Combinator = require ( "./Combinator" ) ;
var MediaFeature = require ( "./MediaFeature" ) ;
var MediaQuery = require ( "./MediaQuery" ) ;
var PropertyName = require ( "./PropertyName" ) ;
var PropertyValue = require ( "./PropertyValue" ) ;
var PropertyValuePart = require ( "./PropertyValuePart" ) ;
var Selector = require ( "./Selector" ) ;
var SelectorPart = require ( "./SelectorPart" ) ;
var SelectorSubPart = require ( "./SelectorSubPart" ) ;
var TokenStream = require ( "./TokenStream" ) ;
var Tokens = require ( "./Tokens" ) ;
var Validation = require ( "./Validation" ) ;
/ * *
* A CSS3 parser .
* @ namespace parserlib . css
* @ class Parser
* @ constructor
* @ param { Object } options ( Optional ) Various options for the parser :
* starHack ( true | false ) to allow IE6 star hack as valid ,
* underscoreHack ( true | false ) to interpret leading underscores
* as IE6 - 7 targeting for known properties , ieFilters ( true | false )
* to indicate that IE < 8 filters should be accepted and not throw
* syntax errors .
* /
function Parser ( options ) {
//inherit event functionality
EventTarget . call ( this ) ;
this . options = options || { } ;
this . _tokenStream = null ;
}
//Static constants
Parser . DEFAULT _TYPE = 0 ;
Parser . COMBINATOR _TYPE = 1 ;
Parser . MEDIA _FEATURE _TYPE = 2 ;
Parser . MEDIA _QUERY _TYPE = 3 ;
Parser . PROPERTY _NAME _TYPE = 4 ;
Parser . PROPERTY _VALUE _TYPE = 5 ;
Parser . PROPERTY _VALUE _PART _TYPE = 6 ;
Parser . SELECTOR _TYPE = 7 ;
Parser . SELECTOR _PART _TYPE = 8 ;
Parser . SELECTOR _SUB _PART _TYPE = 9 ;
Parser . prototype = function ( ) {
var proto = new EventTarget ( ) , //new prototype
prop ,
additions = {
_ _proto _ _ : null ,
//restore constructor
constructor : Parser ,
//instance constants - yuck
DEFAULT _TYPE : 0 ,
COMBINATOR _TYPE : 1 ,
MEDIA _FEATURE _TYPE : 2 ,
MEDIA _QUERY _TYPE : 3 ,
PROPERTY _NAME _TYPE : 4 ,
PROPERTY _VALUE _TYPE : 5 ,
PROPERTY _VALUE _PART _TYPE : 6 ,
SELECTOR _TYPE : 7 ,
SELECTOR _PART _TYPE : 8 ,
SELECTOR _SUB _PART _TYPE : 9 ,
//-----------------------------------------------------------------
// Grammar
//-----------------------------------------------------------------
_stylesheet : function ( ) {
/ *
* stylesheet
* : [ CHARSET _SYM S * STRING S * ';' ] ?
* [ S | CDO | CDC ] * [ import [ S | CDO | CDC ] * ] *
* [ namespace [ S | CDO | CDC ] * ] *
* [ [ ruleset | media | page | font _face | keyframes _rule | supports _rule ] [ S | CDO | CDC ] * ] *
* ;
* /
var tokenStream = this . _tokenStream ,
count ,
token ,
tt ;
this . fire ( "startstylesheet" ) ;
//try to read character set
this . _charset ( ) ;
this . _skipCruft ( ) ;
//try to read imports - may be more than one
while ( tokenStream . peek ( ) === Tokens . IMPORT _SYM ) {
this . _import ( ) ;
this . _skipCruft ( ) ;
}
//try to read namespaces - may be more than one
while ( tokenStream . peek ( ) === Tokens . NAMESPACE _SYM ) {
this . _namespace ( ) ;
this . _skipCruft ( ) ;
}
//get the next token
tt = tokenStream . peek ( ) ;
//try to read the rest
while ( tt > Tokens . EOF ) {
try {
switch ( tt ) {
case Tokens . MEDIA _SYM :
this . _media ( ) ;
this . _skipCruft ( ) ;
break ;
case Tokens . PAGE _SYM :
this . _page ( ) ;
this . _skipCruft ( ) ;
break ;
case Tokens . FONT _FACE _SYM :
this . _font _face ( ) ;
this . _skipCruft ( ) ;
break ;
case Tokens . KEYFRAMES _SYM :
this . _keyframes ( ) ;
this . _skipCruft ( ) ;
break ;
case Tokens . VIEWPORT _SYM :
this . _viewport ( ) ;
this . _skipCruft ( ) ;
break ;
case Tokens . DOCUMENT _SYM :
this . _document ( ) ;
this . _skipCruft ( ) ;
break ;
case Tokens . SUPPORTS _SYM :
this . _supports ( ) ;
this . _skipCruft ( ) ;
break ;
case Tokens . UNKNOWN _SYM : //unknown @ rule
tokenStream . get ( ) ;
if ( ! this . options . strict ) {
//fire error event
this . fire ( {
type : "error" ,
error : null ,
message : "Unknown @ rule: " + tokenStream . LT ( 0 ) . value + "." ,
line : tokenStream . LT ( 0 ) . startLine ,
col : tokenStream . LT ( 0 ) . startCol
} ) ;
//skip braces
count = 0 ;
while ( tokenStream . advance ( [ Tokens . LBRACE , Tokens . RBRACE ] ) === Tokens . LBRACE ) {
count ++ ; //keep track of nesting depth
}
while ( count ) {
tokenStream . advance ( [ Tokens . RBRACE ] ) ;
count -- ;
}
} else {
//not a syntax error, rethrow it
throw new SyntaxError ( "Unknown @ rule." , tokenStream . LT ( 0 ) . startLine , tokenStream . LT ( 0 ) . startCol ) ;
}
break ;
case Tokens . S :
this . _readWhitespace ( ) ;
break ;
default :
if ( ! this . _ruleset ( ) ) {
//error handling for known issues
switch ( tt ) {
case Tokens . CHARSET _SYM :
token = tokenStream . LT ( 1 ) ;
this . _charset ( false ) ;
throw new SyntaxError ( "@charset not allowed here." , token . startLine , token . startCol ) ;
case Tokens . IMPORT _SYM :
token = tokenStream . LT ( 1 ) ;
this . _import ( false ) ;
throw new SyntaxError ( "@import not allowed here." , token . startLine , token . startCol ) ;
case Tokens . NAMESPACE _SYM :
token = tokenStream . LT ( 1 ) ;
this . _namespace ( false ) ;
throw new SyntaxError ( "@namespace not allowed here." , token . startLine , token . startCol ) ;
default :
tokenStream . get ( ) ; //get the last token
this . _unexpectedToken ( tokenStream . token ( ) ) ;
}
}
}
} catch ( ex ) {
if ( ex instanceof SyntaxError && ! this . options . strict ) {
this . fire ( {
type : "error" ,
error : ex ,
message : ex . message ,
line : ex . line ,
col : ex . col
} ) ;
} else {
throw ex ;
}
}
tt = tokenStream . peek ( ) ;
}
if ( tt !== Tokens . EOF ) {
this . _unexpectedToken ( tokenStream . token ( ) ) ;
}
this . fire ( "endstylesheet" ) ;
} ,
_charset : function ( emit ) {
var tokenStream = this . _tokenStream ,
charset ,
token ,
line ,
col ;
if ( tokenStream . match ( Tokens . CHARSET _SYM ) ) {
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . STRING ) ;
token = tokenStream . token ( ) ;
charset = token . value ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . SEMICOLON ) ;
if ( emit !== false ) {
this . fire ( {
type : "charset" ,
charset : charset ,
line : line ,
col : col
} ) ;
}
}
} ,
_import : function ( emit ) {
/ *
* import
* : IMPORT _SYM S *
* [ STRING | URI ] S * media _query _list ? ';' S *
* /
var tokenStream = this . _tokenStream ,
uri ,
importToken ,
mediaList = [ ] ;
//read import symbol
tokenStream . mustMatch ( Tokens . IMPORT _SYM ) ;
importToken = tokenStream . token ( ) ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( [ Tokens . STRING , Tokens . URI ] ) ;
//grab the URI value
uri = tokenStream . token ( ) . value . replace ( /^(?:url\()?["']?([^"']+?)["']?\)?$/ , "$1" ) ;
this . _readWhitespace ( ) ;
mediaList = this . _media _query _list ( ) ;
//must end with a semicolon
tokenStream . mustMatch ( Tokens . SEMICOLON ) ;
this . _readWhitespace ( ) ;
if ( emit !== false ) {
this . fire ( {
type : "import" ,
uri : uri ,
media : mediaList ,
line : importToken . startLine ,
col : importToken . startCol
} ) ;
}
} ,
_namespace : function ( emit ) {
/ *
* namespace
* : NAMESPACE _SYM S * [ namespace _prefix S * ] ? [ STRING | URI ] S * ';' S *
* /
var tokenStream = this . _tokenStream ,
line ,
col ,
prefix ,
uri ;
//read import symbol
tokenStream . mustMatch ( Tokens . NAMESPACE _SYM ) ;
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
this . _readWhitespace ( ) ;
//it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
if ( tokenStream . match ( Tokens . IDENT ) ) {
prefix = tokenStream . token ( ) . value ;
this . _readWhitespace ( ) ;
}
tokenStream . mustMatch ( [ Tokens . STRING , Tokens . URI ] ) ;
/ * i f ( ! t o k e n S t r e a m . m a t c h ( T o k e n s . S T R I N G ) ) {
tokenStream . mustMatch ( Tokens . URI ) ;
} * /
//grab the URI value
uri = tokenStream . token ( ) . value . replace ( /(?:url\()?["']([^"']+)["']\)?/ , "$1" ) ;
this . _readWhitespace ( ) ;
//must end with a semicolon
tokenStream . mustMatch ( Tokens . SEMICOLON ) ;
this . _readWhitespace ( ) ;
if ( emit !== false ) {
this . fire ( {
type : "namespace" ,
prefix : prefix ,
uri : uri ,
line : line ,
col : col
} ) ;
}
} ,
_supports : function ( emit ) {
/ *
* supports _rule
* : SUPPORTS _SYM S * supports _condition S * group _rule _body
* ;
* /
var tokenStream = this . _tokenStream ,
line ,
col ;
if ( tokenStream . match ( Tokens . SUPPORTS _SYM ) ) {
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
this . _readWhitespace ( ) ;
this . _supports _condition ( ) ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . LBRACE ) ;
this . _readWhitespace ( ) ;
if ( emit !== false ) {
this . fire ( {
type : "startsupports" ,
line : line ,
col : col
} ) ;
}
while ( true ) {
if ( ! this . _ruleset ( ) ) {
break ;
}
}
tokenStream . mustMatch ( Tokens . RBRACE ) ;
this . _readWhitespace ( ) ;
this . fire ( {
type : "endsupports" ,
line : line ,
col : col
} ) ;
}
} ,
_supports _condition : function ( ) {
/ *
* supports _condition
* : supports _negation | supports _conjunction | supports _disjunction |
* supports _condition _in _parens
* ;
* /
var tokenStream = this . _tokenStream ,
ident ;
if ( tokenStream . match ( Tokens . IDENT ) ) {
ident = tokenStream . token ( ) . value . toLowerCase ( ) ;
if ( ident === "not" ) {
tokenStream . mustMatch ( Tokens . S ) ;
this . _supports _condition _in _parens ( ) ;
} else {
tokenStream . unget ( ) ;
}
} else {
this . _supports _condition _in _parens ( ) ;
this . _readWhitespace ( ) ;
while ( tokenStream . peek ( ) === Tokens . IDENT ) {
ident = tokenStream . LT ( 1 ) . value . toLowerCase ( ) ;
if ( ident === "and" || ident === "or" ) {
tokenStream . mustMatch ( Tokens . IDENT ) ;
this . _readWhitespace ( ) ;
this . _supports _condition _in _parens ( ) ;
this . _readWhitespace ( ) ;
}
}
}
} ,
_supports _condition _in _parens : function ( ) {
/ *
* supports _condition _in _parens
* : ( '(' S * supports _condition S * ')' ) | supports _declaration _condition |
* general _enclosed
* ;
* /
var tokenStream = this . _tokenStream ,
ident ;
if ( tokenStream . match ( Tokens . LPAREN ) ) {
this . _readWhitespace ( ) ;
if ( tokenStream . match ( Tokens . IDENT ) ) {
// look ahead for not keyword, if not given, continue with declaration condition.
ident = tokenStream . token ( ) . value . toLowerCase ( ) ;
if ( ident === "not" ) {
this . _readWhitespace ( ) ;
this . _supports _condition ( ) ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . RPAREN ) ;
} else {
tokenStream . unget ( ) ;
this . _supports _declaration _condition ( false ) ;
}
} else {
this . _supports _condition ( ) ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . RPAREN ) ;
}
} else {
this . _supports _declaration _condition ( ) ;
}
} ,
_supports _declaration _condition : function ( requireStartParen ) {
/ *
* supports _declaration _condition
* : '(' S * declaration ')'
* ;
* /
var tokenStream = this . _tokenStream ;
if ( requireStartParen !== false ) {
tokenStream . mustMatch ( Tokens . LPAREN ) ;
}
this . _readWhitespace ( ) ;
this . _declaration ( ) ;
tokenStream . mustMatch ( Tokens . RPAREN ) ;
} ,
_media : function ( ) {
/ *
* media
* : MEDIA _SYM S * media _query _list S * '{' S * ruleset * '}' S *
* ;
* /
var tokenStream = this . _tokenStream ,
line ,
col ,
mediaList ; // = [];
//look for @media
tokenStream . mustMatch ( Tokens . MEDIA _SYM ) ;
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
this . _readWhitespace ( ) ;
mediaList = this . _media _query _list ( ) ;
tokenStream . mustMatch ( Tokens . LBRACE ) ;
this . _readWhitespace ( ) ;
this . fire ( {
type : "startmedia" ,
media : mediaList ,
line : line ,
col : col
} ) ;
while ( true ) {
if ( tokenStream . peek ( ) === Tokens . PAGE _SYM ) {
this . _page ( ) ;
} else if ( tokenStream . peek ( ) === Tokens . FONT _FACE _SYM ) {
this . _font _face ( ) ;
} else if ( tokenStream . peek ( ) === Tokens . VIEWPORT _SYM ) {
this . _viewport ( ) ;
} else if ( tokenStream . peek ( ) === Tokens . DOCUMENT _SYM ) {
this . _document ( ) ;
} else if ( tokenStream . peek ( ) === Tokens . SUPPORTS _SYM ) {
this . _supports ( ) ;
} else if ( tokenStream . peek ( ) === Tokens . MEDIA _SYM ) {
this . _media ( ) ;
} else if ( ! this . _ruleset ( ) ) {
break ;
}
}
tokenStream . mustMatch ( Tokens . RBRACE ) ;
this . _readWhitespace ( ) ;
this . fire ( {
type : "endmedia" ,
media : mediaList ,
line : line ,
col : col
} ) ;
} ,
//CSS3 Media Queries
_media _query _list : function ( ) {
/ *
* media _query _list
* : S * [ media _query [ ',' S * media _query ] * ] ?
* ;
* /
var tokenStream = this . _tokenStream ,
mediaList = [ ] ;
this . _readWhitespace ( ) ;
if ( tokenStream . peek ( ) === Tokens . IDENT || tokenStream . peek ( ) === Tokens . LPAREN ) {
mediaList . push ( this . _media _query ( ) ) ;
}
while ( tokenStream . match ( Tokens . COMMA ) ) {
this . _readWhitespace ( ) ;
mediaList . push ( this . _media _query ( ) ) ;
}
return mediaList ;
} ,
/ *
* Note : "expression" in the grammar maps to the _media _expression
* method .
* /
_media _query : function ( ) {
/ *
* media _query
* : [ ONLY | NOT ] ? S * media _type S * [ AND S * expression ] *
* | expression [ AND S * expression ] *
* ;
* /
var tokenStream = this . _tokenStream ,
type = null ,
ident = null ,
token = null ,
expressions = [ ] ;
if ( tokenStream . match ( Tokens . IDENT ) ) {
ident = tokenStream . token ( ) . value . toLowerCase ( ) ;
//since there's no custom tokens for these, need to manually check
if ( ident !== "only" && ident !== "not" ) {
tokenStream . unget ( ) ;
ident = null ;
} else {
token = tokenStream . token ( ) ;
}
}
this . _readWhitespace ( ) ;
if ( tokenStream . peek ( ) === Tokens . IDENT ) {
type = this . _media _type ( ) ;
if ( token === null ) {
token = tokenStream . token ( ) ;
}
} else if ( tokenStream . peek ( ) === Tokens . LPAREN ) {
if ( token === null ) {
token = tokenStream . LT ( 1 ) ;
}
expressions . push ( this . _media _expression ( ) ) ;
}
if ( type === null && expressions . length === 0 ) {
return null ;
} else {
this . _readWhitespace ( ) ;
while ( tokenStream . match ( Tokens . IDENT ) ) {
if ( tokenStream . token ( ) . value . toLowerCase ( ) !== "and" ) {
this . _unexpectedToken ( tokenStream . token ( ) ) ;
}
this . _readWhitespace ( ) ;
expressions . push ( this . _media _expression ( ) ) ;
}
}
return new MediaQuery ( ident , type , expressions , token . startLine , token . startCol ) ;
} ,
//CSS3 Media Queries
_media _type : function ( ) {
/ *
* media _type
* : IDENT
* ;
* /
return this . _media _feature ( ) ;
} ,
/ * *
* Note : in CSS3 Media Queries , this is called "expression" .
* Renamed here to avoid conflict with CSS3 Selectors
* definition of "expression" . Also note that "expr" in the
* grammar now maps to "expression" from CSS3 selectors .
* @ method _media _expression
* @ private
* /
_media _expression : function ( ) {
/ *
* expression
* : '(' S * media _feature S * [ ':' S * expr ] ? ')' S *
* ;
* /
var tokenStream = this . _tokenStream ,
feature = null ,
token ,
expression = null ;
tokenStream . mustMatch ( Tokens . LPAREN ) ;
feature = this . _media _feature ( ) ;
this . _readWhitespace ( ) ;
if ( tokenStream . match ( Tokens . COLON ) ) {
this . _readWhitespace ( ) ;
token = tokenStream . LT ( 1 ) ;
expression = this . _expression ( ) ;
}
tokenStream . mustMatch ( Tokens . RPAREN ) ;
this . _readWhitespace ( ) ;
return new MediaFeature ( feature , expression ? new SyntaxUnit ( expression , token . startLine , token . startCol ) : null ) ;
} ,
//CSS3 Media Queries
_media _feature : function ( ) {
/ *
* media _feature
* : IDENT
* ;
* /
var tokenStream = this . _tokenStream ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . IDENT ) ;
return SyntaxUnit . fromToken ( tokenStream . token ( ) ) ;
} ,
//CSS3 Paged Media
_page : function ( ) {
/ *
* page :
* PAGE _SYM S * IDENT ? pseudo _page ? S *
* '{' S * [ declaration | margin ] ? [ ';' S * [ declaration | margin ] ? ] * '}' S *
* ;
* /
var tokenStream = this . _tokenStream ,
line ,
col ,
identifier = null ,
pseudoPage = null ;
//look for @page
tokenStream . mustMatch ( Tokens . PAGE _SYM ) ;
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
this . _readWhitespace ( ) ;
if ( tokenStream . match ( Tokens . IDENT ) ) {
identifier = tokenStream . token ( ) . value ;
//The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
if ( identifier . toLowerCase ( ) === "auto" ) {
this . _unexpectedToken ( tokenStream . token ( ) ) ;
}
}
//see if there's a colon upcoming
if ( tokenStream . peek ( ) === Tokens . COLON ) {
pseudoPage = this . _pseudo _page ( ) ;
}
this . _readWhitespace ( ) ;
this . fire ( {
type : "startpage" ,
id : identifier ,
pseudo : pseudoPage ,
line : line ,
col : col
} ) ;
this . _readDeclarations ( true , true ) ;
this . fire ( {
type : "endpage" ,
id : identifier ,
pseudo : pseudoPage ,
line : line ,
col : col
} ) ;
} ,
//CSS3 Paged Media
_margin : function ( ) {
/ *
* margin :
* margin _sym S * '{' declaration [ ';' S * declaration ? ] * '}' S *
* ;
* /
var tokenStream = this . _tokenStream ,
line ,
col ,
marginSym = this . _margin _sym ( ) ;
if ( marginSym ) {
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
this . fire ( {
type : "startpagemargin" ,
margin : marginSym ,
line : line ,
col : col
} ) ;
this . _readDeclarations ( true ) ;
this . fire ( {
type : "endpagemargin" ,
margin : marginSym ,
line : line ,
col : col
} ) ;
return true ;
} else {
return false ;
}
} ,
//CSS3 Paged Media
_margin _sym : function ( ) {
/ *
* margin _sym :
* TOPLEFTCORNER _SYM |
* TOPLEFT _SYM |
* TOPCENTER _SYM |
* TOPRIGHT _SYM |
* TOPRIGHTCORNER _SYM |
* BOTTOMLEFTCORNER _SYM |
* BOTTOMLEFT _SYM |
* BOTTOMCENTER _SYM |
* BOTTOMRIGHT _SYM |
* BOTTOMRIGHTCORNER _SYM |
* LEFTTOP _SYM |
* LEFTMIDDLE _SYM |
* LEFTBOTTOM _SYM |
* RIGHTTOP _SYM |
* RIGHTMIDDLE _SYM |
* RIGHTBOTTOM _SYM
* ;
* /
var tokenStream = this . _tokenStream ;
if ( tokenStream . match ( [ Tokens . TOPLEFTCORNER _SYM , Tokens . TOPLEFT _SYM ,
Tokens . TOPCENTER _SYM , Tokens . TOPRIGHT _SYM , Tokens . TOPRIGHTCORNER _SYM ,
Tokens . BOTTOMLEFTCORNER _SYM , Tokens . BOTTOMLEFT _SYM ,
Tokens . BOTTOMCENTER _SYM , Tokens . BOTTOMRIGHT _SYM ,
Tokens . BOTTOMRIGHTCORNER _SYM , Tokens . LEFTTOP _SYM ,
Tokens . LEFTMIDDLE _SYM , Tokens . LEFTBOTTOM _SYM , Tokens . RIGHTTOP _SYM ,
Tokens . RIGHTMIDDLE _SYM , Tokens . RIGHTBOTTOM _SYM ] ) ) {
return SyntaxUnit . fromToken ( tokenStream . token ( ) ) ;
} else {
return null ;
}
} ,
_pseudo _page : function ( ) {
/ *
* pseudo _page
* : ':' IDENT
* ;
* /
var tokenStream = this . _tokenStream ;
tokenStream . mustMatch ( Tokens . COLON ) ;
tokenStream . mustMatch ( Tokens . IDENT ) ;
//TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
return tokenStream . token ( ) . value ;
} ,
_font _face : function ( ) {
/ *
* font _face
* : FONT _FACE _SYM S *
* '{' S * declaration [ ';' S * declaration ] * '}' S *
* ;
* /
var tokenStream = this . _tokenStream ,
line ,
col ;
//look for @page
tokenStream . mustMatch ( Tokens . FONT _FACE _SYM ) ;
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
this . _readWhitespace ( ) ;
this . fire ( {
type : "startfontface" ,
line : line ,
col : col
} ) ;
this . _readDeclarations ( true ) ;
this . fire ( {
type : "endfontface" ,
line : line ,
col : col
} ) ;
} ,
_viewport : function ( ) {
/ *
* viewport
* : VIEWPORT _SYM S *
* '{' S * declaration ? [ ';' S * declaration ? ] * '}' S *
* ;
* /
var tokenStream = this . _tokenStream ,
line ,
col ;
tokenStream . mustMatch ( Tokens . VIEWPORT _SYM ) ;
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
this . _readWhitespace ( ) ;
this . fire ( {
type : "startviewport" ,
line : line ,
col : col
} ) ;
this . _readDeclarations ( true ) ;
this . fire ( {
type : "endviewport" ,
line : line ,
col : col
} ) ;
} ,
_document : function ( ) {
/ *
* document
* : DOCUMENT _SYM S *
* _document _function [ ',' S * _document _function ] * S *
* '{' S * ruleset * '}'
* ;
* /
var tokenStream = this . _tokenStream ,
token ,
functions = [ ] ,
prefix = "" ;
tokenStream . mustMatch ( Tokens . DOCUMENT _SYM ) ;
token = tokenStream . token ( ) ;
if ( /^@\-([^\-]+)\-/ . test ( token . value ) ) {
prefix = RegExp . $1 ;
}
this . _readWhitespace ( ) ;
functions . push ( this . _document _function ( ) ) ;
while ( tokenStream . match ( Tokens . COMMA ) ) {
this . _readWhitespace ( ) ;
functions . push ( this . _document _function ( ) ) ;
}
tokenStream . mustMatch ( Tokens . LBRACE ) ;
this . _readWhitespace ( ) ;
this . fire ( {
type : "startdocument" ,
functions : functions ,
prefix : prefix ,
line : token . startLine ,
col : token . startCol
} ) ;
var ok = true ;
while ( ok ) {
switch ( tokenStream . peek ( ) ) {
case Tokens . PAGE _SYM :
this . _page ( ) ;
break ;
case Tokens . FONT _FACE _SYM :
this . _font _face ( ) ;
break ;
case Tokens . VIEWPORT _SYM :
this . _viewport ( ) ;
break ;
case Tokens . MEDIA _SYM :
this . _media ( ) ;
break ;
case Tokens . KEYFRAMES _SYM :
this . _keyframes ( ) ;
break ;
case Tokens . DOCUMENT _SYM :
this . _document ( ) ;
break ;
default :
ok = Boolean ( this . _ruleset ( ) ) ;
}
}
tokenStream . mustMatch ( Tokens . RBRACE ) ;
token = tokenStream . token ( ) ;
this . _readWhitespace ( ) ;
this . fire ( {
type : "enddocument" ,
functions : functions ,
prefix : prefix ,
line : token . startLine ,
col : token . startCol
} ) ;
} ,
_document _function : function ( ) {
/ *
* document _function
* : function | URI S *
* ;
* /
var tokenStream = this . _tokenStream ,
value ;
if ( tokenStream . match ( Tokens . URI ) ) {
value = tokenStream . token ( ) . value ;
this . _readWhitespace ( ) ;
} else {
value = this . _function ( ) ;
}
return value ;
} ,
_operator : function ( inFunction ) {
/ *
* operator ( outside function )
* : '/' S * | ',' S * | /( empty )/
* operator ( inside function )
* : '/' S * | '+' S * | '*' S * | '-' S * /( empty )/
* ;
* /
var tokenStream = this . _tokenStream ,
token = null ;
if ( tokenStream . match ( [ Tokens . SLASH , Tokens . COMMA ] ) ||
( inFunction && tokenStream . match ( [ Tokens . PLUS , Tokens . STAR , Tokens . MINUS ] ) ) ) {
token = tokenStream . token ( ) ;
this . _readWhitespace ( ) ;
}
return token ? PropertyValuePart . fromToken ( token ) : null ;
} ,
_combinator : function ( ) {
/ *
* combinator
* : PLUS S * | GREATER S * | TILDE S * | S +
* ;
* /
var tokenStream = this . _tokenStream ,
value = null ,
token ;
if ( tokenStream . match ( [ Tokens . PLUS , Tokens . GREATER , Tokens . TILDE ] ) ) {
token = tokenStream . token ( ) ;
value = new Combinator ( token . value , token . startLine , token . startCol ) ;
this . _readWhitespace ( ) ;
}
return value ;
} ,
_unary _operator : function ( ) {
/ *
* unary _operator
* : '-' | '+'
* ;
* /
var tokenStream = this . _tokenStream ;
if ( tokenStream . match ( [ Tokens . MINUS , Tokens . PLUS ] ) ) {
return tokenStream . token ( ) . value ;
} else {
return null ;
}
} ,
_property : function ( ) {
/ *
* property
* : IDENT S *
* ;
* /
var tokenStream = this . _tokenStream ,
value = null ,
hack = null ,
tokenValue ,
token ,
line ,
col ;
//check for star hack - throws error if not allowed
if ( tokenStream . peek ( ) === Tokens . STAR && this . options . starHack ) {
tokenStream . get ( ) ;
token = tokenStream . token ( ) ;
hack = token . value ;
line = token . startLine ;
col = token . startCol ;
}
if ( tokenStream . match ( Tokens . IDENT ) ) {
token = tokenStream . token ( ) ;
tokenValue = token . value ;
//check for underscore hack - no error if not allowed because it's valid CSS syntax
if ( tokenValue . charAt ( 0 ) === "_" && this . options . underscoreHack ) {
hack = "_" ;
tokenValue = tokenValue . substring ( 1 ) ;
}
value = new PropertyName ( tokenValue , hack , ( line || token . startLine ) , ( col || token . startCol ) ) ;
this . _readWhitespace ( ) ;
}
return value ;
} ,
//Augmented with CSS3 Selectors
_ruleset : function ( ) {
/ *
* ruleset
* : selectors _group
* '{' S * declaration ? [ ';' S * declaration ? ] * '}' S *
* ;
* /
var tokenStream = this . _tokenStream ,
tt ,
selectors ;
/ *
* Error Recovery : If even a single selector fails to parse ,
* then the entire ruleset should be thrown away .
* /
try {
selectors = this . _selectors _group ( ) ;
} catch ( ex ) {
if ( ex instanceof SyntaxError && ! this . options . strict ) {
//fire error event
this . fire ( {
type : "error" ,
error : ex ,
message : ex . message ,
line : ex . line ,
col : ex . col
} ) ;
//skip over everything until closing brace
tt = tokenStream . advance ( [ Tokens . RBRACE ] ) ;
if ( tt === Tokens . RBRACE ) {
//if there's a right brace, the rule is finished so don't do anything
} else {
//otherwise, rethrow the error because it wasn't handled properly
throw ex ;
}
} else {
//not a syntax error, rethrow it
throw ex ;
}
//trigger parser to continue
return true ;
}
//if it got here, all selectors parsed
if ( selectors ) {
this . fire ( {
type : "startrule" ,
selectors : selectors ,
line : selectors [ 0 ] . line ,
col : selectors [ 0 ] . col
} ) ;
this . _readDeclarations ( true ) ;
this . fire ( {
type : "endrule" ,
selectors : selectors ,
line : selectors [ 0 ] . line ,
col : selectors [ 0 ] . col
} ) ;
}
return selectors ;
} ,
//CSS3 Selectors
_selectors _group : function ( ) {
/ *
* selectors _group
* : selector [ COMMA S * selector ] *
* ;
* /
var tokenStream = this . _tokenStream ,
selectors = [ ] ,
selector ;
selector = this . _selector ( ) ;
if ( selector !== null ) {
selectors . push ( selector ) ;
while ( tokenStream . match ( Tokens . COMMA ) ) {
this . _readWhitespace ( ) ;
selector = this . _selector ( ) ;
if ( selector !== null ) {
selectors . push ( selector ) ;
} else {
this . _unexpectedToken ( tokenStream . LT ( 1 ) ) ;
}
}
}
return selectors . length ? selectors : null ;
} ,
//CSS3 Selectors
_selector : function ( ) {
/ *
* selector
* : simple _selector _sequence [ combinator simple _selector _sequence ] *
* ;
* /
var tokenStream = this . _tokenStream ,
selector = [ ] ,
nextSelector = null ,
combinator = null ,
ws = null ;
//if there's no simple selector, then there's no selector
nextSelector = this . _simple _selector _sequence ( ) ;
if ( nextSelector === null ) {
return null ;
}
selector . push ( nextSelector ) ;
do {
//look for a combinator
combinator = this . _combinator ( ) ;
if ( combinator !== null ) {
selector . push ( combinator ) ;
nextSelector = this . _simple _selector _sequence ( ) ;
//there must be a next selector
if ( nextSelector === null ) {
this . _unexpectedToken ( tokenStream . LT ( 1 ) ) ;
} else {
//nextSelector is an instance of SelectorPart
selector . push ( nextSelector ) ;
}
} else {
//if there's not whitespace, we're done
if ( this . _readWhitespace ( ) ) {
//add whitespace separator
ws = new Combinator ( tokenStream . token ( ) . value , tokenStream . token ( ) . startLine , tokenStream . token ( ) . startCol ) ;
//combinator is not required
combinator = this . _combinator ( ) ;
//selector is required if there's a combinator
nextSelector = this . _simple _selector _sequence ( ) ;
if ( nextSelector === null ) {
if ( combinator !== null ) {
this . _unexpectedToken ( tokenStream . LT ( 1 ) ) ;
}
} else {
if ( combinator !== null ) {
selector . push ( combinator ) ;
} else {
selector . push ( ws ) ;
}
selector . push ( nextSelector ) ;
}
} else {
break ;
}
}
} while ( true ) ;
return new Selector ( selector , selector [ 0 ] . line , selector [ 0 ] . col ) ;
} ,
//CSS3 Selectors
_simple _selector _sequence : function ( ) {
/ *
* simple _selector _sequence
* : [ type _selector | universal ]
* [ HASH | class | attrib | pseudo | negation ] *
* | [ HASH | class | attrib | pseudo | negation ] +
* ;
* /
var tokenStream = this . _tokenStream ,
//parts of a simple selector
elementName = null ,
modifiers = [ ] ,
//complete selector text
selectorText = "" ,
//the different parts after the element name to search for
components = [
//HASH
function ( ) {
return tokenStream . match ( Tokens . HASH ) ?
new SelectorSubPart ( tokenStream . token ( ) . value , "id" , tokenStream . token ( ) . startLine , tokenStream . token ( ) . startCol ) :
null ;
} ,
this . _class ,
this . _attrib ,
this . _pseudo ,
this . _negation
] ,
i = 0 ,
len = components . length ,
component = null ,
line ,
col ;
//get starting line and column for the selector
line = tokenStream . LT ( 1 ) . startLine ;
col = tokenStream . LT ( 1 ) . startCol ;
elementName = this . _type _selector ( ) ;
if ( ! elementName ) {
elementName = this . _universal ( ) ;
}
if ( elementName !== null ) {
selectorText += elementName ;
}
while ( true ) {
//whitespace means we're done
if ( tokenStream . peek ( ) === Tokens . S ) {
break ;
}
//check for each component
while ( i < len && component === null ) {
component = components [ i ++ ] . call ( this ) ;
}
if ( component === null ) {
//we don't have a selector
if ( selectorText === "" ) {
return null ;
} else {
break ;
}
} else {
i = 0 ;
modifiers . push ( component ) ;
selectorText += component . toString ( ) ;
component = null ;
}
}
return selectorText !== "" ?
new SelectorPart ( elementName , modifiers , selectorText , line , col ) :
null ;
} ,
//CSS3 Selectors
_type _selector : function ( ) {
/ *
* type _selector
* : [ namespace _prefix ] ? element _name
* ;
* /
var tokenStream = this . _tokenStream ,
ns = this . _namespace _prefix ( ) ,
elementName = this . _element _name ( ) ;
if ( ! elementName ) {
/ *
* Need to back out the namespace that was read due to both
* type _selector and universal reading namespace _prefix
* first . Kind of hacky , but only way I can figure out
* right now how to not change the grammar .
* /
if ( ns ) {
tokenStream . unget ( ) ;
if ( ns . length > 1 ) {
tokenStream . unget ( ) ;
}
}
return null ;
} else {
if ( ns ) {
elementName . text = ns + elementName . text ;
elementName . col -= ns . length ;
}
return elementName ;
}
} ,
//CSS3 Selectors
_class : function ( ) {
/ *
* class
* : '.' IDENT
* ;
* /
var tokenStream = this . _tokenStream ,
token ;
if ( tokenStream . match ( Tokens . DOT ) ) {
tokenStream . mustMatch ( Tokens . IDENT ) ;
token = tokenStream . token ( ) ;
return new SelectorSubPart ( "." + token . value , "class" , token . startLine , token . startCol - 1 ) ;
} else {
return null ;
}
} ,
//CSS3 Selectors
_element _name : function ( ) {
/ *
* element _name
* : IDENT
* ;
* /
var tokenStream = this . _tokenStream ,
token ;
if ( tokenStream . match ( Tokens . IDENT ) ) {
token = tokenStream . token ( ) ;
return new SelectorSubPart ( token . value , "elementName" , token . startLine , token . startCol ) ;
} else {
return null ;
}
} ,
//CSS3 Selectors
_namespace _prefix : function ( ) {
/ *
* namespace _prefix
* : [ IDENT | '*' ] ? '|'
* ;
* /
var tokenStream = this . _tokenStream ,
value = "" ;
//verify that this is a namespace prefix
if ( tokenStream . LA ( 1 ) === Tokens . PIPE || tokenStream . LA ( 2 ) === Tokens . PIPE ) {
if ( tokenStream . match ( [ Tokens . IDENT , Tokens . STAR ] ) ) {
value += tokenStream . token ( ) . value ;
}
tokenStream . mustMatch ( Tokens . PIPE ) ;
value += "|" ;
}
return value . length ? value : null ;
} ,
//CSS3 Selectors
_universal : function ( ) {
/ *
* universal
* : [ namespace _prefix ] ? '*'
* ;
* /
var tokenStream = this . _tokenStream ,
value = "" ,
ns ;
ns = this . _namespace _prefix ( ) ;
if ( ns ) {
value += ns ;
}
if ( tokenStream . match ( Tokens . STAR ) ) {
value += "*" ;
}
return value . length ? value : null ;
} ,
//CSS3 Selectors
_attrib : function ( ) {
/ *
* attrib
* : '[' S * [ namespace _prefix ] ? IDENT S *
* [ [ PREFIXMATCH |
* SUFFIXMATCH |
* SUBSTRINGMATCH |
* '=' |
* INCLUDES |
* DASHMATCH ] S * [ IDENT | STRING ] S *
* ] ? ']'
* ;
* /
var tokenStream = this . _tokenStream ,
value = null ,
ns ,
token ;
if ( tokenStream . match ( Tokens . LBRACKET ) ) {
token = tokenStream . token ( ) ;
value = token . value ;
value += this . _readWhitespace ( ) ;
ns = this . _namespace _prefix ( ) ;
if ( ns ) {
value += ns ;
}
tokenStream . mustMatch ( Tokens . IDENT ) ;
value += tokenStream . token ( ) . value ;
value += this . _readWhitespace ( ) ;
if ( tokenStream . match ( [ Tokens . PREFIXMATCH , Tokens . SUFFIXMATCH , Tokens . SUBSTRINGMATCH ,
Tokens . EQUALS , Tokens . INCLUDES , Tokens . DASHMATCH ] ) ) {
value += tokenStream . token ( ) . value ;
value += this . _readWhitespace ( ) ;
tokenStream . mustMatch ( [ Tokens . IDENT , Tokens . STRING ] ) ;
value += tokenStream . token ( ) . value ;
value += this . _readWhitespace ( ) ;
}
tokenStream . mustMatch ( Tokens . RBRACKET ) ;
return new SelectorSubPart ( value + "]" , "attribute" , token . startLine , token . startCol ) ;
} else {
return null ;
}
} ,
//CSS3 Selectors
_pseudo : function ( ) {
/ *
* pseudo
* : ':' ':' ? [ IDENT | functional _pseudo ]
* ;
* /
var tokenStream = this . _tokenStream ,
pseudo = null ,
colons = ":" ,
line ,
col ;
if ( tokenStream . match ( Tokens . COLON ) ) {
if ( tokenStream . match ( Tokens . COLON ) ) {
colons += ":" ;
}
if ( tokenStream . match ( Tokens . IDENT ) ) {
pseudo = tokenStream . token ( ) . value ;
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol - colons . length ;
} else if ( tokenStream . peek ( ) === Tokens . FUNCTION ) {
line = tokenStream . LT ( 1 ) . startLine ;
col = tokenStream . LT ( 1 ) . startCol - colons . length ;
pseudo = this . _functional _pseudo ( ) ;
}
if ( pseudo ) {
pseudo = new SelectorSubPart ( colons + pseudo , "pseudo" , line , col ) ;
} else {
var startLine = tokenStream . LT ( 1 ) . startLine ,
startCol = tokenStream . LT ( 0 ) . startCol ;
throw new SyntaxError ( "Expected a `FUNCTION` or `IDENT` after colon at line " + startLine + ", col " + startCol + "." , startLine , startCol ) ;
}
}
return pseudo ;
} ,
//CSS3 Selectors
_functional _pseudo : function ( ) {
/ *
* functional _pseudo
* : FUNCTION S * expression ')'
* ;
* /
var tokenStream = this . _tokenStream ,
value = null ;
if ( tokenStream . match ( Tokens . FUNCTION ) ) {
value = tokenStream . token ( ) . value ;
value += this . _readWhitespace ( ) ;
value += this . _expression ( ) ;
tokenStream . mustMatch ( Tokens . RPAREN ) ;
value += ")" ;
}
return value ;
} ,
//CSS3 Selectors
_expression : function ( ) {
/ *
* expression
* : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S * ] +
* ;
* /
var tokenStream = this . _tokenStream ,
value = "" ;
while ( tokenStream . match ( [ Tokens . PLUS , Tokens . MINUS , Tokens . DIMENSION ,
Tokens . NUMBER , Tokens . STRING , Tokens . IDENT , Tokens . LENGTH ,
Tokens . FREQ , Tokens . ANGLE , Tokens . TIME ,
Tokens . RESOLUTION , Tokens . SLASH ] ) ) {
value += tokenStream . token ( ) . value ;
value += this . _readWhitespace ( ) ;
}
return value . length ? value : null ;
} ,
//CSS3 Selectors
_negation : function ( ) {
/ *
* negation
* : NOT S * negation _arg S * ')'
* ;
* /
var tokenStream = this . _tokenStream ,
line ,
col ,
value = "" ,
arg ,
subpart = null ;
if ( tokenStream . match ( Tokens . NOT ) ) {
value = tokenStream . token ( ) . value ;
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
value += this . _readWhitespace ( ) ;
arg = this . _negation _arg ( ) ;
value += arg ;
value += this . _readWhitespace ( ) ;
tokenStream . match ( Tokens . RPAREN ) ;
value += tokenStream . token ( ) . value ;
subpart = new SelectorSubPart ( value , "not" , line , col ) ;
subpart . args . push ( arg ) ;
}
return subpart ;
} ,
//CSS3 Selectors
_negation _arg : function ( ) {
/ *
* negation _arg
* : type _selector | universal | HASH | class | attrib | pseudo
* ;
* /
var tokenStream = this . _tokenStream ,
args = [
this . _type _selector ,
this . _universal ,
function ( ) {
return tokenStream . match ( Tokens . HASH ) ?
new SelectorSubPart ( tokenStream . token ( ) . value , "id" , tokenStream . token ( ) . startLine , tokenStream . token ( ) . startCol ) :
null ;
} ,
this . _class ,
this . _attrib ,
this . _pseudo
] ,
arg = null ,
i = 0 ,
len = args . length ,
line ,
col ,
part ;
line = tokenStream . LT ( 1 ) . startLine ;
col = tokenStream . LT ( 1 ) . startCol ;
while ( i < len && arg === null ) {
arg = args [ i ] . call ( this ) ;
i ++ ;
}
//must be a negation arg
if ( arg === null ) {
this . _unexpectedToken ( tokenStream . LT ( 1 ) ) ;
}
//it's an element name
if ( arg . type === "elementName" ) {
part = new SelectorPart ( arg , [ ] , arg . toString ( ) , line , col ) ;
} else {
part = new SelectorPart ( null , [ arg ] , arg . toString ( ) , line , col ) ;
}
return part ;
} ,
_declaration : function ( ) {
/ *
* declaration
* : property ':' S * expr prio ?
* | /( empty )/
* ;
* /
var tokenStream = this . _tokenStream ,
property = null ,
expr = null ,
prio = null ,
invalid = null ,
propertyName = "" ;
property = this . _property ( ) ;
if ( property !== null ) {
tokenStream . mustMatch ( Tokens . COLON ) ;
this . _readWhitespace ( ) ;
expr = this . _expr ( ) ;
//if there's no parts for the value, it's an error
if ( ! expr || expr . length === 0 ) {
this . _unexpectedToken ( tokenStream . LT ( 1 ) ) ;
}
prio = this . _prio ( ) ;
/ *
* If hacks should be allowed , then only check the root
* property . If hacks should not be allowed , treat
* _property or * property as invalid properties .
* /
propertyName = property . toString ( ) ;
if ( this . options . starHack && property . hack === "*" ||
this . options . underscoreHack && property . hack === "_" ) {
propertyName = property . text ;
}
try {
this . _validateProperty ( propertyName , expr ) ;
} catch ( ex ) {
invalid = ex ;
}
this . fire ( {
type : "property" ,
property : property ,
value : expr ,
important : prio ,
line : property . line ,
col : property . col ,
invalid : invalid
} ) ;
return true ;
} else {
return false ;
}
} ,
_prio : function ( ) {
/ *
* prio
* : IMPORTANT _SYM S *
* ;
* /
var tokenStream = this . _tokenStream ,
result = tokenStream . match ( Tokens . IMPORTANT _SYM ) ;
this . _readWhitespace ( ) ;
return result ;
} ,
_expr : function ( inFunction ) {
/ *
* expr
* : term [ operator term ] *
* ;
* /
var values = [ ] ,
//valueParts = [],
value = null ,
operator = null ;
value = this . _term ( inFunction ) ;
if ( value !== null ) {
values . push ( value ) ;
do {
operator = this . _operator ( inFunction ) ;
//if there's an operator, keep building up the value parts
if ( operator ) {
values . push ( operator ) ;
} / * else {
//if there's not an operator, you have a full value
values . push ( new PropertyValue ( valueParts , valueParts [ 0 ] . line , valueParts [ 0 ] . col ) ) ;
valueParts = [ ] ;
} * /
value = this . _term ( inFunction ) ;
if ( value === null ) {
break ;
} else {
values . push ( value ) ;
}
} while ( true ) ;
}
//cleanup
/ * i f ( v a l u e P a r t s . l e n g t h ) {
values . push ( new PropertyValue ( valueParts , valueParts [ 0 ] . line , valueParts [ 0 ] . col ) ) ;
} * /
return values . length > 0 ? new PropertyValue ( values , values [ 0 ] . line , values [ 0 ] . col ) : null ;
} ,
_term : function ( inFunction ) {
/ *
* term
* : unary _operator ?
* [ NUMBER S * | PERCENTAGE S * | LENGTH S * | ANGLE S * |
* TIME S * | FREQ S * | function | ie _function ]
* | STRING S * | IDENT S * | URI S * | UNICODERANGE S * | hexcolor
* ;
* /
var tokenStream = this . _tokenStream ,
unary = null ,
value = null ,
endChar = null ,
part = null ,
token ,
line ,
col ;
//returns the operator or null
unary = this . _unary _operator ( ) ;
if ( unary !== null ) {
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
}
//exception for IE filters
if ( tokenStream . peek ( ) === Tokens . IE _FUNCTION && this . options . ieFilters ) {
value = this . _ie _function ( ) ;
if ( unary === null ) {
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
}
//see if it's a simple block
} else if ( inFunction && tokenStream . match ( [ Tokens . LPAREN , Tokens . LBRACE , Tokens . LBRACKET ] ) ) {
token = tokenStream . token ( ) ;
endChar = token . endChar ;
value = token . value + this . _expr ( inFunction ) . text ;
if ( unary === null ) {
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
}
tokenStream . mustMatch ( Tokens . type ( endChar ) ) ;
value += endChar ;
this . _readWhitespace ( ) ;
//see if there's a simple match
} else if ( tokenStream . match ( [ Tokens . NUMBER , Tokens . PERCENTAGE , Tokens . LENGTH ,
Tokens . ANGLE , Tokens . TIME ,
Tokens . FREQ , Tokens . STRING , Tokens . IDENT , Tokens . URI , Tokens . UNICODE _RANGE ] ) ) {
value = tokenStream . token ( ) . value ;
if ( unary === null ) {
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
// Correct potentially-inaccurate IDENT parsing in
// PropertyValuePart constructor.
part = PropertyValuePart . fromToken ( tokenStream . token ( ) ) ;
}
this . _readWhitespace ( ) ;
} else {
//see if it's a color
token = this . _hexcolor ( ) ;
if ( token === null ) {
//if there's no unary, get the start of the next token for line/col info
if ( unary === null ) {
line = tokenStream . LT ( 1 ) . startLine ;
col = tokenStream . LT ( 1 ) . startCol ;
}
//has to be a function
if ( value === null ) {
/ *
* This checks for alpha ( opacity = 0 ) style of IE
* functions . IE _FUNCTION only presents progid : style .
* /
if ( tokenStream . LA ( 3 ) === Tokens . EQUALS && this . options . ieFilters ) {
value = this . _ie _function ( ) ;
} else {
value = this . _function ( ) ;
}
}
/ * i f ( v a l u e = = = n u l l ) {
return null ;
//throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " + tokenStream.token().startCol + ".");
} * /
} else {
value = token . value ;
if ( unary === null ) {
line = token . startLine ;
col = token . startCol ;
}
}
}
return part !== null ? part : value !== null ?
new PropertyValuePart ( unary !== null ? unary + value : value , line , col ) :
null ;
} ,
_function : function ( ) {
/ *
* function
* : FUNCTION S * expr ')' S *
* ;
* /
var tokenStream = this . _tokenStream ,
functionText = null ,
expr = null ,
lt ;
if ( tokenStream . match ( Tokens . FUNCTION ) ) {
functionText = tokenStream . token ( ) . value ;
this . _readWhitespace ( ) ;
expr = this . _expr ( true ) ;
functionText += expr ;
//START: Horrible hack in case it's an IE filter
if ( this . options . ieFilters && tokenStream . peek ( ) === Tokens . EQUALS ) {
do {
if ( this . _readWhitespace ( ) ) {
functionText += tokenStream . token ( ) . value ;
}
//might be second time in the loop
if ( tokenStream . LA ( 0 ) === Tokens . COMMA ) {
functionText += tokenStream . token ( ) . value ;
}
tokenStream . match ( Tokens . IDENT ) ;
functionText += tokenStream . token ( ) . value ;
tokenStream . match ( Tokens . EQUALS ) ;
functionText += tokenStream . token ( ) . value ;
//functionText += this._term();
lt = tokenStream . peek ( ) ;
while ( lt !== Tokens . COMMA && lt !== Tokens . S && lt !== Tokens . RPAREN ) {
tokenStream . get ( ) ;
functionText += tokenStream . token ( ) . value ;
lt = tokenStream . peek ( ) ;
}
} while ( tokenStream . match ( [ Tokens . COMMA , Tokens . S ] ) ) ;
}
//END: Horrible Hack
tokenStream . match ( Tokens . RPAREN ) ;
functionText += ")" ;
this . _readWhitespace ( ) ;
}
return functionText ;
} ,
_ie _function : function ( ) {
/ * ( M y o w n e x t e n s i o n )
* ie _function
* : IE _FUNCTION S * IDENT '=' term [ S * ',' ? IDENT '=' term ] + ')' S *
* ;
* /
var tokenStream = this . _tokenStream ,
functionText = null ,
lt ;
//IE function can begin like a regular function, too
if ( tokenStream . match ( [ Tokens . IE _FUNCTION , Tokens . FUNCTION ] ) ) {
functionText = tokenStream . token ( ) . value ;
do {
if ( this . _readWhitespace ( ) ) {
functionText += tokenStream . token ( ) . value ;
}
//might be second time in the loop
if ( tokenStream . LA ( 0 ) === Tokens . COMMA ) {
functionText += tokenStream . token ( ) . value ;
}
tokenStream . match ( Tokens . IDENT ) ;
functionText += tokenStream . token ( ) . value ;
tokenStream . match ( Tokens . EQUALS ) ;
functionText += tokenStream . token ( ) . value ;
//functionText += this._term();
lt = tokenStream . peek ( ) ;
while ( lt !== Tokens . COMMA && lt !== Tokens . S && lt !== Tokens . RPAREN ) {
tokenStream . get ( ) ;
functionText += tokenStream . token ( ) . value ;
lt = tokenStream . peek ( ) ;
}
} while ( tokenStream . match ( [ Tokens . COMMA , Tokens . S ] ) ) ;
tokenStream . match ( Tokens . RPAREN ) ;
functionText += ")" ;
this . _readWhitespace ( ) ;
}
return functionText ;
} ,
_hexcolor : function ( ) {
/ *
* There is a constraint on the color that it must
* have either 3 or 6 hex - digits ( i . e . , [ 0 - 9 a - fA - F ] )
* after the "#" ; e . g . , "#000" is OK , but "#abcd" is not .
*
* hexcolor
* : HASH S *
* ;
* /
var tokenStream = this . _tokenStream ,
token = null ,
color ;
if ( tokenStream . match ( Tokens . HASH ) ) {
//need to do some validation here
token = tokenStream . token ( ) ;
color = token . value ;
if ( ! /#[a-f0-9]{3,6}/i . test ( color ) ) {
throw new SyntaxError ( "Expected a hex color but found '" + color + "' at line " + token . startLine + ", col " + token . startCol + "." , token . startLine , token . startCol ) ;
}
this . _readWhitespace ( ) ;
}
return token ;
} ,
//-----------------------------------------------------------------
// Animations methods
//-----------------------------------------------------------------
_keyframes : function ( ) {
/ *
* keyframes :
* : KEYFRAMES _SYM S * keyframe _name S * '{' S * keyframe _rule * '}' {
* ;
* /
var tokenStream = this . _tokenStream ,
token ,
tt ,
name ,
prefix = "" ;
tokenStream . mustMatch ( Tokens . KEYFRAMES _SYM ) ;
token = tokenStream . token ( ) ;
if ( /^@\-([^\-]+)\-/ . test ( token . value ) ) {
prefix = RegExp . $1 ;
}
this . _readWhitespace ( ) ;
name = this . _keyframe _name ( ) ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . LBRACE ) ;
this . fire ( {
type : "startkeyframes" ,
name : name ,
prefix : prefix ,
line : token . startLine ,
col : token . startCol
} ) ;
this . _readWhitespace ( ) ;
tt = tokenStream . peek ( ) ;
//check for key
while ( tt === Tokens . IDENT || tt === Tokens . PERCENTAGE ) {
this . _keyframe _rule ( ) ;
this . _readWhitespace ( ) ;
tt = tokenStream . peek ( ) ;
}
this . fire ( {
type : "endkeyframes" ,
name : name ,
prefix : prefix ,
line : token . startLine ,
col : token . startCol
} ) ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . RBRACE ) ;
this . _readWhitespace ( ) ;
} ,
_keyframe _name : function ( ) {
/ *
* keyframe _name :
* : IDENT
* | STRING
* ;
* /
var tokenStream = this . _tokenStream ;
tokenStream . mustMatch ( [ Tokens . IDENT , Tokens . STRING ] ) ;
return SyntaxUnit . fromToken ( tokenStream . token ( ) ) ;
} ,
_keyframe _rule : function ( ) {
/ *
* keyframe _rule :
* : key _list S *
* '{' S * declaration [ ';' S * declaration ] * '}' S *
* ;
* /
var keyList = this . _key _list ( ) ;
this . fire ( {
type : "startkeyframerule" ,
keys : keyList ,
line : keyList [ 0 ] . line ,
col : keyList [ 0 ] . col
} ) ;
this . _readDeclarations ( true ) ;
this . fire ( {
type : "endkeyframerule" ,
keys : keyList ,
line : keyList [ 0 ] . line ,
col : keyList [ 0 ] . col
} ) ;
} ,
_key _list : function ( ) {
/ *
* key _list :
* : key [ S * ',' S * key ] *
* ;
* /
var tokenStream = this . _tokenStream ,
keyList = [ ] ;
//must be least one key
keyList . push ( this . _key ( ) ) ;
this . _readWhitespace ( ) ;
while ( tokenStream . match ( Tokens . COMMA ) ) {
this . _readWhitespace ( ) ;
keyList . push ( this . _key ( ) ) ;
this . _readWhitespace ( ) ;
}
return keyList ;
} ,
_key : function ( ) {
/ *
* There is a restriction that IDENT can be only "from" or "to" .
*
* key
* : PERCENTAGE
* | IDENT
* ;
* /
var tokenStream = this . _tokenStream ,
token ;
if ( tokenStream . match ( Tokens . PERCENTAGE ) ) {
return SyntaxUnit . fromToken ( tokenStream . token ( ) ) ;
} else if ( tokenStream . match ( Tokens . IDENT ) ) {
token = tokenStream . token ( ) ;
if ( /from|to/i . test ( token . value ) ) {
return SyntaxUnit . fromToken ( token ) ;
}
tokenStream . unget ( ) ;
}
//if it gets here, there wasn't a valid token, so time to explode
this . _unexpectedToken ( tokenStream . LT ( 1 ) ) ;
} ,
//-----------------------------------------------------------------
// Helper methods
//-----------------------------------------------------------------
/ * *
* Not part of CSS grammar , but useful for skipping over
* combination of white space and HTML - style comments .
* @ return { void }
* @ method _skipCruft
* @ private
* /
_skipCruft : function ( ) {
while ( this . _tokenStream . match ( [ Tokens . S , Tokens . CDO , Tokens . CDC ] ) ) {
//noop
}
} ,
/ * *
* Not part of CSS grammar , but this pattern occurs frequently
* in the official CSS grammar . Split out here to eliminate
* duplicate code .
* @ param { Boolean } checkStart Indicates if the rule should check
* for the left brace at the beginning .
* @ param { Boolean } readMargins Indicates if the rule should check
* for margin patterns .
* @ return { void }
* @ method _readDeclarations
* @ private
* /
_readDeclarations : function ( checkStart , readMargins ) {
/ *
* Reads the pattern
* S * '{' S * declaration [ ';' S * declaration ] * '}' S *
* or
* S * '{' S * [ declaration | margin ] ? [ ';' S * [ declaration | margin ] ? ] * '}' S *
* Note that this is how it is described in CSS3 Paged Media , but is actually incorrect .
* A semicolon is only necessary following a declaration if there ' s another declaration
* or margin afterwards .
* /
var tokenStream = this . _tokenStream ,
tt ;
this . _readWhitespace ( ) ;
if ( checkStart ) {
tokenStream . mustMatch ( Tokens . LBRACE ) ;
}
this . _readWhitespace ( ) ;
try {
while ( true ) {
if ( tokenStream . match ( Tokens . SEMICOLON ) || ( readMargins && this . _margin ( ) ) ) {
//noop
} else if ( this . _declaration ( ) ) {
if ( ! tokenStream . match ( Tokens . SEMICOLON ) ) {
break ;
}
} else {
break ;
}
//if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
// break;
//}
this . _readWhitespace ( ) ;
}
tokenStream . mustMatch ( Tokens . RBRACE ) ;
this . _readWhitespace ( ) ;
} catch ( ex ) {
if ( ex instanceof SyntaxError && ! this . options . strict ) {
//fire error event
this . fire ( {
type : "error" ,
error : ex ,
message : ex . message ,
line : ex . line ,
col : ex . col
} ) ;
//see if there's another declaration
tt = tokenStream . advance ( [ Tokens . SEMICOLON , Tokens . RBRACE ] ) ;
if ( tt === Tokens . SEMICOLON ) {
//if there's a semicolon, then there might be another declaration
this . _readDeclarations ( false , readMargins ) ;
} else if ( tt !== Tokens . RBRACE ) {
//if there's a right brace, the rule is finished so don't do anything
//otherwise, rethrow the error because it wasn't handled properly
throw ex ;
}
} else {
//not a syntax error, rethrow it
throw ex ;
}
}
} ,
/ * *
* In some cases , you can end up with two white space tokens in a
* row . Instead of making a change in every function that looks for
* white space , this function is used to match as much white space
* as necessary .
* @ method _readWhitespace
* @ return { String } The white space if found , empty string if not .
* @ private
* /
_readWhitespace : function ( ) {
var tokenStream = this . _tokenStream ,
ws = "" ;
while ( tokenStream . match ( Tokens . S ) ) {
ws += tokenStream . token ( ) . value ;
}
return ws ;
} ,
/ * *
* Throws an error when an unexpected token is found .
* @ param { Object } token The token that was found .
* @ method _unexpectedToken
* @ return { void }
* @ private
* /
_unexpectedToken : function ( token ) {
throw new SyntaxError ( "Unexpected token '" + token . value + "' at line " + token . startLine + ", col " + token . startCol + "." , token . startLine , token . startCol ) ;
} ,
/ * *
* Helper method used for parsing subparts of a style sheet .
* @ return { void }
* @ method _verifyEnd
* @ private
* /
_verifyEnd : function ( ) {
if ( this . _tokenStream . LA ( 1 ) !== Tokens . EOF ) {
this . _unexpectedToken ( this . _tokenStream . LT ( 1 ) ) ;
}
} ,
//-----------------------------------------------------------------
// Validation methods
//-----------------------------------------------------------------
_validateProperty : function ( property , value ) {
Validation . validate ( property , value ) ;
} ,
//-----------------------------------------------------------------
// Parsing methods
//-----------------------------------------------------------------
parse : function ( input ) {
this . _tokenStream = new TokenStream ( input , Tokens ) ;
this . _stylesheet ( ) ;
} ,
parseStyleSheet : function ( input ) {
//just passthrough
return this . parse ( input ) ;
} ,
parseMediaQuery : function ( input ) {
this . _tokenStream = new TokenStream ( input , Tokens ) ;
var result = this . _media _query ( ) ;
//if there's anything more, then it's an invalid selector
this . _verifyEnd ( ) ;
//otherwise return result
return result ;
} ,
/ * *
* Parses a property value ( everything after the semicolon ) .
* @ return { parserlib . css . PropertyValue } The property value .
* @ throws parserlib . util . SyntaxError If an unexpected token is found .
* @ method parserPropertyValue
* /
parsePropertyValue : function ( input ) {
this . _tokenStream = new TokenStream ( input , Tokens ) ;
this . _readWhitespace ( ) ;
var result = this . _expr ( ) ;
//okay to have a trailing white space
this . _readWhitespace ( ) ;
//if there's anything more, then it's an invalid selector
this . _verifyEnd ( ) ;
//otherwise return result
return result ;
} ,
/ * *
* Parses a complete CSS rule , including selectors and
* properties .
* @ param { String } input The text to parser .
* @ return { Boolean } True if the parse completed successfully , false if not .
* @ method parseRule
* /
parseRule : function ( input ) {
this . _tokenStream = new TokenStream ( input , Tokens ) ;
//skip any leading white space
this . _readWhitespace ( ) ;
var result = this . _ruleset ( ) ;
//skip any trailing white space
this . _readWhitespace ( ) ;
//if there's anything more, then it's an invalid selector
this . _verifyEnd ( ) ;
//otherwise return result
return result ;
} ,
/ * *
* Parses a single CSS selector ( no comma )
* @ param { String } input The text to parse as a CSS selector .
* @ return { Selector } An object representing the selector .
* @ throws parserlib . util . SyntaxError If an unexpected token is found .
* @ method parseSelector
* /
parseSelector : function ( input ) {
this . _tokenStream = new TokenStream ( input , Tokens ) ;
//skip any leading white space
this . _readWhitespace ( ) ;
var result = this . _selector ( ) ;
//skip any trailing white space
this . _readWhitespace ( ) ;
//if there's anything more, then it's an invalid selector
this . _verifyEnd ( ) ;
//otherwise return result
return result ;
} ,
/ * *
* Parses an HTML style attribute : a set of CSS declarations
* separated by semicolons .
* @ param { String } input The text to parse as a style attribute
* @ return { void }
* @ method parseStyleAttribute
* /
parseStyleAttribute : function ( input ) {
input += "}" ; // for error recovery in _readDeclarations()
this . _tokenStream = new TokenStream ( input , Tokens ) ;
this . _readDeclarations ( ) ;
}
} ;
//copy over onto prototype
for ( prop in additions ) {
if ( Object . prototype . hasOwnProperty . call ( additions , prop ) ) {
proto [ prop ] = additions [ prop ] ;
}
}
return proto ;
} ( ) ;
/ *
nth
: S * [ [ '-' | '+' ] ? INTEGER ? { N } [ S * [ '-' | '+' ] S * INTEGER ] ? |
[ '-' | '+' ] ? INTEGER | { O } { D } { D } | { E } { V } { E } { N } ] S *
;
* /
} , { "../util/EventTarget" : 23 , "../util/SyntaxError" : 25 , "../util/SyntaxUnit" : 26 , "./Combinator" : 2 , "./MediaFeature" : 4 , "./MediaQuery" : 5 , "./PropertyName" : 8 , "./PropertyValue" : 9 , "./PropertyValuePart" : 11 , "./Selector" : 13 , "./SelectorPart" : 14 , "./SelectorSubPart" : 15 , "./TokenStream" : 17 , "./Tokens" : 18 , "./Validation" : 19 } ] , 7 : [ function ( require , module , exports ) {
"use strict" ;
/* exported Properties */
var Properties = module . exports = {
_ _proto _ _ : null ,
//A
"align-items" : "flex-start | flex-end | center | baseline | stretch" ,
"align-content" : "flex-start | flex-end | center | space-between | space-around | stretch" ,
"align-self" : "auto | flex-start | flex-end | center | baseline | stretch" ,
"all" : "initial | inherit | unset" ,
"-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch" ,
"-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch" ,
"-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch" ,
"alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>" ,
"alignment-baseline" : "auto | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical" ,
"animation" : 1 ,
"animation-delay" : "<time>#" ,
"animation-direction" : "<single-animation-direction>#" ,
"animation-duration" : "<time>#" ,
"animation-fill-mode" : "[ none | forwards | backwards | both ]#" ,
"animation-iteration-count" : "[ <number> | infinite ]#" ,
"animation-name" : "[ none | <single-animation-name> ]#" ,
"animation-play-state" : "[ running | paused ]#" ,
"animation-timing-function" : 1 ,
//vendor prefixed
"-moz-animation-delay" : "<time>#" ,
"-moz-animation-direction" : "[ normal | alternate ]#" ,
"-moz-animation-duration" : "<time>#" ,
"-moz-animation-iteration-count" : "[ <number> | infinite ]#" ,
"-moz-animation-name" : "[ none | <single-animation-name> ]#" ,
"-moz-animation-play-state" : "[ running | paused ]#" ,
"-ms-animation-delay" : "<time>#" ,
"-ms-animation-direction" : "[ normal | alternate ]#" ,
"-ms-animation-duration" : "<time>#" ,
"-ms-animation-iteration-count" : "[ <number> | infinite ]#" ,
"-ms-animation-name" : "[ none | <single-animation-name> ]#" ,
"-ms-animation-play-state" : "[ running | paused ]#" ,
"-webkit-animation-delay" : "<time>#" ,
"-webkit-animation-direction" : "[ normal | alternate ]#" ,
"-webkit-animation-duration" : "<time>#" ,
"-webkit-animation-fill-mode" : "[ none | forwards | backwards | both ]#" ,
"-webkit-animation-iteration-count" : "[ <number> | infinite ]#" ,
"-webkit-animation-name" : "[ none | <single-animation-name> ]#" ,
"-webkit-animation-play-state" : "[ running | paused ]#" ,
"-o-animation-delay" : "<time>#" ,
"-o-animation-direction" : "[ normal | alternate ]#" ,
"-o-animation-duration" : "<time>#" ,
"-o-animation-iteration-count" : "[ <number> | infinite ]#" ,
"-o-animation-name" : "[ none | <single-animation-name> ]#" ,
"-o-animation-play-state" : "[ running | paused ]#" ,
"appearance" : "none | auto" ,
"-moz-appearance" : "none | button | button-arrow-down | button-arrow-next | button-arrow-previous | button-arrow-up | button-bevel | button-focus | caret | checkbox | checkbox-container | checkbox-label | checkmenuitem | dualbutton | groupbox | listbox | listitem | menuarrow | menubar | menucheckbox | menuimage | menuitem | menuitemtext | menulist | menulist-button | menulist-text | menulist-textfield | menupopup | menuradio | menuseparator | meterbar | meterchunk | progressbar | progressbar-vertical | progresschunk | progresschunk-vertical | radio | radio-container | radio-label | radiomenuitem | range | range-thumb | resizer | resizerpanel | scale-horizontal | scalethumbend | scalethumb-horizontal | scalethumbstart | scalethumbtick | scalethumb-vertical | scale-vertical | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical | searchfield | separator | sheet | spinner | spinner-downbutton | spinner-textfield | spinner-upbutton | splitter | statusbar | statusbarpanel | tab | tabpanel | tabpanels | tab-scroll-arrow-back | tab-scroll-arrow-forward | textfield | textfield-multiline | toolbar | toolbarbutton | toolbarbutton-dropdown | toolbargripper | toolbox | tooltip | treeheader | treeheadercell | treeheadersortarrow | treeitem | treeline | treetwisty | treetwistyopen | treeview | -moz-mac-unified-toolbar | -moz-win-borderless-glass | -moz-win-browsertabbar-toolbox | -moz-win-communicationstext | -moz-win-communications-toolbox | -moz-win-exclude-glass | -moz-win-glass | -moz-win-mediatext | -moz-win-media-toolbox | -moz-window-button-box | -moz-window-button-box-maximized | -moz-window-button-close | -moz-window-button-maximize | -moz-window-button-minimize | -moz-window-button-restore | -moz-window-frame-bottom | -moz-window-frame-left | -moz-window-frame-right | -moz-window-titlebar | -moz-window-titlebar-maximized" ,
"-ms-appearance" : "none | icon | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal" ,
"-webkit-appearance" : "none | button | button-bevel | caps-lock-indicator | caret | checkbox | default-button | listbox | listitem | media-fullscreen-button | media-mute-button | media-play-button | media-seek-back-button | media-seek-forward-button | media-slider | media-sliderthumb | menulist | menulist-button | menulist-text | menulist-textfield | push-button | radio | searchfield | searchfield-cancel-button | searchfield-decoration | searchfield-results-button | searchfield-results-decoration | slider-horizontal | slider-vertical | sliderthumb-horizontal | sliderthumb-vertical | square-button | textarea | textfield | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbargripper-horizontal | scrollbargripper-vertical | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical" ,
"-o-appearance" : "none | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal" ,
"azimuth" : "<azimuth>" ,
//B
"backface-visibility" : "visible | hidden" ,
"background" : 1 ,
"background-attachment" : "<attachment>#" ,
"background-clip" : "<box>#" ,
"background-color" : "<color>" ,
"background-image" : "<bg-image>#" ,
"background-origin" : "<box>#" ,
"background-position" : "<bg-position>" ,
"background-repeat" : "<repeat-style>#" ,
"background-size" : "<bg-size>#" ,
"baseline-shift" : "baseline | sub | super | <percentage> | <length>" ,
"behavior" : 1 ,
"binding" : 1 ,
"bleed" : "<length>" ,
"bookmark-label" : "<content> | <attr> | <string>" ,
"bookmark-level" : "none | <integer>" ,
"bookmark-state" : "open | closed" ,
"bookmark-target" : "none | <uri> | <attr>" ,
"border" : "<border-width> || <border-style> || <color>" ,
"border-bottom" : "<border-width> || <border-style> || <color>" ,
"border-bottom-color" : "<color>" ,
"border-bottom-left-radius" : "<x-one-radius>" ,
"border-bottom-right-radius" : "<x-one-radius>" ,
"border-bottom-style" : "<border-style>" ,
"border-bottom-width" : "<border-width>" ,
"border-collapse" : "collapse | separate" ,
"border-color" : "<color>{1,4}" ,
"border-image" : 1 ,
"border-image-outset" : "[ <length> | <number> ]{1,4}" ,
"border-image-repeat" : "[ stretch | repeat | round ]{1,2}" ,
"border-image-slice" : "<border-image-slice>" ,
"border-image-source" : "<image> | none" ,
"border-image-width" : "[ <length> | <percentage> | <number> | auto ]{1,4}" ,
"border-left" : "<border-width> || <border-style> || <color>" ,
"border-left-color" : "<color>" ,
"border-left-style" : "<border-style>" ,
"border-left-width" : "<border-width>" ,
"border-radius" : "<border-radius>" ,
"border-right" : "<border-width> || <border-style> || <color>" ,
"border-right-color" : "<color>" ,
"border-right-style" : "<border-style>" ,
"border-right-width" : "<border-width>" ,
"border-spacing" : "<length>{1,2}" ,
"border-style" : "<border-style>{1,4}" ,
"border-top" : "<border-width> || <border-style> || <color>" ,
"border-top-color" : "<color>" ,
"border-top-left-radius" : "<x-one-radius>" ,
"border-top-right-radius" : "<x-one-radius>" ,
"border-top-style" : "<border-style>" ,
"border-top-width" : "<border-width>" ,
"border-width" : "<border-width>{1,4}" ,
"bottom" : "<margin-width>" ,
"-moz-box-align" : "start | end | center | baseline | stretch" ,
"-moz-box-decoration-break" : "slice | clone" ,
"-moz-box-direction" : "normal | reverse" ,
"-moz-box-flex" : "<number>" ,
"-moz-box-flex-group" : "<integer>" ,
"-moz-box-lines" : "single | multiple" ,
"-moz-box-ordinal-group" : "<integer>" ,
"-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis" ,
"-moz-box-pack" : "start | end | center | justify" ,
"-o-box-decoration-break" : "slice | clone" ,
"-webkit-box-align" : "start | end | center | baseline | stretch" ,
"-webkit-box-decoration-break" : "slice | clone" ,
"-webkit-box-direction" : "normal | reverse" ,
"-webkit-box-flex" : "<number>" ,
"-webkit-box-flex-group" : "<integer>" ,
"-webkit-box-lines" : "single | multiple" ,
"-webkit-box-ordinal-group" : "<integer>" ,
"-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis" ,
"-webkit-box-pack" : "start | end | center | justify" ,
"box-decoration-break" : "slice | clone" ,
"box-shadow" : "<box-shadow>" ,
"box-sizing" : "content-box | border-box" ,
"break-after" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column" ,
"break-before" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column" ,
"break-inside" : "auto | avoid | avoid-page | avoid-column" ,
//C
"caption-side" : "top | bottom" ,
"clear" : "none | right | left | both" ,
"clip" : "<shape> | auto" ,
"-webkit-clip-path" : "<clip-source> | <clip-path> | none" ,
"clip-path" : "<clip-source> | <clip-path> | none" ,
"clip-rule" : "nonzero | evenodd" ,
"color" : "<color>" ,
"color-interpolation" : "auto | sRGB | linearRGB" ,
"color-interpolation-filters" : "auto | sRGB | linearRGB" ,
"color-profile" : 1 ,
"color-rendering" : "auto | optimizeSpeed | optimizeQuality" ,
"column-count" : "<integer> | auto" , //https://www.w3.org/TR/css3-multicol/
"column-fill" : "auto | balance" ,
"column-gap" : "<length> | normal" ,
"column-rule" : "<border-width> || <border-style> || <color>" ,
"column-rule-color" : "<color>" ,
"column-rule-style" : "<border-style>" ,
"column-rule-width" : "<border-width>" ,
"column-span" : "none | all" ,
"column-width" : "<length> | auto" ,
"columns" : 1 ,
"content" : 1 ,
"counter-increment" : 1 ,
"counter-reset" : 1 ,
"crop" : "<shape> | auto" ,
"cue" : "cue-after | cue-before" ,
"cue-after" : 1 ,
"cue-before" : 1 ,
"cursor" : 1 ,
//D
"direction" : "ltr | rtl" ,
"display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | run-in | ruby | ruby-base | ruby-text | ruby-base-container | ruby-text-container | contents | none | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex" ,
"dominant-baseline" : "auto | use-script | no-change | reset-size | ideographic | alphabetic | hanging | mathematical | central | middle | text-after-edge | text-before-edge" ,
"drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>" ,
"drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical" ,
"drop-initial-before-adjust" : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>" ,
"drop-initial-before-align" : "caps-height | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical" ,
"drop-initial-size" : "auto | line | <length> | <percentage>" ,
"drop-initial-value" : "<integer>" ,
//E
"elevation" : "<angle> | below | level | above | higher | lower" ,
"empty-cells" : "show | hide" ,
"enable-background" : 1 ,
//F
"fill" : "<paint>" ,
"fill-opacity" : "<opacity-value>" ,
"fill-rule" : "nonzero | evenodd" ,
"filter" : "<filter-function-list> | none" ,
"fit" : "fill | hidden | meet | slice" ,
"fit-position" : 1 ,
"flex" : "<flex>" ,
"flex-basis" : "<width>" ,
"flex-direction" : "row | row-reverse | column | column-reverse" ,
"flex-flow" : "<flex-direction> || <flex-wrap>" ,
"flex-grow" : "<number>" ,
"flex-shrink" : "<number>" ,
"flex-wrap" : "nowrap | wrap | wrap-reverse" ,
"-webkit-flex" : "<flex>" ,
"-webkit-flex-basis" : "<width>" ,
"-webkit-flex-direction" : "row | row-reverse | column | column-reverse" ,
"-webkit-flex-flow" : "<flex-direction> || <flex-wrap>" ,
"-webkit-flex-grow" : "<number>" ,
"-webkit-flex-shrink" : "<number>" ,
"-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse" ,
"-ms-flex" : "<flex>" ,
"-ms-flex-align" : "start | end | center | stretch | baseline" ,
"-ms-flex-direction" : "row | row-reverse | column | column-reverse" ,
"-ms-flex-order" : "<number>" ,
"-ms-flex-pack" : "start | end | center | justify" ,
"-ms-flex-wrap" : "nowrap | wrap | wrap-reverse" ,
"float" : "left | right | none" ,
"float-offset" : 1 ,
"flood-color" : 1 ,
"flood-opacity" : "<opacity-value>" ,
"font" : "<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar" ,
"font-family" : "<font-family>" ,
"font-feature-settings" : "<feature-tag-value> | normal" ,
"font-kerning" : "auto | normal | none" ,
"font-size" : "<font-size>" ,
"font-size-adjust" : "<number> | none" ,
"font-stretch" : "<font-stretch>" ,
"font-style" : "<font-style>" ,
"font-variant" : "<font-variant> | normal | none" ,
"font-variant-alternates" : "<font-variant-alternates> | normal" ,
"font-variant-caps" : "<font-variant-caps> | normal" ,
"font-variant-east-asian" : "<font-variant-east-asian> | normal" ,
"font-variant-ligatures" : "<font-variant-ligatures> | normal | none" ,
"font-variant-numeric" : "<font-variant-numeric> | normal" ,
"font-variant-position" : "normal | sub | super" ,
"font-weight" : "<font-weight>" ,
//G
"glyph-orientation-horizontal" : "<glyph-angle>" ,
"glyph-orientation-vertical" : "auto | <glyph-angle>" ,
"grid" : 1 ,
"grid-area" : 1 ,
"grid-auto-columns" : 1 ,
"grid-auto-flow" : 1 ,
"grid-auto-position" : 1 ,
"grid-auto-rows" : 1 ,
"grid-cell-stacking" : "columns | rows | layer" ,
"grid-column" : 1 ,
"grid-columns" : 1 ,
"grid-column-align" : "start | end | center | stretch" ,
"grid-column-sizing" : 1 ,
"grid-column-start" : 1 ,
"grid-column-end" : 1 ,
"grid-column-span" : "<integer>" ,
"grid-flow" : "none | rows | columns" ,
"grid-layer" : "<integer>" ,
"grid-row" : 1 ,
"grid-rows" : 1 ,
"grid-row-align" : "start | end | center | stretch" ,
"grid-row-start" : 1 ,
"grid-row-end" : 1 ,
"grid-row-span" : "<integer>" ,
"grid-row-sizing" : 1 ,
"grid-template" : 1 ,
"grid-template-areas" : 1 ,
"grid-template-columns" : 1 ,
"grid-template-rows" : 1 ,
//H
"hanging-punctuation" : 1 ,
"height" : "<margin-width> | <content-sizing>" ,
"hyphenate-after" : "<integer> | auto" ,
"hyphenate-before" : "<integer> | auto" ,
"hyphenate-character" : "<string> | auto" ,
"hyphenate-lines" : "no-limit | <integer>" ,
"hyphenate-resource" : 1 ,
"hyphens" : "none | manual | auto" ,
//I
"icon" : 1 ,
"image-orientation" : "angle | auto" ,
"image-rendering" : "auto | optimizeSpeed | optimizeQuality" ,
"image-resolution" : 1 ,
"ime-mode" : "auto | normal | active | inactive | disabled" ,
"inline-box-align" : "last | <integer>" ,
//J
"justify-content" : "flex-start | flex-end | center | space-between | space-around" ,
"-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around" ,
//K
"kerning" : "auto | <length>" ,
//L
"left" : "<margin-width>" ,
"letter-spacing" : "<length> | normal" ,
"line-height" : "<line-height>" ,
"line-break" : "auto | loose | normal | strict" ,
"line-stacking" : 1 ,
"line-stacking-ruby" : "exclude-ruby | include-ruby" ,
"line-stacking-shift" : "consider-shifts | disregard-shifts" ,
"line-stacking-strategy" : "inline-line-height | block-line-height | max-height | grid-height" ,
"list-style" : 1 ,
"list-style-image" : "<uri> | none" ,
"list-style-position" : "inside | outside" ,
"list-style-type" : "disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha | none" ,
//M
"margin" : "<margin-width>{1,4}" ,
"margin-bottom" : "<margin-width>" ,
"margin-left" : "<margin-width>" ,
"margin-right" : "<margin-width>" ,
"margin-top" : "<margin-width>" ,
"mark" : 1 ,
"mark-after" : 1 ,
"mark-before" : 1 ,
"marker" : 1 ,
"marker-end" : 1 ,
"marker-mid" : 1 ,
"marker-start" : 1 ,
"marks" : 1 ,
"marquee-direction" : 1 ,
"marquee-play-count" : 1 ,
"marquee-speed" : 1 ,
"marquee-style" : 1 ,
"mask" : 1 ,
"max-height" : "<length> | <percentage> | <content-sizing> | none" ,
"max-width" : "<length> | <percentage> | <content-sizing> | none" ,
"min-height" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats" ,
"min-width" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats" ,
"move-to" : 1 ,
//N
"nav-down" : 1 ,
"nav-index" : 1 ,
"nav-left" : 1 ,
"nav-right" : 1 ,
"nav-up" : 1 ,
//O
"object-fit" : "fill | contain | cover | none | scale-down" ,
"object-position" : "<position>" ,
"opacity" : "<opacity-value>" ,
"order" : "<integer>" ,
"-webkit-order" : "<integer>" ,
"orphans" : "<integer>" ,
"outline" : 1 ,
"outline-color" : "<color> | invert" ,
"outline-offset" : 1 ,
"outline-style" : "<border-style>" ,
"outline-width" : "<border-width>" ,
"overflow" : "visible | hidden | scroll | auto" ,
"overflow-style" : 1 ,
"overflow-wrap" : "normal | break-word" ,
"overflow-x" : 1 ,
"overflow-y" : 1 ,
//P
"padding" : "<padding-width>{1,4}" ,
"padding-bottom" : "<padding-width>" ,
"padding-left" : "<padding-width>" ,
"padding-right" : "<padding-width>" ,
"padding-top" : "<padding-width>" ,
"page" : 1 ,
"page-break-after" : "auto | always | avoid | left | right" ,
"page-break-before" : "auto | always | avoid | left | right" ,
"page-break-inside" : "auto | avoid" ,
"page-policy" : 1 ,
"pause" : 1 ,
"pause-after" : 1 ,
"pause-before" : 1 ,
"perspective" : 1 ,
"perspective-origin" : 1 ,
"phonemes" : 1 ,
"pitch" : 1 ,
"pitch-range" : 1 ,
"play-during" : 1 ,
"pointer-events" : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all" ,
"position" : "static | relative | absolute | fixed" ,
"presentation-level" : 1 ,
"punctuation-trim" : 1 ,
//Q
"quotes" : 1 ,
//R
"rendering-intent" : 1 ,
"resize" : 1 ,
"rest" : 1 ,
"rest-after" : 1 ,
"rest-before" : 1 ,
"richness" : 1 ,
"right" : "<margin-width>" ,
"rotation" : 1 ,
"rotation-point" : 1 ,
"ruby-align" : 1 ,
"ruby-overhang" : 1 ,
"ruby-position" : 1 ,
"ruby-span" : 1 ,
//S
"shape-rendering" : "auto | optimizeSpeed | crispEdges | geometricPrecision" ,
"size" : 1 ,
"speak" : "normal | none | spell-out" ,
"speak-header" : "once | always" ,
"speak-numeral" : "digits | continuous" ,
"speak-punctuation" : "code | none" ,
"speech-rate" : 1 ,
"src" : 1 ,
"stop-color" : 1 ,
"stop-opacity" : "<opacity-value>" ,
"stress" : 1 ,
"string-set" : 1 ,
"stroke" : "<paint>" ,
"stroke-dasharray" : "none | <dasharray>" ,
"stroke-dashoffset" : "<percentage> | <length>" ,
"stroke-linecap" : "butt | round | square" ,
"stroke-linejoin" : "miter | round | bevel" ,
"stroke-miterlimit" : "<miterlimit>" ,
"stroke-opacity" : "<opacity-value>" ,
"stroke-width" : "<percentage> | <length>" ,
"table-layout" : "auto | fixed" ,
"tab-size" : "<integer> | <length>" ,
"target" : 1 ,
"target-name" : 1 ,
"target-new" : 1 ,
"target-position" : 1 ,
"text-align" : "left | right | center | justify | match-parent | start | end" ,
"text-align-last" : 1 ,
"text-anchor" : "start | middle | end" ,
"text-decoration" : "<text-decoration-line> || <text-decoration-style> || <text-decoration-color>" ,
"text-decoration-color" : "<text-decoration-color>" ,
"text-decoration-line" : "<text-decoration-line>" ,
"text-decoration-style" : "<text-decoration-style>" ,
"text-emphasis" : 1 ,
"text-height" : 1 ,
"text-indent" : "<length> | <percentage>" ,
"text-justify" : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida" ,
"text-outline" : 1 ,
"text-overflow" : 1 ,
"text-rendering" : "auto | optimizeSpeed | optimizeLegibility | geometricPrecision" ,
"text-shadow" : 1 ,
"text-transform" : "capitalize | uppercase | lowercase | none" ,
"text-wrap" : "normal | none | avoid" ,
"top" : "<margin-width>" ,
"-ms-touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation" ,
"touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation" ,
"transform" : 1 ,
"transform-origin" : 1 ,
"transform-style" : 1 ,
"transition" : 1 ,
"transition-delay" : 1 ,
"transition-duration" : 1 ,
"transition-property" : 1 ,
"transition-timing-function" : 1 ,
//U
"unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext" ,
"user-modify" : "read-only | read-write | write-only" ,
"user-select" : "none | text | toggle | element | elements | all" ,
//V
"vertical-align" : "auto | use-script | baseline | sub | super | top | text-top | central | middle | bottom | text-bottom | <percentage> | <length>" ,
"visibility" : "visible | hidden | collapse" ,
"voice-balance" : 1 ,
"voice-duration" : 1 ,
"voice-family" : 1 ,
"voice-pitch" : 1 ,
"voice-pitch-range" : 1 ,
"voice-rate" : 1 ,
"voice-stress" : 1 ,
"voice-volume" : 1 ,
"volume" : 1 ,
//W
"white-space" : "normal | pre | nowrap | pre-wrap | pre-line | -pre-wrap | -o-pre-wrap | -moz-pre-wrap | -hp-pre-wrap" , // https://perishablepress.com/wrapping-content/
"white-space-collapse" : 1 ,
"widows" : "<integer>" ,
"width" : "<length> | <percentage> | <content-sizing> | auto" ,
"will-change" : "<will-change>" ,
"word-break" : "normal | keep-all | break-all" ,
"word-spacing" : "<length> | normal" ,
"word-wrap" : "normal | break-word" ,
"writing-mode" : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb" ,
//Z
"z-index" : "<integer> | auto" ,
"zoom" : "<number> | <percentage> | normal"
} ;
} , { } ] , 8 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = PropertyName ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Parser = require ( "./Parser" ) ;
/ * *
* Represents a selector combinator ( whitespace , + , > ) .
* @ namespace parserlib . css
* @ class PropertyName
* @ extends parserlib . util . SyntaxUnit
* @ constructor
* @ param { String } text The text representation of the unit .
* @ param { String } hack The type of IE hack applied ( "*" , "_" , or null ) .
* @ param { int } line The line of text on which the unit resides .
* @ param { int } col The column of text on which the unit resides .
* /
function PropertyName ( text , hack , line , col ) {
SyntaxUnit . call ( this , text , line , col , Parser . PROPERTY _NAME _TYPE ) ;
/ * *
* The type of IE hack applied ( "*" , "_" , or null ) .
* @ type String
* @ property hack
* /
this . hack = hack ;
}
PropertyName . prototype = new SyntaxUnit ( ) ;
PropertyName . prototype . constructor = PropertyName ;
PropertyName . prototype . toString = function ( ) {
return ( this . hack ? this . hack : "" ) + this . text ;
} ;
} , { "../util/SyntaxUnit" : 26 , "./Parser" : 6 } ] , 9 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = PropertyValue ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Parser = require ( "./Parser" ) ;
/ * *
* Represents a single part of a CSS property value , meaning that it represents
* just everything single part between ":" and ";" . If there are multiple values
* separated by commas , this type represents just one of the values .
* @ param { String [ ] } parts An array of value parts making up this value .
* @ param { int } line The line of text on which the unit resides .
* @ param { int } col The column of text on which the unit resides .
* @ namespace parserlib . css
* @ class PropertyValue
* @ extends parserlib . util . SyntaxUnit
* @ constructor
* /
function PropertyValue ( parts , line , col ) {
SyntaxUnit . call ( this , parts . join ( " " ) , line , col , Parser . PROPERTY _VALUE _TYPE ) ;
/ * *
* The parts that make up the selector .
* @ type Array
* @ property parts
* /
this . parts = parts ;
}
PropertyValue . prototype = new SyntaxUnit ( ) ;
PropertyValue . prototype . constructor = PropertyValue ;
} , { "../util/SyntaxUnit" : 26 , "./Parser" : 6 } ] , 10 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = PropertyValueIterator ;
/ * *
* A utility class that allows for easy iteration over the various parts of a
* property value .
* @ param { parserlib . css . PropertyValue } value The property value to iterate over .
* @ namespace parserlib . css
* @ class PropertyValueIterator
* @ constructor
* /
function PropertyValueIterator ( value ) {
/ * *
* Iterator value
* @ type int
* @ property _i
* @ private
* /
this . _i = 0 ;
/ * *
* The parts that make up the value .
* @ type Array
* @ property _parts
* @ private
* /
this . _parts = value . parts ;
/ * *
* Keeps track of bookmarks along the way .
* @ type Array
* @ property _marks
* @ private
* /
this . _marks = [ ] ;
/ * *
* Holds the original property value .
* @ type parserlib . css . PropertyValue
* @ property value
* /
this . value = value ;
}
/ * *
* Returns the total number of parts in the value .
* @ return { int } The total number of parts in the value .
* @ method count
* /
PropertyValueIterator . prototype . count = function ( ) {
return this . _parts . length ;
} ;
/ * *
* Indicates if the iterator is positioned at the first item .
* @ return { Boolean } True if positioned at first item , false if not .
* @ method isFirst
* /
PropertyValueIterator . prototype . isFirst = function ( ) {
return this . _i === 0 ;
} ;
/ * *
* Indicates if there are more parts of the property value .
* @ return { Boolean } True if there are more parts , false if not .
* @ method hasNext
* /
PropertyValueIterator . prototype . hasNext = function ( ) {
return this . _i < this . _parts . length ;
} ;
/ * *
* Marks the current spot in the iteration so it can be restored to
* later on .
* @ return { void }
* @ method mark
* /
PropertyValueIterator . prototype . mark = function ( ) {
this . _marks . push ( this . _i ) ;
} ;
/ * *
* Returns the next part of the property value or null if there is no next
* part . Does not move the internal counter forward .
* @ return { parserlib . css . PropertyValuePart } The next part of the property value or null if there is no next
* part .
* @ method peek
* /
PropertyValueIterator . prototype . peek = function ( count ) {
return this . hasNext ( ) ? this . _parts [ this . _i + ( count || 0 ) ] : null ;
} ;
/ * *
* Returns the next part of the property value or null if there is no next
* part .
* @ return { parserlib . css . PropertyValuePart } The next part of the property value or null if there is no next
* part .
* @ method next
* /
PropertyValueIterator . prototype . next = function ( ) {
return this . hasNext ( ) ? this . _parts [ this . _i ++ ] : null ;
} ;
/ * *
* Returns the previous part of the property value or null if there is no
* previous part .
* @ return { parserlib . css . PropertyValuePart } The previous part of the
* property value or null if there is no previous part .
* @ method previous
* /
PropertyValueIterator . prototype . previous = function ( ) {
return this . _i > 0 ? this . _parts [ -- this . _i ] : null ;
} ;
/ * *
* Restores the last saved bookmark .
* @ return { void }
* @ method restore
* /
PropertyValueIterator . prototype . restore = function ( ) {
if ( this . _marks . length ) {
this . _i = this . _marks . pop ( ) ;
}
} ;
/ * *
* Drops the last saved bookmark .
* @ return { void }
* @ method drop
* /
PropertyValueIterator . prototype . drop = function ( ) {
this . _marks . pop ( ) ;
} ;
} , { } ] , 11 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = PropertyValuePart ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Colors = require ( "./Colors" ) ;
var Parser = require ( "./Parser" ) ;
var Tokens = require ( "./Tokens" ) ;
/ * *
* Represents a single part of a CSS property value , meaning that it represents
* just one part of the data between ":" and ";" .
* @ param { String } text The text representation of the unit .
* @ param { int } line The line of text on which the unit resides .
* @ param { int } col The column of text on which the unit resides .
* @ namespace parserlib . css
* @ class PropertyValuePart
* @ extends parserlib . util . SyntaxUnit
* @ constructor
* /
function PropertyValuePart ( text , line , col , optionalHint ) {
var hint = optionalHint || { } ;
SyntaxUnit . call ( this , text , line , col , Parser . PROPERTY _VALUE _PART _TYPE ) ;
/ * *
* Indicates the type of value unit .
* @ type String
* @ property type
* /
this . type = "unknown" ;
//figure out what type of data it is
var temp ;
//it is a measurement?
if ( /^([+\-]?[\d\.]+)([a-z]+)$/i . test ( text ) ) { //dimension
this . type = "dimension" ;
this . value = + RegExp . $1 ;
this . units = RegExp . $2 ;
//try to narrow down
switch ( this . units . toLowerCase ( ) ) {
case "em" :
case "rem" :
case "ex" :
case "px" :
case "cm" :
case "mm" :
case "in" :
case "pt" :
case "pc" :
case "ch" :
case "vh" :
case "vw" :
case "vmax" :
case "vmin" :
this . type = "length" ;
break ;
case "fr" :
this . type = "grid" ;
break ;
case "deg" :
case "rad" :
case "grad" :
case "turn" :
this . type = "angle" ;
break ;
case "ms" :
case "s" :
this . type = "time" ;
break ;
case "hz" :
case "khz" :
this . type = "frequency" ;
break ;
case "dpi" :
case "dpcm" :
this . type = "resolution" ;
break ;
//default
}
} else if ( /^([+\-]?[\d\.]+)%$/i . test ( text ) ) { //percentage
this . type = "percentage" ;
this . value = + RegExp . $1 ;
} else if ( /^([+\-]?\d+)$/i . test ( text ) ) { //integer
this . type = "integer" ;
this . value = + RegExp . $1 ;
} else if ( /^([+\-]?[\d\.]+)$/i . test ( text ) ) { //number
this . type = "number" ;
this . value = + RegExp . $1 ;
} else if ( /^#([a-f0-9]{3,6})/i . test ( text ) ) { //hexcolor
this . type = "color" ;
temp = RegExp . $1 ;
if ( temp . length === 3 ) {
this . red = parseInt ( temp . charAt ( 0 ) + temp . charAt ( 0 ) , 16 ) ;
this . green = parseInt ( temp . charAt ( 1 ) + temp . charAt ( 1 ) , 16 ) ;
this . blue = parseInt ( temp . charAt ( 2 ) + temp . charAt ( 2 ) , 16 ) ;
} else {
this . red = parseInt ( temp . substring ( 0 , 2 ) , 16 ) ;
this . green = parseInt ( temp . substring ( 2 , 4 ) , 16 ) ;
this . blue = parseInt ( temp . substring ( 4 , 6 ) , 16 ) ;
}
} else if ( /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i . test ( text ) ) { //rgb() color with absolute numbers
this . type = "color" ;
this . red = + RegExp . $1 ;
this . green = + RegExp . $2 ;
this . blue = + RegExp . $3 ;
} else if ( /^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i . test ( text ) ) { //rgb() color with percentages
this . type = "color" ;
this . red = + RegExp . $1 * 255 / 100 ;
this . green = + RegExp . $2 * 255 / 100 ;
this . blue = + RegExp . $3 * 255 / 100 ;
} else if ( /^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i . test ( text ) ) { //rgba() color with absolute numbers
this . type = "color" ;
this . red = + RegExp . $1 ;
this . green = + RegExp . $2 ;
this . blue = + RegExp . $3 ;
this . alpha = + RegExp . $4 ;
} else if ( /^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i . test ( text ) ) { //rgba() color with percentages
this . type = "color" ;
this . red = + RegExp . $1 * 255 / 100 ;
this . green = + RegExp . $2 * 255 / 100 ;
this . blue = + RegExp . $3 * 255 / 100 ;
this . alpha = + RegExp . $4 ;
} else if ( /^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i . test ( text ) ) { //hsl()
this . type = "color" ;
this . hue = + RegExp . $1 ;
this . saturation = + RegExp . $2 / 100 ;
this . lightness = + RegExp . $3 / 100 ;
} else if ( /^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i . test ( text ) ) { //hsla() color with percentages
this . type = "color" ;
this . hue = + RegExp . $1 ;
this . saturation = + RegExp . $2 / 100 ;
this . lightness = + RegExp . $3 / 100 ;
this . alpha = + RegExp . $4 ;
} else if ( /^url\(("([^\\"]|\\.)*")\)/i . test ( text ) ) { //URI
// generated by TokenStream.readURI, so always double-quoted.
this . type = "uri" ;
this . uri = PropertyValuePart . parseString ( RegExp . $1 ) ;
} else if ( /^([^\(]+)\(/i . test ( text ) ) {
this . type = "function" ;
this . name = RegExp . $1 ;
this . value = text ;
} else if ( /^"([^\n\r\f\\"]|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*"/i . test ( text ) ) { //double-quoted string
this . type = "string" ;
this . value = PropertyValuePart . parseString ( text ) ;
} else if ( /^'([^\n\r\f\\']|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*'/i . test ( text ) ) { //single-quoted string
this . type = "string" ;
this . value = PropertyValuePart . parseString ( text ) ;
} else if ( Colors [ text . toLowerCase ( ) ] ) { //named color
this . type = "color" ;
temp = Colors [ text . toLowerCase ( ) ] . substring ( 1 ) ;
this . red = parseInt ( temp . substring ( 0 , 2 ) , 16 ) ;
this . green = parseInt ( temp . substring ( 2 , 4 ) , 16 ) ;
this . blue = parseInt ( temp . substring ( 4 , 6 ) , 16 ) ;
} else if ( /^[,\/]$/ . test ( text ) ) {
this . type = "operator" ;
this . value = text ;
} else if ( /^-?[a-z_\u00A0-\uFFFF][a-z0-9\-_\u00A0-\uFFFF]*$/i . test ( text ) ) {
this . type = "identifier" ;
this . value = text ;
}
// There can be ambiguity with escape sequences in identifiers, as
// well as with "color" parts which are also "identifiers", so record
// an explicit hint when the token generating this PropertyValuePart
// was an identifier.
this . wasIdent = Boolean ( hint . ident ) ;
}
PropertyValuePart . prototype = new SyntaxUnit ( ) ;
PropertyValuePart . prototype . constructor = PropertyValuePart ;
/ * *
* Helper method to parse a CSS string .
* /
PropertyValuePart . parseString = function ( str ) {
str = str . slice ( 1 , - 1 ) ; // Strip surrounding single/double quotes
var replacer = function ( match , esc ) {
if ( /^(\n|\r\n|\r|\f)$/ . test ( esc ) ) {
return "" ;
}
var m = /^[0-9a-f]{1,6}/i . exec ( esc ) ;
if ( m ) {
var codePoint = parseInt ( m [ 0 ] , 16 ) ;
if ( String . fromCodePoint ) {
return String . fromCodePoint ( codePoint ) ;
} else {
// XXX No support for surrogates on old JavaScript engines.
return String . fromCharCode ( codePoint ) ;
}
}
return esc ;
} ;
return str . replace ( /\\(\r\n|[^\r0-9a-f]|[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)/ig ,
replacer ) ;
} ;
/ * *
* Helper method to serialize a CSS string .
* /
PropertyValuePart . serializeString = function ( value ) {
var replacer = function ( match , c ) {
if ( c === "\"" ) {
return "\\" + c ;
}
var cp = String . codePointAt ? String . codePointAt ( 0 ) :
// We only escape non-surrogate chars, so using charCodeAt
// is harmless here.
String . charCodeAt ( 0 ) ;
return "\\" + cp . toString ( 16 ) + " " ;
} ;
return "\"" + value . replace ( /["\r\n\f]/g , replacer ) + "\"" ;
} ;
/ * *
* Create a new syntax unit based solely on the given token .
* Convenience method for creating a new syntax unit when
* it represents a single token instead of multiple .
* @ param { Object } token The token object to represent .
* @ return { parserlib . css . PropertyValuePart } The object representing the token .
* @ static
* @ method fromToken
* /
PropertyValuePart . fromToken = function ( token ) {
var part = new PropertyValuePart ( token . value , token . startLine , token . startCol , {
// Tokens can have escaped characters that would fool the type
// identification in the PropertyValuePart constructor, so pass
// in a hint if this was an identifier.
ident : token . type === Tokens . IDENT
} ) ;
return part ;
} ;
} , { "../util/SyntaxUnit" : 26 , "./Colors" : 1 , "./Parser" : 6 , "./Tokens" : 18 } ] , 12 : [ function ( require , module , exports ) {
"use strict" ;
var Pseudos = module . exports = {
_ _proto _ _ : null ,
":first-letter" : 1 ,
":first-line" : 1 ,
":before" : 1 ,
":after" : 1
} ;
Pseudos . ELEMENT = 1 ;
Pseudos . CLASS = 2 ;
Pseudos . isElement = function ( pseudo ) {
return pseudo . indexOf ( "::" ) === 0 || Pseudos [ pseudo . toLowerCase ( ) ] === Pseudos . ELEMENT ;
} ;
} , { } ] , 13 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = Selector ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Parser = require ( "./Parser" ) ;
var Specificity = require ( "./Specificity" ) ;
/ * *
* Represents an entire single selector , including all parts but not
* including multiple selectors ( those separated by commas ) .
* @ namespace parserlib . css
* @ class Selector
* @ extends parserlib . util . SyntaxUnit
* @ constructor
* @ param { Array } parts Array of selectors parts making up this selector .
* @ param { int } line The line of text on which the unit resides .
* @ param { int } col The column of text on which the unit resides .
* /
function Selector ( parts , line , col ) {
SyntaxUnit . call ( this , parts . join ( " " ) , line , col , Parser . SELECTOR _TYPE ) ;
/ * *
* The parts that make up the selector .
* @ type Array
* @ property parts
* /
this . parts = parts ;
/ * *
* The specificity of the selector .
* @ type parserlib . css . Specificity
* @ property specificity
* /
this . specificity = Specificity . calculate ( this ) ;
}
Selector . prototype = new SyntaxUnit ( ) ;
Selector . prototype . constructor = Selector ;
} , { "../util/SyntaxUnit" : 26 , "./Parser" : 6 , "./Specificity" : 16 } ] , 14 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = SelectorPart ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Parser = require ( "./Parser" ) ;
/ * *
* Represents a single part of a selector string , meaning a single set of
* element name and modifiers . This does not include combinators such as
* spaces , + , > , etc .
* @ namespace parserlib . css
* @ class SelectorPart
* @ extends parserlib . util . SyntaxUnit
* @ constructor
* @ param { String } elementName The element name in the selector or null
* if there is no element name .
* @ param { Array } modifiers Array of individual modifiers for the element .
* May be empty if there are none .
* @ param { String } text The text representation of the unit .
* @ param { int } line The line of text on which the unit resides .
* @ param { int } col The column of text on which the unit resides .
* /
function SelectorPart ( elementName , modifiers , text , line , col ) {
SyntaxUnit . call ( this , text , line , col , Parser . SELECTOR _PART _TYPE ) ;
/ * *
* The tag name of the element to which this part
* of the selector affects .
* @ type String
* @ property elementName
* /
this . elementName = elementName ;
/ * *
* The parts that come after the element name , such as class names , IDs ,
* pseudo classes / elements , etc .
* @ type Array
* @ property modifiers
* /
this . modifiers = modifiers ;
}
SelectorPart . prototype = new SyntaxUnit ( ) ;
SelectorPart . prototype . constructor = SelectorPart ;
} , { "../util/SyntaxUnit" : 26 , "./Parser" : 6 } ] , 15 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = SelectorSubPart ;
var SyntaxUnit = require ( "../util/SyntaxUnit" ) ;
var Parser = require ( "./Parser" ) ;
/ * *
* Represents a selector modifier string , meaning a class name , element name ,
* element ID , pseudo rule , etc .
* @ namespace parserlib . css
* @ class SelectorSubPart
* @ extends parserlib . util . SyntaxUnit
* @ constructor
* @ param { String } text The text representation of the unit .
* @ param { String } type The type of selector modifier .
* @ param { int } line The line of text on which the unit resides .
* @ param { int } col The column of text on which the unit resides .
* /
function SelectorSubPart ( text , type , line , col ) {
SyntaxUnit . call ( this , text , line , col , Parser . SELECTOR _SUB _PART _TYPE ) ;
/ * *
* The type of modifier .
* @ type String
* @ property type
* /
this . type = type ;
/ * *
* Some subparts have arguments , this represents them .
* @ type Array
* @ property args
* /
this . args = [ ] ;
}
SelectorSubPart . prototype = new SyntaxUnit ( ) ;
SelectorSubPart . prototype . constructor = SelectorSubPart ;
} , { "../util/SyntaxUnit" : 26 , "./Parser" : 6 } ] , 16 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = Specificity ;
var Pseudos = require ( "./Pseudos" ) ;
var SelectorPart = require ( "./SelectorPart" ) ;
/ * *
* Represents a selector ' s specificity .
* @ namespace parserlib . css
* @ class Specificity
* @ constructor
* @ param { int } a Should be 1 for inline styles , zero for stylesheet styles
* @ param { int } b Number of ID selectors
* @ param { int } c Number of classes and pseudo classes
* @ param { int } d Number of element names and pseudo elements
* /
function Specificity ( a , b , c , d ) {
this . a = a ;
this . b = b ;
this . c = c ;
this . d = d ;
}
Specificity . prototype = {
constructor : Specificity ,
/ * *
* Compare this specificity to another .
* @ param { Specificity } other The other specificity to compare to .
* @ return { int } - 1 if the other specificity is larger , 1 if smaller , 0 if equal .
* @ method compare
* /
compare : function ( other ) {
var comps = [ "a" , "b" , "c" , "d" ] ,
i , len ;
for ( i = 0 , len = comps . length ; i < len ; i ++ ) {
if ( this [ comps [ i ] ] < other [ comps [ i ] ] ) {
return - 1 ;
} else if ( this [ comps [ i ] ] > other [ comps [ i ] ] ) {
return 1 ;
}
}
return 0 ;
} ,
/ * *
* Creates a numeric value for the specificity .
* @ return { int } The numeric value for the specificity .
* @ method valueOf
* /
valueOf : function ( ) {
return ( this . a * 1000 ) + ( this . b * 100 ) + ( this . c * 10 ) + this . d ;
} ,
/ * *
* Returns a string representation for specificity .
* @ return { String } The string representation of specificity .
* @ method toString
* /
toString : function ( ) {
return this . a + "," + this . b + "," + this . c + "," + this . d ;
}
} ;
/ * *
* Calculates the specificity of the given selector .
* @ param { parserlib . css . Selector } The selector to calculate specificity for .
* @ return { parserlib . css . Specificity } The specificity of the selector .
* @ static
* @ method calculate
* /
Specificity . calculate = function ( selector ) {
var i , len ,
part ,
b = 0 , c = 0 , d = 0 ;
function updateValues ( part ) {
var i , j , len , num ,
elementName = part . elementName ? part . elementName . text : "" ,
modifier ;
if ( elementName && elementName . charAt ( elementName . length - 1 ) !== "*" ) {
d ++ ;
}
for ( i = 0 , len = part . modifiers . length ; i < len ; i ++ ) {
modifier = part . modifiers [ i ] ;
switch ( modifier . type ) {
case "class" :
case "attribute" :
c ++ ;
break ;
case "id" :
b ++ ;
break ;
case "pseudo" :
if ( Pseudos . isElement ( modifier . text ) ) {
d ++ ;
} else {
c ++ ;
}
break ;
case "not" :
for ( j = 0 , num = modifier . args . length ; j < num ; j ++ ) {
updateValues ( modifier . args [ j ] ) ;
}
}
}
}
for ( i = 0 , len = selector . parts . length ; i < len ; i ++ ) {
part = selector . parts [ i ] ;
if ( part instanceof SelectorPart ) {
updateValues ( part ) ;
}
}
return new Specificity ( 0 , b , c , d ) ;
} ;
} , { "./Pseudos" : 12 , "./SelectorPart" : 14 } ] , 17 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = TokenStream ;
var TokenStreamBase = require ( "../util/TokenStreamBase" ) ;
var PropertyValuePart = require ( "./PropertyValuePart" ) ;
var Tokens = require ( "./Tokens" ) ;
var h = /^[0-9a-fA-F]$/ ,
nonascii = /^[\u00A0-\uFFFF]$/ ,
nl = /\n|\r\n|\r|\f/ ,
whitespace = /\u0009|\u000a|\u000c|\u000d|\u0020/ ;
//-----------------------------------------------------------------------------
// Helper functions
//-----------------------------------------------------------------------------
function isHexDigit ( c ) {
return c !== null && h . test ( c ) ;
}
function isDigit ( c ) {
return c !== null && /\d/ . test ( c ) ;
}
function isWhitespace ( c ) {
return c !== null && whitespace . test ( c ) ;
}
function isNewLine ( c ) {
return c !== null && nl . test ( c ) ;
}
function isNameStart ( c ) {
return c !== null && /[a-z_\u00A0-\uFFFF\\]/i . test ( c ) ;
}
function isNameChar ( c ) {
return c !== null && ( isNameStart ( c ) || /[0-9\-\\]/ . test ( c ) ) ;
}
function isIdentStart ( c ) {
return c !== null && ( isNameStart ( c ) || /\-\\/ . test ( c ) ) ;
}
function mix ( receiver , supplier ) {
for ( var prop in supplier ) {
if ( Object . prototype . hasOwnProperty . call ( supplier , prop ) ) {
receiver [ prop ] = supplier [ prop ] ;
}
}
return receiver ;
}
//-----------------------------------------------------------------------------
// CSS Token Stream
//-----------------------------------------------------------------------------
/ * *
* A token stream that produces CSS tokens .
* @ param { String | Reader } input The source of text to tokenize .
* @ constructor
* @ class TokenStream
* @ namespace parserlib . css
* /
function TokenStream ( input ) {
TokenStreamBase . call ( this , input , Tokens ) ;
}
TokenStream . prototype = mix ( new TokenStreamBase ( ) , {
/ * *
* Overrides the TokenStreamBase method of the same name
* to produce CSS tokens .
* @ return { Object } A token object representing the next token .
* @ method _getToken
* @ private
* /
_getToken : function ( ) {
var c ,
reader = this . _reader ,
token = null ,
startLine = reader . getLine ( ) ,
startCol = reader . getCol ( ) ;
c = reader . read ( ) ;
while ( c ) {
switch ( c ) {
/ *
* Potential tokens :
* - COMMENT
* - SLASH
* - CHAR
* /
case "/" :
if ( reader . peek ( ) === "*" ) {
token = this . commentToken ( c , startLine , startCol ) ;
} else {
token = this . charToken ( c , startLine , startCol ) ;
}
break ;
/ *
* Potential tokens :
* - DASHMATCH
* - INCLUDES
* - PREFIXMATCH
* - SUFFIXMATCH
* - SUBSTRINGMATCH
* - CHAR
* /
case "|" :
case "~" :
case "^" :
case "$" :
case "*" :
if ( reader . peek ( ) === "=" ) {
token = this . comparisonToken ( c , startLine , startCol ) ;
} else {
token = this . charToken ( c , startLine , startCol ) ;
}
break ;
/ *
* Potential tokens :
* - STRING
* - INVALID
* /
case "\"" :
case "'" :
token = this . stringToken ( c , startLine , startCol ) ;
break ;
/ *
* Potential tokens :
* - HASH
* - CHAR
* /
case "#" :
if ( isNameChar ( reader . peek ( ) ) ) {
token = this . hashToken ( c , startLine , startCol ) ;
} else {
token = this . charToken ( c , startLine , startCol ) ;
}
break ;
/ *
* Potential tokens :
* - DOT
* - NUMBER
* - DIMENSION
* - PERCENTAGE
* /
case "." :
if ( isDigit ( reader . peek ( ) ) ) {
token = this . numberToken ( c , startLine , startCol ) ;
} else {
token = this . charToken ( c , startLine , startCol ) ;
}
break ;
/ *
* Potential tokens :
* - CDC
* - MINUS
* - NUMBER
* - DIMENSION
* - PERCENTAGE
* /
case "-" :
if ( reader . peek ( ) === "-" ) { //could be closing HTML-style comment
token = this . htmlCommentEndToken ( c , startLine , startCol ) ;
} else if ( isNameStart ( reader . peek ( ) ) ) {
token = this . identOrFunctionToken ( c , startLine , startCol ) ;
} else {
token = this . charToken ( c , startLine , startCol ) ;
}
break ;
/ *
* Potential tokens :
* - IMPORTANT _SYM
* - CHAR
* /
case "!" :
token = this . importantToken ( c , startLine , startCol ) ;
break ;
/ *
* Any at - keyword or CHAR
* /
case "@" :
token = this . atRuleToken ( c , startLine , startCol ) ;
break ;
/ *
* Potential tokens :
* - NOT
* - CHAR
* /
case ":" :
token = this . notToken ( c , startLine , startCol ) ;
break ;
/ *
* Potential tokens :
* - CDO
* - CHAR
* /
case "<" :
token = this . htmlCommentStartToken ( c , startLine , startCol ) ;
break ;
/ *
* Potential tokens :
* - IDENT
* - CHAR
* /
case "\\" :
if ( /[^\r\n\f]/ . test ( reader . peek ( ) ) ) {
token = this . identOrFunctionToken ( this . readEscape ( c , true ) , startLine , startCol ) ;
} else {
token = this . charToken ( c , startLine , startCol ) ;
}
break ;
/ *
* Potential tokens :
* - UNICODE _RANGE
* - URL
* - CHAR
* /
case "U" :
case "u" :
if ( reader . peek ( ) === "+" ) {
token = this . unicodeRangeToken ( c , startLine , startCol ) ;
break ;
}
/* falls through */
default :
/ *
* Potential tokens :
* - NUMBER
* - DIMENSION
* - LENGTH
* - FREQ
* - TIME
* - EMS
* - EXS
* - ANGLE
* /
if ( isDigit ( c ) ) {
token = this . numberToken ( c , startLine , startCol ) ;
} else
/ *
* Potential tokens :
* - S
* /
if ( isWhitespace ( c ) ) {
token = this . whitespaceToken ( c , startLine , startCol ) ;
} else
/ *
* Potential tokens :
* - IDENT
* /
if ( isIdentStart ( c ) ) {
token = this . identOrFunctionToken ( c , startLine , startCol ) ;
} else {
/ *
* Potential tokens :
* - CHAR
* - PLUS
* /
token = this . charToken ( c , startLine , startCol ) ;
}
}
//make sure this token is wanted
//TODO: check channel
break ;
}
if ( ! token && c === null ) {
token = this . createToken ( Tokens . EOF , null , startLine , startCol ) ;
}
return token ;
} ,
//-------------------------------------------------------------------------
// Methods to create tokens
//-------------------------------------------------------------------------
/ * *
* Produces a token based on available data and the current
* reader position information . This method is called by other
* private methods to create tokens and is never called directly .
* @ param { int } tt The token type .
* @ param { String } value The text value of the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ param { Object } options ( Optional ) Specifies a channel property
* to indicate that a different channel should be scanned
* and / or a hide property indicating that the token should
* be hidden .
* @ return { Object } A token object .
* @ method createToken
* /
createToken : function ( tt , value , startLine , startCol , options ) {
var reader = this . _reader ;
options = options || { } ;
return {
value : value ,
type : tt ,
channel : options . channel ,
endChar : options . endChar ,
hide : options . hide || false ,
startLine : startLine ,
startCol : startCol ,
endLine : reader . getLine ( ) ,
endCol : reader . getCol ( )
} ;
} ,
//-------------------------------------------------------------------------
// Methods to create specific tokens
//-------------------------------------------------------------------------
/ * *
* Produces a token for any at - rule . If the at - rule is unknown , then
* the token is for a single "@" character .
* @ param { String } first The first character for the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method atRuleToken
* /
atRuleToken : function ( first , startLine , startCol ) {
var rule = first ,
reader = this . _reader ,
tt = Tokens . CHAR ,
ident ;
/ *
* First , mark where we are . There are only four @ rules ,
* so anything else is really just an invalid token .
* Basically , if this doesn ' t match one of the known @
* rules , just return '@' as an unknown token and allow
* parsing to continue after that point .
* /
reader . mark ( ) ;
//try to find the at-keyword
ident = this . readName ( ) ;
rule = first + ident ;
tt = Tokens . type ( rule . toLowerCase ( ) ) ;
//if it's not valid, use the first character only and reset the reader
if ( tt === Tokens . CHAR || tt === Tokens . UNKNOWN ) {
if ( rule . length > 1 ) {
tt = Tokens . UNKNOWN _SYM ;
} else {
tt = Tokens . CHAR ;
rule = first ;
reader . reset ( ) ;
}
}
return this . createToken ( tt , rule , startLine , startCol ) ;
} ,
/ * *
* Produces a character token based on the given character
* and location in the stream . If there ' s a special ( non - standard )
* token name , this is used ; otherwise CHAR is used .
* @ param { String } c The character for the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method charToken
* /
charToken : function ( c , startLine , startCol ) {
var tt = Tokens . type ( c ) ;
var opts = { } ;
if ( tt === - 1 ) {
tt = Tokens . CHAR ;
} else {
opts . endChar = Tokens [ tt ] . endChar ;
}
return this . createToken ( tt , c , startLine , startCol , opts ) ;
} ,
/ * *
* Produces a character token based on the given character
* and location in the stream . If there ' s a special ( non - standard )
* token name , this is used ; otherwise CHAR is used .
* @ param { String } first The first character for the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method commentToken
* /
commentToken : function ( first , startLine , startCol ) {
var comment = this . readComment ( first ) ;
return this . createToken ( Tokens . COMMENT , comment , startLine , startCol ) ;
} ,
/ * *
* Produces a comparison token based on the given character
* and location in the stream . The next character must be
* read and is already known to be an equals sign .
* @ param { String } c The character for the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method comparisonToken
* /
comparisonToken : function ( c , startLine , startCol ) {
var reader = this . _reader ,
comparison = c + reader . read ( ) ,
tt = Tokens . type ( comparison ) || Tokens . CHAR ;
return this . createToken ( tt , comparison , startLine , startCol ) ;
} ,
/ * *
* Produces a hash token based on the specified information . The
* first character provided is the pound sign ( # ) and then this
* method reads a name afterward .
* @ param { String } first The first character ( # ) in the hash name .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method hashToken
* /
hashToken : function ( first , startLine , startCol ) {
var name = this . readName ( first ) ;
return this . createToken ( Tokens . HASH , name , startLine , startCol ) ;
} ,
/ * *
* Produces a CDO or CHAR token based on the specified information . The
* first character is provided and the rest is read by the function to determine
* the correct token to create .
* @ param { String } first The first character in the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method htmlCommentStartToken
* /
htmlCommentStartToken : function ( first , startLine , startCol ) {
var reader = this . _reader ,
text = first ;
reader . mark ( ) ;
text += reader . readCount ( 3 ) ;
if ( text === "<!--" ) {
return this . createToken ( Tokens . CDO , text , startLine , startCol ) ;
} else {
reader . reset ( ) ;
return this . charToken ( first , startLine , startCol ) ;
}
} ,
/ * *
* Produces a CDC or CHAR token based on the specified information . The
* first character is provided and the rest is read by the function to determine
* the correct token to create .
* @ param { String } first The first character in the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method htmlCommentEndToken
* /
htmlCommentEndToken : function ( first , startLine , startCol ) {
var reader = this . _reader ,
text = first ;
reader . mark ( ) ;
text += reader . readCount ( 2 ) ;
if ( text === "-->" ) {
return this . createToken ( Tokens . CDC , text , startLine , startCol ) ;
} else {
reader . reset ( ) ;
return this . charToken ( first , startLine , startCol ) ;
}
} ,
/ * *
* Produces an IDENT or FUNCTION token based on the specified information . The
* first character is provided and the rest is read by the function to determine
* the correct token to create .
* @ param { String } first The first character in the identifier .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method identOrFunctionToken
* /
identOrFunctionToken : function ( first , startLine , startCol ) {
var reader = this . _reader ,
ident = this . readName ( first ) ,
tt = Tokens . IDENT ,
uriFns = [ "url(" , "url-prefix(" , "domain(" ] ,
uri ;
//if there's a left paren immediately after, it's a URI or function
if ( reader . peek ( ) === "(" ) {
ident += reader . read ( ) ;
if ( uriFns . indexOf ( ident . toLowerCase ( ) ) > - 1 ) {
reader . mark ( ) ;
uri = this . readURI ( ident ) ;
if ( uri === null ) {
//didn't find a valid URL or there's no closing paren
reader . reset ( ) ;
tt = Tokens . FUNCTION ;
} else {
tt = Tokens . URI ;
ident = uri ;
}
} else {
tt = Tokens . FUNCTION ;
}
} else if ( reader . peek ( ) === ":" ) { //might be an IE function
//IE-specific functions always being with progid:
if ( ident . toLowerCase ( ) === "progid" ) {
ident += reader . readTo ( "(" ) ;
tt = Tokens . IE _FUNCTION ;
}
}
return this . createToken ( tt , ident , startLine , startCol ) ;
} ,
/ * *
* Produces an IMPORTANT _SYM or CHAR token based on the specified information . The
* first character is provided and the rest is read by the function to determine
* the correct token to create .
* @ param { String } first The first character in the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method importantToken
* /
importantToken : function ( first , startLine , startCol ) {
var reader = this . _reader ,
important = first ,
tt = Tokens . CHAR ,
temp ,
c ;
reader . mark ( ) ;
c = reader . read ( ) ;
while ( c ) {
//there can be a comment in here
if ( c === "/" ) {
//if the next character isn't a star, then this isn't a valid !important token
if ( reader . peek ( ) !== "*" ) {
break ;
} else {
temp = this . readComment ( c ) ;
if ( temp === "" ) { //broken!
break ;
}
}
} else if ( isWhitespace ( c ) ) {
important += c + this . readWhitespace ( ) ;
} else if ( /i/i . test ( c ) ) {
temp = reader . readCount ( 8 ) ;
if ( /mportant/i . test ( temp ) ) {
important += c + temp ;
tt = Tokens . IMPORTANT _SYM ;
}
break ; //we're done
} else {
break ;
}
c = reader . read ( ) ;
}
if ( tt === Tokens . CHAR ) {
reader . reset ( ) ;
return this . charToken ( first , startLine , startCol ) ;
} else {
return this . createToken ( tt , important , startLine , startCol ) ;
}
} ,
/ * *
* Produces a NOT or CHAR token based on the specified information . The
* first character is provided and the rest is read by the function to determine
* the correct token to create .
* @ param { String } first The first character in the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method notToken
* /
notToken : function ( first , startLine , startCol ) {
var reader = this . _reader ,
text = first ;
reader . mark ( ) ;
text += reader . readCount ( 4 ) ;
if ( text . toLowerCase ( ) === ":not(" ) {
return this . createToken ( Tokens . NOT , text , startLine , startCol ) ;
} else {
reader . reset ( ) ;
return this . charToken ( first , startLine , startCol ) ;
}
} ,
/ * *
* Produces a number token based on the given character
* and location in the stream . This may return a token of
* NUMBER , EMS , EXS , LENGTH , ANGLE , TIME , FREQ , DIMENSION ,
* or PERCENTAGE .
* @ param { String } first The first character for the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method numberToken
* /
numberToken : function ( first , startLine , startCol ) {
var reader = this . _reader ,
value = this . readNumber ( first ) ,
ident ,
tt = Tokens . NUMBER ,
c = reader . peek ( ) ;
if ( isIdentStart ( c ) ) {
ident = this . readName ( reader . read ( ) ) ;
value += ident ;
if ( /^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vmax$|^vmin$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i . test ( ident ) ) {
tt = Tokens . LENGTH ;
} else if ( /^deg|^rad$|^grad$|^turn$/i . test ( ident ) ) {
tt = Tokens . ANGLE ;
} else if ( /^ms$|^s$/i . test ( ident ) ) {
tt = Tokens . TIME ;
} else if ( /^hz$|^khz$/i . test ( ident ) ) {
tt = Tokens . FREQ ;
} else if ( /^dpi$|^dpcm$/i . test ( ident ) ) {
tt = Tokens . RESOLUTION ;
} else {
tt = Tokens . DIMENSION ;
}
} else if ( c === "%" ) {
value += reader . read ( ) ;
tt = Tokens . PERCENTAGE ;
}
return this . createToken ( tt , value , startLine , startCol ) ;
} ,
/ * *
* Produces a string token based on the given character
* and location in the stream . Since strings may be indicated
* by single or double quotes , a failure to match starting
* and ending quotes results in an INVALID token being generated .
* The first character in the string is passed in and then
* the rest are read up to and including the final quotation mark .
* @ param { String } first The first character in the string .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method stringToken
* /
stringToken : function ( first , startLine , startCol ) {
var delim = first ,
string = first ,
reader = this . _reader ,
tt = Tokens . STRING ,
c = reader . read ( ) ,
i ;
while ( c ) {
string += c ;
if ( c === "\\" ) {
c = reader . read ( ) ;
if ( c === null ) {
break ; // premature EOF after backslash
} else if ( /[^\r\n\f0-9a-f]/i . test ( c ) ) {
// single-character escape
string += c ;
} else {
// read up to six hex digits
for ( i = 0 ; isHexDigit ( c ) && i < 6 ; i ++ ) {
string += c ;
c = reader . read ( ) ;
}
// swallow trailing newline or space
if ( c === "\r" && reader . peek ( ) === "\n" ) {
string += c ;
c = reader . read ( ) ;
}
if ( isWhitespace ( c ) ) {
string += c ;
} else {
// This character is null or not part of the escape;
// jump back to the top to process it.
continue ;
}
}
} else if ( c === delim ) {
break ; // delimiter found.
} else if ( isNewLine ( reader . peek ( ) ) ) {
// newline without an escapement: it's an invalid string
tt = Tokens . INVALID ;
break ;
}
c = reader . read ( ) ;
}
//if c is null, that means we're out of input and the string was never closed
if ( c === null ) {
tt = Tokens . INVALID ;
}
return this . createToken ( tt , string , startLine , startCol ) ;
} ,
unicodeRangeToken : function ( first , startLine , startCol ) {
var reader = this . _reader ,
value = first ,
temp ,
tt = Tokens . CHAR ;
//then it should be a unicode range
if ( reader . peek ( ) === "+" ) {
reader . mark ( ) ;
value += reader . read ( ) ;
value += this . readUnicodeRangePart ( true ) ;
//ensure there's an actual unicode range here
if ( value . length === 2 ) {
reader . reset ( ) ;
} else {
tt = Tokens . UNICODE _RANGE ;
//if there's a ? in the first part, there can't be a second part
if ( value . indexOf ( "?" ) === - 1 ) {
if ( reader . peek ( ) === "-" ) {
reader . mark ( ) ;
temp = reader . read ( ) ;
temp += this . readUnicodeRangePart ( false ) ;
//if there's not another value, back up and just take the first
if ( temp . length === 1 ) {
reader . reset ( ) ;
} else {
value += temp ;
}
}
}
}
}
return this . createToken ( tt , value , startLine , startCol ) ;
} ,
/ * *
* Produces a S token based on the specified information . Since whitespace
* may have multiple characters , this consumes all whitespace characters
* into a single token .
* @ param { String } first The first character in the token .
* @ param { int } startLine The beginning line for the character .
* @ param { int } startCol The beginning column for the character .
* @ return { Object } A token object .
* @ method whitespaceToken
* /
whitespaceToken : function ( first , startLine , startCol ) {
var value = first + this . readWhitespace ( ) ;
return this . createToken ( Tokens . S , value , startLine , startCol ) ;
} ,
//-------------------------------------------------------------------------
// Methods to read values from the string stream
//-------------------------------------------------------------------------
readUnicodeRangePart : function ( allowQuestionMark ) {
var reader = this . _reader ,
part = "" ,
c = reader . peek ( ) ;
//first read hex digits
while ( isHexDigit ( c ) && part . length < 6 ) {
reader . read ( ) ;
part += c ;
c = reader . peek ( ) ;
}
//then read question marks if allowed
if ( allowQuestionMark ) {
while ( c === "?" && part . length < 6 ) {
reader . read ( ) ;
part += c ;
c = reader . peek ( ) ;
}
}
//there can't be any other characters after this point
return part ;
} ,
readWhitespace : function ( ) {
var reader = this . _reader ,
whitespace = "" ,
c = reader . peek ( ) ;
while ( isWhitespace ( c ) ) {
reader . read ( ) ;
whitespace += c ;
c = reader . peek ( ) ;
}
return whitespace ;
} ,
readNumber : function ( first ) {
var reader = this . _reader ,
number = first ,
hasDot = ( first === "." ) ,
c = reader . peek ( ) ;
while ( c ) {
if ( isDigit ( c ) ) {
number += reader . read ( ) ;
} else if ( c === "." ) {
if ( hasDot ) {
break ;
} else {
hasDot = true ;
number += reader . read ( ) ;
}
} else {
break ;
}
c = reader . peek ( ) ;
}
return number ;
} ,
// returns null w/o resetting reader if string is invalid.
readString : function ( ) {
var token = this . stringToken ( this . _reader . read ( ) , 0 , 0 ) ;
return token . type === Tokens . INVALID ? null : token . value ;
} ,
// returns null w/o resetting reader if URI is invalid.
readURI : function ( first ) {
var reader = this . _reader ,
uri = first ,
inner = "" ,
c = reader . peek ( ) ;
//skip whitespace before
while ( c && isWhitespace ( c ) ) {
reader . read ( ) ;
c = reader . peek ( ) ;
}
//it's a string
if ( c === "'" || c === "\"" ) {
inner = this . readString ( ) ;
if ( inner !== null ) {
inner = PropertyValuePart . parseString ( inner ) ;
}
} else {
inner = this . readUnquotedURL ( ) ;
}
c = reader . peek ( ) ;
//skip whitespace after
while ( c && isWhitespace ( c ) ) {
reader . read ( ) ;
c = reader . peek ( ) ;
}
//if there was no inner value or the next character isn't closing paren, it's not a URI
if ( inner === null || c !== ")" ) {
uri = null ;
} else {
// Ensure argument to URL is always double-quoted
// (This simplifies later processing in PropertyValuePart.)
uri += PropertyValuePart . serializeString ( inner ) + reader . read ( ) ;
}
return uri ;
} ,
// This method never fails, although it may return an empty string.
readUnquotedURL : function ( first ) {
var reader = this . _reader ,
url = first || "" ,
c ;
for ( c = reader . peek ( ) ; c ; c = reader . peek ( ) ) {
// Note that the grammar at
// https://www.w3.org/TR/CSS2/grammar.html#scanner
// incorrectly includes the backslash character in the
// `url` production, although it is correctly omitted in
// the `baduri1` production.
if ( nonascii . test ( c ) || /^[\-!#$%&*-\[\]-~]$/ . test ( c ) ) {
url += c ;
reader . read ( ) ;
} else if ( c === "\\" ) {
if ( /^[^\r\n\f]$/ . test ( reader . peek ( 2 ) ) ) {
url += this . readEscape ( reader . read ( ) , true ) ;
} else {
break ; // bad escape sequence.
}
} else {
break ; // bad character
}
}
return url ;
} ,
readName : function ( first ) {
var reader = this . _reader ,
ident = first || "" ,
c ;
for ( c = reader . peek ( ) ; c ; c = reader . peek ( ) ) {
if ( c === "\\" ) {
if ( /^[^\r\n\f]$/ . test ( reader . peek ( 2 ) ) ) {
ident += this . readEscape ( reader . read ( ) , true ) ;
} else {
// Bad escape sequence.
break ;
}
} else if ( isNameChar ( c ) ) {
ident += reader . read ( ) ;
} else {
break ;
}
}
return ident ;
} ,
readEscape : function ( first , unescape ) {
var reader = this . _reader ,
cssEscape = first || "" ,
i = 0 ,
c = reader . peek ( ) ;
if ( isHexDigit ( c ) ) {
do {
cssEscape += reader . read ( ) ;
c = reader . peek ( ) ;
} while ( c && isHexDigit ( c ) && ++ i < 6 ) ;
}
if ( cssEscape . length === 1 ) {
if ( /^[^\r\n\f0-9a-f]$/ . test ( c ) ) {
reader . read ( ) ;
if ( unescape ) {
return c ;
}
} else {
// We should never get here (readName won't call readEscape
// if the escape sequence is bad).
throw new Error ( "Bad escape sequence." ) ;
}
} else if ( c === "\r" ) {
reader . read ( ) ;
if ( reader . peek ( ) === "\n" ) {
c += reader . read ( ) ;
}
} else if ( /^[ \t\n\f]$/ . test ( c ) ) {
reader . read ( ) ;
} else {
c = "" ;
}
if ( unescape ) {
var cp = parseInt ( cssEscape . slice ( first . length ) , 16 ) ;
return String . fromCodePoint ? String . fromCodePoint ( cp ) :
String . fromCharCode ( cp ) ;
}
return cssEscape + c ;
} ,
readComment : function ( first ) {
var reader = this . _reader ,
comment = first || "" ,
c = reader . read ( ) ;
if ( c === "*" ) {
while ( c ) {
comment += c ;
//look for end of comment
if ( comment . length > 2 && c === "*" && reader . peek ( ) === "/" ) {
comment += reader . read ( ) ;
break ;
}
c = reader . read ( ) ;
}
return comment ;
} else {
return "" ;
}
}
} ) ;
} , { "../util/TokenStreamBase" : 27 , "./PropertyValuePart" : 11 , "./Tokens" : 18 } ] , 18 : [ function ( require , module , exports ) {
"use strict" ;
var Tokens = module . exports = [
/ *
* The following token names are defined in CSS3 Grammar : https : //www.w3.org/TR/css3-syntax/#lexical
* /
// HTML-style comments
{ name : "CDO" } ,
{ name : "CDC" } ,
// ignorables
{ name : "S" , whitespace : true /*, channel: "ws"*/ } ,
{ name : "COMMENT" , comment : true , hide : true , channel : "comment" } ,
// attribute equality
{ name : "INCLUDES" , text : "~=" } ,
{ name : "DASHMATCH" , text : "|=" } ,
{ name : "PREFIXMATCH" , text : "^=" } ,
{ name : "SUFFIXMATCH" , text : "$=" } ,
{ name : "SUBSTRINGMATCH" , text : "*=" } ,
// identifier types
{ name : "STRING" } ,
{ name : "IDENT" } ,
{ name : "HASH" } ,
// at-keywords
{ name : "IMPORT_SYM" , text : "@import" } ,
{ name : "PAGE_SYM" , text : "@page" } ,
{ name : "MEDIA_SYM" , text : "@media" } ,
{ name : "FONT_FACE_SYM" , text : "@font-face" } ,
{ name : "CHARSET_SYM" , text : "@charset" } ,
{ name : "NAMESPACE_SYM" , text : "@namespace" } ,
{ name : "SUPPORTS_SYM" , text : "@supports" } ,
{ name : "VIEWPORT_SYM" , text : [ "@viewport" , "@-ms-viewport" , "@-o-viewport" ] } ,
{ name : "DOCUMENT_SYM" , text : [ "@document" , "@-moz-document" ] } ,
{ name : "UNKNOWN_SYM" } ,
//{ name: "ATKEYWORD"},
// CSS3 animations
{ name : "KEYFRAMES_SYM" , text : [ "@keyframes" , "@-webkit-keyframes" , "@-moz-keyframes" , "@-o-keyframes" ] } ,
// important symbol
{ name : "IMPORTANT_SYM" } ,
// measurements
{ name : "LENGTH" } ,
{ name : "ANGLE" } ,
{ name : "TIME" } ,
{ name : "FREQ" } ,
{ name : "DIMENSION" } ,
{ name : "PERCENTAGE" } ,
{ name : "NUMBER" } ,
// functions
{ name : "URI" } ,
{ name : "FUNCTION" } ,
// Unicode ranges
{ name : "UNICODE_RANGE" } ,
/ *
* The following token names are defined in CSS3 Selectors : https : //www.w3.org/TR/css3-selectors/#selector-syntax
* /
// invalid string
{ name : "INVALID" } ,
// combinators
{ name : "PLUS" , text : "+" } ,
{ name : "GREATER" , text : ">" } ,
{ name : "COMMA" , text : "," } ,
{ name : "TILDE" , text : "~" } ,
// modifier
{ name : "NOT" } ,
/ *
* Defined in CSS3 Paged Media
* /
{ name : "TOPLEFTCORNER_SYM" , text : "@top-left-corner" } ,
{ name : "TOPLEFT_SYM" , text : "@top-left" } ,
{ name : "TOPCENTER_SYM" , text : "@top-center" } ,
{ name : "TOPRIGHT_SYM" , text : "@top-right" } ,
{ name : "TOPRIGHTCORNER_SYM" , text : "@top-right-corner" } ,
{ name : "BOTTOMLEFTCORNER_SYM" , text : "@bottom-left-corner" } ,
{ name : "BOTTOMLEFT_SYM" , text : "@bottom-left" } ,
{ name : "BOTTOMCENTER_SYM" , text : "@bottom-center" } ,
{ name : "BOTTOMRIGHT_SYM" , text : "@bottom-right" } ,
{ name : "BOTTOMRIGHTCORNER_SYM" , text : "@bottom-right-corner" } ,
{ name : "LEFTTOP_SYM" , text : "@left-top" } ,
{ name : "LEFTMIDDLE_SYM" , text : "@left-middle" } ,
{ name : "LEFTBOTTOM_SYM" , text : "@left-bottom" } ,
{ name : "RIGHTTOP_SYM" , text : "@right-top" } ,
{ name : "RIGHTMIDDLE_SYM" , text : "@right-middle" } ,
{ name : "RIGHTBOTTOM_SYM" , text : "@right-bottom" } ,
/ *
* The following token names are defined in CSS3 Media Queries : https : //www.w3.org/TR/css3-mediaqueries/#syntax
* /
/ * { n a m e : " M E D I A _ O N L Y " , s t a t e : " m e d i a " } ,
{ name : "MEDIA_NOT" , state : "media" } ,
{ name : "MEDIA_AND" , state : "media" } , * /
{ name : "RESOLUTION" , state : "media" } ,
/ *
* The following token names are not defined in any CSS specification but are used by the lexer .
* /
// not a real token, but useful for stupid IE filters
{ name : "IE_FUNCTION" } ,
// part of CSS3 grammar but not the Flex code
{ name : "CHAR" } ,
// TODO: Needed?
// Not defined as tokens, but might as well be
{
name : "PIPE" ,
text : "|"
} ,
{
name : "SLASH" ,
text : "/"
} ,
{
name : "MINUS" ,
text : "-"
} ,
{
name : "STAR" ,
text : "*"
} ,
{
name : "LBRACE" ,
endChar : "}" ,
text : "{"
} ,
{
name : "RBRACE" ,
text : "}"
} ,
{
name : "LBRACKET" ,
endChar : "]" ,
text : "["
} ,
{
name : "RBRACKET" ,
text : "]"
} ,
{
name : "EQUALS" ,
text : "="
} ,
{
name : "COLON" ,
text : ":"
} ,
{
name : "SEMICOLON" ,
text : ";"
} ,
{
name : "LPAREN" ,
endChar : ")" ,
text : "("
} ,
{
name : "RPAREN" ,
text : ")"
} ,
{
name : "DOT" ,
text : "."
}
] ;
( function ( ) {
var nameMap = [ ] ,
typeMap = Object . create ( null ) ;
Tokens . UNKNOWN = - 1 ;
Tokens . unshift ( { name : "EOF" } ) ;
for ( var i = 0 , len = Tokens . length ; i < len ; i ++ ) {
nameMap . push ( Tokens [ i ] . name ) ;
Tokens [ Tokens [ i ] . name ] = i ;
if ( Tokens [ i ] . text ) {
if ( Tokens [ i ] . text instanceof Array ) {
for ( var j = 0 ; j < Tokens [ i ] . text . length ; j ++ ) {
typeMap [ Tokens [ i ] . text [ j ] ] = i ;
}
} else {
typeMap [ Tokens [ i ] . text ] = i ;
}
}
}
Tokens . name = function ( tt ) {
return nameMap [ tt ] ;
} ;
Tokens . type = function ( c ) {
return typeMap [ c ] || - 1 ;
} ;
} ) ( ) ;
} , { } ] , 19 : [ function ( require , module , exports ) {
"use strict" ;
/* exported Validation */
var Matcher = require ( "./Matcher" ) ;
var Properties = require ( "./Properties" ) ;
var ValidationTypes = require ( "./ValidationTypes" ) ;
var ValidationError = require ( "./ValidationError" ) ;
var PropertyValueIterator = require ( "./PropertyValueIterator" ) ;
var Validation = module . exports = {
validate : function ( property , value ) {
//normalize name
var name = property . toString ( ) . toLowerCase ( ) ,
expression = new PropertyValueIterator ( value ) ,
spec = Properties [ name ] ,
part ;
if ( ! spec ) {
if ( name . indexOf ( "-" ) !== 0 ) { //vendor prefixed are ok
throw new ValidationError ( "Unknown property '" + property + "'." , property . line , property . col ) ;
}
} else if ( typeof spec !== "number" ) {
// All properties accept some CSS-wide values.
// https://drafts.csswg.org/css-values-3/#common-keywords
if ( ValidationTypes . isAny ( expression , "inherit | initial | unset" ) ) {
if ( expression . hasNext ( ) ) {
part = expression . next ( ) ;
throw new ValidationError ( "Expected end of value but found '" + part + "'." , part . line , part . col ) ;
}
return ;
}
// Property-specific validation.
this . singleProperty ( spec , expression ) ;
}
} ,
singleProperty : function ( types , expression ) {
var result = false ,
value = expression . value ,
part ;
result = Matcher . parse ( types ) . match ( expression ) ;
if ( ! result ) {
if ( expression . hasNext ( ) && ! expression . isFirst ( ) ) {
part = expression . peek ( ) ;
throw new ValidationError ( "Expected end of value but found '" + part + "'." , part . line , part . col ) ;
} else {
throw new ValidationError ( "Expected (" + ValidationTypes . describe ( types ) + ") but found '" + value + "'." , value . line , value . col ) ;
}
} else if ( expression . hasNext ( ) ) {
part = expression . next ( ) ;
throw new ValidationError ( "Expected end of value but found '" + part + "'." , part . line , part . col ) ;
}
}
} ;
} , { "./Matcher" : 3 , "./Properties" : 7 , "./PropertyValueIterator" : 10 , "./ValidationError" : 20 , "./ValidationTypes" : 21 } ] , 20 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = ValidationError ;
/ * *
* Type to use when a validation error occurs .
* @ class ValidationError
* @ namespace parserlib . util
* @ constructor
* @ param { String } message The error message .
* @ param { int } line The line at which the error occurred .
* @ param { int } col The column at which the error occurred .
* /
function ValidationError ( message , line , col ) {
/ * *
* The column at which the error occurred .
* @ type int
* @ property col
* /
this . col = col ;
/ * *
* The line at which the error occurred .
* @ type int
* @ property line
* /
this . line = line ;
/ * *
* The text representation of the unit .
* @ type String
* @ property text
* /
this . message = message ;
}
//inherit from Error
ValidationError . prototype = new Error ( ) ;
} , { } ] , 21 : [ function ( require , module , exports ) {
"use strict" ;
var ValidationTypes = module . exports ;
var Matcher = require ( "./Matcher" ) ;
function copy ( to , from ) {
Object . keys ( from ) . forEach ( function ( prop ) {
to [ prop ] = from [ prop ] ;
} ) ;
}
copy ( ValidationTypes , {
isLiteral : function ( part , literals ) {
var text = part . text . toString ( ) . toLowerCase ( ) ,
args = literals . split ( " | " ) ,
i , len , found = false ;
for ( i = 0 , len = args . length ; i < len && ! found ; i ++ ) {
if ( args [ i ] . charAt ( 0 ) === "<" ) {
found = this . simple [ args [ i ] ] ( part ) ;
} else if ( args [ i ] . slice ( - 2 ) === "()" ) {
found = ( part . type === "function" &&
part . name === args [ i ] . slice ( 0 , - 2 ) ) ;
} else if ( text === args [ i ] . toLowerCase ( ) ) {
found = true ;
}
}
return found ;
} ,
isSimple : function ( type ) {
return Boolean ( this . simple [ type ] ) ;
} ,
isComplex : function ( type ) {
return Boolean ( this . complex [ type ] ) ;
} ,
describe : function ( type ) {
if ( this . complex [ type ] instanceof Matcher ) {
return this . complex [ type ] . toString ( 0 ) ;
}
return type ;
} ,
/ * *
* Determines if the next part ( s ) of the given expression
* are any of the given types .
* /
isAny : function ( expression , types ) {
var args = types . split ( " | " ) ,
i , len , found = false ;
for ( i = 0 , len = args . length ; i < len && ! found && expression . hasNext ( ) ; i ++ ) {
found = this . isType ( expression , args [ i ] ) ;
}
return found ;
} ,
/ * *
* Determines if the next part ( s ) of the given expression
* are one of a group .
* /
isAnyOfGroup : function ( expression , types ) {
var args = types . split ( " || " ) ,
i , len , found = false ;
for ( i = 0 , len = args . length ; i < len && ! found ; i ++ ) {
found = this . isType ( expression , args [ i ] ) ;
}
return found ? args [ i - 1 ] : false ;
} ,
/ * *
* Determines if the next part ( s ) of the given expression
* are of a given type .
* /
isType : function ( expression , type ) {
var part = expression . peek ( ) ,
result = false ;
if ( type . charAt ( 0 ) !== "<" ) {
result = this . isLiteral ( part , type ) ;
if ( result ) {
expression . next ( ) ;
}
} else if ( this . simple [ type ] ) {
result = this . simple [ type ] ( part ) ;
if ( result ) {
expression . next ( ) ;
}
} else if ( this . complex [ type ] instanceof Matcher ) {
result = this . complex [ type ] . match ( expression ) ;
} else {
result = this . complex [ type ] ( expression ) ;
}
return result ;
} ,
simple : {
_ _proto _ _ : null ,
"<absolute-size>" :
"xx-small | x-small | small | medium | large | x-large | xx-large" ,
"<animateable-feature>" :
"scroll-position | contents | <animateable-feature-name>" ,
"<animateable-feature-name>" : function ( part ) {
return this [ "<ident>" ] ( part ) &&
! /^(unset|initial|inherit|will-change|auto|scroll-position|contents)$/i . test ( part ) ;
} ,
"<angle>" : function ( part ) {
return part . type === "angle" ;
} ,
"<attachment>" : "scroll | fixed | local" ,
"<attr>" : "attr()" ,
// inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )
// circle() = circle( [<shape-radius>]? [at <position>]? )
// ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )
// polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
"<basic-shape>" : "inset() | circle() | ellipse() | polygon()" ,
"<bg-image>" : "<image> | <gradient> | none" ,
"<border-style>" :
"none | hidden | dotted | dashed | solid | double | groove | " +
"ridge | inset | outset" ,
"<border-width>" : "<length> | thin | medium | thick" ,
"<box>" : "padding-box | border-box | content-box" ,
"<clip-source>" : "<uri>" ,
"<color>" : function ( part ) {
return part . type === "color" || String ( part ) === "transparent" || String ( part ) === "currentColor" ;
} ,
// The SVG <color> spec doesn't include "currentColor" or "transparent" as a color.
"<color-svg>" : function ( part ) {
return part . type === "color" ;
} ,
"<content>" : "content()" ,
// https://www.w3.org/TR/css3-sizing/#width-height-keywords
"<content-sizing>" :
"fill-available | -moz-available | -webkit-fill-available | " +
"max-content | -moz-max-content | -webkit-max-content | " +
"min-content | -moz-min-content | -webkit-min-content | " +
"fit-content | -moz-fit-content | -webkit-fit-content" ,
"<feature-tag-value>" : function ( part ) {
return part . type === "function" && /^[A-Z0-9]{4}$/i . test ( part ) ;
} ,
// custom() isn't actually in the spec
"<filter-function>" :
"blur() | brightness() | contrast() | custom() | " +
"drop-shadow() | grayscale() | hue-rotate() | invert() | " +
"opacity() | saturate() | sepia()" ,
"<flex-basis>" : "<width>" ,
"<flex-direction>" : "row | row-reverse | column | column-reverse" ,
"<flex-grow>" : "<number>" ,
"<flex-shrink>" : "<number>" ,
"<flex-wrap>" : "nowrap | wrap | wrap-reverse" ,
"<font-size>" :
"<absolute-size> | <relative-size> | <length> | <percentage>" ,
"<font-stretch>" :
"normal | ultra-condensed | extra-condensed | condensed | " +
"semi-condensed | semi-expanded | expanded | extra-expanded | " +
"ultra-expanded" ,
"<font-style>" : "normal | italic | oblique" ,
"<font-variant-caps>" :
"small-caps | all-small-caps | petite-caps | all-petite-caps | " +
"unicase | titling-caps" ,
"<font-variant-css21>" : "normal | small-caps" ,
"<font-weight>" :
"normal | bold | bolder | lighter | " +
"100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900" ,
"<generic-family>" :
"serif | sans-serif | cursive | fantasy | monospace" ,
"<geometry-box>" : "<shape-box> | fill-box | stroke-box | view-box" ,
"<glyph-angle>" : function ( part ) {
return part . type === "angle" && part . units === "deg" ;
} ,
"<gradient>" : function ( part ) {
return part . type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i . test ( part ) ;
} ,
"<icccolor>" :
"cielab() | cielch() | cielchab() | " +
"icc-color() | icc-named-color()" ,
//any identifier
"<ident>" : function ( part ) {
return part . type === "identifier" || part . wasIdent ;
} ,
"<ident-not-generic-family>" : function ( part ) {
return this [ "<ident>" ] ( part ) && ! this [ "<generic-family>" ] ( part ) ;
} ,
"<image>" : "<uri>" ,
"<integer>" : function ( part ) {
return part . type === "integer" ;
} ,
"<length>" : function ( part ) {
if ( part . type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?calc/i . test ( part ) ) {
return true ;
} else {
return part . type === "length" || part . type === "number" || part . type === "integer" || String ( part ) === "0" ;
}
} ,
"<line>" : function ( part ) {
return part . type === "integer" ;
} ,
"<line-height>" : "<number> | <length> | <percentage> | normal" ,
"<margin-width>" : "<length> | <percentage> | auto" ,
"<miterlimit>" : function ( part ) {
return this [ "<number>" ] ( part ) && part . value >= 1 ;
} ,
"<nonnegative-length-or-percentage>" : function ( part ) {
return ( this [ "<length>" ] ( part ) || this [ "<percentage>" ] ( part ) ) &&
( String ( part ) === "0" || part . type === "function" || ( part . value ) >= 0 ) ;
} ,
"<nonnegative-number-or-percentage>" : function ( part ) {
return ( this [ "<number>" ] ( part ) || this [ "<percentage>" ] ( part ) ) &&
( String ( part ) === "0" || part . type === "function" || ( part . value ) >= 0 ) ;
} ,
"<number>" : function ( part ) {
return part . type === "number" || this [ "<integer>" ] ( part ) ;
} ,
"<opacity-value>" : function ( part ) {
return this [ "<number>" ] ( part ) && part . value >= 0 && part . value <= 1 ;
} ,
"<padding-width>" : "<nonnegative-length-or-percentage>" ,
"<percentage>" : function ( part ) {
return part . type === "percentage" || String ( part ) === "0" ;
} ,
"<relative-size>" : "smaller | larger" ,
"<shape>" : "rect() | inset-rect()" ,
"<shape-box>" : "<box> | margin-box" ,
"<single-animation-direction>" :
"normal | reverse | alternate | alternate-reverse" ,
"<single-animation-name>" : function ( part ) {
return this [ "<ident>" ] ( part ) &&
/^-?[a-z_][-a-z0-9_]+$/i . test ( part ) &&
! /^(none|unset|initial|inherit)$/i . test ( part ) ;
} ,
"<string>" : function ( part ) {
return part . type === "string" ;
} ,
"<time>" : function ( part ) {
return part . type === "time" ;
} ,
"<uri>" : function ( part ) {
return part . type === "uri" ;
} ,
"<width>" : "<margin-width>"
} ,
complex : {
_ _proto _ _ : null ,
"<azimuth>" :
"<angle>" +
" | " +
"[ [ left-side | far-left | left | center-left | center | " +
"center-right | right | far-right | right-side ] || behind ]" +
" | " +
"leftwards | rightwards" ,
"<bg-position>" : "<position>#" ,
"<bg-size>" :
"[ <length> | <percentage> | auto ]{1,2} | cover | contain" ,
"<border-image-slice>" :
// [<number> | <percentage>]{1,4} && fill?
// *but* fill can appear between any of the numbers
Matcher . many ( [ true /* first element is required */ ] ,
Matcher . cast ( "<nonnegative-number-or-percentage>" ) ,
Matcher . cast ( "<nonnegative-number-or-percentage>" ) ,
Matcher . cast ( "<nonnegative-number-or-percentage>" ) ,
Matcher . cast ( "<nonnegative-number-or-percentage>" ) ,
"fill" ) ,
"<border-radius>" :
"<nonnegative-length-or-percentage>{1,4} " +
"[ / <nonnegative-length-or-percentage>{1,4} ]?" ,
"<box-shadow>" : "none | <shadow>#" ,
"<clip-path>" : "<basic-shape> || <geometry-box>" ,
"<dasharray>" :
// "list of comma and/or white space separated <length>s and
// <percentage>s". There is a non-negative constraint.
Matcher . cast ( "<nonnegative-length-or-percentage>" )
. braces ( 1 , Infinity , "#" , Matcher . cast ( "," ) . question ( ) ) ,
"<family-name>" :
// <string> | <IDENT>+
"<string> | <ident-not-generic-family> <ident>*" ,
"<filter-function-list>" : "[ <filter-function> | <uri> ]+" ,
// https://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/#flex-property
"<flex>" :
"none | [ <flex-grow> <flex-shrink>? || <flex-basis> ]" ,
"<font-family>" : "[ <generic-family> | <family-name> ]#" ,
"<font-shorthand>" :
"[ <font-style> || <font-variant-css21> || " +
"<font-weight> || <font-stretch> ]? <font-size> " +
"[ / <line-height> ]? <font-family>" ,
"<font-variant-alternates>" :
// stylistic(<feature-value-name>)
"stylistic() || " +
"historical-forms || " +
// styleset(<feature-value-name> #)
"styleset() || " +
// character-variant(<feature-value-name> #)
"character-variant() || " +
// swash(<feature-value-name>)
"swash() || " +
// ornaments(<feature-value-name>)
"ornaments() || " +
// annotation(<feature-value-name>)
"annotation()" ,
"<font-variant-ligatures>" :
// <common-lig-values>
"[ common-ligatures | no-common-ligatures ] || " +
// <discretionary-lig-values>
"[ discretionary-ligatures | no-discretionary-ligatures ] || " +
// <historical-lig-values>
"[ historical-ligatures | no-historical-ligatures ] || " +
// <contextual-alt-values>
"[ contextual | no-contextual ]" ,
"<font-variant-numeric>" :
// <numeric-figure-values>
"[ lining-nums | oldstyle-nums ] || " +
// <numeric-spacing-values>
"[ proportional-nums | tabular-nums ] || " +
// <numeric-fraction-values>
"[ diagonal-fractions | stacked-fractions ] || " +
"ordinal || slashed-zero" ,
"<font-variant-east-asian>" :
// <east-asian-variant-values>
"[ jis78 | jis83 | jis90 | jis04 | simplified | traditional ] || " +
// <east-asian-width-values>
"[ full-width | proportional-width ] || " +
"ruby" ,
// Note that <color> here is "as defined in the SVG spec", which
// is more restrictive that the <color> defined in the CSS spec.
// none | currentColor | <color> [<icccolor>]? |
// <funciri> [ none | currentColor | <color> [<icccolor>]? ]?
"<paint>" : "<paint-basic> | <uri> <paint-basic>?" ,
// Helper definition for <paint> above.
"<paint-basic>" : "none | currentColor | <color-svg> <icccolor>?" ,
"<position>" :
// Because our `alt` combinator is ordered, we need to test these
// in order from longest possible match to shortest.
"[ center | [ left | right ] [ <percentage> | <length> ]? ] && " +
"[ center | [ top | bottom ] [ <percentage> | <length> ]? ]" +
" | " +
"[ left | center | right | <percentage> | <length> ] " +
"[ top | center | bottom | <percentage> | <length> ]" +
" | " +
"[ left | center | right | top | bottom | <percentage> | <length> ]" ,
"<repeat-style>" :
"repeat-x | repeat-y | [ repeat | space | round | no-repeat ]{1,2}" ,
"<shadow>" :
//inset? && [ <length>{2,4} && <color>? ]
Matcher . many ( [ true /* length is required */ ] ,
Matcher . cast ( "<length>" ) . braces ( 2 , 4 ) , "inset" , "<color>" ) ,
"<text-decoration-color>" :
"<color>" ,
"<text-decoration-line>" :
"none | [ underline || overline || line-through || blink ]" ,
"<text-decoration-style>" :
"solid | double | dotted | dashed | wavy" ,
"<will-change>" :
"auto | <animateable-feature>#" ,
"<x-one-radius>" :
//[ <length> | <percentage> ] [ <length> | <percentage> ]?
"[ <length> | <percentage> ]{1,2}"
}
} ) ;
Object . keys ( ValidationTypes . simple ) . forEach ( function ( nt ) {
var rule = ValidationTypes . simple [ nt ] ;
if ( typeof rule === "string" ) {
ValidationTypes . simple [ nt ] = function ( part ) {
return ValidationTypes . isLiteral ( part , rule ) ;
} ;
}
} ) ;
Object . keys ( ValidationTypes . complex ) . forEach ( function ( nt ) {
var rule = ValidationTypes . complex [ nt ] ;
if ( typeof rule === "string" ) {
ValidationTypes . complex [ nt ] = Matcher . parse ( rule ) ;
}
} ) ;
// Because this is defined relative to other complex validation types,
// we need to define it *after* the rest of the types are initialized.
ValidationTypes . complex [ "<font-variant>" ] =
Matcher . oror ( { expand : "<font-variant-ligatures>" } ,
{ expand : "<font-variant-alternates>" } ,
"<font-variant-caps>" ,
{ expand : "<font-variant-numeric>" } ,
{ expand : "<font-variant-east-asian>" } ) ;
} , { "./Matcher" : 3 } ] , 22 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = {
Colors : require ( "./Colors" ) ,
Combinator : require ( "./Combinator" ) ,
Parser : require ( "./Parser" ) ,
PropertyName : require ( "./PropertyName" ) ,
PropertyValue : require ( "./PropertyValue" ) ,
PropertyValuePart : require ( "./PropertyValuePart" ) ,
Matcher : require ( "./Matcher" ) ,
MediaFeature : require ( "./MediaFeature" ) ,
MediaQuery : require ( "./MediaQuery" ) ,
Selector : require ( "./Selector" ) ,
SelectorPart : require ( "./SelectorPart" ) ,
SelectorSubPart : require ( "./SelectorSubPart" ) ,
Specificity : require ( "./Specificity" ) ,
TokenStream : require ( "./TokenStream" ) ,
Tokens : require ( "./Tokens" ) ,
ValidationError : require ( "./ValidationError" )
} ;
} , { "./Colors" : 1 , "./Combinator" : 2 , "./Matcher" : 3 , "./MediaFeature" : 4 , "./MediaQuery" : 5 , "./Parser" : 6 , "./PropertyName" : 8 , "./PropertyValue" : 9 , "./PropertyValuePart" : 11 , "./Selector" : 13 , "./SelectorPart" : 14 , "./SelectorSubPart" : 15 , "./Specificity" : 16 , "./TokenStream" : 17 , "./Tokens" : 18 , "./ValidationError" : 20 } ] , 23 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = EventTarget ;
/ * *
* A generic base to inherit from for any object
* that needs event handling .
* @ class EventTarget
* @ constructor
* /
function EventTarget ( ) {
/ * *
* The array of listeners for various events .
* @ type Object
* @ property _listeners
* @ private
* /
this . _listeners = Object . create ( null ) ;
}
EventTarget . prototype = {
//restore constructor
constructor : EventTarget ,
/ * *
* Adds a listener for a given event type .
* @ param { String } type The type of event to add a listener for .
* @ param { Function } listener The function to call when the event occurs .
* @ return { void }
* @ method addListener
* /
addListener : function ( type , listener ) {
if ( ! this . _listeners [ type ] ) {
this . _listeners [ type ] = [ ] ;
}
this . _listeners [ type ] . push ( listener ) ;
} ,
/ * *
* Fires an event based on the passed - in object .
* @ param { Object | String } event An object with at least a 'type' attribute
* or a string indicating the event name .
* @ return { void }
* @ method fire
* /
fire : function ( event ) {
if ( typeof event === "string" ) {
event = { type : event } ;
}
if ( typeof event . target !== "undefined" ) {
event . target = this ;
}
if ( typeof event . type === "undefined" ) {
throw new Error ( "Event object missing 'type' property." ) ;
}
if ( this . _listeners [ event . type ] ) {
//create a copy of the array and use that so listeners can't chane
var listeners = this . _listeners [ event . type ] . concat ( ) ;
for ( var i = 0 , len = listeners . length ; i < len ; i ++ ) {
listeners [ i ] . call ( this , event ) ;
}
}
} ,
/ * *
* Removes a listener for a given event type .
* @ param { String } type The type of event to remove a listener from .
* @ param { Function } listener The function to remove from the event .
* @ return { void }
* @ method removeListener
* /
removeListener : function ( type , listener ) {
if ( this . _listeners [ type ] ) {
var listeners = this . _listeners [ type ] ;
for ( var i = 0 , len = listeners . length ; i < len ; i ++ ) {
if ( listeners [ i ] === listener ) {
listeners . splice ( i , 1 ) ;
break ;
}
}
}
}
} ;
} , { } ] , 24 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = StringReader ;
/ * *
* Convenient way to read through strings .
* @ namespace parserlib . util
* @ class StringReader
* @ constructor
* @ param { String } text The text to read .
* /
function StringReader ( text ) {
/ * *
* The input text with line endings normalized .
* @ property _input
* @ type String
* @ private
* /
this . _input = text . replace ( /(\r\n?|\n)/g , "\n" ) ;
/ * *
* The row for the character to be read next .
* @ property _line
* @ type int
* @ private
* /
this . _line = 1 ;
/ * *
* The column for the character to be read next .
* @ property _col
* @ type int
* @ private
* /
this . _col = 1 ;
/ * *
* The index of the character in the input to be read next .
* @ property _cursor
* @ type int
* @ private
* /
this . _cursor = 0 ;
}
StringReader . prototype = {
// restore constructor
constructor : StringReader ,
//-------------------------------------------------------------------------
// Position info
//-------------------------------------------------------------------------
/ * *
* Returns the column of the character to be read next .
* @ return { int } The column of the character to be read next .
* @ method getCol
* /
getCol : function ( ) {
return this . _col ;
} ,
/ * *
* Returns the row of the character to be read next .
* @ return { int } The row of the character to be read next .
* @ method getLine
* /
getLine : function ( ) {
return this . _line ;
} ,
/ * *
* Determines if you ' re at the end of the input .
* @ return { Boolean } True if there ' s no more input , false otherwise .
* @ method eof
* /
eof : function ( ) {
return this . _cursor === this . _input . length ;
} ,
//-------------------------------------------------------------------------
// Basic reading
//-------------------------------------------------------------------------
/ * *
* Reads the next character without advancing the cursor .
* @ param { int } count How many characters to look ahead ( default is 1 ) .
* @ return { String } The next character or null if there is no next character .
* @ method peek
* /
peek : function ( count ) {
var c = null ;
count = typeof count === "undefined" ? 1 : count ;
// if we're not at the end of the input...
if ( this . _cursor < this . _input . length ) {
// get character and increment cursor and column
c = this . _input . charAt ( this . _cursor + count - 1 ) ;
}
return c ;
} ,
/ * *
* Reads the next character from the input and adjusts the row and column
* accordingly .
* @ return { String } The next character or null if there is no next character .
* @ method read
* /
read : function ( ) {
var c = null ;
// if we're not at the end of the input...
if ( this . _cursor < this . _input . length ) {
// if the last character was a newline, increment row count
// and reset column count
if ( this . _input . charAt ( this . _cursor ) === "\n" ) {
this . _line ++ ;
this . _col = 1 ;
} else {
this . _col ++ ;
}
// get character and increment cursor and column
c = this . _input . charAt ( this . _cursor ++ ) ;
}
return c ;
} ,
//-------------------------------------------------------------------------
// Misc
//-------------------------------------------------------------------------
/ * *
* Saves the current location so it can be returned to later .
* @ method mark
* @ return { void }
* /
mark : function ( ) {
this . _bookmark = {
cursor : this . _cursor ,
line : this . _line ,
col : this . _col
} ;
} ,
reset : function ( ) {
if ( this . _bookmark ) {
this . _cursor = this . _bookmark . cursor ;
this . _line = this . _bookmark . line ;
this . _col = this . _bookmark . col ;
delete this . _bookmark ;
}
} ,
//-------------------------------------------------------------------------
// Advanced reading
//-------------------------------------------------------------------------
/ * *
* Reads up to and including the given string . Throws an error if that
* string is not found .
* @ param { String } pattern The string to read .
* @ return { String } The string when it is found .
* @ throws Error when the string pattern is not found .
* @ method readTo
* /
readTo : function ( pattern ) {
var buffer = "" ,
c ;
/ *
* First , buffer must be the same length as the pattern .
* Then , buffer must end with the pattern or else reach the
* end of the input .
* /
while ( buffer . length < pattern . length || buffer . lastIndexOf ( pattern ) !== buffer . length - pattern . length ) {
c = this . read ( ) ;
if ( c ) {
buffer += c ;
} else {
throw new Error ( "Expected \"" + pattern + "\" at line " + this . _line + ", col " + this . _col + "." ) ;
}
}
return buffer ;
} ,
/ * *
* Reads characters while each character causes the given
* filter function to return true . The function is passed
* in each character and either returns true to continue
* reading or false to stop .
* @ param { Function } filter The function to read on each character .
* @ return { String } The string made up of all characters that passed the
* filter check .
* @ method readWhile
* /
readWhile : function ( filter ) {
var buffer = "" ,
c = this . peek ( ) ;
while ( c !== null && filter ( c ) ) {
buffer += this . read ( ) ;
c = this . peek ( ) ;
}
return buffer ;
} ,
/ * *
* Reads characters that match either text or a regular expression and
* returns those characters . If a match is found , the row and column
* are adjusted ; if no match is found , the reader ' s state is unchanged .
* reading or false to stop .
* @ param { String | RegExp } matcher If a string , then the literal string
* value is searched for . If a regular expression , then any string
* matching the pattern is search for .
* @ return { String } The string made up of all characters that matched or
* null if there was no match .
* @ method readMatch
* /
readMatch : function ( matcher ) {
var source = this . _input . substring ( this . _cursor ) ,
value = null ;
// if it's a string, just do a straight match
if ( typeof matcher === "string" ) {
if ( source . slice ( 0 , matcher . length ) === matcher ) {
value = this . readCount ( matcher . length ) ;
}
} else if ( matcher instanceof RegExp ) {
if ( matcher . test ( source ) ) {
value = this . readCount ( RegExp . lastMatch . length ) ;
}
}
return value ;
} ,
/ * *
* Reads a given number of characters . If the end of the input is reached ,
* it reads only the remaining characters and does not throw an error .
* @ param { int } count The number of characters to read .
* @ return { String } The string made up the read characters .
* @ method readCount
* /
readCount : function ( count ) {
var buffer = "" ;
while ( count -- ) {
buffer += this . read ( ) ;
}
return buffer ;
}
} ;
} , { } ] , 25 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = SyntaxError ;
/ * *
* Type to use when a syntax error occurs .
* @ class SyntaxError
* @ namespace parserlib . util
* @ constructor
* @ param { String } message The error message .
* @ param { int } line The line at which the error occurred .
* @ param { int } col The column at which the error occurred .
* /
function SyntaxError ( message , line , col ) {
Error . call ( this ) ;
this . name = this . constructor . name ;
/ * *
* The column at which the error occurred .
* @ type int
* @ property col
* /
this . col = col ;
/ * *
* The line at which the error occurred .
* @ type int
* @ property line
* /
this . line = line ;
/ * *
* The text representation of the unit .
* @ type String
* @ property text
* /
this . message = message ;
}
//inherit from Error
SyntaxError . prototype = Object . create ( Error . prototype ) ; // jshint ignore:line
SyntaxError . prototype . constructor = SyntaxError ; // jshint ignore:line
} , { } ] , 26 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = SyntaxUnit ;
/ * *
* Base type to represent a single syntactic unit .
* @ class SyntaxUnit
* @ namespace parserlib . util
* @ constructor
* @ param { String } text The text of the unit .
* @ param { int } line The line of text on which the unit resides .
* @ param { int } col The column of text on which the unit resides .
* /
function SyntaxUnit ( text , line , col , type ) {
/ * *
* The column of text on which the unit resides .
* @ type int
* @ property col
* /
this . col = col ;
/ * *
* The line of text on which the unit resides .
* @ type int
* @ property line
* /
this . line = line ;
/ * *
* The text representation of the unit .
* @ type String
* @ property text
* /
this . text = text ;
/ * *
* The type of syntax unit .
* @ type int
* @ property type
* /
this . type = type ;
}
/ * *
* Create a new syntax unit based solely on the given token .
* Convenience method for creating a new syntax unit when
* it represents a single token instead of multiple .
* @ param { Object } token The token object to represent .
* @ return { parserlib . util . SyntaxUnit } The object representing the token .
* @ static
* @ method fromToken
* /
SyntaxUnit . fromToken = function ( token ) {
return new SyntaxUnit ( token . value , token . startLine , token . startCol ) ;
} ;
SyntaxUnit . prototype = {
//restore constructor
constructor : SyntaxUnit ,
/ * *
* Returns the text representation of the unit .
* @ return { String } The text representation of the unit .
* @ method valueOf
* /
valueOf : function ( ) {
return this . toString ( ) ;
} ,
/ * *
* Returns the text representation of the unit .
* @ return { String } The text representation of the unit .
* @ method toString
* /
toString : function ( ) {
return this . text ;
}
} ;
} , { } ] , 27 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = TokenStreamBase ;
var StringReader = require ( "./StringReader" ) ;
var SyntaxError = require ( "./SyntaxError" ) ;
/ * *
* Generic TokenStream providing base functionality .
* @ class TokenStreamBase
* @ namespace parserlib . util
* @ constructor
* @ param { String | StringReader } input The text to tokenize or a reader from
* which to read the input .
* /
function TokenStreamBase ( input , tokenData ) {
/ * *
* The string reader for easy access to the text .
* @ type StringReader
* @ property _reader
* @ private
* /
this . _reader = new StringReader ( input ? input . toString ( ) : "" ) ;
/ * *
* Token object for the last consumed token .
* @ type Token
* @ property _token
* @ private
* /
this . _token = null ;
/ * *
* The array of token information .
* @ type Array
* @ property _tokenData
* @ private
* /
this . _tokenData = tokenData ;
/ * *
* Lookahead token buffer .
* @ type Array
* @ property _lt
* @ private
* /
this . _lt = [ ] ;
/ * *
* Lookahead token buffer index .
* @ type int
* @ property _ltIndex
* @ private
* /
this . _ltIndex = 0 ;
this . _ltIndexCache = [ ] ;
}
/ * *
* Accepts an array of token information and outputs
* an array of token data containing key - value mappings
* and matching functions that the TokenStream needs .
* @ param { Array } tokens An array of token descriptors .
* @ return { Array } An array of processed token data .
* @ method createTokenData
* @ static
* /
TokenStreamBase . createTokenData = function ( tokens ) {
var nameMap = [ ] ,
typeMap = Object . create ( null ) ,
tokenData = tokens . concat ( [ ] ) ,
i = 0 ,
len = tokenData . length + 1 ;
tokenData . UNKNOWN = - 1 ;
tokenData . unshift ( { name : "EOF" } ) ;
for ( ; i < len ; i ++ ) {
nameMap . push ( tokenData [ i ] . name ) ;
tokenData [ tokenData [ i ] . name ] = i ;
if ( tokenData [ i ] . text ) {
typeMap [ tokenData [ i ] . text ] = i ;
}
}
tokenData . name = function ( tt ) {
return nameMap [ tt ] ;
} ;
tokenData . type = function ( c ) {
return typeMap [ c ] ;
} ;
return tokenData ;
} ;
TokenStreamBase . prototype = {
//restore constructor
constructor : TokenStreamBase ,
//-------------------------------------------------------------------------
// Matching methods
//-------------------------------------------------------------------------
/ * *
* Determines if the next token matches the given token type .
* If so , that token is consumed ; if not , the token is placed
* back onto the token stream . You can pass in any number of
* token types and this will return true if any of the token
* types is found .
* @ param { int | int [ ] } tokenTypes Either a single token type or an array of
* token types that the next token might be . If an array is passed ,
* it ' s assumed that the token can be any of these .
* @ param { variant } channel ( Optional ) The channel to read from . If not
* provided , reads from the default ( unnamed ) channel .
* @ return { Boolean } True if the token type matches , false if not .
* @ method match
* /
match : function ( tokenTypes , channel ) {
//always convert to an array, makes things easier
if ( ! ( tokenTypes instanceof Array ) ) {
tokenTypes = [ tokenTypes ] ;
}
var tt = this . get ( channel ) ,
i = 0 ,
len = tokenTypes . length ;
while ( i < len ) {
if ( tt === tokenTypes [ i ++ ] ) {
return true ;
}
}
//no match found, put the token back
this . unget ( ) ;
return false ;
} ,
/ * *
* Determines if the next token matches the given token type .
* If so , that token is consumed ; if not , an error is thrown .
* @ param { int | int [ ] } tokenTypes Either a single token type or an array of
* token types that the next token should be . If an array is passed ,
* it ' s assumed that the token must be one of these .
* @ return { void }
* @ method mustMatch
* /
mustMatch : function ( tokenTypes ) {
var token ;
//always convert to an array, makes things easier
if ( ! ( tokenTypes instanceof Array ) ) {
tokenTypes = [ tokenTypes ] ;
}
if ( ! this . match . apply ( this , arguments ) ) {
token = this . LT ( 1 ) ;
throw new SyntaxError ( "Expected " + this . _tokenData [ tokenTypes [ 0 ] ] . name +
" at line " + token . startLine + ", col " + token . startCol + "." , token . startLine , token . startCol ) ;
}
} ,
//-------------------------------------------------------------------------
// Consuming methods
//-------------------------------------------------------------------------
/ * *
* Keeps reading from the token stream until either one of the specified
* token types is found or until the end of the input is reached .
* @ param { int | int [ ] } tokenTypes Either a single token type or an array of
* token types that the next token should be . If an array is passed ,
* it ' s assumed that the token must be one of these .
* @ param { variant } channel ( Optional ) The channel to read from . If not
* provided , reads from the default ( unnamed ) channel .
* @ return { void }
* @ method advance
* /
advance : function ( tokenTypes , channel ) {
while ( this . LA ( 0 ) !== 0 && ! this . match ( tokenTypes , channel ) ) {
this . get ( ) ;
}
return this . LA ( 0 ) ;
} ,
/ * *
* Consumes the next token from the token stream .
* @ return { int } The token type of the token that was just consumed .
* @ method get
* /
get : function ( channel ) {
var tokenInfo = this . _tokenData ,
i = 0 ,
token ,
info ;
//check the lookahead buffer first
if ( this . _lt . length && this . _ltIndex >= 0 && this . _ltIndex < this . _lt . length ) {
i ++ ;
this . _token = this . _lt [ this . _ltIndex ++ ] ;
info = tokenInfo [ this . _token . type ] ;
//obey channels logic
while ( ( info . channel !== undefined && channel !== info . channel ) &&
this . _ltIndex < this . _lt . length ) {
this . _token = this . _lt [ this . _ltIndex ++ ] ;
info = tokenInfo [ this . _token . type ] ;
i ++ ;
}
//here be dragons
if ( ( info . channel === undefined || channel === info . channel ) &&
this . _ltIndex <= this . _lt . length ) {
this . _ltIndexCache . push ( i ) ;
return this . _token . type ;
}
}
//call token retriever method
token = this . _getToken ( ) ;
//if it should be hidden, don't save a token
if ( token . type > - 1 && ! tokenInfo [ token . type ] . hide ) {
//apply token channel
token . channel = tokenInfo [ token . type ] . channel ;
//save for later
this . _token = token ;
this . _lt . push ( token ) ;
//save space that will be moved (must be done before array is truncated)
this . _ltIndexCache . push ( this . _lt . length - this . _ltIndex + i ) ;
//keep the buffer under 5 items
if ( this . _lt . length > 5 ) {
this . _lt . shift ( ) ;
}
//also keep the shift buffer under 5 items
if ( this . _ltIndexCache . length > 5 ) {
this . _ltIndexCache . shift ( ) ;
}
//update lookahead index
this . _ltIndex = this . _lt . length ;
}
/ *
* Skip to the next token if :
* 1. The token type is marked as hidden .
* 2. The token type has a channel specified and it isn ' t the current channel .
* /
info = tokenInfo [ token . type ] ;
if ( info &&
( info . hide ||
( info . channel !== undefined && channel !== info . channel ) ) ) {
return this . get ( channel ) ;
} else {
//return just the type
return token . type ;
}
} ,
/ * *
* Looks ahead a certain number of tokens and returns the token type at
* that position . This will throw an error if you lookahead past the
* end of input , past the size of the lookahead buffer , or back past
* the first token in the lookahead buffer .
* @ param { int } The index of the token type to retrieve . 0 for the
* current token , 1 for the next , - 1 for the previous , etc .
* @ return { int } The token type of the token in the given position .
* @ method LA
* /
LA : function ( index ) {
var total = index ,
tt ;
if ( index > 0 ) {
//TODO: Store 5 somewhere
if ( index > 5 ) {
throw new Error ( "Too much lookahead." ) ;
}
//get all those tokens
while ( total ) {
tt = this . get ( ) ;
total -- ;
}
//unget all those tokens
while ( total < index ) {
this . unget ( ) ;
total ++ ;
}
} else if ( index < 0 ) {
if ( this . _lt [ this . _ltIndex + index ] ) {
tt = this . _lt [ this . _ltIndex + index ] . type ;
} else {
throw new Error ( "Too much lookbehind." ) ;
}
} else {
tt = this . _token . type ;
}
return tt ;
} ,
/ * *
* Looks ahead a certain number of tokens and returns the token at
* that position . This will throw an error if you lookahead past the
* end of input , past the size of the lookahead buffer , or back past
* the first token in the lookahead buffer .
* @ param { int } The index of the token type to retrieve . 0 for the
* current token , 1 for the next , - 1 for the previous , etc .
* @ return { Object } The token of the token in the given position .
* @ method LA
* /
LT : function ( index ) {
//lookahead first to prime the token buffer
this . LA ( index ) ;
//now find the token, subtract one because _ltIndex is already at the next index
return this . _lt [ this . _ltIndex + index - 1 ] ;
} ,
/ * *
* Returns the token type for the next token in the stream without
* consuming it .
* @ return { int } The token type of the next token in the stream .
* @ method peek
* /
peek : function ( ) {
return this . LA ( 1 ) ;
} ,
/ * *
* Returns the actual token object for the last consumed token .
* @ return { Token } The token object for the last consumed token .
* @ method token
* /
token : function ( ) {
return this . _token ;
} ,
/ * *
* Returns the name of the token for the given token type .
* @ param { int } tokenType The type of token to get the name of .
* @ return { String } The name of the token or "UNKNOWN_TOKEN" for any
* invalid token type .
* @ method tokenName
* /
tokenName : function ( tokenType ) {
if ( tokenType < 0 || tokenType > this . _tokenData . length ) {
return "UNKNOWN_TOKEN" ;
} else {
return this . _tokenData [ tokenType ] . name ;
}
} ,
/ * *
* Returns the token type value for the given token name .
* @ param { String } tokenName The name of the token whose value should be returned .
* @ return { int } The token type value for the given token name or - 1
* for an unknown token .
* @ method tokenName
* /
tokenType : function ( tokenName ) {
return this . _tokenData [ tokenName ] || - 1 ;
} ,
/ * *
* Returns the last consumed token to the token stream .
* @ method unget
* /
unget : function ( ) {
//if (this._ltIndex > -1) {
if ( this . _ltIndexCache . length ) {
this . _ltIndex -= this . _ltIndexCache . pop ( ) ; //--;
this . _token = this . _lt [ this . _ltIndex - 1 ] ;
} else {
throw new Error ( "Too much lookahead." ) ;
}
}
} ;
} , { "./StringReader" : 24 , "./SyntaxError" : 25 } ] , 28 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = {
StringReader : require ( "./StringReader" ) ,
SyntaxError : require ( "./SyntaxError" ) ,
SyntaxUnit : require ( "./SyntaxUnit" ) ,
EventTarget : require ( "./EventTarget" ) ,
TokenStreamBase : require ( "./TokenStreamBase" )
} ;
} , { "./EventTarget" : 23 , "./StringReader" : 24 , "./SyntaxError" : 25 , "./SyntaxUnit" : 26 , "./TokenStreamBase" : 27 } ] , "parserlib" : [ function ( require , module , exports ) {
"use strict" ;
module . exports = {
css : require ( "./css" ) ,
util : require ( "./util" )
} ;
} , { "./css" : 22 , "./util" : 28 } ] } , { } , [ ] ) ;
return require ( 'parserlib' ) ;
} ) ( ) ;
var clone = ( function ( ) {
'use strict' ;
var nativeMap ;
try {
nativeMap = Map ;
} catch ( _ ) {
// maybe a reference error because no `Map`. Give it a dummy value that no
// value will ever be an instanceof.
nativeMap = function ( ) { } ;
}
var nativeSet ;
try {
nativeSet = Set ;
} catch ( _ ) {
nativeSet = function ( ) { } ;
}
var nativePromise ;
try {
nativePromise = Promise ;
} catch ( _ ) {
nativePromise = function ( ) { } ;
}
/ * *
* Clones ( copies ) an Object using deep copying .
*
* This function supports circular references by default , but if you are certain
* there are no circular references in your object , you can save some CPU time
* by calling clone ( obj , false ) .
*
* Caution : if ` circular ` is false and ` parent ` contains circular references ,
* your program may enter an infinite loop and crash .
*
* @ param ` parent ` - the object to be cloned
* @ param ` circular ` - set to true if the object to be cloned may contain
* circular references . ( optional - true by default )
* @ param ` depth ` - set to a number if the object is only to be cloned to
* a particular depth . ( optional - defaults to Infinity )
* @ param ` prototype ` - sets the prototype to be used when cloning an object .
* ( optional - defaults to parent prototype ) .
* @ param ` includeNonEnumerable ` - set to true if the non - enumerable properties
* should be cloned as well . Non - enumerable properties on the prototype
* chain will be ignored . ( optional - false by default )
* /
function clone ( parent , circular , depth , prototype , includeNonEnumerable ) {
if ( typeof circular === 'object' ) {
depth = circular . depth ;
prototype = circular . prototype ;
includeNonEnumerable = circular . includeNonEnumerable ;
circular = circular . circular ;
}
// maintain two arrays for circular references, where corresponding parents
// and children have the same index
var allParents = [ ] ;
var allChildren = [ ] ;
var useBuffer = typeof Buffer != 'undefined' ;
if ( typeof circular == 'undefined' )
circular = true ;
if ( typeof depth == 'undefined' )
depth = Infinity ;
// recurse this function so we don't reset allParents and allChildren
function _clone ( parent , depth ) {
// cloning null always returns null
if ( parent === null )
return null ;
if ( depth === 0 )
return parent ;
var child ;
var proto ;
if ( typeof parent != 'object' ) {
return parent ;
}
if ( parent instanceof nativeMap ) {
child = new nativeMap ( ) ;
} else if ( parent instanceof nativeSet ) {
child = new nativeSet ( ) ;
} else if ( parent instanceof nativePromise ) {
child = new nativePromise ( function ( resolve , reject ) {
parent . then ( function ( value ) {
resolve ( _clone ( value , depth - 1 ) ) ;
} , function ( err ) {
reject ( _clone ( err , depth - 1 ) ) ;
} ) ;
} ) ;
} else if ( clone . _ _isArray ( parent ) ) {
child = [ ] ;
} else if ( clone . _ _isRegExp ( parent ) ) {
child = new RegExp ( parent . source , _ _getRegExpFlags ( parent ) ) ;
if ( parent . lastIndex ) child . lastIndex = parent . lastIndex ;
} else if ( clone . _ _isDate ( parent ) ) {
child = new Date ( parent . getTime ( ) ) ;
} else if ( useBuffer && Buffer . isBuffer ( parent ) ) {
child = new Buffer ( parent . length ) ;
parent . copy ( child ) ;
return child ;
} else if ( parent instanceof Error ) {
child = Object . create ( parent ) ;
} else {
if ( typeof prototype == 'undefined' ) {
proto = Object . getPrototypeOf ( parent ) ;
child = Object . create ( proto ) ;
}
else {
child = Object . create ( prototype ) ;
proto = prototype ;
}
}
if ( circular ) {
var index = allParents . indexOf ( parent ) ;
if ( index != - 1 ) {
return allChildren [ index ] ;
}
allParents . push ( parent ) ;
allChildren . push ( child ) ;
}
if ( parent instanceof nativeMap ) {
var keyIterator = parent . keys ( ) ;
while ( true ) {
var next = keyIterator . next ( ) ;
if ( next . done ) {
break ;
}
var keyChild = _clone ( next . value , depth - 1 ) ;
var valueChild = _clone ( parent . get ( next . value ) , depth - 1 ) ;
child . set ( keyChild , valueChild ) ;
}
}
if ( parent instanceof nativeSet ) {
var iterator = parent . keys ( ) ;
while ( true ) {
var next = iterator . next ( ) ;
if ( next . done ) {
break ;
}
var entryChild = _clone ( next . value , depth - 1 ) ;
child . add ( entryChild ) ;
}
}
for ( var i in parent ) {
var attrs ;
if ( proto ) {
attrs = Object . getOwnPropertyDescriptor ( proto , i ) ;
}
if ( attrs && attrs . set == null ) {
continue ;
}
child [ i ] = _clone ( parent [ i ] , depth - 1 ) ;
}
if ( Object . getOwnPropertySymbols ) {
var symbols = Object . getOwnPropertySymbols ( parent ) ;
for ( var i = 0 ; i < symbols . length ; i ++ ) {
// Don't need to worry about cloning a symbol because it is a primitive,
// like a number or string.
var symbol = symbols [ i ] ;
var descriptor = Object . getOwnPropertyDescriptor ( parent , symbol ) ;
if ( descriptor && ! descriptor . enumerable && ! includeNonEnumerable ) {
continue ;
}
child [ symbol ] = _clone ( parent [ symbol ] , depth - 1 ) ;
if ( ! descriptor . enumerable ) {
Object . defineProperty ( child , symbol , {
enumerable : false
} ) ;
}
}
}
if ( includeNonEnumerable ) {
var allPropertyNames = Object . getOwnPropertyNames ( parent ) ;
for ( var i = 0 ; i < allPropertyNames . length ; i ++ ) {
var propertyName = allPropertyNames [ i ] ;
var descriptor = Object . getOwnPropertyDescriptor ( parent , propertyName ) ;
if ( descriptor && descriptor . enumerable ) {
continue ;
}
child [ propertyName ] = _clone ( parent [ propertyName ] , depth - 1 ) ;
Object . defineProperty ( child , propertyName , {
enumerable : false
} ) ;
}
}
return child ;
}
return _clone ( parent , depth ) ;
}
/ * *
* Simple flat clone using prototype , accepts only objects , usefull for property
* override on FLAT configuration object ( no nested props ) .
*
* USE WITH CAUTION ! This may not behave as you wish if you do not know how this
* works .
* /
clone . clonePrototype = function clonePrototype ( parent ) {
if ( parent === null )
return null ;
var c = function ( ) { } ;
c . prototype = parent ;
return new c ( ) ;
} ;
// private utility functions
function _ _objToStr ( o ) {
return Object . prototype . toString . call ( o ) ;
}
clone . _ _objToStr = _ _objToStr ;
function _ _isDate ( o ) {
return typeof o === 'object' && _ _objToStr ( o ) === '[object Date]' ;
}
clone . _ _isDate = _ _isDate ;
function _ _isArray ( o ) {
return typeof o === 'object' && _ _objToStr ( o ) === '[object Array]' ;
}
clone . _ _isArray = _ _isArray ;
function _ _isRegExp ( o ) {
return typeof o === 'object' && _ _objToStr ( o ) === '[object RegExp]' ;
}
clone . _ _isRegExp = _ _isRegExp ;
function _ _getRegExpFlags ( re ) {
var flags = '' ;
if ( re . global ) flags += 'g' ;
if ( re . ignoreCase ) flags += 'i' ;
if ( re . multiline ) flags += 'm' ;
return flags ;
}
clone . _ _getRegExpFlags = _ _getRegExpFlags ;
return clone ;
} ) ( ) ;
if ( typeof module === 'object' && module . exports ) {
module . exports = clone ;
}
/ * *
* Main CSSLint object .
* @ class CSSLint
* @ static
* @ extends parserlib . util . EventTarget
* /
/* global parserlib, clone, Reporter */
/* exported CSSLint */
var CSSLint = ( function ( ) {
"use strict" ;
var rules = [ ] ,
formatters = [ ] ,
embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\// ,
api = new parserlib . util . EventTarget ( ) ;
api . version = "1.0.4" ;
//-------------------------------------------------------------------------
// Rule Management
//-------------------------------------------------------------------------
/ * *
* Adds a new rule to the engine .
* @ param { Object } rule The rule to add .
* @ method addRule
* /
api . addRule = function ( rule ) {
rules . push ( rule ) ;
rules [ rule . id ] = rule ;
} ;
/ * *
* Clears all rule from the engine .
* @ method clearRules
* /
api . clearRules = function ( ) {
rules = [ ] ;
} ;
/ * *
* Returns the rule objects .
* @ return An array of rule objects .
* @ method getRules
* /
api . getRules = function ( ) {
return [ ] . concat ( rules ) . sort ( function ( a , b ) {
return a . id > b . id ? 1 : 0 ;
} ) ;
} ;
/ * *
* Returns a ruleset configuration object with all current rules .
* @ return A ruleset object .
* @ method getRuleset
* /
api . getRuleset = function ( ) {
var ruleset = { } ,
i = 0 ,
len = rules . length ;
while ( i < len ) {
ruleset [ rules [ i ++ ] . id ] = 1 ; // by default, everything is a warning
}
return ruleset ;
} ;
/ * *
* Returns a ruleset object based on embedded rules .
* @ param { String } text A string of css containing embedded rules .
* @ param { Object } ruleset A ruleset object to modify .
* @ return { Object } A ruleset object .
* @ method getEmbeddedRuleset
* /
function applyEmbeddedRuleset ( text , ruleset ) {
var valueMap ,
embedded = text && text . match ( embeddedRuleset ) ,
rules = embedded && embedded [ 1 ] ;
if ( rules ) {
valueMap = {
"true" : 2 , // true is error
"" : 1 , // blank is warning
"false" : 0 , // false is ignore
"2" : 2 , // explicit error
"1" : 1 , // explicit warning
"0" : 0 // explicit ignore
} ;
rules . toLowerCase ( ) . split ( "," ) . forEach ( function ( rule ) {
var pair = rule . split ( ":" ) ,
property = pair [ 0 ] || "" ,
value = pair [ 1 ] || "" ;
ruleset [ property . trim ( ) ] = valueMap [ value . trim ( ) ] ;
} ) ;
}
return ruleset ;
}
//-------------------------------------------------------------------------
// Formatters
//-------------------------------------------------------------------------
/ * *
* Adds a new formatter to the engine .
* @ param { Object } formatter The formatter to add .
* @ method addFormatter
* /
api . addFormatter = function ( formatter ) {
// formatters.push(formatter);
formatters [ formatter . id ] = formatter ;
} ;
/ * *
* Retrieves a formatter for use .
* @ param { String } formatId The name of the format to retrieve .
* @ return { Object } The formatter or undefined .
* @ method getFormatter
* /
api . getFormatter = function ( formatId ) {
return formatters [ formatId ] ;
} ;
/ * *
* Formats the results in a particular format for a single file .
* @ param { Object } result The results returned from CSSLint . verify ( ) .
* @ param { String } filename The filename for which the results apply .
* @ param { String } formatId The name of the formatter to use .
* @ param { Object } options ( Optional ) for special output handling .
* @ return { String } A formatted string for the results .
* @ method format
* /
api . format = function ( results , filename , formatId , options ) {
var formatter = this . getFormatter ( formatId ) ,
result = null ;
if ( formatter ) {
result = formatter . startFormat ( ) ;
result += formatter . formatResults ( results , filename , options || { } ) ;
result += formatter . endFormat ( ) ;
}
return result ;
} ;
/ * *
* Indicates if the given format is supported .
* @ param { String } formatId The ID of the format to check .
* @ return { Boolean } True if the format exists , false if not .
* @ method hasFormat
* /
api . hasFormat = function ( formatId ) {
return formatters . hasOwnProperty ( formatId ) ;
} ;
//-------------------------------------------------------------------------
// Verification
//-------------------------------------------------------------------------
/ * *
* Starts the verification process for the given CSS text .
* @ param { String } text The CSS text to verify .
* @ param { Object } ruleset ( Optional ) List of rules to apply . If null , then
* all rules are used . If a rule has a value of 1 then it ' s a warning ,
* a value of 2 means it ' s an error .
* @ return { Object } Results of the verification .
* @ method verify
* /
api . verify = function ( text , ruleset ) {
var i = 0 ,
reporter ,
lines ,
allow = { } ,
ignore = [ ] ,
report ,
parser = new parserlib . css . Parser ( {
starHack : true ,
ieFilters : true ,
underscoreHack : true ,
strict : false
} ) ;
// normalize line endings
lines = text . replace ( /\n\r?/g , "$split$" ) . split ( "$split$" ) ;
// find 'allow' comments
CSSLint . Util . forEach ( lines , function ( line , lineno ) {
var allowLine = line && line . match ( /\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i ) ,
allowRules = allowLine && allowLine [ 1 ] ,
allowRuleset = { } ;
if ( allowRules ) {
allowRules . toLowerCase ( ) . split ( "," ) . forEach ( function ( allowRule ) {
allowRuleset [ allowRule . trim ( ) ] = true ;
} ) ;
if ( Object . keys ( allowRuleset ) . length > 0 ) {
allow [ lineno + 1 ] = allowRuleset ;
}
}
} ) ;
var ignoreStart = null ,
ignoreEnd = null ;
CSSLint . Util . forEach ( lines , function ( line , lineno ) {
// Keep oldest, "unclosest" ignore:start
if ( ignoreStart === null && line . match ( /\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i ) ) {
ignoreStart = lineno ;
}
if ( line . match ( /\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i ) ) {
ignoreEnd = lineno ;
}
if ( ignoreStart !== null && ignoreEnd !== null ) {
ignore . push ( [ ignoreStart , ignoreEnd ] ) ;
ignoreStart = ignoreEnd = null ;
}
} ) ;
// Close remaining ignore block, if any
if ( ignoreStart !== null ) {
ignore . push ( [ ignoreStart , lines . length ] ) ;
}
if ( ! ruleset ) {
ruleset = this . getRuleset ( ) ;
}
if ( embeddedRuleset . test ( text ) ) {
// defensively copy so that caller's version does not get modified
ruleset = clone ( ruleset ) ;
ruleset = applyEmbeddedRuleset ( text , ruleset ) ;
}
reporter = new Reporter ( lines , ruleset , allow , ignore ) ;
ruleset . errors = 2 ; // always report parsing errors as errors
for ( i in ruleset ) {
if ( ruleset . hasOwnProperty ( i ) && ruleset [ i ] ) {
if ( rules [ i ] ) {
rules [ i ] . init ( parser , reporter ) ;
}
}
}
// capture most horrible error type
try {
parser . parse ( text ) ;
} catch ( ex ) {
reporter . error ( "Fatal error, cannot continue: " + ex . message , ex . line , ex . col , { } ) ;
}
report = {
messages : reporter . messages ,
stats : reporter . stats ,
ruleset : reporter . ruleset ,
allow : reporter . allow ,
ignore : reporter . ignore
} ;
// sort by line numbers, rollups at the bottom
report . messages . sort ( function ( a , b ) {
if ( a . rollup && ! b . rollup ) {
return 1 ;
} else if ( ! a . rollup && b . rollup ) {
return - 1 ;
} else {
return a . line - b . line ;
}
} ) ;
return report ;
} ;
//-------------------------------------------------------------------------
// Publish the API
//-------------------------------------------------------------------------
return api ;
} ) ( ) ;
/ * *
* An instance of Report is used to report results of the
* verification back to the main API .
* @ class Reporter
* @ constructor
* @ param { String [ ] } lines The text lines of the source .
* @ param { Object } ruleset The set of rules to work with , including if
* they are errors or warnings .
* @ param { Object } explicitly allowed lines
* @ param { [ ] [ ] } ingore list of line ranges to be ignored
* /
function Reporter ( lines , ruleset , allow , ignore ) {
"use strict" ;
/ * *
* List of messages being reported .
* @ property messages
* @ type String [ ]
* /
this . messages = [ ] ;
/ * *
* List of statistics being reported .
* @ property stats
* @ type String [ ]
* /
this . stats = [ ] ;
/ * *
* Lines of code being reported on . Used to provide contextual information
* for messages .
* @ property lines
* @ type String [ ]
* /
this . lines = lines ;
/ * *
* Information about the rules . Used to determine whether an issue is an
* error or warning .
* @ property ruleset
* @ type Object
* /
this . ruleset = ruleset ;
/ * *
* Lines with specific rule messages to leave out of the report .
* @ property allow
* @ type Object
* /
this . allow = allow ;
if ( ! this . allow ) {
this . allow = { } ;
}
/ * *
* Linesets not to include in the report .
* @ property ignore
* @ type [ ] [ ]
* /
this . ignore = ignore ;
if ( ! this . ignore ) {
this . ignore = [ ] ;
}
}
Reporter . prototype = {
// restore constructor
constructor : Reporter ,
/ * *
* Report an error .
* @ param { String } message The message to store .
* @ param { int } line The line number .
* @ param { int } col The column number .
* @ param { Object } rule The rule this message relates to .
* @ method error
* /
error : function ( message , line , col , rule ) {
"use strict" ;
this . messages . push ( {
type : "error" ,
line : line ,
col : col ,
message : message ,
evidence : this . lines [ line - 1 ] ,
rule : rule || { }
} ) ;
} ,
/ * *
* Report an warning .
* @ param { String } message The message to store .
* @ param { int } line The line number .
* @ param { int } col The column number .
* @ param { Object } rule The rule this message relates to .
* @ method warn
* @ deprecated Use report instead .
* /
warn : function ( message , line , col , rule ) {
"use strict" ;
this . report ( message , line , col , rule ) ;
} ,
/ * *
* Report an issue .
* @ param { String } message The message to store .
* @ param { int } line The line number .
* @ param { int } col The column number .
* @ param { Object } rule The rule this message relates to .
* @ method report
* /
report : function ( message , line , col , rule ) {
"use strict" ;
// Check if rule violation should be allowed
if ( this . allow . hasOwnProperty ( line ) && this . allow [ line ] . hasOwnProperty ( rule . id ) ) {
return ;
}
var ignore = false ;
CSSLint . Util . forEach ( this . ignore , function ( range ) {
if ( range [ 0 ] <= line && line <= range [ 1 ] ) {
ignore = true ;
}
} ) ;
if ( ignore ) {
return ;
}
this . messages . push ( {
type : this . ruleset [ rule . id ] === 2 ? "error" : "warning" ,
line : line ,
col : col ,
message : message ,
evidence : this . lines [ line - 1 ] ,
rule : rule
} ) ;
} ,
/ * *
* Report some informational text .
* @ param { String } message The message to store .
* @ param { int } line The line number .
* @ param { int } col The column number .
* @ param { Object } rule The rule this message relates to .
* @ method info
* /
info : function ( message , line , col , rule ) {
"use strict" ;
this . messages . push ( {
type : "info" ,
line : line ,
col : col ,
message : message ,
evidence : this . lines [ line - 1 ] ,
rule : rule
} ) ;
} ,
/ * *
* Report some rollup error information .
* @ param { String } message The message to store .
* @ param { Object } rule The rule this message relates to .
* @ method rollupError
* /
rollupError : function ( message , rule ) {
"use strict" ;
this . messages . push ( {
type : "error" ,
rollup : true ,
message : message ,
rule : rule
} ) ;
} ,
/ * *
* Report some rollup warning information .
* @ param { String } message The message to store .
* @ param { Object } rule The rule this message relates to .
* @ method rollupWarn
* /
rollupWarn : function ( message , rule ) {
"use strict" ;
this . messages . push ( {
type : "warning" ,
rollup : true ,
message : message ,
rule : rule
} ) ;
} ,
/ * *
* Report a statistic .
* @ param { String } name The name of the stat to store .
* @ param { Variant } value The value of the stat .
* @ method stat
* /
stat : function ( name , value ) {
"use strict" ;
this . stats [ name ] = value ;
}
} ;
// expose for testing purposes
CSSLint . _Reporter = Reporter ;
/ *
* Utility functions that make life easier .
* /
CSSLint . Util = {
/ *
* Adds all properties from supplier onto receiver ,
* overwriting if the same name already exists on
* receiver .
* @ param { Object } The object to receive the properties .
* @ param { Object } The object to provide the properties .
* @ return { Object } The receiver
* /
mix : function ( receiver , supplier ) {
"use strict" ;
var prop ;
for ( prop in supplier ) {
if ( supplier . hasOwnProperty ( prop ) ) {
receiver [ prop ] = supplier [ prop ] ;
}
}
return prop ;
} ,
/ *
* Polyfill for array indexOf ( ) method .
* @ param { Array } values The array to search .
* @ param { Variant } value The value to search for .
* @ return { int } The index of the value if found , - 1 if not .
* /
indexOf : function ( values , value ) {
"use strict" ;
if ( values . indexOf ) {
return values . indexOf ( value ) ;
} else {
for ( var i = 0 , len = values . length ; i < len ; i ++ ) {
if ( values [ i ] === value ) {
return i ;
}
}
return - 1 ;
}
} ,
/ *
* Polyfill for array forEach ( ) method .
* @ param { Array } values The array to operate on .
* @ param { Function } func The function to call on each item .
* @ return { void }
* /
forEach : function ( values , func ) {
"use strict" ;
if ( values . forEach ) {
return values . forEach ( func ) ;
} else {
for ( var i = 0 , len = values . length ; i < len ; i ++ ) {
func ( values [ i ] , i , values ) ;
}
}
}
} ;
/ *
* Rule : Don ' t use adjoining classes ( . foo . bar ) .
* /
CSSLint . addRule ( {
// rule information
id : "adjoining-classes" ,
name : "Disallow adjoining classes" ,
desc : "Don't use adjoining classes." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes" ,
browsers : "IE6" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "startrule" , function ( event ) {
var selectors = event . selectors ,
selector ,
part ,
modifier ,
classCount ,
i , j , k ;
for ( i = 0 ; i < selectors . length ; i ++ ) {
selector = selectors [ i ] ;
for ( j = 0 ; j < selector . parts . length ; j ++ ) {
part = selector . parts [ j ] ;
if ( part . type === parser . SELECTOR _PART _TYPE ) {
classCount = 0 ;
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( modifier . type === "class" ) {
classCount ++ ;
}
if ( classCount > 1 ) {
reporter . report ( "Adjoining classes: " + selectors [ i ] . text , part . line , part . col , rule ) ;
}
}
}
}
}
} ) ;
}
} ) ;
/ *
* Rule : Don ' t use width or height when using padding or border .
* /
CSSLint . addRule ( {
// rule information
id : "box-model" ,
name : "Beware of broken box size" ,
desc : "Don't use width or height when using padding or border." ,
url : "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
widthProperties = {
border : 1 ,
"border-left" : 1 ,
"border-right" : 1 ,
padding : 1 ,
"padding-left" : 1 ,
"padding-right" : 1
} ,
heightProperties = {
border : 1 ,
"border-bottom" : 1 ,
"border-top" : 1 ,
padding : 1 ,
"padding-bottom" : 1 ,
"padding-top" : 1
} ,
properties ,
boxSizing = false ;
function startRule ( ) {
properties = { } ;
boxSizing = false ;
}
function endRule ( ) {
var prop , value ;
if ( ! boxSizing ) {
if ( properties . height ) {
for ( prop in heightProperties ) {
if ( heightProperties . hasOwnProperty ( prop ) && properties [ prop ] ) {
value = properties [ prop ] . value ;
// special case for padding
if ( ! ( prop === "padding" && value . parts . length === 2 && value . parts [ 0 ] . value === 0 ) ) {
reporter . report ( "Using height with " + prop + " can sometimes make elements larger than you expect." , properties [ prop ] . line , properties [ prop ] . col , rule ) ;
}
}
}
}
if ( properties . width ) {
for ( prop in widthProperties ) {
if ( widthProperties . hasOwnProperty ( prop ) && properties [ prop ] ) {
value = properties [ prop ] . value ;
if ( ! ( prop === "padding" && value . parts . length === 2 && value . parts [ 1 ] . value === 0 ) ) {
reporter . report ( "Using width with " + prop + " can sometimes make elements larger than you expect." , properties [ prop ] . line , properties [ prop ] . col , rule ) ;
}
}
}
}
}
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
parser . addListener ( "startpage" , startRule ) ;
parser . addListener ( "startpagemargin" , startRule ) ;
parser . addListener ( "startkeyframerule" , startRule ) ;
parser . addListener ( "startviewport" , startRule ) ;
parser . addListener ( "property" , function ( event ) {
var name = event . property . text . toLowerCase ( ) ;
if ( heightProperties [ name ] || widthProperties [ name ] ) {
if ( ! /^0\S*$/ . test ( event . value ) && ! ( name === "border" && event . value . toString ( ) === "none" ) ) {
properties [ name ] = {
line : event . property . line ,
col : event . property . col ,
value : event . value
} ;
}
} else {
if ( /^(width|height)/i . test ( name ) && /^(length|percentage)/ . test ( event . value . parts [ 0 ] . type ) ) {
properties [ name ] = 1 ;
} else if ( name === "box-sizing" ) {
boxSizing = true ;
}
}
} ) ;
parser . addListener ( "endrule" , endRule ) ;
parser . addListener ( "endfontface" , endRule ) ;
parser . addListener ( "endpage" , endRule ) ;
parser . addListener ( "endpagemargin" , endRule ) ;
parser . addListener ( "endkeyframerule" , endRule ) ;
parser . addListener ( "endviewport" , endRule ) ;
}
} ) ;
/ *
* Rule : box - sizing doesn ' t work in IE6 and IE7 .
* /
CSSLint . addRule ( {
// rule information
id : "box-sizing" ,
name : "Disallow use of box-sizing" ,
desc : "The box-sizing properties isn't supported in IE6 and IE7." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing" ,
browsers : "IE6, IE7" ,
tags : [ "Compatibility" ] ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "property" , function ( event ) {
var name = event . property . text . toLowerCase ( ) ;
if ( name === "box-sizing" ) {
reporter . report ( "The box-sizing property isn't supported in IE6 and IE7." , event . line , event . col , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Use the bulletproof @ font - face syntax to avoid 404 ' s in old IE
* ( http : //www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
* /
CSSLint . addRule ( {
// rule information
id : "bulletproof-font-face" ,
name : "Use the bulletproof @font-face syntax" ,
desc : "Use the bulletproof @font-face syntax to avoid 404's in old IE (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)." ,
url : "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
fontFaceRule = false ,
firstSrc = true ,
ruleFailed = false ,
line , col ;
// Mark the start of a @font-face declaration so we only test properties inside it
parser . addListener ( "startfontface" , function ( ) {
fontFaceRule = true ;
} ) ;
parser . addListener ( "property" , function ( event ) {
// If we aren't inside an @font-face declaration then just return
if ( ! fontFaceRule ) {
return ;
}
var propertyName = event . property . toString ( ) . toLowerCase ( ) ,
value = event . value . toString ( ) ;
// Set the line and col numbers for use in the endfontface listener
line = event . line ;
col = event . col ;
// This is the property that we care about, we can ignore the rest
if ( propertyName === "src" ) {
var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i ;
// We need to handle the advanced syntax with two src properties
if ( ! value . match ( regex ) && firstSrc ) {
ruleFailed = true ;
firstSrc = false ;
} else if ( value . match ( regex ) && ! firstSrc ) {
ruleFailed = false ;
}
}
} ) ;
// Back to normal rules that we don't need to test
parser . addListener ( "endfontface" , function ( ) {
fontFaceRule = false ;
if ( ruleFailed ) {
reporter . report ( "@font-face declaration doesn't follow the fontspring bulletproof syntax." , line , col , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Include all compatible vendor prefixes to reach a wider
* range of users .
* /
CSSLint . addRule ( {
// rule information
id : "compatible-vendor-prefixes" ,
name : "Require compatible vendor prefixes" ,
desc : "Include all compatible vendor prefixes to reach a wider range of users." ,
url : "https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
compatiblePrefixes ,
properties ,
prop ,
variations ,
prefixed ,
i ,
len ,
inKeyFrame = false ,
arrayPush = Array . prototype . push ,
applyTo = [ ] ;
// See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
compatiblePrefixes = {
"animation" : "webkit" ,
"animation-delay" : "webkit" ,
"animation-direction" : "webkit" ,
"animation-duration" : "webkit" ,
"animation-fill-mode" : "webkit" ,
"animation-iteration-count" : "webkit" ,
"animation-name" : "webkit" ,
"animation-play-state" : "webkit" ,
"animation-timing-function" : "webkit" ,
"appearance" : "webkit moz" ,
"border-end" : "webkit moz" ,
"border-end-color" : "webkit moz" ,
"border-end-style" : "webkit moz" ,
"border-end-width" : "webkit moz" ,
"border-image" : "webkit moz o" ,
"border-radius" : "webkit" ,
"border-start" : "webkit moz" ,
"border-start-color" : "webkit moz" ,
"border-start-style" : "webkit moz" ,
"border-start-width" : "webkit moz" ,
"box-align" : "webkit moz ms" ,
"box-direction" : "webkit moz ms" ,
"box-flex" : "webkit moz ms" ,
"box-lines" : "webkit ms" ,
"box-ordinal-group" : "webkit moz ms" ,
"box-orient" : "webkit moz ms" ,
"box-pack" : "webkit moz ms" ,
"box-sizing" : "" ,
"box-shadow" : "" ,
"column-count" : "webkit moz ms" ,
"column-gap" : "webkit moz ms" ,
"column-rule" : "webkit moz ms" ,
"column-rule-color" : "webkit moz ms" ,
"column-rule-style" : "webkit moz ms" ,
"column-rule-width" : "webkit moz ms" ,
"column-width" : "webkit moz ms" ,
"hyphens" : "epub moz" ,
"line-break" : "webkit ms" ,
"margin-end" : "webkit moz" ,
"margin-start" : "webkit moz" ,
"marquee-speed" : "webkit wap" ,
"marquee-style" : "webkit wap" ,
"padding-end" : "webkit moz" ,
"padding-start" : "webkit moz" ,
"tab-size" : "moz o" ,
"text-size-adjust" : "webkit ms" ,
"transform" : "webkit ms" ,
"transform-origin" : "webkit ms" ,
"transition" : "" ,
"transition-delay" : "" ,
"transition-duration" : "" ,
"transition-property" : "" ,
"transition-timing-function" : "" ,
"user-modify" : "webkit moz" ,
"user-select" : "webkit moz ms" ,
"word-break" : "epub ms" ,
"writing-mode" : "epub ms"
} ;
for ( prop in compatiblePrefixes ) {
if ( compatiblePrefixes . hasOwnProperty ( prop ) ) {
variations = [ ] ;
prefixed = compatiblePrefixes [ prop ] . split ( " " ) ;
for ( i = 0 , len = prefixed . length ; i < len ; i ++ ) {
variations . push ( "-" + prefixed [ i ] + "-" + prop ) ;
}
compatiblePrefixes [ prop ] = variations ;
arrayPush . apply ( applyTo , variations ) ;
}
}
parser . addListener ( "startrule" , function ( ) {
properties = [ ] ;
} ) ;
parser . addListener ( "startkeyframes" , function ( event ) {
inKeyFrame = event . prefix || true ;
} ) ;
parser . addListener ( "endkeyframes" , function ( ) {
inKeyFrame = false ;
} ) ;
parser . addListener ( "property" , function ( event ) {
var name = event . property ;
if ( CSSLint . Util . indexOf ( applyTo , name . text ) > - 1 ) {
// e.g., -moz-transform is okay to be alone in @-moz-keyframes
if ( ! inKeyFrame || typeof inKeyFrame !== "string" ||
name . text . indexOf ( "-" + inKeyFrame + "-" ) !== 0 ) {
properties . push ( name ) ;
}
}
} ) ;
parser . addListener ( "endrule" , function ( ) {
if ( ! properties . length ) {
return ;
}
var propertyGroups = { } ,
i ,
len ,
name ,
prop ,
variations ,
value ,
full ,
actual ,
item ,
propertiesSpecified ;
for ( i = 0 , len = properties . length ; i < len ; i ++ ) {
name = properties [ i ] ;
for ( prop in compatiblePrefixes ) {
if ( compatiblePrefixes . hasOwnProperty ( prop ) ) {
variations = compatiblePrefixes [ prop ] ;
if ( CSSLint . Util . indexOf ( variations , name . text ) > - 1 ) {
if ( ! propertyGroups [ prop ] ) {
propertyGroups [ prop ] = {
full : variations . slice ( 0 ) ,
actual : [ ] ,
actualNodes : [ ]
} ;
}
if ( CSSLint . Util . indexOf ( propertyGroups [ prop ] . actual , name . text ) === - 1 ) {
propertyGroups [ prop ] . actual . push ( name . text ) ;
propertyGroups [ prop ] . actualNodes . push ( name ) ;
}
}
}
}
}
for ( prop in propertyGroups ) {
if ( propertyGroups . hasOwnProperty ( prop ) ) {
value = propertyGroups [ prop ] ;
full = value . full ;
actual = value . actual ;
if ( full . length > actual . length ) {
for ( i = 0 , len = full . length ; i < len ; i ++ ) {
item = full [ i ] ;
if ( CSSLint . Util . indexOf ( actual , item ) === - 1 ) {
propertiesSpecified = ( actual . length === 1 ) ? actual [ 0 ] : ( actual . length === 2 ) ? actual . join ( " and " ) : actual . join ( ", " ) ;
reporter . report ( "The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well." , value . actualNodes [ 0 ] . line , value . actualNodes [ 0 ] . col , rule ) ;
}
}
}
}
}
} ) ;
}
} ) ;
/ *
* Rule : Certain properties don ' t play well with certain display values .
* - float should not be used with inline - block
* - height , width , margin - top , margin - bottom , float should not be used with inline
* - vertical - align should not be used with block
* - margin , float should not be used with table - *
* /
CSSLint . addRule ( {
// rule information
id : "display-property-grouping" ,
name : "Require properties appropriate for display" ,
desc : "Certain properties shouldn't be used with certain display property values." ,
url : "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
var propertiesToCheck = {
display : 1 ,
"float" : "none" ,
height : 1 ,
width : 1 ,
margin : 1 ,
"margin-left" : 1 ,
"margin-right" : 1 ,
"margin-bottom" : 1 ,
"margin-top" : 1 ,
padding : 1 ,
"padding-left" : 1 ,
"padding-right" : 1 ,
"padding-bottom" : 1 ,
"padding-top" : 1 ,
"vertical-align" : 1
} ,
properties ;
function reportProperty ( name , display , msg ) {
if ( properties [ name ] ) {
if ( typeof propertiesToCheck [ name ] !== "string" || properties [ name ] . value . toLowerCase ( ) !== propertiesToCheck [ name ] ) {
reporter . report ( msg || name + " can't be used with display: " + display + "." , properties [ name ] . line , properties [ name ] . col , rule ) ;
}
}
}
function startRule ( ) {
properties = { } ;
}
function endRule ( ) {
var display = properties . display ? properties . display . value : null ;
if ( display ) {
switch ( display ) {
case "inline" :
// height, width, margin-top, margin-bottom, float should not be used with inline
reportProperty ( "height" , display ) ;
reportProperty ( "width" , display ) ;
reportProperty ( "margin" , display ) ;
reportProperty ( "margin-top" , display ) ;
reportProperty ( "margin-bottom" , display ) ;
reportProperty ( "float" , display , "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug)." ) ;
break ;
case "block" :
// vertical-align should not be used with block
reportProperty ( "vertical-align" , display ) ;
break ;
case "inline-block" :
// float should not be used with inline-block
reportProperty ( "float" , display ) ;
break ;
default :
// margin, float should not be used with table
if ( display . indexOf ( "table-" ) === 0 ) {
reportProperty ( "margin" , display ) ;
reportProperty ( "margin-left" , display ) ;
reportProperty ( "margin-right" , display ) ;
reportProperty ( "margin-top" , display ) ;
reportProperty ( "margin-bottom" , display ) ;
reportProperty ( "float" , display ) ;
}
// otherwise do nothing
}
}
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
parser . addListener ( "startkeyframerule" , startRule ) ;
parser . addListener ( "startpagemargin" , startRule ) ;
parser . addListener ( "startpage" , startRule ) ;
parser . addListener ( "startviewport" , startRule ) ;
parser . addListener ( "property" , function ( event ) {
var name = event . property . text . toLowerCase ( ) ;
if ( propertiesToCheck [ name ] ) {
properties [ name ] = {
value : event . value . text ,
line : event . property . line ,
col : event . property . col
} ;
}
} ) ;
parser . addListener ( "endrule" , endRule ) ;
parser . addListener ( "endfontface" , endRule ) ;
parser . addListener ( "endkeyframerule" , endRule ) ;
parser . addListener ( "endpagemargin" , endRule ) ;
parser . addListener ( "endpage" , endRule ) ;
parser . addListener ( "endviewport" , endRule ) ;
}
} ) ;
/ *
* Rule : Disallow duplicate background - images ( using url ) .
* /
CSSLint . addRule ( {
// rule information
id : "duplicate-background-images" ,
name : "Disallow duplicate background images" ,
desc : "Every background-image should be unique. Use a common class for e.g. sprites." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
stack = { } ;
parser . addListener ( "property" , function ( event ) {
var name = event . property . text ,
value = event . value ,
i , len ;
if ( name . match ( /background/i ) ) {
for ( i = 0 , len = value . parts . length ; i < len ; i ++ ) {
if ( value . parts [ i ] . type === "uri" ) {
if ( typeof stack [ value . parts [ i ] . uri ] === "undefined" ) {
stack [ value . parts [ i ] . uri ] = event ;
} else {
reporter . report ( "Background image '" + value . parts [ i ] . uri + "' was used multiple times, first declared at line " + stack [ value . parts [ i ] . uri ] . line + ", col " + stack [ value . parts [ i ] . uri ] . col + "." , event . line , event . col , rule ) ;
}
}
}
}
} ) ;
}
} ) ;
/ *
* Rule : Duplicate properties must appear one after the other . If an already - defined
* property appears somewhere else in the rule , then it ' s likely an error .
* /
CSSLint . addRule ( {
// rule information
id : "duplicate-properties" ,
name : "Disallow duplicate properties" ,
desc : "Duplicate properties must appear one after the other." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
properties ,
lastProperty ;
function startRule ( ) {
properties = { } ;
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
parser . addListener ( "startpage" , startRule ) ;
parser . addListener ( "startpagemargin" , startRule ) ;
parser . addListener ( "startkeyframerule" , startRule ) ;
parser . addListener ( "startviewport" , startRule ) ;
parser . addListener ( "property" , function ( event ) {
var property = event . property ,
name = property . text . toLowerCase ( ) ;
if ( properties [ name ] && ( lastProperty !== name || properties [ name ] === event . value . text ) ) {
reporter . report ( "Duplicate property '" + event . property + "' found." , event . line , event . col , rule ) ;
}
properties [ name ] = event . value . text ;
lastProperty = name ;
} ) ;
}
} ) ;
/ *
* Rule : Style rules without any properties defined should be removed .
* /
CSSLint . addRule ( {
// rule information
id : "empty-rules" ,
name : "Disallow empty rules" ,
desc : "Rules without any properties specified should be removed." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
count = 0 ;
parser . addListener ( "startrule" , function ( ) {
count = 0 ;
} ) ;
parser . addListener ( "property" , function ( ) {
count ++ ;
} ) ;
parser . addListener ( "endrule" , function ( event ) {
var selectors = event . selectors ;
if ( count === 0 ) {
reporter . report ( "Rule is empty." , selectors [ 0 ] . line , selectors [ 0 ] . col , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : There should be no syntax errors . ( Duh . )
* /
CSSLint . addRule ( {
// rule information
id : "errors" ,
name : "Parsing Errors" ,
desc : "This rule looks for recoverable syntax errors." ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "error" , function ( event ) {
reporter . error ( event . message , event . line , event . col , rule ) ;
} ) ;
}
} ) ;
CSSLint . addRule ( {
// rule information
id : "fallback-colors" ,
name : "Require fallback colors" ,
desc : "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color." ,
url : "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors" ,
browsers : "IE6,IE7,IE8" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
lastProperty ,
propertiesToCheck = {
color : 1 ,
background : 1 ,
"border-color" : 1 ,
"border-top-color" : 1 ,
"border-right-color" : 1 ,
"border-bottom-color" : 1 ,
"border-left-color" : 1 ,
border : 1 ,
"border-top" : 1 ,
"border-right" : 1 ,
"border-bottom" : 1 ,
"border-left" : 1 ,
"background-color" : 1
} ;
function startRule ( ) {
lastProperty = null ;
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
parser . addListener ( "startpage" , startRule ) ;
parser . addListener ( "startpagemargin" , startRule ) ;
parser . addListener ( "startkeyframerule" , startRule ) ;
parser . addListener ( "startviewport" , startRule ) ;
parser . addListener ( "property" , function ( event ) {
var property = event . property ,
name = property . text . toLowerCase ( ) ,
parts = event . value . parts ,
i = 0 ,
colorType = "" ,
len = parts . length ;
if ( propertiesToCheck [ name ] ) {
while ( i < len ) {
if ( parts [ i ] . type === "color" ) {
if ( "alpha" in parts [ i ] || "hue" in parts [ i ] ) {
if ( /([^\)]+)\(/ . test ( parts [ i ] ) ) {
colorType = RegExp . $1 . toUpperCase ( ) ;
}
if ( ! lastProperty || ( lastProperty . property . text . toLowerCase ( ) !== name || lastProperty . colorType !== "compat" ) ) {
reporter . report ( "Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + "." , event . line , event . col , rule ) ;
}
} else {
event . colorType = "compat" ;
}
}
i ++ ;
}
}
lastProperty = event ;
} ) ;
}
} ) ;
/ *
* Rule : You shouldn 't use more than 10 floats. If you do, there' s probably
* room for some abstraction .
* /
CSSLint . addRule ( {
// rule information
id : "floats" ,
name : "Disallow too many floats" ,
desc : "This rule tests if the float property is used too many times" ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
var count = 0 ;
// count how many times "float" is used
parser . addListener ( "property" , function ( event ) {
if ( event . property . text . toLowerCase ( ) === "float" &&
event . value . text . toLowerCase ( ) !== "none" ) {
count ++ ;
}
} ) ;
// report the results
parser . addListener ( "endstylesheet" , function ( ) {
reporter . stat ( "floats" , count ) ;
if ( count >= 10 ) {
reporter . rollupWarn ( "Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead." , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Avoid too many @ font - face declarations in the same stylesheet .
* /
CSSLint . addRule ( {
// rule information
id : "font-faces" ,
name : "Don't use too many web fonts" ,
desc : "Too many different web fonts in the same stylesheet." ,
url : "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
count = 0 ;
parser . addListener ( "startfontface" , function ( ) {
count ++ ;
} ) ;
parser . addListener ( "endstylesheet" , function ( ) {
if ( count > 5 ) {
reporter . rollupWarn ( "Too many @font-face declarations (" + count + ")." , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : You shouldn ' t need more than 9 font - size declarations .
* /
CSSLint . addRule ( {
// rule information
id : "font-sizes" ,
name : "Disallow too many font sizes" ,
desc : "Checks the number of font-size declarations." ,
url : "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
count = 0 ;
// check for use of "font-size"
parser . addListener ( "property" , function ( event ) {
if ( event . property . toString ( ) === "font-size" ) {
count ++ ;
}
} ) ;
// report the results
parser . addListener ( "endstylesheet" , function ( ) {
reporter . stat ( "font-sizes" , count ) ;
if ( count >= 10 ) {
reporter . rollupWarn ( "Too many font-size declarations (" + count + "), abstraction needed." , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : When using a vendor - prefixed gradient , make sure to use them all .
* /
CSSLint . addRule ( {
// rule information
id : "gradients" ,
name : "Require all gradient definitions" ,
desc : "When using a vendor-prefixed gradient, make sure to use them all." ,
url : "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
gradients ;
parser . addListener ( "startrule" , function ( ) {
gradients = {
moz : 0 ,
webkit : 0 ,
oldWebkit : 0 ,
o : 0
} ;
} ) ;
parser . addListener ( "property" , function ( event ) {
if ( /\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i . test ( event . value ) ) {
gradients [ RegExp . $1 ] = 1 ;
} else if ( /\-webkit\-gradient/i . test ( event . value ) ) {
gradients . oldWebkit = 1 ;
}
} ) ;
parser . addListener ( "endrule" , function ( event ) {
var missing = [ ] ;
if ( ! gradients . moz ) {
missing . push ( "Firefox 3.6+" ) ;
}
if ( ! gradients . webkit ) {
missing . push ( "Webkit (Safari 5+, Chrome)" ) ;
}
if ( ! gradients . oldWebkit ) {
missing . push ( "Old Webkit (Safari 4+, Chrome)" ) ;
}
if ( ! gradients . o ) {
missing . push ( "Opera 11.1+" ) ;
}
if ( missing . length && missing . length < 4 ) {
reporter . report ( "Missing vendor-prefixed CSS gradients for " + missing . join ( ", " ) + "." , event . selectors [ 0 ] . line , event . selectors [ 0 ] . col , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Don ' t use IDs for selectors .
* /
CSSLint . addRule ( {
// rule information
id : "ids" ,
name : "Disallow IDs in selectors" ,
desc : "Selectors should not contain IDs." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "startrule" , function ( event ) {
var selectors = event . selectors ,
selector ,
part ,
modifier ,
idCount ,
i , j , k ;
for ( i = 0 ; i < selectors . length ; i ++ ) {
selector = selectors [ i ] ;
idCount = 0 ;
for ( j = 0 ; j < selector . parts . length ; j ++ ) {
part = selector . parts [ j ] ;
if ( part . type === parser . SELECTOR _PART _TYPE ) {
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( modifier . type === "id" ) {
idCount ++ ;
}
}
}
}
if ( idCount === 1 ) {
reporter . report ( "Don't use IDs in selectors." , selector . line , selector . col , rule ) ;
} else if ( idCount > 1 ) {
reporter . report ( idCount + " IDs in the selector, really?" , selector . line , selector . col , rule ) ;
}
}
} ) ;
}
} ) ;
/ *
* Rule : IE6 - 9 supports up to 31 stylesheet import .
* Reference :
* http : //blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
* /
CSSLint . addRule ( {
// rule information
id : "import-ie-limit" ,
name : "@import limit on IE6-IE9" ,
desc : "IE6-9 supports up to 31 @import per stylesheet" ,
browsers : "IE6, IE7, IE8, IE9" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
MAX _IMPORT _COUNT = 31 ,
count = 0 ;
function startPage ( ) {
count = 0 ;
}
parser . addListener ( "startpage" , startPage ) ;
parser . addListener ( "import" , function ( ) {
count ++ ;
} ) ;
parser . addListener ( "endstylesheet" , function ( ) {
if ( count > MAX _IMPORT _COUNT ) {
reporter . rollupError (
"Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet." ,
rule
) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Don ' t use @ import , use < link > instead .
* /
CSSLint . addRule ( {
// rule information
id : "import" ,
name : "Disallow @import" ,
desc : "Don't use @import, use <link> instead." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-%40import" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "import" , function ( event ) {
reporter . report ( "@import prevents parallel downloads, use <link> instead." , event . line , event . col , rule ) ;
} ) ;
}
} ) ;
/ *
* Rule : Make sure ! important is not overused , this could lead to specificity
* war . Display a warning on ! important declarations , an error if it ' s
* used more at least 10 times .
* /
CSSLint . addRule ( {
// rule information
id : "important" ,
name : "Disallow !important" ,
desc : "Be careful when using !important declaration" ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-%21important" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
count = 0 ;
// warn that important is used and increment the declaration counter
parser . addListener ( "property" , function ( event ) {
if ( event . important === true ) {
count ++ ;
reporter . report ( "Use of !important" , event . line , event . col , rule ) ;
}
} ) ;
// if there are more than 10, show an error
parser . addListener ( "endstylesheet" , function ( ) {
reporter . stat ( "important" , count ) ;
if ( count >= 10 ) {
reporter . rollupWarn ( "Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues." , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Properties should be known ( listed in CSS3 specification ) or
* be a vendor - prefixed property .
* /
CSSLint . addRule ( {
// rule information
id : "known-properties" ,
name : "Require use of known properties" ,
desc : "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property." ,
url : "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "property" , function ( event ) {
// the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
if ( event . invalid ) {
reporter . report ( event . invalid . message , event . line , event . col , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : All properties should be in alphabetical order .
* /
CSSLint . addRule ( {
// rule information
id : "order-alphabetical" ,
name : "Alphabetical order" ,
desc : "Assure properties are in alphabetical order" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
properties ;
var startRule = function ( ) {
properties = [ ] ;
} ;
var endRule = function ( event ) {
var currentProperties = properties . join ( "," ) ,
expectedProperties = properties . sort ( ) . join ( "," ) ;
if ( currentProperties !== expectedProperties ) {
reporter . report ( "Rule doesn't have all its properties in alphabetical order." , event . line , event . col , rule ) ;
}
} ;
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
parser . addListener ( "startpage" , startRule ) ;
parser . addListener ( "startpagemargin" , startRule ) ;
parser . addListener ( "startkeyframerule" , startRule ) ;
parser . addListener ( "startviewport" , startRule ) ;
parser . addListener ( "property" , function ( event ) {
var name = event . property . text ,
lowerCasePrefixLessName = name . toLowerCase ( ) . replace ( /^-.*?-/ , "" ) ;
properties . push ( lowerCasePrefixLessName ) ;
} ) ;
parser . addListener ( "endrule" , endRule ) ;
parser . addListener ( "endfontface" , endRule ) ;
parser . addListener ( "endpage" , endRule ) ;
parser . addListener ( "endpagemargin" , endRule ) ;
parser . addListener ( "endkeyframerule" , endRule ) ;
parser . addListener ( "endviewport" , endRule ) ;
}
} ) ;
/ *
* Rule : outline : none or outline : 0 should only be used in a : focus rule
* and only if there are other properties in the same rule .
* /
CSSLint . addRule ( {
// rule information
id : "outline-none" ,
name : "Disallow outline: none" ,
desc : "Use of outline: none or outline: 0 should be limited to :focus rules." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone" ,
browsers : "All" ,
tags : [ "Accessibility" ] ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
lastRule ;
function startRule ( event ) {
if ( event . selectors ) {
lastRule = {
line : event . line ,
col : event . col ,
selectors : event . selectors ,
propCount : 0 ,
outline : false
} ;
} else {
lastRule = null ;
}
}
function endRule ( ) {
if ( lastRule ) {
if ( lastRule . outline ) {
if ( lastRule . selectors . toString ( ) . toLowerCase ( ) . indexOf ( ":focus" ) === - 1 ) {
reporter . report ( "Outlines should only be modified using :focus." , lastRule . line , lastRule . col , rule ) ;
} else if ( lastRule . propCount === 1 ) {
reporter . report ( "Outlines shouldn't be hidden unless other visual changes are made." , lastRule . line , lastRule . col , rule ) ;
}
}
}
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
parser . addListener ( "startpage" , startRule ) ;
parser . addListener ( "startpagemargin" , startRule ) ;
parser . addListener ( "startkeyframerule" , startRule ) ;
parser . addListener ( "startviewport" , startRule ) ;
parser . addListener ( "property" , function ( event ) {
var name = event . property . text . toLowerCase ( ) ,
value = event . value ;
if ( lastRule ) {
lastRule . propCount ++ ;
if ( name === "outline" && ( value . toString ( ) === "none" || value . toString ( ) === "0" ) ) {
lastRule . outline = true ;
}
}
} ) ;
parser . addListener ( "endrule" , endRule ) ;
parser . addListener ( "endfontface" , endRule ) ;
parser . addListener ( "endpage" , endRule ) ;
parser . addListener ( "endpagemargin" , endRule ) ;
parser . addListener ( "endkeyframerule" , endRule ) ;
parser . addListener ( "endviewport" , endRule ) ;
}
} ) ;
/ *
* Rule : Don ' t use classes or IDs with elements ( a . foo or a # foo ) .
* /
CSSLint . addRule ( {
// rule information
id : "overqualified-elements" ,
name : "Disallow overqualified elements" ,
desc : "Don't use classes or IDs with elements (a.foo or a#foo)." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
classes = { } ;
parser . addListener ( "startrule" , function ( event ) {
var selectors = event . selectors ,
selector ,
part ,
modifier ,
i , j , k ;
for ( i = 0 ; i < selectors . length ; i ++ ) {
selector = selectors [ i ] ;
for ( j = 0 ; j < selector . parts . length ; j ++ ) {
part = selector . parts [ j ] ;
if ( part . type === parser . SELECTOR _PART _TYPE ) {
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( part . elementName && modifier . type === "id" ) {
reporter . report ( "Element (" + part + ") is overqualified, just use " + modifier + " without element name." , part . line , part . col , rule ) ;
} else if ( modifier . type === "class" ) {
if ( ! classes [ modifier ] ) {
classes [ modifier ] = [ ] ;
}
classes [ modifier ] . push ( {
modifier : modifier ,
part : part
} ) ;
}
}
}
}
}
} ) ;
parser . addListener ( "endstylesheet" , function ( ) {
var prop ;
for ( prop in classes ) {
if ( classes . hasOwnProperty ( prop ) ) {
// one use means that this is overqualified
if ( classes [ prop ] . length === 1 && classes [ prop ] [ 0 ] . part . elementName ) {
reporter . report ( "Element (" + classes [ prop ] [ 0 ] . part + ") is overqualified, just use " + classes [ prop ] [ 0 ] . modifier + " without element name." , classes [ prop ] [ 0 ] . part . line , classes [ prop ] [ 0 ] . part . col , rule ) ;
}
}
}
} ) ;
}
} ) ;
/ *
* Rule : Headings ( h1 - h6 ) should not be qualified ( namespaced ) .
* /
CSSLint . addRule ( {
// rule information
id : "qualified-headings" ,
name : "Disallow qualified headings" ,
desc : "Headings should not be qualified (namespaced)." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "startrule" , function ( event ) {
var selectors = event . selectors ,
selector ,
part ,
i , j ;
for ( i = 0 ; i < selectors . length ; i ++ ) {
selector = selectors [ i ] ;
for ( j = 0 ; j < selector . parts . length ; j ++ ) {
part = selector . parts [ j ] ;
if ( part . type === parser . SELECTOR _PART _TYPE ) {
if ( part . elementName && /h[1-6]/ . test ( part . elementName . toString ( ) ) && j > 0 ) {
reporter . report ( "Heading (" + part . elementName + ") should not be qualified." , part . line , part . col , rule ) ;
}
}
}
}
} ) ;
}
} ) ;
/ *
* Rule : Selectors that look like regular expressions are slow and should be avoided .
* /
CSSLint . addRule ( {
// rule information
id : "regex-selectors" ,
name : "Disallow selectors that look like regexs" ,
desc : "Selectors that look like regular expressions are slow and should be avoided." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "startrule" , function ( event ) {
var selectors = event . selectors ,
selector ,
part ,
modifier ,
i , j , k ;
for ( i = 0 ; i < selectors . length ; i ++ ) {
selector = selectors [ i ] ;
for ( j = 0 ; j < selector . parts . length ; j ++ ) {
part = selector . parts [ j ] ;
if ( part . type === parser . SELECTOR _PART _TYPE ) {
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( modifier . type === "attribute" ) {
if ( /([~\|\^\$\*]=)/ . test ( modifier ) ) {
reporter . report ( "Attribute selectors with " + RegExp . $1 + " are slow!" , modifier . line , modifier . col , rule ) ;
}
}
}
}
}
}
} ) ;
}
} ) ;
/ *
* Rule : Total number of rules should not exceed x .
* /
CSSLint . addRule ( {
// rule information
id : "rules-count" ,
name : "Rules Count" ,
desc : "Track how many rules there are." ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var count = 0 ;
// count each rule
parser . addListener ( "startrule" , function ( ) {
count ++ ;
} ) ;
parser . addListener ( "endstylesheet" , function ( ) {
reporter . stat ( "rule-count" , count ) ;
} ) ;
}
} ) ;
/ *
* Rule : Warn people with approaching the IE 4095 limit
* /
CSSLint . addRule ( {
// rule information
id : "selector-max-approaching" ,
name : "Warn when approaching the 4095 selector limit for IE" ,
desc : "Will warn when selector count is >= 3800 selectors." ,
browsers : "IE" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this , count = 0 ;
parser . addListener ( "startrule" , function ( event ) {
count += event . selectors . length ;
} ) ;
parser . addListener ( "endstylesheet" , function ( ) {
if ( count >= 3800 ) {
reporter . report ( "You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring." , 0 , 0 , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Warn people past the IE 4095 limit
* /
CSSLint . addRule ( {
// rule information
id : "selector-max" ,
name : "Error when past the 4095 selector limit for IE" ,
desc : "Will error when selector count is > 4095." ,
browsers : "IE" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this , count = 0 ;
parser . addListener ( "startrule" , function ( event ) {
count += event . selectors . length ;
} ) ;
parser . addListener ( "endstylesheet" , function ( ) {
if ( count > 4095 ) {
reporter . report ( "You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring." , 0 , 0 , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Avoid new - line characters in selectors .
* /
CSSLint . addRule ( {
// rule information
id : "selector-newline" ,
name : "Disallow new-line characters in selectors" ,
desc : "New-line characters in selectors are usually a forgotten comma and not a descendant combinator." ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
function startRule ( event ) {
var i , len , selector , p , n , pLen , part , part2 , type , currentLine , nextLine ,
selectors = event . selectors ;
for ( i = 0 , len = selectors . length ; i < len ; i ++ ) {
selector = selectors [ i ] ;
for ( p = 0 , pLen = selector . parts . length ; p < pLen ; p ++ ) {
for ( n = p + 1 ; n < pLen ; n ++ ) {
part = selector . parts [ p ] ;
part2 = selector . parts [ n ] ;
type = part . type ;
currentLine = part . line ;
nextLine = part2 . line ;
if ( type === "descendant" && nextLine > currentLine ) {
reporter . report ( "newline character found in selector (forgot a comma?)" , currentLine , selectors [ i ] . parts [ 0 ] . col , rule ) ;
}
}
}
}
}
parser . addListener ( "startrule" , startRule ) ;
}
} ) ;
/ *
* Rule : Use shorthand properties where possible .
*
* /
CSSLint . addRule ( {
// rule information
id : "shorthand" ,
name : "Require shorthand properties" ,
desc : "Use shorthand properties where possible." ,
url : "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
prop , i , len ,
propertiesToCheck = { } ,
properties ,
mapping = {
"margin" : [
"margin-top" ,
"margin-bottom" ,
"margin-left" ,
"margin-right"
] ,
"padding" : [
"padding-top" ,
"padding-bottom" ,
"padding-left" ,
"padding-right"
]
} ;
// initialize propertiesToCheck
for ( prop in mapping ) {
if ( mapping . hasOwnProperty ( prop ) ) {
for ( i = 0 , len = mapping [ prop ] . length ; i < len ; i ++ ) {
propertiesToCheck [ mapping [ prop ] [ i ] ] = prop ;
}
}
}
function startRule ( ) {
properties = { } ;
}
// event handler for end of rules
function endRule ( event ) {
var prop , i , len , total ;
// check which properties this rule has
for ( prop in mapping ) {
if ( mapping . hasOwnProperty ( prop ) ) {
total = 0 ;
for ( i = 0 , len = mapping [ prop ] . length ; i < len ; i ++ ) {
total += properties [ mapping [ prop ] [ i ] ] ? 1 : 0 ;
}
if ( total === mapping [ prop ] . length ) {
reporter . report ( "The properties " + mapping [ prop ] . join ( ", " ) + " can be replaced by " + prop + "." , event . line , event . col , rule ) ;
}
}
}
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
// check for use of "font-size"
parser . addListener ( "property" , function ( event ) {
var name = event . property . toString ( ) . toLowerCase ( ) ;
if ( propertiesToCheck [ name ] ) {
properties [ name ] = 1 ;
}
} ) ;
parser . addListener ( "endrule" , endRule ) ;
parser . addListener ( "endfontface" , endRule ) ;
}
} ) ;
/ *
* Rule : Don ' t use properties with a star prefix .
*
* /
CSSLint . addRule ( {
// rule information
id : "star-property-hack" ,
name : "Disallow properties with a star prefix" ,
desc : "Checks for the star property hack (targets IE6/7)" ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
// check if property name starts with "*"
parser . addListener ( "property" , function ( event ) {
var property = event . property ;
if ( property . hack === "*" ) {
reporter . report ( "Property with star prefix found." , event . property . line , event . property . col , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Don ' t use text - indent for image replacement if you need to support rtl .
*
* /
CSSLint . addRule ( {
// rule information
id : "text-indent" ,
name : "Disallow negative text-indent" ,
desc : "Checks for text indent less than -99px" ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
textIndent ,
direction ;
function startRule ( ) {
textIndent = false ;
direction = "inherit" ;
}
// event handler for end of rules
function endRule ( ) {
if ( textIndent && direction !== "ltr" ) {
reporter . report ( "Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr." , textIndent . line , textIndent . col , rule ) ;
}
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
// check for use of "font-size"
parser . addListener ( "property" , function ( event ) {
var name = event . property . toString ( ) . toLowerCase ( ) ,
value = event . value ;
if ( name === "text-indent" && value . parts [ 0 ] . value < - 99 ) {
textIndent = event . property ;
} else if ( name === "direction" && value . toString ( ) === "ltr" ) {
direction = "ltr" ;
}
} ) ;
parser . addListener ( "endrule" , endRule ) ;
parser . addListener ( "endfontface" , endRule ) ;
}
} ) ;
/ *
* Rule : Don ' t use properties with a underscore prefix .
*
* /
CSSLint . addRule ( {
// rule information
id : "underscore-property-hack" ,
name : "Disallow properties with an underscore prefix" ,
desc : "Checks for the underscore property hack (targets IE6)" ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
// check if property name starts with "_"
parser . addListener ( "property" , function ( event ) {
var property = event . property ;
if ( property . hack === "_" ) {
reporter . report ( "Property with underscore prefix found." , event . property . line , event . property . col , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Headings ( h1 - h6 ) should be defined only once .
* /
CSSLint . addRule ( {
// rule information
id : "unique-headings" ,
name : "Headings should only be defined once" ,
desc : "Headings should be defined only once." ,
url : "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
var headings = {
h1 : 0 ,
h2 : 0 ,
h3 : 0 ,
h4 : 0 ,
h5 : 0 ,
h6 : 0
} ;
parser . addListener ( "startrule" , function ( event ) {
var selectors = event . selectors ,
selector ,
part ,
pseudo ,
i , j ;
for ( i = 0 ; i < selectors . length ; i ++ ) {
selector = selectors [ i ] ;
part = selector . parts [ selector . parts . length - 1 ] ;
if ( part . elementName && /(h[1-6])/i . test ( part . elementName . toString ( ) ) ) {
for ( j = 0 ; j < part . modifiers . length ; j ++ ) {
if ( part . modifiers [ j ] . type === "pseudo" ) {
pseudo = true ;
break ;
}
}
if ( ! pseudo ) {
headings [ RegExp . $1 ] ++ ;
if ( headings [ RegExp . $1 ] > 1 ) {
reporter . report ( "Heading (" + part . elementName + ") has already been defined." , part . line , part . col , rule ) ;
}
}
}
}
} ) ;
parser . addListener ( "endstylesheet" , function ( ) {
var prop ,
messages = [ ] ;
for ( prop in headings ) {
if ( headings . hasOwnProperty ( prop ) ) {
if ( headings [ prop ] > 1 ) {
messages . push ( headings [ prop ] + " " + prop + "s" ) ;
}
}
}
if ( messages . length ) {
reporter . rollupWarn ( "You have " + messages . join ( ", " ) + " defined in this stylesheet." , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Don 't use universal selector because it' s slow .
* /
CSSLint . addRule ( {
// rule information
id : "universal-selector" ,
name : "Disallow universal selector" ,
desc : "The universal selector (*) is known to be slow." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "startrule" , function ( event ) {
var selectors = event . selectors ,
selector ,
part ,
i ;
for ( i = 0 ; i < selectors . length ; i ++ ) {
selector = selectors [ i ] ;
part = selector . parts [ selector . parts . length - 1 ] ;
if ( part . elementName === "*" ) {
reporter . report ( rule . desc , part . line , part . col , rule ) ;
}
}
} ) ;
}
} ) ;
/ *
* Rule : Don 't use unqualified attribute selectors because they' re just like universal selectors .
* /
CSSLint . addRule ( {
// rule information
id : "unqualified-attributes" ,
name : "Disallow unqualified attribute selectors" ,
desc : "Unqualified attribute selectors are known to be slow." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
parser . addListener ( "startrule" , function ( event ) {
var selectors = event . selectors ,
selectorContainsClassOrId = false ,
selector ,
part ,
modifier ,
i , k ;
for ( i = 0 ; i < selectors . length ; i ++ ) {
selector = selectors [ i ] ;
part = selector . parts [ selector . parts . length - 1 ] ;
if ( part . type === parser . SELECTOR _PART _TYPE ) {
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( modifier . type === "class" || modifier . type === "id" ) {
selectorContainsClassOrId = true ;
break ;
}
}
if ( ! selectorContainsClassOrId ) {
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( modifier . type === "attribute" && ( ! part . elementName || part . elementName === "*" ) ) {
reporter . report ( rule . desc , part . line , part . col , rule ) ;
}
}
}
}
}
} ) ;
}
} ) ;
/ *
* Rule : When using a vendor - prefixed property , make sure to
* include the standard one .
* /
CSSLint . addRule ( {
// rule information
id : "vendor-prefix" ,
name : "Require standard property with vendor prefix" ,
desc : "When using a vendor-prefixed property, make sure to include the standard one." ,
url : "https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ,
properties ,
num ,
propertiesToCheck = {
"-webkit-border-radius" : "border-radius" ,
"-webkit-border-top-left-radius" : "border-top-left-radius" ,
"-webkit-border-top-right-radius" : "border-top-right-radius" ,
"-webkit-border-bottom-left-radius" : "border-bottom-left-radius" ,
"-webkit-border-bottom-right-radius" : "border-bottom-right-radius" ,
"-o-border-radius" : "border-radius" ,
"-o-border-top-left-radius" : "border-top-left-radius" ,
"-o-border-top-right-radius" : "border-top-right-radius" ,
"-o-border-bottom-left-radius" : "border-bottom-left-radius" ,
"-o-border-bottom-right-radius" : "border-bottom-right-radius" ,
"-moz-border-radius" : "border-radius" ,
"-moz-border-radius-topleft" : "border-top-left-radius" ,
"-moz-border-radius-topright" : "border-top-right-radius" ,
"-moz-border-radius-bottomleft" : "border-bottom-left-radius" ,
"-moz-border-radius-bottomright" : "border-bottom-right-radius" ,
"-moz-column-count" : "column-count" ,
"-webkit-column-count" : "column-count" ,
"-moz-column-gap" : "column-gap" ,
"-webkit-column-gap" : "column-gap" ,
"-moz-column-rule" : "column-rule" ,
"-webkit-column-rule" : "column-rule" ,
"-moz-column-rule-style" : "column-rule-style" ,
"-webkit-column-rule-style" : "column-rule-style" ,
"-moz-column-rule-color" : "column-rule-color" ,
"-webkit-column-rule-color" : "column-rule-color" ,
"-moz-column-rule-width" : "column-rule-width" ,
"-webkit-column-rule-width" : "column-rule-width" ,
"-moz-column-width" : "column-width" ,
"-webkit-column-width" : "column-width" ,
"-webkit-column-span" : "column-span" ,
"-webkit-columns" : "columns" ,
"-moz-box-shadow" : "box-shadow" ,
"-webkit-box-shadow" : "box-shadow" ,
"-moz-transform" : "transform" ,
"-webkit-transform" : "transform" ,
"-o-transform" : "transform" ,
"-ms-transform" : "transform" ,
"-moz-transform-origin" : "transform-origin" ,
"-webkit-transform-origin" : "transform-origin" ,
"-o-transform-origin" : "transform-origin" ,
"-ms-transform-origin" : "transform-origin" ,
"-moz-box-sizing" : "box-sizing" ,
"-webkit-box-sizing" : "box-sizing"
} ;
// event handler for beginning of rules
function startRule ( ) {
properties = { } ;
num = 1 ;
}
// event handler for end of rules
function endRule ( ) {
var prop ,
i ,
len ,
needed ,
actual ,
needsStandard = [ ] ;
for ( prop in properties ) {
if ( propertiesToCheck [ prop ] ) {
needsStandard . push ( {
actual : prop ,
needed : propertiesToCheck [ prop ]
} ) ;
}
}
for ( i = 0 , len = needsStandard . length ; i < len ; i ++ ) {
needed = needsStandard [ i ] . needed ;
actual = needsStandard [ i ] . actual ;
if ( ! properties [ needed ] ) {
reporter . report ( "Missing standard property '" + needed + "' to go along with '" + actual + "'." , properties [ actual ] [ 0 ] . name . line , properties [ actual ] [ 0 ] . name . col , rule ) ;
} else {
// make sure standard property is last
if ( properties [ needed ] [ 0 ] . pos < properties [ actual ] [ 0 ] . pos ) {
reporter . report ( "Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'." , properties [ actual ] [ 0 ] . name . line , properties [ actual ] [ 0 ] . name . col , rule ) ;
}
}
}
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
parser . addListener ( "startpage" , startRule ) ;
parser . addListener ( "startpagemargin" , startRule ) ;
parser . addListener ( "startkeyframerule" , startRule ) ;
parser . addListener ( "startviewport" , startRule ) ;
parser . addListener ( "property" , function ( event ) {
var name = event . property . text . toLowerCase ( ) ;
if ( ! properties [ name ] ) {
properties [ name ] = [ ] ;
}
properties [ name ] . push ( {
name : event . property ,
value : event . value ,
pos : num ++
} ) ;
} ) ;
parser . addListener ( "endrule" , endRule ) ;
parser . addListener ( "endfontface" , endRule ) ;
parser . addListener ( "endpage" , endRule ) ;
parser . addListener ( "endpagemargin" , endRule ) ;
parser . addListener ( "endkeyframerule" , endRule ) ;
parser . addListener ( "endviewport" , endRule ) ;
}
} ) ;
/ *
* Rule : You don ' t need to specify units when a value is 0.
* /
CSSLint . addRule ( {
// rule information
id : "zero-units" ,
name : "Disallow units for 0 values" ,
desc : "You don't need to specify units when a value is 0." ,
url : "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values" ,
browsers : "All" ,
// initialization
init : function ( parser , reporter ) {
"use strict" ;
var rule = this ;
// count how many times "float" is used
parser . addListener ( "property" , function ( event ) {
var parts = event . value . parts ,
i = 0 ,
len = parts . length ;
while ( i < len ) {
if ( ( parts [ i ] . units || parts [ i ] . type === "percentage" ) && parts [ i ] . value === 0 && parts [ i ] . type !== "time" ) {
reporter . report ( "Values of 0 shouldn't have units specified." , parts [ i ] . line , parts [ i ] . col , rule ) ;
}
i ++ ;
}
} ) ;
}
} ) ;
( function ( ) {
"use strict" ;
/ * *
* Replace special characters before write to output .
*
* Rules :
* - single quotes is the escape sequence for double - quotes
* - & amp ; is the escape sequence for &
* - & lt ; is the escape sequence for <
* - & gt ; is the escape sequence for >
*
* @ param { String } message to escape
* @ return escaped message as { String }
* /
var xmlEscape = function ( str ) {
if ( ! str || str . constructor !== String ) {
return "" ;
}
return str . replace ( /["&><]/g , function ( match ) {
switch ( match ) {
case "\"" :
return """ ;
case "&" :
return "&" ;
case "<" :
return "<" ;
case ">" :
return ">" ;
}
} ) ;
} ;
CSSLint . addFormatter ( {
// format information
id : "checkstyle-xml" ,
name : "Checkstyle XML format" ,
/ * *
* Return opening root XML tag .
* @ return { String } to prepend before all results
* /
startFormat : function ( ) {
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>" ;
} ,
/ * *
* Return closing root XML tag .
* @ return { String } to append after all results
* /
endFormat : function ( ) {
return "</checkstyle>" ;
} ,
/ * *
* Returns message when there is a file read error .
* @ param { String } filename The name of the file that caused the error .
* @ param { String } message The error message
* @ return { String } The error message .
* /
readError : function ( filename , message ) {
return "<file name=\"" + xmlEscape ( filename ) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape ( message ) + "\"></error></file>" ;
} ,
/ * *
* Given CSS Lint results for a file , return output for this format .
* @ param results { Object } with error and warning messages
* @ param filename { String } relative file path
* @ param options { Object } ( UNUSED for now ) specifies special handling of output
* @ return { String } output for results
* /
formatResults : function ( results , filename /*, options*/ ) {
var messages = results . messages ,
output = [ ] ;
/ * *
* Generate a source string for a rule .
* Checkstyle source strings usually resemble Java class names e . g
* net . csslint . SomeRuleName
* @ param { Object } rule
* @ return rule source as { String }
* /
var generateSource = function ( rule ) {
if ( ! rule || ! ( "name" in rule ) ) {
return "" ;
}
return "net.csslint." + rule . name . replace ( /\s/g , "" ) ;
} ;
if ( messages . length > 0 ) {
output . push ( "<file name=\"" + filename + "\">" ) ;
CSSLint . Util . forEach ( messages , function ( message ) {
// ignore rollups for now
if ( ! message . rollup ) {
output . push ( "<error line=\"" + message . line + "\" column=\"" + message . col + "\" severity=\"" + message . type + "\"" +
" message=\"" + xmlEscape ( message . message ) + "\" source=\"" + generateSource ( message . rule ) + "\"/>" ) ;
}
} ) ;
output . push ( "</file>" ) ;
}
return output . join ( "" ) ;
}
} ) ;
} ( ) ) ;
CSSLint . addFormatter ( {
// format information
id : "compact" ,
name : "Compact, 'porcelain' format" ,
/ * *
* Return content to be printed before all file results .
* @ return { String } to prepend before all results
* /
startFormat : function ( ) {
"use strict" ;
return "" ;
} ,
/ * *
* Return content to be printed after all file results .
* @ return { String } to append after all results
* /
endFormat : function ( ) {
"use strict" ;
return "" ;
} ,
/ * *
* Given CSS Lint results for a file , return output for this format .
* @ param results { Object } with error and warning messages
* @ param filename { String } relative file path
* @ param options { Object } ( Optional ) specifies special handling of output
* @ return { String } output for results
* /
formatResults : function ( results , filename , options ) {
"use strict" ;
var messages = results . messages ,
output = "" ;
options = options || { } ;
/ * *
* Capitalize and return given string .
* @ param str { String } to capitalize
* @ return { String } capitalized
* /
var capitalize = function ( str ) {
return str . charAt ( 0 ) . toUpperCase ( ) + str . slice ( 1 ) ;
} ;
if ( messages . length === 0 ) {
return options . quiet ? "" : filename + ": Lint Free!" ;
}
CSSLint . Util . forEach ( messages , function ( message ) {
if ( message . rollup ) {
output += filename + ": " + capitalize ( message . type ) + " - " + message . message + " (" + message . rule . id + ")\n" ;
} else {
output += filename + ": line " + message . line +
", col " + message . col + ", " + capitalize ( message . type ) + " - " + message . message + " (" + message . rule . id + ")\n" ;
}
} ) ;
return output ;
}
} ) ;
CSSLint . addFormatter ( {
// format information
id : "csslint-xml" ,
name : "CSSLint XML format" ,
/ * *
* Return opening root XML tag .
* @ return { String } to prepend before all results
* /
startFormat : function ( ) {
"use strict" ;
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>" ;
} ,
/ * *
* Return closing root XML tag .
* @ return { String } to append after all results
* /
endFormat : function ( ) {
"use strict" ;
return "</csslint>" ;
} ,
/ * *
* Given CSS Lint results for a file , return output for this format .
* @ param results { Object } with error and warning messages
* @ param filename { String } relative file path
* @ param options { Object } ( UNUSED for now ) specifies special handling of output
* @ return { String } output for results
* /
formatResults : function ( results , filename /*, options*/ ) {
"use strict" ;
var messages = results . messages ,
output = [ ] ;
/ * *
* Replace special characters before write to output .
*
* Rules :
* - single quotes is the escape sequence for double - quotes
* - & amp ; is the escape sequence for &
* - & lt ; is the escape sequence for <
* - & gt ; is the escape sequence for >
*
* @ param { String } message to escape
* @ return escaped message as { String }
* /
var escapeSpecialCharacters = function ( str ) {
if ( ! str || str . constructor !== String ) {
return "" ;
}
return str . replace ( /"/g , "'" ) . replace ( /&/g , "&" ) . replace ( /</g , "<" ) . replace ( />/g , ">" ) ;
} ;
if ( messages . length > 0 ) {
output . push ( "<file name=\"" + filename + "\">" ) ;
CSSLint . Util . forEach ( messages , function ( message ) {
if ( message . rollup ) {
output . push ( "<issue severity=\"" + message . type + "\" reason=\"" + escapeSpecialCharacters ( message . message ) + "\" evidence=\"" + escapeSpecialCharacters ( message . evidence ) + "\"/>" ) ;
} else {
output . push ( "<issue line=\"" + message . line + "\" char=\"" + message . col + "\" severity=\"" + message . type + "\"" +
" reason=\"" + escapeSpecialCharacters ( message . message ) + "\" evidence=\"" + escapeSpecialCharacters ( message . evidence ) + "\"/>" ) ;
}
} ) ;
output . push ( "</file>" ) ;
}
return output . join ( "" ) ;
}
} ) ;
/* globals JSON: true */
CSSLint . addFormatter ( {
// format information
id : "json" ,
name : "JSON" ,
/ * *
* Return content to be printed before all file results .
* @ return { String } to prepend before all results
* /
startFormat : function ( ) {
"use strict" ;
this . json = [ ] ;
return "" ;
} ,
/ * *
* Return content to be printed after all file results .
* @ return { String } to append after all results
* /
endFormat : function ( ) {
"use strict" ;
var ret = "" ;
if ( this . json . length > 0 ) {
if ( this . json . length === 1 ) {
ret = JSON . stringify ( this . json [ 0 ] ) ;
} else {
ret = JSON . stringify ( this . json ) ;
}
}
return ret ;
} ,
/ * *
* Given CSS Lint results for a file , return output for this format .
* @ param results { Object } with error and warning messages
* @ param filename { String } relative file path ( Unused )
* @ return { String } output for results
* /
formatResults : function ( results , filename , options ) {
"use strict" ;
if ( results . messages . length > 0 || ! options . quiet ) {
this . json . push ( {
filename : filename ,
messages : results . messages ,
stats : results . stats
} ) ;
}
return "" ;
}
} ) ;
CSSLint . addFormatter ( {
// format information
id : "junit-xml" ,
name : "JUNIT XML format" ,
/ * *
* Return opening root XML tag .
* @ return { String } to prepend before all results
* /
startFormat : function ( ) {
"use strict" ;
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>" ;
} ,
/ * *
* Return closing root XML tag .
* @ return { String } to append after all results
* /
endFormat : function ( ) {
"use strict" ;
return "</testsuites>" ;
} ,
/ * *
* Given CSS Lint results for a file , return output for this format .
* @ param results { Object } with error and warning messages
* @ param filename { String } relative file path
* @ param options { Object } ( UNUSED for now ) specifies special handling of output
* @ return { String } output for results
* /
formatResults : function ( results , filename /*, options*/ ) {
"use strict" ;
var messages = results . messages ,
output = [ ] ,
tests = {
"error" : 0 ,
"failure" : 0
} ;
/ * *
* Generate a source string for a rule .
* JUNIT source strings usually resemble Java class names e . g
* net . csslint . SomeRuleName
* @ param { Object } rule
* @ return rule source as { String }
* /
var generateSource = function ( rule ) {
if ( ! rule || ! ( "name" in rule ) ) {
return "" ;
}
return "net.csslint." + rule . name . replace ( /\s/g , "" ) ;
} ;
/ * *
* Replace special characters before write to output .
*
* Rules :
* - single quotes is the escape sequence for double - quotes
* - & lt ; is the escape sequence for <
* - & gt ; is the escape sequence for >
*
* @ param { String } message to escape
* @ return escaped message as { String }
* /
var escapeSpecialCharacters = function ( str ) {
if ( ! str || str . constructor !== String ) {
return "" ;
}
return str . replace ( /"/g , "'" ) . replace ( /</g , "<" ) . replace ( />/g , ">" ) ;
} ;
if ( messages . length > 0 ) {
messages . forEach ( function ( message ) {
// since junit has no warning class
// all issues as errors
var type = message . type === "warning" ? "error" : message . type ;
// ignore rollups for now
if ( ! message . rollup ) {
// build the test case separately, once joined
// we'll add it to a custom array filtered by type
output . push ( "<testcase time=\"0\" name=\"" + generateSource ( message . rule ) + "\">" ) ;
output . push ( "<" + type + " message=\"" + escapeSpecialCharacters ( message . message ) + "\"><![CDATA[" + message . line + ":" + message . col + ":" + escapeSpecialCharacters ( message . evidence ) + "]]></" + type + ">" ) ;
output . push ( "</testcase>" ) ;
tests [ type ] += 1 ;
}
} ) ;
output . unshift ( "<testsuite time=\"0\" tests=\"" + messages . length + "\" skipped=\"0\" errors=\"" + tests . error + "\" failures=\"" + tests . failure + "\" package=\"net.csslint\" name=\"" + filename + "\">" ) ;
output . push ( "</testsuite>" ) ;
}
return output . join ( "" ) ;
}
} ) ;
CSSLint . addFormatter ( {
// format information
id : "lint-xml" ,
name : "Lint XML format" ,
/ * *
* Return opening root XML tag .
* @ return { String } to prepend before all results
* /
startFormat : function ( ) {
"use strict" ;
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>" ;
} ,
/ * *
* Return closing root XML tag .
* @ return { String } to append after all results
* /
endFormat : function ( ) {
"use strict" ;
return "</lint>" ;
} ,
/ * *
* Given CSS Lint results for a file , return output for this format .
* @ param results { Object } with error and warning messages
* @ param filename { String } relative file path
* @ param options { Object } ( UNUSED for now ) specifies special handling of output
* @ return { String } output for results
* /
formatResults : function ( results , filename /*, options*/ ) {
"use strict" ;
var messages = results . messages ,
output = [ ] ;
/ * *
* Replace special characters before write to output .
*
* Rules :
* - single quotes is the escape sequence for double - quotes
* - & amp ; is the escape sequence for &
* - & lt ; is the escape sequence for <
* - & gt ; is the escape sequence for >
*
* @ param { String } message to escape
* @ return escaped message as { String }
* /
var escapeSpecialCharacters = function ( str ) {
if ( ! str || str . constructor !== String ) {
return "" ;
}
return str . replace ( /"/g , "'" ) . replace ( /&/g , "&" ) . replace ( /</g , "<" ) . replace ( />/g , ">" ) ;
} ;
if ( messages . length > 0 ) {
output . push ( "<file name=\"" + filename + "\">" ) ;
CSSLint . Util . forEach ( messages , function ( message ) {
if ( message . rollup ) {
output . push ( "<issue severity=\"" + message . type + "\" reason=\"" + escapeSpecialCharacters ( message . message ) + "\" evidence=\"" + escapeSpecialCharacters ( message . evidence ) + "\"/>" ) ;
} else {
var rule = "" ;
if ( message . rule && message . rule . id ) {
rule = "rule=\"" + escapeSpecialCharacters ( message . rule . id ) + "\" " ;
}
output . push ( "<issue " + rule + "line=\"" + message . line + "\" char=\"" + message . col + "\" severity=\"" + message . type + "\"" +
" reason=\"" + escapeSpecialCharacters ( message . message ) + "\" evidence=\"" + escapeSpecialCharacters ( message . evidence ) + "\"/>" ) ;
}
} ) ;
output . push ( "</file>" ) ;
}
return output . join ( "" ) ;
}
} ) ;
CSSLint . addFormatter ( {
// format information
id : "text" ,
name : "Plain Text" ,
/ * *
* Return content to be printed before all file results .
* @ return { String } to prepend before all results
* /
startFormat : function ( ) {
"use strict" ;
return "" ;
} ,
/ * *
* Return content to be printed after all file results .
* @ return { String } to append after all results
* /
endFormat : function ( ) {
"use strict" ;
return "" ;
} ,
/ * *
* Given CSS Lint results for a file , return output for this format .
* @ param results { Object } with error and warning messages
* @ param filename { String } relative file path
* @ param options { Object } ( Optional ) specifies special handling of output
* @ return { String } output for results
* /
formatResults : function ( results , filename , options ) {
"use strict" ;
var messages = results . messages ,
output = "" ;
options = options || { } ;
if ( messages . length === 0 ) {
return options . quiet ? "" : "\n\ncsslint: No errors in " + filename + "." ;
}
output = "\n\ncsslint: There " ;
if ( messages . length === 1 ) {
output += "is 1 problem" ;
} else {
output += "are " + messages . length + " problems" ;
}
output += " in " + filename + "." ;
var pos = filename . lastIndexOf ( "/" ) ,
shortFilename = filename ;
if ( pos === - 1 ) {
pos = filename . lastIndexOf ( "\\" ) ;
}
if ( pos > - 1 ) {
shortFilename = filename . substring ( pos + 1 ) ;
}
CSSLint . Util . forEach ( messages , function ( message , i ) {
output = output + "\n\n" + shortFilename ;
if ( message . rollup ) {
output += "\n" + ( i + 1 ) + ": " + message . type ;
output += "\n" + message . message ;
} else {
output += "\n" + ( i + 1 ) + ": " + message . type + " at line " + message . line + ", col " + message . col ;
output += "\n" + message . message ;
output += "\n" + message . evidence ;
}
} ) ;
return output ;
}
} ) ;
return CSSLint ;
Editor: Add CodeMirror-powered code editor with syntax highlighting, linting, and auto-completion.
* Code editor is integrated into the Theme/Plugin Editor, Additional CSS in Customizer, and Custom HTML widget. Code editor is not yet integrated into the post editor, and it may not be until accessibility concerns are addressed.
* The CodeMirror component in the Custom HTML widget is integrated in a similar way to TinyMCE being integrated into the Text widget, adopting the same approach for integrating dynamic JavaScript-initialized fields.
* Linting is performed for JS, CSS, HTML, and JSON via JSHint, CSSLint, HTMLHint, and JSONLint respectively. Linting is not yet supported for PHP.
* When user lacks `unfiltered_html` the capability, the Custom HTML widget will report any Kses-invalid elements and attributes as errors via a custom Kses rule for HTMLHint.
* When linting errors are detected, the user will be prevented from saving the code until the errors are fixed, reducing instances of broken websites.
* The placeholder value is removed from Custom CSS in favor of a fleshed-out section description which now auto-expands when the CSS field is empty. See #39892.
* The CodeMirror library is included as `wp.CodeMirror` to prevent conflicts with any existing `CodeMirror` global.
* An `wp.codeEditor.initialize()` API in JS is provided to convert a `textarea` into CodeMirror, with a `wp_enqueue_code_editor()` function in PHP to manage enqueueing the assets and settings needed to edit a given type of code.
* A user preference is added to manage whether or not "syntax highlighting" is enabled. The feature is opt-out, being enabled by default.
* Allowed file extensions in the theme and plugin editors have been updated to include formats which CodeMirror has modes for: `conf`, `css`, `diff`, `patch`, `html`, `htm`, `http`, `js`, `json`, `jsx`, `less`, `md`, `php`, `phtml`, `php3`, `php4`, `php5`, `php7`, `phps`, `scss`, `sass`, `sh`, `bash`, `sql`, `svg`, `xml`, `yml`, `yaml`, `txt`.
Props westonruter, georgestephanis, obenland, melchoyce, pixolin, mizejewski, michelleweber, afercia, grahamarmfield, samikeijonen, rianrietveld, iseulde.
See #38707.
Fixes #12423, #39892.
Built from https://develop.svn.wordpress.org/trunk@41376
git-svn-id: http://core.svn.wordpress.org/trunk@41209 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2017-09-13 02:08:47 -04:00
} ) ( ) ;