2012-01-01 19:45:30 +00:00
"no use strict" ;
var console = {
log : function ( msg ) {
postMessage ( { type : "log" , data : msg } ) ;
}
} ;
var window = {
console : console
} ;
var normalizeModule = function ( parentId , moduleName ) {
// normalize plugin requires
if ( moduleName . indexOf ( "!" ) !== - 1 ) {
var chunks = moduleName . split ( "!" ) ;
return normalizeModule ( parentId , chunks [ 0 ] ) + "!" + normalizeModule ( parentId , chunks [ 1 ] ) ;
}
// normalize relative requires
if ( moduleName . charAt ( 0 ) == "." ) {
var base = parentId . split ( "/" ) . slice ( 0 , - 1 ) . join ( "/" ) ;
var moduleName = base + "/" + moduleName ;
while ( moduleName . indexOf ( "." ) !== - 1 && previous != moduleName ) {
var previous = moduleName ;
var moduleName = moduleName . replace ( /\/\.\// , "/" ) . replace ( /[^\/]+\/\.\.\// , "" ) ;
}
}
return moduleName ;
} ;
var require = function ( parentId , id ) {
var id = normalizeModule ( parentId , id ) ;
var module = require . modules [ id ] ;
if ( module ) {
if ( ! module . initialized ) {
module . exports = module . factory ( ) . exports ;
module . initialized = true ;
}
return module . exports ;
}
var chunks = id . split ( "/" ) ;
chunks [ 0 ] = require . tlns [ chunks [ 0 ] ] || chunks [ 0 ] ;
var path = chunks . join ( "/" ) + ".js" ;
require . id = id ;
importScripts ( path ) ;
return require ( parentId , id ) ;
} ;
require . modules = { } ;
require . tlns = { } ;
var define = function ( id , deps , factory ) {
if ( arguments . length == 2 ) {
factory = deps ;
} else if ( arguments . length == 1 ) {
factory = id ;
id = require . id ;
}
if ( id . indexOf ( "text!" ) === 0 )
return ;
var req = function ( deps , factory ) {
return require ( id , deps , factory ) ;
} ;
require . modules [ id ] = {
factory : function ( ) {
var module = {
exports : { }
} ;
var returnExports = factory ( req , module . exports , module ) ;
if ( returnExports )
module . exports = returnExports ;
return module ;
}
} ;
} ;
function initBaseUrls ( topLevelNamespaces ) {
require . tlns = topLevelNamespaces ;
}
function initSender ( ) {
var EventEmitter = require ( null , "ace/lib/event_emitter" ) . EventEmitter ;
var oop = require ( null , "ace/lib/oop" ) ;
var Sender = function ( ) { } ;
( function ( ) {
oop . implement ( this , EventEmitter ) ;
this . callback = function ( data , callbackId ) {
postMessage ( {
type : "call" ,
id : callbackId ,
data : data
} ) ;
} ;
this . emit = function ( name , data ) {
postMessage ( {
type : "event" ,
name : name ,
data : data
} ) ;
} ;
} ) . call ( Sender . prototype ) ;
return new Sender ( ) ;
}
var main ;
var sender ;
onmessage = function ( e ) {
var msg = e . data ;
if ( msg . command ) {
main [ msg . command ] . apply ( main , msg . args ) ;
}
else if ( msg . init ) {
initBaseUrls ( msg . tlns ) ;
require ( null , "ace/lib/fixoldbrowsers" ) ;
sender = initSender ( ) ;
var clazz = require ( null , msg . module ) [ msg . classname ] ;
main = new clazz ( sender ) ;
}
else if ( msg . event && sender ) {
sender . _emit ( msg . event , msg . data ) ;
}
} ;
// vim:set ts=4 sts=4 sw=4 st:
// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
// -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project)
// -- dantman Daniel Friesen Copyright(C) 2010 XXX No License Specified
// -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License
// -- Irakli Gozalishvili Copyright (C) 2010 MIT License
/ * !
Copyright ( c ) 2009 , 280 North Inc . http : //280north.com/
MIT License . http : //github.com/280north/narwhal/blob/master/README.md
* /
define ( 'ace/lib/fixoldbrowsers' , [ 'require' , 'exports' , 'module' , 'ace/lib/regexp' , 'ace/lib/es5-shim' ] , function ( require , exports , module ) {
"use strict" ;
require ( "./regexp" ) ;
require ( "./es5-shim" ) ;
} ) ; / * *
* Based on code from :
*
* XRegExp 1.5 . 0
* ( c ) 2007 - 2010 Steven Levithan
* MIT License
* < http : //xregexp.com>
* Provides an augmented , extensible , cross - browser implementation of regular expressions ,
* including support for additional syntax , flags , and methods
* /
define ( 'ace/lib/regexp' , [ 'require' , 'exports' , 'module' ] , function ( require , exports , module ) {
"use strict" ;
//---------------------------------
// Private variables
//---------------------------------
var real = {
exec : RegExp . prototype . exec ,
test : RegExp . prototype . test ,
match : String . prototype . match ,
replace : String . prototype . replace ,
split : String . prototype . split
} ,
compliantExecNpcg = real . exec . call ( /()??/ , "" ) [ 1 ] === undefined , // check `exec` handling of nonparticipating capturing groups
compliantLastIndexIncrement = function ( ) {
var x = /^/g ;
real . test . call ( x , "" ) ;
return ! x . lastIndex ;
} ( ) ;
//---------------------------------
// Overriden native methods
//---------------------------------
// Adds named capture support (with backreferences returned as `result.name`), and fixes two
// cross-browser issues per ES3:
// - Captured values for nonparticipating capturing groups should be returned as `undefined`,
// rather than the empty string.
// - `lastIndex` should not be incremented after zero-length matches.
RegExp . prototype . exec = function ( str ) {
var match = real . exec . apply ( this , arguments ) ,
name , r2 ;
if ( match ) {
// Fix browsers whose `exec` methods don't consistently return `undefined` for
// nonparticipating capturing groups
if ( ! compliantExecNpcg && match . length > 1 && indexOf ( match , "" ) > - 1 ) {
r2 = RegExp ( this . source , real . replace . call ( getNativeFlags ( this ) , "g" , "" ) ) ;
// Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed
// matching due to characters outside the match
real . replace . call ( str . slice ( match . index ) , r2 , function ( ) {
for ( var i = 1 ; i < arguments . length - 2 ; i ++ ) {
if ( arguments [ i ] === undefined )
match [ i ] = undefined ;
}
} ) ;
}
// Attach named capture properties
if ( this . _xregexp && this . _xregexp . captureNames ) {
for ( var i = 1 ; i < match . length ; i ++ ) {
name = this . _xregexp . captureNames [ i - 1 ] ;
if ( name )
match [ name ] = match [ i ] ;
}
}
// Fix browsers that increment `lastIndex` after zero-length matches
if ( ! compliantLastIndexIncrement && this . global && ! match [ 0 ] . length && ( this . lastIndex > match . index ) )
this . lastIndex -- ;
}
return match ;
} ;
// Don't override `test` if it won't change anything
if ( ! compliantLastIndexIncrement ) {
// Fix browser bug in native method
RegExp . prototype . test = function ( str ) {
// Use the native `exec` to skip some processing overhead, even though the overriden
// `exec` would take care of the `lastIndex` fix
var match = real . exec . call ( this , str ) ;
// Fix browsers that increment `lastIndex` after zero-length matches
if ( match && this . global && ! match [ 0 ] . length && ( this . lastIndex > match . index ) )
this . lastIndex -- ;
return ! ! match ;
} ;
}
//---------------------------------
// Private helper functions
//---------------------------------
function getNativeFlags ( regex ) {
return ( regex . global ? "g" : "" ) +
( regex . ignoreCase ? "i" : "" ) +
( regex . multiline ? "m" : "" ) +
( regex . extended ? "x" : "" ) + // Proposed for ES4; included in AS3
( regex . sticky ? "y" : "" ) ;
} ;
function indexOf ( array , item , from ) {
if ( Array . prototype . indexOf ) // Use the native array method if available
return array . indexOf ( item , from ) ;
for ( var i = from || 0 ; i < array . length ; i ++ ) {
if ( array [ i ] === item )
return i ;
}
return - 1 ;
} ;
} ) ; // vim: ts=4 sts=4 sw=4 expandtab
// -- kriskowal Kris Kowal Copyright (C) 2009-2011 MIT License
// -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project)
// -- dantman Daniel Friesen Copyright (C) 2010 XXX TODO License or CLA
// -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License
// -- Gozala Irakli Gozalishvili Copyright (C) 2010 MIT License
// -- kitcambridge Kit Cambridge Copyright (C) 2011 MIT License
// -- kossnocorp Sasha Koss XXX TODO License or CLA
// -- bryanforbes Bryan Forbes XXX TODO License or CLA
// -- killdream Quildreen Motta Copyright (C) 2011 MIT Licence
// -- michaelficarra Michael Ficarra Copyright (C) 2011 3-clause BSD License
// -- sharkbrainguy Gerard Paapu Copyright (C) 2011 MIT License
// -- bbqsrc Brendan Molloy (C) 2011 Creative Commons Zero (public domain)
// -- iwyg XXX TODO License or CLA
// -- DomenicDenicola Domenic Denicola Copyright (C) 2011 MIT License
// -- xavierm02 Montillet Xavier XXX TODO License or CLA
// -- Raynos Raynos XXX TODO License or CLA
// -- samsonjs Sami Samhuri Copyright (C) 2010 MIT License
// -- rwldrn Rick Waldron Copyright (C) 2011 MIT License
// -- lexer Alexey Zakharov XXX TODO License or CLA
/ * !
Copyright ( c ) 2009 , 280 North Inc . http : //280north.com/
MIT License . http : //github.com/280north/narwhal/blob/master/README.md
* /
define ( 'ace/lib/es5-shim' , [ 'require' , 'exports' , 'module' ] , function ( require , exports , module ) {
/ * *
* Brings an environment as close to ECMAScript 5 compliance
* as is possible with the facilities of erstwhile engines .
*
* Annotated ES5 : http : //es5.github.com/ (specific links below)
* ES5 Spec : http : //www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
*
* @ module
* /
/*whatsupdoc*/
//
// Function
// ========
//
// ES-5 15.3.4.5
// http://es5.github.com/#x15.3.4.5
if ( ! Function . prototype . bind ) {
Function . prototype . bind = function bind ( that ) { // .length is 1
// 1. Let Target be the this value.
var target = this ;
// 2. If IsCallable(Target) is false, throw a TypeError exception.
if ( typeof target != "function" )
throw new TypeError ( ) ; // TODO message
// 3. Let A be a new (possibly empty) internal list of all of the
// argument values provided after thisArg (arg1, arg2 etc), in order.
// XXX slicedArgs will stand in for "A" if used
var args = slice . call ( arguments , 1 ) ; // for normal call
// 4. Let F be a new native ECMAScript object.
// 11. Set the [[Prototype]] internal property of F to the standard
// built-in Function prototype object as specified in 15.3.3.1.
// 12. Set the [[Call]] internal property of F as described in
// 15.3.4.5.1.
// 13. Set the [[Construct]] internal property of F as described in
// 15.3.4.5.2.
// 14. Set the [[HasInstance]] internal property of F as described in
// 15.3.4.5.3.
var bound = function ( ) {
if ( this instanceof bound ) {
// 15.3.4.5.2 [[Construct]]
// When the [[Construct]] internal method of a function object,
// F that was created using the bind function is called with a
// list of arguments ExtraArgs, the following steps are taken:
// 1. Let target be the value of F's [[TargetFunction]]
// internal property.
// 2. If target has no [[Construct]] internal method, a
// TypeError exception is thrown.
// 3. Let boundArgs be the value of F's [[BoundArgs]] internal
// property.
// 4. Let args be a new list containing the same values as the
// list boundArgs in the same order followed by the same
// values as the list ExtraArgs in the same order.
// 5. Return the result of calling the [[Construct]] internal
// method of target providing args as the arguments.
var F = function ( ) { } ;
F . prototype = target . prototype ;
var self = new F ;
var result = target . apply (
self ,
args . concat ( slice . call ( arguments ) )
) ;
if ( result !== null && Object ( result ) === result )
return result ;
return self ;
} else {
// 15.3.4.5.1 [[Call]]
// When the [[Call]] internal method of a function object, F,
// which was created using the bind function is called with a
// this value and a list of arguments ExtraArgs, the following
// steps are taken:
// 1. Let boundArgs be the value of F's [[BoundArgs]] internal
// property.
// 2. Let boundThis be the value of F's [[BoundThis]] internal
// property.
// 3. Let target be the value of F's [[TargetFunction]] internal
// property.
// 4. Let args be a new list containing the same values as the
// list boundArgs in the same order followed by the same
// values as the list ExtraArgs in the same order.
// 5. Return the result of calling the [[Call]] internal method
// of target providing boundThis as the this value and
// providing args as the arguments.
// equiv: target.call(this, ...boundArgs, ...args)
return target . apply (
that ,
args . concat ( slice . call ( arguments ) )
) ;
}
} ;
// XXX bound.length is never writable, so don't even try
//
// 15. If the [[Class]] internal property of Target is "Function", then
// a. Let L be the length property of Target minus the length of A.
// b. Set the length own property of F to either 0 or L, whichever is
// larger.
// 16. Else set the length own property of F to 0.
// 17. Set the attributes of the length own property of F to the values
// specified in 15.3.5.1.
// TODO
// 18. Set the [[Extensible]] internal property of F to true.
// TODO
// 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
// 20. Call the [[DefineOwnProperty]] internal method of F with
// arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
// thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
// false.
// 21. Call the [[DefineOwnProperty]] internal method of F with
// arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
// [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
// and false.
// TODO
// NOTE Function objects created using Function.prototype.bind do not
// have a prototype property or the [[Code]], [[FormalParameters]], and
// [[Scope]] internal properties.
// XXX can't delete prototype in pure-js.
// 22. Return F.
return bound ;
} ;
}
// Shortcut to an often accessed properties, in order to avoid multiple
// dereference that costs universally.
// _Please note: Shortcuts are defined after `Function.prototype.bind` as we
// us it in defining shortcuts.
var call = Function . prototype . call ;
var prototypeOfArray = Array . prototype ;
var prototypeOfObject = Object . prototype ;
var slice = prototypeOfArray . slice ;
var toString = call . bind ( prototypeOfObject . toString ) ;
var owns = call . bind ( prototypeOfObject . hasOwnProperty ) ;
// If JS engine supports accessors creating shortcuts.
var defineGetter ;
var defineSetter ;
var lookupGetter ;
var lookupSetter ;
var supportsAccessors ;
if ( ( supportsAccessors = owns ( prototypeOfObject , "__defineGetter__" ) ) ) {
defineGetter = call . bind ( prototypeOfObject . _ _defineGetter _ _ ) ;
defineSetter = call . bind ( prototypeOfObject . _ _defineSetter _ _ ) ;
lookupGetter = call . bind ( prototypeOfObject . _ _lookupGetter _ _ ) ;
lookupSetter = call . bind ( prototypeOfObject . _ _lookupSetter _ _ ) ;
}
//
// Array
// =====
//
// ES5 15.4.3.2
// http://es5.github.com/#x15.4.3.2
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
if ( ! Array . isArray ) {
Array . isArray = function isArray ( obj ) {
return toString ( obj ) == "[object Array]" ;
} ;
}
// The IsCallable() check in the Array functions
// has been replaced with a strict check on the
// internal class of the object to trap cases where
// the provided function was actually a regular
// expression literal, which in V8 and
// JavaScriptCore is a typeof "function". Only in
// V8 are regular expression literals permitted as
// reduce parameters, so it is desirable in the
// general case for the shim to match the more
// strict and common behavior of rejecting regular
// expressions.
// ES5 15.4.4.18
// http://es5.github.com/#x15.4.4.18
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach
if ( ! Array . prototype . forEach ) {
Array . prototype . forEach = function forEach ( fun /*, thisp*/ ) {
var self = toObject ( this ) ,
thisp = arguments [ 1 ] ,
i = 0 ,
length = self . length >>> 0 ;
// If no callback function or if callback is not a callable function
if ( toString ( fun ) != "[object Function]" ) {
throw new TypeError ( ) ; // TODO message
}
while ( i < length ) {
if ( i in self ) {
// Invoke the callback function with call, passing arguments:
// context, property value, property key, thisArg object context
fun . call ( thisp , self [ i ] , i , self ) ;
}
i ++ ;
}
} ;
}
// ES5 15.4.4.19
// http://es5.github.com/#x15.4.4.19
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
if ( ! Array . prototype . map ) {
Array . prototype . map = function map ( fun /*, thisp*/ ) {
var self = toObject ( this ) ,
length = self . length >>> 0 ,
result = Array ( length ) ,
thisp = arguments [ 1 ] ;
// If no callback function or if callback is not a callable function
if ( toString ( fun ) != "[object Function]" ) {
throw new TypeError ( ) ; // TODO message
}
for ( var i = 0 ; i < length ; i ++ ) {
if ( i in self )
result [ i ] = fun . call ( thisp , self [ i ] , i , self ) ;
}
return result ;
} ;
}
// ES5 15.4.4.20
// http://es5.github.com/#x15.4.4.20
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
if ( ! Array . prototype . filter ) {
Array . prototype . filter = function filter ( fun /*, thisp */ ) {
var self = toObject ( this ) ,
length = self . length >>> 0 ,
result = [ ] ,
thisp = arguments [ 1 ] ;
// If no callback function or if callback is not a callable function
if ( toString ( fun ) != "[object Function]" ) {
throw new TypeError ( ) ; // TODO message
}
for ( var i = 0 ; i < length ; i ++ ) {
if ( i in self && fun . call ( thisp , self [ i ] , i , self ) )
result . push ( self [ i ] ) ;
}
return result ;
} ;
}
// ES5 15.4.4.16
// http://es5.github.com/#x15.4.4.16
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
if ( ! Array . prototype . every ) {
Array . prototype . every = function every ( fun /*, thisp */ ) {
var self = toObject ( this ) ,
length = self . length >>> 0 ,
thisp = arguments [ 1 ] ;
// If no callback function or if callback is not a callable function
if ( toString ( fun ) != "[object Function]" ) {
throw new TypeError ( ) ; // TODO message
}
for ( var i = 0 ; i < length ; i ++ ) {
if ( i in self && ! fun . call ( thisp , self [ i ] , i , self ) )
return false ;
}
return true ;
} ;
}
// ES5 15.4.4.17
// http://es5.github.com/#x15.4.4.17
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
if ( ! Array . prototype . some ) {
Array . prototype . some = function some ( fun /*, thisp */ ) {
var self = toObject ( this ) ,
length = self . length >>> 0 ,
thisp = arguments [ 1 ] ;
// If no callback function or if callback is not a callable function
if ( toString ( fun ) != "[object Function]" ) {
throw new TypeError ( ) ; // TODO message
}
for ( var i = 0 ; i < length ; i ++ ) {
if ( i in self && fun . call ( thisp , self [ i ] , i , self ) )
return true ;
}
return false ;
} ;
}
// ES5 15.4.4.21
// http://es5.github.com/#x15.4.4.21
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
if ( ! Array . prototype . reduce ) {
Array . prototype . reduce = function reduce ( fun /*, initial*/ ) {
var self = toObject ( this ) ,
length = self . length >>> 0 ;
// If no callback function or if callback is not a callable function
if ( toString ( fun ) != "[object Function]" ) {
throw new TypeError ( ) ; // TODO message
}
// no value to return if no initial value and an empty array
if ( ! length && arguments . length == 1 )
throw new TypeError ( ) ; // TODO message
var i = 0 ;
var result ;
if ( arguments . length >= 2 ) {
result = arguments [ 1 ] ;
} else {
do {
if ( i in self ) {
result = self [ i ++ ] ;
break ;
}
// if array contains no values, no initial value to return
if ( ++ i >= length )
throw new TypeError ( ) ; // TODO message
} while ( true ) ;
}
for ( ; i < length ; i ++ ) {
if ( i in self )
result = fun . call ( void 0 , result , self [ i ] , i , self ) ;
}
return result ;
} ;
}
// ES5 15.4.4.22
// http://es5.github.com/#x15.4.4.22
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
if ( ! Array . prototype . reduceRight ) {
Array . prototype . reduceRight = function reduceRight ( fun /*, initial*/ ) {
var self = toObject ( this ) ,
length = self . length >>> 0 ;
// If no callback function or if callback is not a callable function
if ( toString ( fun ) != "[object Function]" ) {
throw new TypeError ( ) ; // TODO message
}
// no value to return if no initial value, empty array
if ( ! length && arguments . length == 1 )
throw new TypeError ( ) ; // TODO message
var result , i = length - 1 ;
if ( arguments . length >= 2 ) {
result = arguments [ 1 ] ;
} else {
do {
if ( i in self ) {
result = self [ i -- ] ;
break ;
}
// if array contains no values, no initial value to return
if ( -- i < 0 )
throw new TypeError ( ) ; // TODO message
} while ( true ) ;
}
do {
if ( i in this )
result = fun . call ( void 0 , result , self [ i ] , i , self ) ;
} while ( i -- ) ;
return result ;
} ;
}
// ES5 15.4.4.14
// http://es5.github.com/#x15.4.4.14
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
if ( ! Array . prototype . indexOf ) {
Array . prototype . indexOf = function indexOf ( sought /*, fromIndex */ ) {
var self = toObject ( this ) ,
length = self . length >>> 0 ;
if ( ! length )
return - 1 ;
var i = 0 ;
if ( arguments . length > 1 )
i = toInteger ( arguments [ 1 ] ) ;
// handle negative indices
i = i >= 0 ? i : Math . max ( 0 , length + i ) ;
for ( ; i < length ; i ++ ) {
if ( i in self && self [ i ] === sought ) {
return i ;
}
}
return - 1 ;
} ;
}
// ES5 15.4.4.15
// http://es5.github.com/#x15.4.4.15
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
if ( ! Array . prototype . lastIndexOf ) {
Array . prototype . lastIndexOf = function lastIndexOf ( sought /*, fromIndex */ ) {
var self = toObject ( this ) ,
length = self . length >>> 0 ;
if ( ! length )
return - 1 ;
var i = length - 1 ;
if ( arguments . length > 1 )
i = Math . min ( i , toInteger ( arguments [ 1 ] ) ) ;
// handle negative indices
i = i >= 0 ? i : length - Math . abs ( i ) ;
for ( ; i >= 0 ; i -- ) {
if ( i in self && sought === self [ i ] )
return i ;
}
return - 1 ;
} ;
}
//
// Object
// ======
//
// ES5 15.2.3.2
// http://es5.github.com/#x15.2.3.2
if ( ! Object . getPrototypeOf ) {
// https://github.com/kriskowal/es5-shim/issues#issue/2
// http://ejohn.org/blog/objectgetprototypeof/
// recommended by fschaefer on github
Object . getPrototypeOf = function getPrototypeOf ( object ) {
return object . _ _proto _ _ || (
object . constructor ?
object . constructor . prototype :
prototypeOfObject
) ;
} ;
}
// ES5 15.2.3.3
// http://es5.github.com/#x15.2.3.3
if ( ! Object . getOwnPropertyDescriptor ) {
var ERR _NON _OBJECT = "Object.getOwnPropertyDescriptor called on a " +
"non-object: " ;
Object . getOwnPropertyDescriptor = function getOwnPropertyDescriptor ( object , property ) {
if ( ( typeof object != "object" && typeof object != "function" ) || object === null )
throw new TypeError ( ERR _NON _OBJECT + object ) ;
// If object does not owns property return undefined immediately.
if ( ! owns ( object , property ) )
return ;
var descriptor , getter , setter ;
// If object has a property then it's for sure both `enumerable` and
// `configurable`.
descriptor = { enumerable : true , configurable : true } ;
// If JS engine supports accessor properties then property may be a
// getter or setter.
if ( supportsAccessors ) {
// Unfortunately `__lookupGetter__` will return a getter even
// if object has own non getter property along with a same named
// inherited getter. To avoid misbehavior we temporary remove
// `__proto__` so that `__lookupGetter__` will return getter only
// if it's owned by an object.
var prototype = object . _ _proto _ _ ;
object . _ _proto _ _ = prototypeOfObject ;
var getter = lookupGetter ( object , property ) ;
var setter = lookupSetter ( object , property ) ;
// Once we have getter and setter we can put values back.
object . _ _proto _ _ = prototype ;
if ( getter || setter ) {
if ( getter ) descriptor . get = getter ;
if ( setter ) descriptor . set = setter ;
// If it was accessor property we're done and return here
// in order to avoid adding `value` to the descriptor.
return descriptor ;
}
}
// If we got this far we know that object has an own property that is
// not an accessor so we set it as a value and return descriptor.
descriptor . value = object [ property ] ;
return descriptor ;
} ;
}
// ES5 15.2.3.4
// http://es5.github.com/#x15.2.3.4
if ( ! Object . getOwnPropertyNames ) {
Object . getOwnPropertyNames = function getOwnPropertyNames ( object ) {
return Object . keys ( object ) ;
} ;
}
// ES5 15.2.3.5
// http://es5.github.com/#x15.2.3.5
if ( ! Object . create ) {
Object . create = function create ( prototype , properties ) {
var object ;
if ( prototype === null ) {
object = { "__proto__" : null } ;
} else {
if ( typeof prototype != "object" )
throw new TypeError ( "typeof prototype[" + ( typeof prototype ) + "] != 'object'" ) ;
var Type = function ( ) { } ;
Type . prototype = prototype ;
object = new Type ( ) ;
// IE has no built-in implementation of `Object.getPrototypeOf`
// neither `__proto__`, but this manually setting `__proto__` will
// guarantee that `Object.getPrototypeOf` will work as expected with
// objects created using `Object.create`
object . _ _proto _ _ = prototype ;
}
if ( properties !== void 0 )
Object . defineProperties ( object , properties ) ;
return object ;
} ;
}
// ES5 15.2.3.6
// http://es5.github.com/#x15.2.3.6
// Patch for WebKit and IE8 standard mode
// Designed by hax <hax.github.com>
// related issue: https://github.com/kriskowal/es5-shim/issues#issue/5
// IE8 Reference:
// http://msdn.microsoft.com/en-us/library/dd282900.aspx
// http://msdn.microsoft.com/en-us/library/dd229916.aspx
// WebKit Bugs:
// https://bugs.webkit.org/show_bug.cgi?id=36423
function doesDefinePropertyWork ( object ) {
try {
Object . defineProperty ( object , "sentinel" , { } ) ;
return "sentinel" in object ;
} catch ( exception ) {
// returns falsy
}
}
// check whether defineProperty works if it's given. Otherwise,
// shim partially.
if ( Object . defineProperty ) {
var definePropertyWorksOnObject = doesDefinePropertyWork ( { } ) ;
var definePropertyWorksOnDom = typeof document == "undefined" ||
doesDefinePropertyWork ( document . createElement ( "div" ) ) ;
if ( ! definePropertyWorksOnObject || ! definePropertyWorksOnDom ) {
var definePropertyFallback = Object . defineProperty ;
}
}
if ( ! Object . defineProperty || definePropertyFallback ) {
var ERR _NON _OBJECT _DESCRIPTOR = "Property description must be an object: " ;
var ERR _NON _OBJECT _TARGET = "Object.defineProperty called on non-object: "
var ERR _ACCESSORS _NOT _SUPPORTED = "getters & setters can not be defined " +
"on this javascript engine" ;
Object . defineProperty = function defineProperty ( object , property , descriptor ) {
if ( ( typeof object != "object" && typeof object != "function" ) || object === null )
throw new TypeError ( ERR _NON _OBJECT _TARGET + object ) ;
if ( ( typeof descriptor != "object" && typeof descriptor != "function" ) || descriptor === null )
throw new TypeError ( ERR _NON _OBJECT _DESCRIPTOR + descriptor ) ;
// make a valiant attempt to use the real defineProperty
// for I8's DOM elements.
if ( definePropertyFallback ) {
try {
return definePropertyFallback . call ( Object , object , property , descriptor ) ;
} catch ( exception ) {
// try the shim if the real one doesn't work
}
}
// If it's a data property.
if ( owns ( descriptor , "value" ) ) {
// fail silently if "writable", "enumerable", or "configurable"
// are requested but not supported
/ *
// alternate approach:
if ( // can't implement these features; allow false but not true
! ( owns ( descriptor , "writable" ) ? descriptor . writable : true ) ||
! ( owns ( descriptor , "enumerable" ) ? descriptor . enumerable : true ) ||
! ( owns ( descriptor , "configurable" ) ? descriptor . configurable : true )
)
throw new RangeError (
"This implementation of Object.defineProperty does not " +
"support configurable, enumerable, or writable."
) ;
* /
if ( supportsAccessors && ( lookupGetter ( object , property ) ||
lookupSetter ( object , property ) ) )
{
// As accessors are supported only on engines implementing
// `__proto__` we can safely override `__proto__` while defining
// a property to make sure that we don't hit an inherited
// accessor.
var prototype = object . _ _proto _ _ ;
object . _ _proto _ _ = prototypeOfObject ;
// Deleting a property anyway since getter / setter may be
// defined on object itself.
delete object [ property ] ;
object [ property ] = descriptor . value ;
// Setting original `__proto__` back now.
object . _ _proto _ _ = prototype ;
} else {
object [ property ] = descriptor . value ;
}
} else {
if ( ! supportsAccessors )
throw new TypeError ( ERR _ACCESSORS _NOT _SUPPORTED ) ;
// If we got that far then getters and setters can be defined !!
if ( owns ( descriptor , "get" ) )
defineGetter ( object , property , descriptor . get ) ;
if ( owns ( descriptor , "set" ) )
defineSetter ( object , property , descriptor . set ) ;
}
return object ;
} ;
}
// ES5 15.2.3.7
// http://es5.github.com/#x15.2.3.7
if ( ! Object . defineProperties ) {
Object . defineProperties = function defineProperties ( object , properties ) {
for ( var property in properties ) {
if ( owns ( properties , property ) )
Object . defineProperty ( object , property , properties [ property ] ) ;
}
return object ;
} ;
}
// ES5 15.2.3.8
// http://es5.github.com/#x15.2.3.8
if ( ! Object . seal ) {
Object . seal = function seal ( object ) {
// this is misleading and breaks feature-detection, but
// allows "securable" code to "gracefully" degrade to working
// but insecure code.
return object ;
} ;
}
// ES5 15.2.3.9
// http://es5.github.com/#x15.2.3.9
if ( ! Object . freeze ) {
Object . freeze = function freeze ( object ) {
// this is misleading and breaks feature-detection, but
// allows "securable" code to "gracefully" degrade to working
// but insecure code.
return object ;
} ;
}
// detect a Rhino bug and patch it
try {
Object . freeze ( function ( ) { } ) ;
} catch ( exception ) {
Object . freeze = ( function freeze ( freezeObject ) {
return function freeze ( object ) {
if ( typeof object == "function" ) {
return object ;
} else {
return freezeObject ( object ) ;
}
} ;
} ) ( Object . freeze ) ;
}
// ES5 15.2.3.10
// http://es5.github.com/#x15.2.3.10
if ( ! Object . preventExtensions ) {
Object . preventExtensions = function preventExtensions ( object ) {
// this is misleading and breaks feature-detection, but
// allows "securable" code to "gracefully" degrade to working
// but insecure code.
return object ;
} ;
}
// ES5 15.2.3.11
// http://es5.github.com/#x15.2.3.11
if ( ! Object . isSealed ) {
Object . isSealed = function isSealed ( object ) {
return false ;
} ;
}
// ES5 15.2.3.12
// http://es5.github.com/#x15.2.3.12
if ( ! Object . isFrozen ) {
Object . isFrozen = function isFrozen ( object ) {
return false ;
} ;
}
// ES5 15.2.3.13
// http://es5.github.com/#x15.2.3.13
if ( ! Object . isExtensible ) {
Object . isExtensible = function isExtensible ( object ) {
// 1. If Type(O) is not Object throw a TypeError exception.
if ( Object ( object ) === object ) {
throw new TypeError ( ) ; // TODO message
}
// 2. Return the Boolean value of the [[Extensible]] internal property of O.
var name = '' ;
while ( owns ( object , name ) ) {
name += '?' ;
}
object [ name ] = true ;
var returnValue = owns ( object , name ) ;
delete object [ name ] ;
return returnValue ;
} ;
}
// ES5 15.2.3.14
// http://es5.github.com/#x15.2.3.14
if ( ! Object . keys ) {
// http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
var hasDontEnumBug = true ,
dontEnums = [
"toString" ,
"toLocaleString" ,
"valueOf" ,
"hasOwnProperty" ,
"isPrototypeOf" ,
"propertyIsEnumerable" ,
"constructor"
] ,
dontEnumsLength = dontEnums . length ;
for ( var key in { "toString" : null } )
hasDontEnumBug = false ;
Object . keys = function keys ( object ) {
if ( ( typeof object != "object" && typeof object != "function" ) || object === null )
throw new TypeError ( "Object.keys called on a non-object" ) ;
var keys = [ ] ;
for ( var name in object ) {
if ( owns ( object , name ) ) {
keys . push ( name ) ;
}
}
if ( hasDontEnumBug ) {
for ( var i = 0 , ii = dontEnumsLength ; i < ii ; i ++ ) {
var dontEnum = dontEnums [ i ] ;
if ( owns ( object , dontEnum ) ) {
keys . push ( dontEnum ) ;
}
}
}
return keys ;
} ;
}
//
// Date
// ====
//
// ES5 15.9.5.43
// http://es5.github.com/#x15.9.5.43
// This function returns a String value represent the instance in time
// represented by this Date object. The format of the String is the Date Time
// string format defined in 15.9.1.15. All fields are present in the String.
// The time zone is always UTC, denoted by the suffix Z. If the time value of
// this object is not a finite Number a RangeError exception is thrown.
if ( ! Date . prototype . toISOString || ( new Date ( - 62198755200000 ) . toISOString ( ) . indexOf ( '-000001' ) === - 1 ) ) {
Date . prototype . toISOString = function toISOString ( ) {
var result , length , value , year ;
if ( ! isFinite ( this ) )
throw new RangeError ;
// the date time string format is specified in 15.9.1.15.
result = [ this . getUTCMonth ( ) + 1 , this . getUTCDate ( ) ,
this . getUTCHours ( ) , this . getUTCMinutes ( ) , this . getUTCSeconds ( ) ] ;
year = this . getUTCFullYear ( ) ;
year = ( year < 0 ? '-' : ( year > 9999 ? '+' : '' ) ) + ( '00000' + Math . abs ( year ) ) . slice ( 0 <= year && year <= 9999 ? - 4 : - 6 ) ;
length = result . length ;
while ( length -- ) {
value = result [ length ] ;
// pad months, days, hours, minutes, and seconds to have two digits.
if ( value < 10 )
result [ length ] = "0" + value ;
}
// pad milliseconds to have three digits.
return year + "-" + result . slice ( 0 , 2 ) . join ( "-" ) + "T" + result . slice ( 2 ) . join ( ":" ) + "." +
( "000" + this . getUTCMilliseconds ( ) ) . slice ( - 3 ) + "Z" ;
}
}
// ES5 15.9.4.4
// http://es5.github.com/#x15.9.4.4
if ( ! Date . now ) {
Date . now = function now ( ) {
return new Date ( ) . getTime ( ) ;
} ;
}
// ES5 15.9.5.44
// http://es5.github.com/#x15.9.5.44
// This function provides a String representation of a Date object for use by
// JSON.stringify (15.12.3).
if ( ! Date . prototype . toJSON ) {
Date . prototype . toJSON = function toJSON ( key ) {
// When the toJSON method is called with argument key, the following
// steps are taken:
// 1. Let O be the result of calling ToObject, giving it the this
// value as its argument.
// 2. Let tv be ToPrimitive(O, hint Number).
// 3. If tv is a Number and is not finite, return null.
// XXX
// 4. Let toISO be the result of calling the [[Get]] internal method of
// O with argument "toISOString".
// 5. If IsCallable(toISO) is false, throw a TypeError exception.
if ( typeof this . toISOString != "function" )
throw new TypeError ( ) ; // TODO message
// 6. Return the result of calling the [[Call]] internal method of
// toISO with O as the this value and an empty argument list.
return this . toISOString ( ) ;
// NOTE 1 The argument is ignored.
// NOTE 2 The toJSON function is intentionally generic; it does not
// require that its this value be a Date object. Therefore, it can be
// transferred to other kinds of objects for use as a method. However,
// it does require that any such object have a toISOString method. An
// object is free to use the argument key to filter its
// stringification.
} ;
}
// ES5 15.9.4.2
// http://es5.github.com/#x15.9.4.2
// based on work shared by Daniel Friesen (dantman)
// http://gist.github.com/303249
if ( Date . parse ( "+275760-09-13T00:00:00.000Z" ) !== 8.64 e15 ) {
// XXX global assignment won't work in embeddings that use
// an alternate object for the context.
Date = ( function ( NativeDate ) {
// Date.length === 7
var Date = function Date ( Y , M , D , h , m , s , ms ) {
var length = arguments . length ;
if ( this instanceof NativeDate ) {
var date = length == 1 && String ( Y ) === Y ? // isString(Y)
// We explicitly pass it through parse:
new NativeDate ( Date . parse ( Y ) ) :
// We have to manually make calls depending on argument
// length here
length >= 7 ? new NativeDate ( Y , M , D , h , m , s , ms ) :
length >= 6 ? new NativeDate ( Y , M , D , h , m , s ) :
length >= 5 ? new NativeDate ( Y , M , D , h , m ) :
length >= 4 ? new NativeDate ( Y , M , D , h ) :
length >= 3 ? new NativeDate ( Y , M , D ) :
length >= 2 ? new NativeDate ( Y , M ) :
length >= 1 ? new NativeDate ( Y ) :
new NativeDate ( ) ;
// Prevent mixups with unfixed Date object
date . constructor = Date ;
return date ;
}
return NativeDate . apply ( this , arguments ) ;
} ;
// 15.9.1.15 Date Time String Format.
var isoDateExpression = new RegExp ( "^" +
"(\\d{4}|[\+\-]\\d{6})" + // four-digit year capture or sign + 6-digit extended year
"(?:-(\\d{2})" + // optional month capture
"(?:-(\\d{2})" + // optional day capture
"(?:" + // capture hours:minutes:seconds.milliseconds
"T(\\d{2})" + // hours capture
":(\\d{2})" + // minutes capture
"(?:" + // optional :seconds.milliseconds
":(\\d{2})" + // seconds capture
"(?:\\.(\\d{3}))?" + // milliseconds capture
")?" +
"(?:" + // capture UTC offset component
"Z|" + // UTC capture
"(?:" + // offset specifier +/-hours:minutes
"([-+])" + // sign capture
"(\\d{2})" + // hours offset capture
":(\\d{2})" + // minutes offset capture
")" +
")?)?)?)?" +
"$" ) ;
// Copy any custom methods a 3rd party library may have added
for ( var key in NativeDate )
Date [ key ] = NativeDate [ key ] ;
// Copy "native" methods explicitly; they may be non-enumerable
Date . now = NativeDate . now ;
Date . UTC = NativeDate . UTC ;
Date . prototype = NativeDate . prototype ;
Date . prototype . constructor = Date ;
// Upgrade Date.parse to handle simplified ISO 8601 strings
Date . parse = function parse ( string ) {
var match = isoDateExpression . exec ( string ) ;
if ( match ) {
match . shift ( ) ; // kill match[0], the full match
// parse months, days, hours, minutes, seconds, and milliseconds
for ( var i = 1 ; i < 7 ; i ++ ) {
// provide default values if necessary
match [ i ] = + ( match [ i ] || ( i < 3 ? 1 : 0 ) ) ;
// match[1] is the month. Months are 0-11 in JavaScript
// `Date` objects, but 1-12 in ISO notation, so we
// decrement.
if ( i == 1 )
match [ i ] -- ;
}
// parse the UTC offset component
var minuteOffset = + match . pop ( ) , hourOffset = + match . pop ( ) , sign = match . pop ( ) ;
// compute the explicit time zone offset if specified
var offset = 0 ;
if ( sign ) {
// detect invalid offsets and return early
if ( hourOffset > 23 || minuteOffset > 59 )
return NaN ;
// express the provided time zone offset in minutes. The offset is
// negative for time zones west of UTC; positive otherwise.
offset = ( hourOffset * 60 + minuteOffset ) * 6e4 * ( sign == "+" ? - 1 : 1 ) ;
}
// Date.UTC for years between 0 and 99 converts year to 1900 + year
// The Gregorian calendar has a 400-year cycle, so
// to Date.UTC(year + 400, .... ) - 12622780800000 == Date.UTC(year, ...),
// where 12622780800000 - number of milliseconds in Gregorian calendar 400 years
var year = + match [ 0 ] ;
if ( 0 <= year && year <= 99 ) {
match [ 0 ] = year + 400 ;
return NativeDate . UTC . apply ( this , match ) + offset - 12622780800000 ;
}
// compute a new UTC date value, accounting for the optional offset
return NativeDate . UTC . apply ( this , match ) + offset ;
}
return NativeDate . parse . apply ( this , arguments ) ;
} ;
return Date ;
} ) ( Date ) ;
}
//
// String
// ======
//
// ES5 15.5.4.20
// http://es5.github.com/#x15.5.4.20
var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" +
"\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
"\u2029\uFEFF" ;
if ( ! String . prototype . trim || ws . trim ( ) ) {
// http://blog.stevenlevithan.com/archives/faster-trim-javascript
// http://perfectionkills.com/whitespace-deviations/
ws = "[" + ws + "]" ;
var trimBeginRegexp = new RegExp ( "^" + ws + ws + "*" ) ,
trimEndRegexp = new RegExp ( ws + ws + "*$" ) ;
String . prototype . trim = function trim ( ) {
return String ( this ) . replace ( trimBeginRegexp , "" ) . replace ( trimEndRegexp , "" ) ;
} ;
}
//
// Util
// ======
//
// ES5 9.4
// http://es5.github.com/#x9.4
// http://jsperf.com/to-integer
var toInteger = function ( n ) {
n = + n ;
if ( n !== n ) // isNaN
n = 0 ;
else if ( n !== 0 && n !== ( 1 / 0 ) && n !== - ( 1 / 0 ) )
n = ( n > 0 || - 1 ) * Math . floor ( Math . abs ( n ) ) ;
return n ;
} ;
var prepareString = "a" [ 0 ] != "a" ,
// ES5 9.9
// http://es5.github.com/#x9.9
toObject = function ( o ) {
if ( o == null ) { // this matches both null and undefined
throw new TypeError ( ) ; // TODO message
}
// If the implementation doesn't support by-index access of
// string characters (ex. IE < 7), split the string
if ( prepareString && typeof o == "string" && o ) {
return o . split ( "" ) ;
}
return Object ( o ) ;
} ;
} ) ; / * v i m : t s = 4 : s t s = 4 : s w = 4 :
* * * * * * BEGIN LICENSE BLOCK * * * * *
* Version : MPL 1.1 / GPL 2.0 / LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the "License" ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is Ajax . org Code Editor ( ACE ) .
*
* The Initial Developer of the Original Code is
* Ajax . org B . V .
* Portions created by the Initial Developer are Copyright ( C ) 2010
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
* Fabian Jakobs < fabian AT ajax DOT org >
* Irakli Gozalishvili < rfobic @ gmail . com > ( http : //jeditoolkit.com)
* Mike de Boer < mike AT ajax DOT org >
*
* Alternatively , the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later ( the "GPL" ) , or
* the GNU Lesser General Public License Version 2.1 or later ( the "LGPL" ) ,
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above . If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL , and not to allow others to
* use your version of this file under the terms of the MPL , indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL . If you do not delete
* the provisions above , a recipient may use your version of this file under
* the terms of any one of the MPL , the GPL or the LGPL .
*
* * * * * * END LICENSE BLOCK * * * * * * /
define ( 'ace/lib/event_emitter' , [ 'require' , 'exports' , 'module' ] , function ( require , exports , module ) {
"use strict" ;
var EventEmitter = { } ;
EventEmitter . _emit =
EventEmitter . _dispatchEvent = function ( eventName , e ) {
this . _eventRegistry = this . _eventRegistry || { } ;
this . _defaultHandlers = this . _defaultHandlers || { } ;
var listeners = this . _eventRegistry [ eventName ] || [ ] ;
var defaultHandler = this . _defaultHandlers [ eventName ] ;
if ( ! listeners . length && ! defaultHandler )
return ;
e = e || { } ;
e . type = eventName ;
if ( ! e . stopPropagation ) {
e . stopPropagation = function ( ) {
this . propagationStopped = true ;
} ;
}
if ( ! e . preventDefault ) {
e . preventDefault = function ( ) {
this . defaultPrevented = true ;
} ;
}
for ( var i = 0 ; i < listeners . length ; i ++ ) {
listeners [ i ] ( e ) ;
if ( e . propagationStopped )
break ;
}
if ( defaultHandler && ! e . defaultPrevented )
defaultHandler ( e ) ;
} ;
EventEmitter . setDefaultHandler = function ( eventName , callback ) {
this . _defaultHandlers = this . _defaultHandlers || { } ;
if ( this . _defaultHandlers [ eventName ] )
throw new Error ( "The default handler for '" + eventName + "' is already set" ) ;
this . _defaultHandlers [ eventName ] = callback ;
} ;
EventEmitter . on =
EventEmitter . addEventListener = function ( eventName , callback ) {
this . _eventRegistry = this . _eventRegistry || { } ;
var listeners = this . _eventRegistry [ eventName ] ;
if ( ! listeners )
var listeners = this . _eventRegistry [ eventName ] = [ ] ;
if ( listeners . indexOf ( callback ) == - 1 )
listeners . push ( callback ) ;
} ;
EventEmitter . removeListener =
EventEmitter . removeEventListener = function ( eventName , callback ) {
this . _eventRegistry = this . _eventRegistry || { } ;
var listeners = this . _eventRegistry [ eventName ] ;
if ( ! listeners )
return ;
var index = listeners . indexOf ( callback ) ;
if ( index !== - 1 )
listeners . splice ( index , 1 ) ;
} ;
EventEmitter . removeAllListeners = function ( eventName ) {
if ( this . _eventRegistry ) this . _eventRegistry [ eventName ] = [ ] ;
} ;
exports . EventEmitter = EventEmitter ;
} ) ; / * * * * * * B E G I N L I C E N S E B L O C K * * * * *
* Version : MPL 1.1 / GPL 2.0 / LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the "License" ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is Ajax . org Code Editor ( ACE ) .
*
* The Initial Developer of the Original Code is
* Ajax . org B . V .
* Portions created by the Initial Developer are Copyright ( C ) 2010
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
* Fabian Jakobs < fabian AT ajax DOT org >
*
* Alternatively , the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later ( the "GPL" ) , or
* the GNU Lesser General Public License Version 2.1 or later ( the "LGPL" ) ,
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above . If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL , and not to allow others to
* use your version of this file under the terms of the MPL , indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL . If you do not delete
* the provisions above , a recipient may use your version of this file under
* the terms of any one of the MPL , the GPL or the LGPL .
*
* * * * * * END LICENSE BLOCK * * * * * * /
define ( 'ace/lib/oop' , [ 'require' , 'exports' , 'module' ] , function ( require , exports , module ) {
"use strict" ;
exports . inherits = ( function ( ) {
var tempCtor = function ( ) { } ;
return function ( ctor , superCtor ) {
tempCtor . prototype = superCtor . prototype ;
ctor . super _ = superCtor . prototype ;
ctor . prototype = new tempCtor ( ) ;
ctor . prototype . constructor = ctor ;
} ;
} ( ) ) ;
exports . mixin = function ( obj , mixin ) {
for ( var key in mixin ) {
obj [ key ] = mixin [ key ] ;
}
} ;
exports . implement = function ( proto , mixin ) {
exports . mixin ( proto , mixin ) ;
} ;
} ) ;
/ * * * * * * B E G I N L I C E N S E B L O C K * * * * *
* Version : MPL 1.1 / GPL 2.0 / LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the "License" ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is Ajax . org Code Editor ( ACE ) .
*
* The Initial Developer of the Original Code is
* Ajax . org B . V .
* Portions created by the Initial Developer are Copyright ( C ) 2010
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
* Fabian Jakobs < fabian AT ajax DOT org >
*
* Alternatively , the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later ( the "GPL" ) , or
* the GNU Lesser General Public License Version 2.1 or later ( the "LGPL" ) ,
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above . If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL , and not to allow others to
* use your version of this file under the terms of the MPL , indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL . If you do not delete
* the provisions above , a recipient may use your version of this file under
* the terms of any one of the MPL , the GPL or the LGPL .
*
* * * * * * END LICENSE BLOCK * * * * * * /
define ( 'ace/mode/css_worker' , [ 'require' , 'exports' , 'module' , 'ace/lib/oop' , 'ace/worker/mirror' , 'ace/mode/css/csslint' ] , function ( require , exports , module ) {
"use strict" ;
var oop = require ( "../lib/oop" ) ;
var Mirror = require ( "../worker/mirror" ) . Mirror ;
var CSSLint = require ( "./css/csslint" ) . CSSLint ;
var Worker = exports . Worker = function ( sender ) {
Mirror . call ( this , sender ) ;
this . setTimeout ( 200 ) ;
} ;
oop . inherits ( Worker , Mirror ) ;
( function ( ) {
this . onUpdate = function ( ) {
var value = this . doc . getValue ( ) ;
var result = CSSLint . verify ( value ) ;
this . sender . emit ( "csslint" , result . messages . map ( function ( msg ) {
delete msg . rule ;
return msg ;
} ) ) ;
} ;
} ) . call ( Worker . prototype ) ;
} ) ; define ( 'ace/worker/mirror' , [ 'require' , 'exports' , 'module' , 'ace/document' , 'ace/lib/lang' ] , function ( require , exports , module ) {
"use strict" ;
var Document = require ( "../document" ) . Document ;
var lang = require ( "../lib/lang" ) ;
var Mirror = exports . Mirror = function ( sender ) {
this . sender = sender ;
var doc = this . doc = new Document ( "" ) ;
var deferredUpdate = this . deferredUpdate = lang . deferredCall ( this . onUpdate . bind ( this ) ) ;
var _self = this ;
sender . on ( "change" , function ( e ) {
doc . applyDeltas ( [ e . data ] ) ;
deferredUpdate . schedule ( _self . $timeout ) ;
} ) ;
} ;
( function ( ) {
this . $timeout = 500 ;
this . setTimeout = function ( timeout ) {
this . $timeout = timeout ;
} ;
this . setValue = function ( value ) {
this . doc . setValue ( value ) ;
this . deferredUpdate . schedule ( this . $timeout ) ;
} ;
this . getValue = function ( callbackId ) {
this . sender . callback ( this . doc . getValue ( ) , callbackId ) ;
} ;
this . onUpdate = function ( ) {
// abstract method
} ;
} ) . call ( Mirror . prototype ) ;
} ) ; / * * * * * * B E G I N L I C E N S E B L O C K * * * * *
* Version : MPL 1.1 / GPL 2.0 / LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the "License" ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is Ajax . org Code Editor ( ACE ) .
*
* The Initial Developer of the Original Code is
* Ajax . org B . V .
* Portions created by the Initial Developer are Copyright ( C ) 2010
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
* Fabian Jakobs < fabian AT ajax DOT org >
*
* Alternatively , the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later ( the "GPL" ) , or
* the GNU Lesser General Public License Version 2.1 or later ( the "LGPL" ) ,
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above . If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL , and not to allow others to
* use your version of this file under the terms of the MPL , indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL . If you do not delete
* the provisions above , a recipient may use your version of this file under
* the terms of any one of the MPL , the GPL or the LGPL .
*
* * * * * * END LICENSE BLOCK * * * * * * /
define ( 'ace/document' , [ 'require' , 'exports' , 'module' , 'ace/lib/oop' , 'ace/lib/event_emitter' , 'ace/range' , 'ace/anchor' ] , function ( require , exports , module ) {
"use strict" ;
var oop = require ( "./lib/oop" ) ;
var EventEmitter = require ( "./lib/event_emitter" ) . EventEmitter ;
var Range = require ( "./range" ) . Range ;
var Anchor = require ( "./anchor" ) . Anchor ;
var Document = function ( text ) {
this . $lines = [ ] ;
if ( Array . isArray ( text ) ) {
this . insertLines ( 0 , text ) ;
}
// There has to be one line at least in the document. If you pass an empty
// string to the insert function, nothing will happen. Workaround.
else if ( text . length == 0 ) {
this . $lines = [ "" ] ;
} else {
this . insert ( { row : 0 , column : 0 } , text ) ;
}
} ;
( function ( ) {
oop . implement ( this , EventEmitter ) ;
this . setValue = function ( text ) {
var len = this . getLength ( ) ;
this . remove ( new Range ( 0 , 0 , len , this . getLine ( len - 1 ) . length ) ) ;
this . insert ( { row : 0 , column : 0 } , text ) ;
} ;
this . getValue = function ( ) {
return this . getAllLines ( ) . join ( this . getNewLineCharacter ( ) ) ;
} ;
this . createAnchor = function ( row , column ) {
return new Anchor ( this , row , column ) ;
} ;
// check for IE split bug
if ( "aaa" . split ( /a/ ) . length == 0 )
this . $split = function ( text ) {
return text . replace ( /\r\n|\r/g , "\n" ) . split ( "\n" ) ;
}
else
this . $split = function ( text ) {
return text . split ( /\r\n|\r|\n/ ) ;
} ;
this . $detectNewLine = function ( text ) {
var match = text . match ( /^.*?(\r\n|\r|\n)/m ) ;
if ( match ) {
this . $autoNewLine = match [ 1 ] ;
} else {
this . $autoNewLine = "\n" ;
}
} ;
this . getNewLineCharacter = function ( ) {
switch ( this . $newLineMode ) {
case "windows" :
return "\r\n" ;
case "unix" :
return "\n" ;
case "auto" :
return this . $autoNewLine ;
}
} ;
this . $autoNewLine = "\n" ;
this . $newLineMode = "auto" ;
this . setNewLineMode = function ( newLineMode ) {
if ( this . $newLineMode === newLineMode )
return ;
this . $newLineMode = newLineMode ;
} ;
this . getNewLineMode = function ( ) {
return this . $newLineMode ;
} ;
this . isNewLine = function ( text ) {
return ( text == "\r\n" || text == "\r" || text == "\n" ) ;
} ;
/ * *
* Get a verbatim copy of the given line as it is in the document
* /
this . getLine = function ( row ) {
return this . $lines [ row ] || "" ;
} ;
this . getLines = function ( firstRow , lastRow ) {
return this . $lines . slice ( firstRow , lastRow + 1 ) ;
} ;
/ * *
* Returns all lines in the document as string array . Warning : The caller
* should not modify this array !
* /
this . getAllLines = function ( ) {
return this . getLines ( 0 , this . getLength ( ) ) ;
} ;
this . getLength = function ( ) {
return this . $lines . length ;
} ;
this . getTextRange = function ( range ) {
if ( range . start . row == range . end . row ) {
return this . $lines [ range . start . row ] . substring ( range . start . column ,
range . end . column ) ;
}
else {
var lines = [ ] ;
lines . push ( this . $lines [ range . start . row ] . substring ( range . start . column ) ) ;
lines . push . apply ( lines , this . getLines ( range . start . row + 1 , range . end . row - 1 ) ) ;
lines . push ( this . $lines [ range . end . row ] . substring ( 0 , range . end . column ) ) ;
return lines . join ( this . getNewLineCharacter ( ) ) ;
}
} ;
this . $clipPosition = function ( position ) {
var length = this . getLength ( ) ;
if ( position . row >= length ) {
position . row = Math . max ( 0 , length - 1 ) ;
position . column = this . getLine ( length - 1 ) . length ;
}
return position ;
} ;
this . insert = function ( position , text ) {
if ( text . length == 0 )
return position ;
position = this . $clipPosition ( position ) ;
if ( this . getLength ( ) <= 1 )
this . $detectNewLine ( text ) ;
var lines = this . $split ( text ) ;
var firstLine = lines . splice ( 0 , 1 ) [ 0 ] ;
var lastLine = lines . length == 0 ? null : lines . splice ( lines . length - 1 , 1 ) [ 0 ] ;
position = this . insertInLine ( position , firstLine ) ;
if ( lastLine !== null ) {
position = this . insertNewLine ( position ) ; // terminate first line
position = this . insertLines ( position . row , lines ) ;
position = this . insertInLine ( position , lastLine || "" ) ;
}
return position ;
} ;
this . insertLines = function ( row , lines ) {
if ( lines . length == 0 )
return { row : row , column : 0 } ;
var args = [ row , 0 ] ;
args . push . apply ( args , lines ) ;
this . $lines . splice . apply ( this . $lines , args ) ;
var range = new Range ( row , 0 , row + lines . length , 0 ) ;
var delta = {
action : "insertLines" ,
range : range ,
lines : lines
} ;
this . _emit ( "change" , { data : delta } ) ;
return range . end ;
} ;
this . insertNewLine = function ( position ) {
position = this . $clipPosition ( position ) ;
var line = this . $lines [ position . row ] || "" ;
this . $lines [ position . row ] = line . substring ( 0 , position . column ) ;
this . $lines . splice ( position . row + 1 , 0 , line . substring ( position . column , line . length ) ) ;
var end = {
row : position . row + 1 ,
column : 0
} ;
var delta = {
action : "insertText" ,
range : Range . fromPoints ( position , end ) ,
text : this . getNewLineCharacter ( )
} ;
this . _emit ( "change" , { data : delta } ) ;
return end ;
} ;
this . insertInLine = function ( position , text ) {
if ( text . length == 0 )
return position ;
var line = this . $lines [ position . row ] || "" ;
this . $lines [ position . row ] = line . substring ( 0 , position . column ) + text
+ line . substring ( position . column ) ;
var end = {
row : position . row ,
column : position . column + text . length
} ;
var delta = {
action : "insertText" ,
range : Range . fromPoints ( position , end ) ,
text : text
} ;
this . _emit ( "change" , { data : delta } ) ;
return end ;
} ;
this . remove = function ( range ) {
// clip to document
range . start = this . $clipPosition ( range . start ) ;
range . end = this . $clipPosition ( range . end ) ;
if ( range . isEmpty ( ) )
return range . start ;
var firstRow = range . start . row ;
var lastRow = range . end . row ;
if ( range . isMultiLine ( ) ) {
var firstFullRow = range . start . column == 0 ? firstRow : firstRow + 1 ;
var lastFullRow = lastRow - 1 ;
if ( range . end . column > 0 )
this . removeInLine ( lastRow , 0 , range . end . column ) ;
if ( lastFullRow >= firstFullRow )
this . removeLines ( firstFullRow , lastFullRow ) ;
if ( firstFullRow != firstRow ) {
this . removeInLine ( firstRow , range . start . column , this . getLine ( firstRow ) . length ) ;
this . removeNewLine ( range . start . row ) ;
}
}
else {
this . removeInLine ( firstRow , range . start . column , range . end . column ) ;
}
return range . start ;
} ;
this . removeInLine = function ( row , startColumn , endColumn ) {
if ( startColumn == endColumn )
return ;
var range = new Range ( row , startColumn , row , endColumn ) ;
var line = this . getLine ( row ) ;
var removed = line . substring ( startColumn , endColumn ) ;
var newLine = line . substring ( 0 , startColumn ) + line . substring ( endColumn , line . length ) ;
this . $lines . splice ( row , 1 , newLine ) ;
var delta = {
action : "removeText" ,
range : range ,
text : removed
} ;
this . _emit ( "change" , { data : delta } ) ;
return range . start ;
} ;
/ * *
* Removes a range of full lines
*
* @ param firstRow { Integer } The first row to be removed
* @ param lastRow { Integer } The last row to be removed
* @ return { String [ ] } The removed lines
* /
this . removeLines = function ( firstRow , lastRow ) {
var range = new Range ( firstRow , 0 , lastRow + 1 , 0 ) ;
var removed = this . $lines . splice ( firstRow , lastRow - firstRow + 1 ) ;
var delta = {
action : "removeLines" ,
range : range ,
nl : this . getNewLineCharacter ( ) ,
lines : removed
} ;
this . _emit ( "change" , { data : delta } ) ;
return removed ;
} ;
this . removeNewLine = function ( row ) {
var firstLine = this . getLine ( row ) ;
var secondLine = this . getLine ( row + 1 ) ;
var range = new Range ( row , firstLine . length , row + 1 , 0 ) ;
var line = firstLine + secondLine ;
this . $lines . splice ( row , 2 , line ) ;
var delta = {
action : "removeText" ,
range : range ,
text : this . getNewLineCharacter ( )
} ;
this . _emit ( "change" , { data : delta } ) ;
} ;
this . replace = function ( range , text ) {
if ( text . length == 0 && range . isEmpty ( ) )
return range . start ;
// Shortcut: If the text we want to insert is the same as it is already
// in the document, we don't have to replace anything.
if ( text == this . getTextRange ( range ) )
return range . end ;
this . remove ( range ) ;
if ( text ) {
var end = this . insert ( range . start , text ) ;
}
else {
end = range . start ;
}
return end ;
} ;
this . applyDeltas = function ( deltas ) {
for ( var i = 0 ; i < deltas . length ; i ++ ) {
var delta = deltas [ i ] ;
var range = Range . fromPoints ( delta . range . start , delta . range . end ) ;
if ( delta . action == "insertLines" )
this . insertLines ( range . start . row , delta . lines ) ;
else if ( delta . action == "insertText" )
this . insert ( range . start , delta . text ) ;
else if ( delta . action == "removeLines" )
this . removeLines ( range . start . row , range . end . row - 1 ) ;
else if ( delta . action == "removeText" )
this . remove ( range ) ;
}
} ;
this . revertDeltas = function ( deltas ) {
for ( var i = deltas . length - 1 ; i >= 0 ; i -- ) {
var delta = deltas [ i ] ;
var range = Range . fromPoints ( delta . range . start , delta . range . end ) ;
if ( delta . action == "insertLines" )
this . removeLines ( range . start . row , range . end . row - 1 ) ;
else if ( delta . action == "insertText" )
this . remove ( range ) ;
else if ( delta . action == "removeLines" )
this . insertLines ( range . start . row , delta . lines ) ;
else if ( delta . action == "removeText" )
this . insert ( range . start , delta . text ) ;
}
} ;
} ) . call ( Document . prototype ) ;
exports . Document = Document ;
} ) ;
/ * * * * * * B E G I N L I C E N S E B L O C K * * * * *
* Version : MPL 1.1 / GPL 2.0 / LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the "License" ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is Ajax . org Code Editor ( ACE ) .
*
* The Initial Developer of the Original Code is
* Ajax . org B . V .
* Portions created by the Initial Developer are Copyright ( C ) 2010
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
* Fabian Jakobs < fabian AT ajax DOT org >
*
* Alternatively , the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later ( the "GPL" ) , or
* the GNU Lesser General Public License Version 2.1 or later ( the "LGPL" ) ,
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above . If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL , and not to allow others to
* use your version of this file under the terms of the MPL , indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL . If you do not delete
* the provisions above , a recipient may use your version of this file under
* the terms of any one of the MPL , the GPL or the LGPL .
*
* * * * * * END LICENSE BLOCK * * * * * * /
define ( 'ace/range' , [ 'require' , 'exports' , 'module' ] , function ( require , exports , module ) {
"use strict" ;
var Range = function ( startRow , startColumn , endRow , endColumn ) {
this . start = {
row : startRow ,
column : startColumn
} ;
this . end = {
row : endRow ,
column : endColumn
} ;
} ;
( function ( ) {
this . isEequal = function ( range ) {
return this . start . row == range . start . row &&
this . end . row == range . end . row &&
this . start . column == range . start . column &&
this . end . column == range . end . column
} ;
this . toString = function ( ) {
return ( "Range: [" + this . start . row + "/" + this . start . column +
"] -> [" + this . end . row + "/" + this . end . column + "]" ) ;
} ;
this . contains = function ( row , column ) {
return this . compare ( row , column ) == 0 ;
} ;
/ * *
* Compares this range ( A ) with another range ( B ) , where B is the passed in
* range .
*
* Return values :
* - 2 : ( B ) is infront of ( A ) and doesn ' t intersect with ( A )
* - 1 : ( B ) begins before ( A ) but ends inside of ( A )
* 0 : ( B ) is completly inside of ( A ) OR ( A ) is complety inside of ( B )
* + 1 : ( B ) begins inside of ( A ) but ends outside of ( A )
* + 2 : ( B ) is after ( A ) and doesn ' t intersect with ( A )
*
* 42 : FTW state : ( B ) ends in ( A ) but starts outside of ( A )
* /
this . compareRange = function ( range ) {
var cmp ,
end = range . end ,
start = range . start ;
cmp = this . compare ( end . row , end . column ) ;
if ( cmp == 1 ) {
cmp = this . compare ( start . row , start . column ) ;
if ( cmp == 1 ) {
return 2 ;
} else if ( cmp == 0 ) {
return 1 ;
} else {
return 0 ;
}
} else if ( cmp == - 1 ) {
return - 2 ;
} else {
cmp = this . compare ( start . row , start . column ) ;
if ( cmp == - 1 ) {
return - 1 ;
} else if ( cmp == 1 ) {
return 42 ;
} else {
return 0 ;
}
}
}
this . comparePoint = function ( p ) {
return this . compare ( p . row , p . column ) ;
}
this . containsRange = function ( range ) {
return this . comparePoint ( range . start ) == 0 && this . comparePoint ( range . end ) == 0 ;
}
this . isEnd = function ( row , column ) {
return this . end . row == row && this . end . column == column ;
}
this . isStart = function ( row , column ) {
return this . start . row == row && this . start . column == column ;
}
this . setStart = function ( row , column ) {
if ( typeof row == "object" ) {
this . start . column = row . column ;
this . start . row = row . row ;
} else {
this . start . row = row ;
this . start . column = column ;
}
}
this . setEnd = function ( row , column ) {
if ( typeof row == "object" ) {
this . end . column = row . column ;
this . end . row = row . row ;
} else {
this . end . row = row ;
this . end . column = column ;
}
}
this . inside = function ( row , column ) {
if ( this . compare ( row , column ) == 0 ) {
if ( this . isEnd ( row , column ) || this . isStart ( row , column ) ) {
return false ;
} else {
return true ;
}
}
return false ;
}
this . insideStart = function ( row , column ) {
if ( this . compare ( row , column ) == 0 ) {
if ( this . isEnd ( row , column ) ) {
return false ;
} else {
return true ;
}
}
return false ;
}
this . insideEnd = function ( row , column ) {
if ( this . compare ( row , column ) == 0 ) {
if ( this . isStart ( row , column ) ) {
return false ;
} else {
return true ;
}
}
return false ;
}
this . compare = function ( row , column ) {
if ( ! this . isMultiLine ( ) ) {
if ( row === this . start . row ) {
return column < this . start . column ? - 1 : ( column > this . end . column ? 1 : 0 ) ;
} ;
}
if ( row < this . start . row )
return - 1 ;
if ( row > this . end . row )
return 1 ;
if ( this . start . row === row )
return column >= this . start . column ? 0 : - 1 ;
if ( this . end . row === row )
return column <= this . end . column ? 0 : 1 ;
return 0 ;
} ;
/ * *
* Like . compare ( ) , but if isStart is true , return - 1 ;
* /
this . compareStart = function ( row , column ) {
if ( this . start . row == row && this . start . column == column ) {
return - 1 ;
} else {
return this . compare ( row , column ) ;
}
}
/ * *
* Like . compare ( ) , but if isEnd is true , return 1 ;
* /
this . compareEnd = function ( row , column ) {
if ( this . end . row == row && this . end . column == column ) {
return 1 ;
} else {
return this . compare ( row , column ) ;
}
}
this . compareInside = function ( row , column ) {
if ( this . end . row == row && this . end . column == column ) {
return 1 ;
} else if ( this . start . row == row && this . start . column == column ) {
return - 1 ;
} else {
return this . compare ( row , column ) ;
}
}
this . clipRows = function ( firstRow , lastRow ) {
if ( this . end . row > lastRow ) {
var end = {
row : lastRow + 1 ,
column : 0
} ;
}
if ( this . start . row > lastRow ) {
var start = {
row : lastRow + 1 ,
column : 0
} ;
}
if ( this . start . row < firstRow ) {
var start = {
row : firstRow ,
column : 0
} ;
}
if ( this . end . row < firstRow ) {
var end = {
row : firstRow ,
column : 0
} ;
}
return Range . fromPoints ( start || this . start , end || this . end ) ;
} ;
this . extend = function ( row , column ) {
var cmp = this . compare ( row , column ) ;
if ( cmp == 0 )
return this ;
else if ( cmp == - 1 )
var start = { row : row , column : column } ;
else
var end = { row : row , column : column } ;
return Range . fromPoints ( start || this . start , end || this . end ) ;
} ;
this . isEmpty = function ( ) {
return ( this . start . row == this . end . row && this . start . column == this . end . column ) ;
} ;
this . isMultiLine = function ( ) {
return ( this . start . row !== this . end . row ) ;
} ;
this . clone = function ( ) {
return Range . fromPoints ( this . start , this . end ) ;
} ;
this . collapseRows = function ( ) {
if ( this . end . column == 0 )
return new Range ( this . start . row , 0 , Math . max ( this . start . row , this . end . row - 1 ) , 0 )
else
return new Range ( this . start . row , 0 , this . end . row , 0 )
} ;
this . toScreenRange = function ( session ) {
var screenPosStart =
session . documentToScreenPosition ( this . start ) ;
var screenPosEnd =
session . documentToScreenPosition ( this . end ) ;
return new Range (
screenPosStart . row , screenPosStart . column ,
screenPosEnd . row , screenPosEnd . column
) ;
} ;
} ) . call ( Range . prototype ) ;
Range . fromPoints = function ( start , end ) {
return new Range ( start . row , start . column , end . row , end . column ) ;
} ;
exports . Range = Range ;
} ) ;
/ * * * * * * B E G I N L I C E N S E B L O C K * * * * *
* Version : MPL 1.1 / GPL 2.0 / LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the "License" ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is Ajax . org Code Editor ( ACE ) .
*
* The Initial Developer of the Original Code is
* Ajax . org B . V .
* Portions created by the Initial Developer are Copyright ( C ) 2010
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
* Fabian Jakobs < fabian AT ajax DOT org >
*
* Alternatively , the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later ( the "GPL" ) , or
* the GNU Lesser General Public License Version 2.1 or later ( the "LGPL" ) ,
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above . If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL , and not to allow others to
* use your version of this file under the terms of the MPL , indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL . If you do not delete
* the provisions above , a recipient may use your version of this file under
* the terms of any one of the MPL , the GPL or the LGPL .
*
* * * * * * END LICENSE BLOCK * * * * * * /
define ( 'ace/anchor' , [ 'require' , 'exports' , 'module' , 'ace/lib/oop' , 'ace/lib/event_emitter' ] , function ( require , exports , module ) {
"use strict" ;
var oop = require ( "./lib/oop" ) ;
var EventEmitter = require ( "./lib/event_emitter" ) . EventEmitter ;
/ * *
* An Anchor is a floating pointer in the document . Whenever text is inserted or
* deleted before the cursor , the position of the cursor is updated
* /
var Anchor = exports . Anchor = function ( doc , row , column ) {
this . document = doc ;
if ( typeof column == "undefined" )
this . setPosition ( row . row , row . column ) ;
else
this . setPosition ( row , column ) ;
this . $onChange = this . onChange . bind ( this ) ;
doc . on ( "change" , this . $onChange ) ;
} ;
( function ( ) {
oop . implement ( this , EventEmitter ) ;
this . getPosition = function ( ) {
return this . $clipPositionToDocument ( this . row , this . column ) ;
} ;
this . getDocument = function ( ) {
return this . document ;
} ;
this . onChange = function ( e ) {
var delta = e . data ;
var range = delta . range ;
if ( range . start . row == range . end . row && range . start . row != this . row )
return ;
if ( range . start . row > this . row )
return ;
if ( range . start . row == this . row && range . start . column > this . column )
return ;
var row = this . row ;
var column = this . column ;
if ( delta . action === "insertText" ) {
if ( range . start . row === row && range . start . column <= column ) {
if ( range . start . row === range . end . row ) {
column += range . end . column - range . start . column ;
}
else {
column -= range . start . column ;
row += range . end . row - range . start . row ;
}
}
else if ( range . start . row !== range . end . row && range . start . row < row ) {
row += range . end . row - range . start . row ;
}
} else if ( delta . action === "insertLines" ) {
if ( range . start . row <= row ) {
row += range . end . row - range . start . row ;
}
}
else if ( delta . action == "removeText" ) {
if ( range . start . row == row && range . start . column < column ) {
if ( range . end . column >= column )
column = range . start . column ;
else
column = Math . max ( 0 , column - ( range . end . column - range . start . column ) ) ;
} else if ( range . start . row !== range . end . row && range . start . row < row ) {
if ( range . end . row == row ) {
column = Math . max ( 0 , column - range . end . column ) + range . start . column ;
}
row -= ( range . end . row - range . start . row ) ;
}
else if ( range . end . row == row ) {
row -= range . end . row - range . start . row ;
column = Math . max ( 0 , column - range . end . column ) + range . start . column ;
}
} else if ( delta . action == "removeLines" ) {
if ( range . start . row <= row ) {
if ( range . end . row <= row )
row -= range . end . row - range . start . row ;
else {
row = range . start . row ;
column = 0 ;
}
}
}
this . setPosition ( row , column , true ) ;
} ;
this . setPosition = function ( row , column , noClip ) {
var pos ;
if ( noClip ) {
pos = {
row : row ,
column : column
} ;
}
else {
pos = this . $clipPositionToDocument ( row , column ) ;
}
if ( this . row == pos . row && this . column == pos . column )
return ;
var old = {
row : this . row ,
column : this . column
} ;
this . row = pos . row ;
this . column = pos . column ;
this . _emit ( "change" , {
old : old ,
value : pos
} ) ;
} ;
this . detach = function ( ) {
this . document . removeEventListener ( "change" , this . $onChange ) ;
} ;
this . $clipPositionToDocument = function ( row , column ) {
var pos = { } ;
if ( row >= this . document . getLength ( ) ) {
pos . row = Math . max ( 0 , this . document . getLength ( ) - 1 ) ;
pos . column = this . document . getLine ( pos . row ) . length ;
}
else if ( row < 0 ) {
pos . row = 0 ;
pos . column = 0 ;
}
else {
pos . row = row ;
pos . column = Math . min ( this . document . getLine ( pos . row ) . length , Math . max ( 0 , column ) ) ;
}
if ( column < 0 )
pos . column = 0 ;
return pos ;
} ;
} ) . call ( Anchor . prototype ) ;
} ) ;
/ * * * * * * B E G I N L I C E N S E B L O C K * * * * *
* Version : MPL 1.1 / GPL 2.0 / LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the "License" ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is Ajax . org Code Editor ( ACE ) .
*
* The Initial Developer of the Original Code is
* Ajax . org B . V .
* Portions created by the Initial Developer are Copyright ( C ) 2010
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
* Fabian Jakobs < fabian AT ajax DOT org >
*
* Alternatively , the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later ( the "GPL" ) , or
* the GNU Lesser General Public License Version 2.1 or later ( the "LGPL" ) ,
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above . If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL , and not to allow others to
* use your version of this file under the terms of the MPL , indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL . If you do not delete
* the provisions above , a recipient may use your version of this file under
* the terms of any one of the MPL , the GPL or the LGPL .
*
* * * * * * END LICENSE BLOCK * * * * * * /
define ( 'ace/lib/lang' , [ 'require' , 'exports' , 'module' ] , function ( require , exports , module ) {
"use strict" ;
exports . stringReverse = function ( string ) {
return string . split ( "" ) . reverse ( ) . join ( "" ) ;
} ;
exports . stringRepeat = function ( string , count ) {
return new Array ( count + 1 ) . join ( string ) ;
} ;
var trimBeginRegexp = /^\s\s*/ ;
var trimEndRegexp = /\s\s*$/ ;
exports . stringTrimLeft = function ( string ) {
return string . replace ( trimBeginRegexp , '' ) ;
} ;
exports . stringTrimRight = function ( string ) {
return string . replace ( trimEndRegexp , '' ) ;
} ;
exports . copyObject = function ( obj ) {
var copy = { } ;
for ( var key in obj ) {
copy [ key ] = obj [ key ] ;
}
return copy ;
} ;
exports . copyArray = function ( array ) {
var copy = [ ] ;
for ( var i = 0 , l = array . length ; i < l ; i ++ ) {
if ( array [ i ] && typeof array [ i ] == "object" )
copy [ i ] = this . copyObject ( array [ i ] ) ;
else
copy [ i ] = array [ i ] ;
}
return copy ;
} ;
exports . deepCopy = function ( obj ) {
if ( typeof obj != "object" ) {
return obj ;
}
var copy = obj . constructor ( ) ;
for ( var key in obj ) {
if ( typeof obj [ key ] == "object" ) {
copy [ key ] = this . deepCopy ( obj [ key ] ) ;
} else {
copy [ key ] = obj [ key ] ;
}
}
return copy ;
} ;
exports . arrayToMap = function ( arr ) {
var map = { } ;
for ( var i = 0 ; i < arr . length ; i ++ ) {
map [ arr [ i ] ] = 1 ;
}
return map ;
} ;
/ * *
* splice out of 'array' anything that === 'value'
* /
exports . arrayRemove = function ( array , value ) {
for ( var i = 0 ; i <= array . length ; i ++ ) {
if ( value === array [ i ] ) {
array . splice ( i , 1 ) ;
}
}
} ;
exports . escapeRegExp = function ( str ) {
return str . replace ( /([.*+?^${}()|[\]\/\\])/g , '\\$1' ) ;
} ;
exports . deferredCall = function ( fcn ) {
var timer = null ;
var callback = function ( ) {
timer = null ;
fcn ( ) ;
} ;
var deferred = function ( timeout ) {
deferred . cancel ( ) ;
timer = setTimeout ( callback , timeout || 0 ) ;
return deferred ;
} ;
deferred . schedule = deferred ;
deferred . call = function ( ) {
this . cancel ( ) ;
fcn ( ) ;
return deferred ;
} ;
deferred . cancel = function ( ) {
clearTimeout ( timer ) ;
timer = null ;
return deferred ;
} ;
return deferred ;
} ;
} ) ;
/ *
CSSLint
Copyright ( c ) 2011 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 .
* /
define ( 'ace/mode/css/csslint' , [ 'require' , 'exports' , 'module' ] , function ( require , exports , module ) {
/ * !
Parser - Lib
Copyright ( c ) 2009 - 2011 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 .
* /
/* Build time: 13-July-2011 04:35:28 */
var parserlib = { } ;
( function ( ) {
/ * *
* 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 = { } ;
}
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 ( ! event . target ) {
event . target = this ;
}
if ( ! event . type ) {
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 ;
}
}
}
}
} ;
/ * *
* 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 ( /\n\r?/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 . read ( ) ;
while ( c !== null && filter ( c ) ) {
buffer += c ;
c = this . read ( ) ;
}
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 } matchter 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 . indexOf ( matcher ) === 0 ) {
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 ;
}
} ;
/ * *
* 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 ) {
/ * *
* 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 = new Error ( ) ;
/ * *
* 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 ) {
/ * *
* 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 ;
}
/ * *
* 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 ;
}
} ;
/ * *
* 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 = (typeof input == "string") ? new StringReader(input) : input;
this . _reader = input ? new StringReader ( input . toString ( ) ) : null ;
/ * *
* 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 = { } ,
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 .
* @ param { variant } channel ( Optional ) The channel to read from . If not
* provided , reads from the default ( unnamed ) channel .
* @ return { void }
* @ method mustMatch
* /
mustMatch : function ( tokenTypes , channel ) {
//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 ,
reader = this . _reader ,
value ,
i = 0 ,
len = tokenInfo . length ,
found = false ,
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." ) ;
}
}
} ;
parserlib . util = {
StringReader : StringReader ,
SyntaxError : SyntaxError ,
SyntaxUnit : SyntaxUnit ,
EventTarget : EventTarget ,
TokenStreamBase : TokenStreamBase
} ;
} ) ( ) ;
/ *
Parser - Lib
Copyright ( c ) 2009 - 2011 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 .
* /
/* Build time: 13-July-2011 04:35:28 */
( function ( ) {
var EventTarget = parserlib . util . EventTarget ,
TokenStreamBase = parserlib . util . TokenStreamBase ,
StringReader = parserlib . util . StringReader ,
SyntaxError = parserlib . util . SyntaxError ,
SyntaxUnit = parserlib . util . SyntaxUnit ;
var Colors = {
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" ,
darkgreen : "#006400" ,
darkkhaki : "#bdb76b" ,
darkmagenta : "#8b008b" ,
darkolivegreen : "#556b2f" ,
darkorange : "#ff8c00" ,
darkorchid : "#9932cc" ,
darkred : "#8b0000" ,
darksalmon : "#e9967a" ,
darkseagreen : "#8fbc8f" ,
darkslateblue : "#483d8b" ,
darkslategray : "#2f4f4f" ,
darkturquoise : "#00ced1" ,
darkviolet : "#9400d3" ,
deeppink : "#ff1493" ,
deepskyblue : "#00bfff" ,
dimgray : "#696969" ,
dodgerblue : "#1e90ff" ,
firebrick : "#b22222" ,
floralwhite : "#fffaf0" ,
forestgreen : "#228b22" ,
fuchsia : "#ff00ff" ,
gainsboro : "#dcdcdc" ,
ghostwhite : "#f8f8ff" ,
gold : "#ffd700" ,
goldenrod : "#daa520" ,
gray : "#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" ,
lightgrey : "#d3d3d3" ,
lightgreen : "#90ee90" ,
lightpink : "#ffb6c1" ,
lightsalmon : "#ffa07a" ,
lightseagreen : "#20b2aa" ,
lightskyblue : "#87cefa" ,
lightslategray : "#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" ,
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"
} ;
/ * *
* 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 ) ;
/ * *
* 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 ;
var Level1Properties = {
"background" : 1 ,
"background-attachment" : 1 ,
"background-color" : 1 ,
"background-image" : 1 ,
"background-position" : 1 ,
"background-repeat" : 1 ,
"border" : 1 ,
"border-bottom" : 1 ,
"border-bottom-width" : 1 ,
"border-color" : 1 ,
"border-left" : 1 ,
"border-left-width" : 1 ,
"border-right" : 1 ,
"border-right-width" : 1 ,
"border-style" : 1 ,
"border-top" : 1 ,
"border-top-width" : 1 ,
"border-width" : 1 ,
"clear" : 1 ,
"color" : 1 ,
"display" : 1 ,
"float" : 1 ,
"font" : 1 ,
"font-family" : 1 ,
"font-size" : 1 ,
"font-style" : 1 ,
"font-variant" : 1 ,
"font-weight" : 1 ,
"height" : 1 ,
"letter-spacing" : 1 ,
"line-height" : 1 ,
"list-style" : 1 ,
"list-style-image" : 1 ,
"list-style-position" : 1 ,
"list-style-type" : 1 ,
"margin" : 1 ,
"margin-bottom" : 1 ,
"margin-left" : 1 ,
"margin-right" : 1 ,
"margin-top" : 1 ,
"padding" : 1 ,
"padding-bottom" : 1 ,
"padding-left" : 1 ,
"padding-right" : 1 ,
"padding-top" : 1 ,
"text-align" : 1 ,
"text-decoration" : 1 ,
"text-indent" : 1 ,
"text-transform" : 1 ,
"vertical-align" : 1 ,
"white-space" : 1 ,
"width" : 1 ,
"word-spacing" : 1
} ;
var Level2Properties = {
//Aural
"azimuth" : 1 ,
"cue-after" : 1 ,
"cue-before" : 1 ,
"cue" : 1 ,
"elevation" : 1 ,
"pause-after" : 1 ,
"pause-before" : 1 ,
"pause" : 1 ,
"pitch-range" : 1 ,
"pitch" : 1 ,
"play-during" : 1 ,
"richness" : 1 ,
"speak-header" : 1 ,
"speak-numeral" : 1 ,
"speak-punctuation" : 1 ,
"speak" : 1 ,
"speech-rate" : 1 ,
"stress" : 1 ,
"voice-family" : 1 ,
"volume" : 1 ,
//Paged
"orphans" : 1 ,
"page-break-after" : 1 ,
"page-break-before" : 1 ,
"page-break-inside" : 1 ,
"widows" : 1 ,
//Interactive
"cursor" : 1 ,
"outline-color" : 1 ,
"outline-style" : 1 ,
"outline-width" : 1 ,
"outline" : 1 ,
//Visual
"background-attachment" : 1 ,
"background-color" : 1 ,
"background-image" : 1 ,
"background-position" : 1 ,
"background-repeat" : 1 ,
"background" : 1 ,
"border-collapse" : 1 ,
"border-color" : 1 ,
"border-spacing" : 1 ,
"border-style" : 1 ,
"border-top" : 1 ,
"border-top-color" : 1 ,
"border-top-style" : 1 ,
"border-top-width" : 1 ,
"border-width" : 1 ,
"border" : 1 ,
"bottom" : 1 ,
"caption-side" : 1 ,
"clear" : 1 ,
"clip" : 1 ,
"color" : 1 ,
"content" : 1 ,
"counter-increment" : 1 ,
"counter-reset" : 1 ,
"direction" : 1 ,
"display" : 1 ,
"empty-cells" : 1 ,
"float" : 1 ,
"font-family" : 1 ,
"font-size" : 1 ,
"font-style" : 1 ,
"font-variant" : 1 ,
"font-weight" : 1 ,
"font" : 1 ,
"height" : 1 ,
"left" : 1 ,
"letter-spacing" : 1 ,
"line-height" : 1 ,
"list-style-image" : 1 ,
"list-style-position" : 1 ,
"list-style-type" : 1 ,
"list-style" : 1 ,
"margin-right" : 1 ,
"margin-top" : 1 ,
"margin" : 1 ,
"max-height" : 1 ,
"max-width" : 1 ,
"min-height" : 1 ,
"min-width" : 1 ,
"overflow" : 1 ,
"padding-top" : 1 ,
"padding" : 1 ,
"position" : 1 ,
"quotes" : 1 ,
"right" : 1 ,
"table-layout" : 1 ,
"text-align" : 1 ,
"text-decoration" : 1 ,
"text-indent" : 1 ,
"text-transform" : 1 ,
"top" : 1 ,
"unicode-bidi" : 1 ,
"vertical-align" : 1 ,
"visibility" : 1 ,
"white-space" : 1 ,
"width" : 1 ,
"word-spacing" : 1 ,
"z-index" : 1
} ;
/ * *
* 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 ) ;
/ * *
* 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 ;
/ * *
* 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 + " " : "" ) + features . join ( " and " ) , line , col ) ;
/ * *
* 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 ;
/ * *
* 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 ;
}
Parser . prototype = function ( ) {
var proto = new EventTarget ( ) , //new prototype
prop ,
additions = {
//restore constructor
constructor : Parser ,
//-----------------------------------------------------------------
// 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 ] [ S | CDO | CDC ] * ] *
* ;
* /
var tokenStream = this . _tokenStream ,
charset = null ,
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 . 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 ,
tt ,
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
} ) ;
}
} ,
_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 ( ! 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 ;
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
} ) ;
} ,
_operator : function ( ) {
/ *
* operator
* : '/' S * | ',' S * | /( empty )/
* ;
* /
var tokenStream = this . _tokenStream ,
token = null ;
if ( tokenStream . match ( [ Tokens . SLASH , Tokens . COMMA ] ) ) {
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 ( this . 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 ,
found = false ,
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 ) ;
}
}
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 ] ) ) {
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 ,
elementName ,
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 ;
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 ( ) ;
this . fire ( {
type : "property" ,
property : property ,
value : expr ,
important : prio ,
line : property . line ,
col : property . col
} ) ;
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 ( ) {
/ *
* expr
* : term [ operator term ] *
* ;
* /
var tokenStream = this . _tokenStream ,
values = [ ] ,
//valueParts = [],
value = null ,
operator = null ;
value = this . _term ( ) ;
if ( value !== null ) {
values . push ( value ) ;
do {
operator = this . _operator ( ) ;
//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 ( ) ;
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 ] . startLine , values [ 0 ] . startCol ) : null ;
} ,
_term : function ( ) {
/ *
* 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 ,
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 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 ;
}
this . _readWhitespace ( ) ;
} else {
//see if it's a color
value = this . _hexcolor ( ) ;
if ( value === 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 {
if ( unary === null ) {
line = tokenStream . token ( ) . startLine ;
col = tokenStream . token ( ) . startCol ;
}
}
}
return 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 ;
if ( tokenStream . match ( Tokens . FUNCTION ) ) {
functionText = tokenStream . token ( ) . value ;
this . _readWhitespace ( ) ;
expr = this . _expr ( ) ;
tokenStream . match ( Tokens . RPAREN ) ;
functionText += expr + ")" ;
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 ,
expr = 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 ,
color = null ;
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 color ;
} ,
//-----------------------------------------------------------------
// Animations methods
//-----------------------------------------------------------------
_keyframes : function ( ) {
/ *
* keyframes :
* : KEYFRAMES _SYM S * keyframe _name S * '{' S * keyframe _rule * '}' {
* ;
* /
var tokenStream = this . _tokenStream ,
token ,
tt ,
name ;
tokenStream . mustMatch ( Tokens . KEYFRAMES _SYM ) ;
this . _readWhitespace ( ) ;
name = this . _keyframe _name ( ) ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . LBRACE ) ;
this . fire ( {
type : "startkeyframes" ,
name : name ,
line : name . line ,
col : name . col
} ) ;
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 ,
line : name . line ,
col : name . col
} ) ;
this . _readWhitespace ( ) ;
tokenStream . mustMatch ( Tokens . RBRACE ) ;
} ,
_keyframe _name : function ( ) {
/ *
* keyframe _name :
* : IDENT
* | STRING
* ;
* /
var tokenStream = this . _tokenStream ,
token ;
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 tokenStream = this . _tokenStream ,
token ,
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 ,
token ,
key ,
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 delcaration is 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 ( 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
} else {
//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 ) ) ;
}
} ,
//-----------------------------------------------------------------
// 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 ;
}
} ;
//copy over onto prototype
for ( prop in additions ) {
proto [ prop ] = additions [ prop ] ;
}
return proto ;
} ( ) ;
/ *
nth
: S * [ [ '-' | '+' ] ? INTEGER ? { N } [ S * [ '-' | '+' ] S * INTEGER ] ? |
[ '-' | '+' ] ? INTEGER | { O } { D } { D } | { E } { V } { E } { N } ] S *
;
* /
/ * *
* 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 , ( hack || "" ) + text , line , col ) ;
/ * *
* The type of IE hack applied ( "*" , "_" , or null ) .
* @ type String
* @ property hack
* /
this . hack = hack ;
}
PropertyName . prototype = new SyntaxUnit ( ) ;
PropertyName . prototype . constructor = PropertyName ;
/ * *
* 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 ) ;
/ * *
* The parts that make up the selector .
* @ type Array
* @ property parts
* /
this . parts = parts ;
}
PropertyValue . prototype = new SyntaxUnit ( ) ;
PropertyValue . prototype . constructor = PropertyValue ;
/ * *
* 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 ) {
SyntaxUnit . apply ( this , arguments ) ;
/ * *
* 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" :
this . type = "length" ;
break ;
case "deg" :
case "rad" :
case "grad" :
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 ) ) { //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 ( /^url\(["']?([^\)"']+)["']?\)/i . test ( text ) ) { //URI
this . type = "uri" ;
this . uri = RegExp . $1 ;
} else if ( /^["'][^"']*["']/ . test ( text ) ) { //string
this . type = "string" ;
this . value = eval ( 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\-\u0080-\uFFFF][a-z0-9\-\u0080-\uFFFF]*$/i . test ( text ) ) {
this . type = "identifier" ;
this . value = text ;
}
}
PropertyValuePart . prototype = new SyntaxUnit ( ) ;
PropertyValuePart . prototype . constructor = PropertyValue ;
/ * *
* 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 ) {
return new PropertyValuePart ( token . value , token . startLine , token . startCol ) ;
} ;
/ * *
* 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 ) ;
/ * *
* The parts that make up the selector .
* @ type Array
* @ property parts
* /
this . parts = parts ;
}
Selector . prototype = new SyntaxUnit ( ) ;
Selector . prototype . constructor = Selector ;
/ * *
* 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 ) ;
/ * *
* 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 ;
/ * *
* 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 ) ;
/ * *
* 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 ;
var h = /^[0-9a-fA-F]$/ ,
nonascii = /^[\u0080-\uFFFF]$/ ,
nl = /\n|\r\n|\r|\f/ ;
//-----------------------------------------------------------------------------
// 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 && /\s/ . test ( c ) ;
}
function isNewLine ( c ) {
return c != null && nl . test ( c ) ;
}
function isNameStart ( c ) {
return c != null && ( /[a-z_\u0080-\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 ( supplier . hasOwnProperty ( 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 .
* @ param { variant } channel The name of the channel to use
* for the next token .
* @ return { Object } A token object representing the next token .
* @ method _getToken
* @ private
* /
_getToken : function ( channel ) {
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 :
* - 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 ;
c = reader . read ( ) ;
}
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 ,
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 ,
valid = false ,
ident ,
c ;
/ *
* 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 ) {
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 ) ;
if ( tt == - 1 ) {
tt = Tokens . CHAR ;
}
return this . createToken ( tt , c , 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 } 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 reader = this . _reader ,
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 reader = this . _reader ,
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 ;
//if there's a left paren immediately after, it's a URI or function
if ( reader . peek ( ) == "(" ) {
ident += reader . read ( ) ;
if ( ident . toLowerCase ( ) == "url(" ) {
tt = Tokens . URI ;
ident = this . readURI ( ident ) ;
//didn't find a valid URL or there's no closing paren
if ( ident . toLowerCase ( ) == "url(" ) {
tt = Tokens . FUNCTION ;
}
} 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$|^vm$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i . test ( ident ) ) {
tt = Tokens . LENGTH ;
} else if ( /^deg|^rad$|^grad$/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 ,
prev = first ,
tt = Tokens . STRING ,
c = reader . read ( ) ;
while ( c ) {
string += c ;
//if the delimiter is found with an escapement, we're done.
if ( c == delim && prev != "\\" ) {
break ;
}
//if there's a newline without an escapement, it's an invalid string
if ( isNewLine ( reader . peek ( ) ) && c != "\\" ) {
tt = Tokens . INVALID ;
break ;
}
//save previous and get next
prev = c ;
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 reader = this . _reader ,
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 ;
} ,
readString : function ( ) {
var reader = this . _reader ,
delim = reader . read ( ) ,
string = delim ,
prev = delim ,
c = reader . peek ( ) ;
while ( c ) {
c = reader . read ( ) ;
string += c ;
//if the delimiter is found with an escapement, we're done.
if ( c == delim && prev != "\\" ) {
break ;
}
//if there's a newline without an escapement, it's an invalid string
if ( isNewLine ( reader . peek ( ) ) && c != "\\" ) {
string = "" ;
break ;
}
//save previous and get next
prev = c ;
c = reader . peek ( ) ;
}
//if c is null, that means we're out of input and the string was never closed
if ( c == null ) {
string = "" ;
}
return string ;
} ,
readURI : function ( first ) {
var reader = this . _reader ,
uri = first ,
inner = "" ,
c = reader . peek ( ) ;
reader . mark ( ) ;
//skip whitespace before
while ( c && isWhitespace ( c ) ) {
reader . read ( ) ;
c = reader . peek ( ) ;
}
//it's a string
if ( c == "'" || c == "\"" ) {
inner = this . readString ( ) ;
} else {
inner = this . readURL ( ) ;
}
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 == "" || c != ")" ) {
uri = first ;
reader . reset ( ) ;
} else {
uri += inner + reader . read ( ) ;
}
return uri ;
} ,
readURL : function ( ) {
var reader = this . _reader ,
url = "" ,
c = reader . peek ( ) ;
//TODO: Check for escape and nonascii
while ( /^[!#$%&\\*-~]$/ . test ( c ) ) {
url += reader . read ( ) ;
c = reader . peek ( ) ;
}
return url ;
} ,
readName : function ( first ) {
var reader = this . _reader ,
ident = first || "" ,
c = reader . peek ( ) ;
while ( true ) {
if ( c == "\\" ) {
ident += this . readEscape ( reader . read ( ) ) ;
c = reader . peek ( ) ;
} else if ( c && isNameChar ( c ) ) {
ident += reader . read ( ) ;
c = reader . peek ( ) ;
} else {
break ;
}
}
return ident ;
} ,
readEscape : function ( first ) {
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 == 3 && /\s/ . test ( c ) ||
cssEscape . length == 7 || cssEscape . length == 1 ) {
reader . read ( ) ;
} else {
c = "" ;
}
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 ( c == "*" && reader . peek ( ) == "/" ) {
comment += reader . read ( ) ;
break ;
}
c = reader . read ( ) ;
}
return comment ;
} else {
return "" ;
}
}
} ) ;
var Tokens = [
/ *
* The following token names are defined in CSS3 Grammar : http : //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: "ATKEYWORD"},
//CSS3 animations
{ name : "KEYFRAMES_SYM" , text : [ "@keyframes" , "@-webkit-keyframes" , "@-moz-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 : http : //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 : http : //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" ,
text : "{"
} ,
{
name : "RBRACE" ,
text : "}"
} ,
{
name : "LBRACKET" ,
text : "["
} ,
{
name : "RBRACKET" ,
text : "]"
} ,
{
name : "EQUALS" ,
text : "="
} ,
{
name : "COLON" ,
text : ":"
} ,
{
name : "SEMICOLON" ,
text : ";"
} ,
{
name : "LPAREN" ,
text : "("
} ,
{
name : "RPAREN" ,
text : ")"
} ,
{
name : "DOT" ,
text : "."
}
] ;
( function ( ) {
var nameMap = [ ] ,
typeMap = { } ;
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 ;
} ;
} ) ( ) ;
parserlib . css = {
Colors : Colors ,
Combinator : Combinator ,
Parser : Parser ,
PropertyName : PropertyName ,
PropertyValue : PropertyValue ,
PropertyValuePart : PropertyValuePart ,
MediaFeature : MediaFeature ,
MediaQuery : MediaQuery ,
Selector : Selector ,
SelectorPart : SelectorPart ,
SelectorSubPart : SelectorSubPart ,
TokenStream : TokenStream ,
Tokens : Tokens
} ;
} ) ( ) ;
/ * *
* Main CSSLint object .
* @ class CSSLint
* @ static
* @ extends parserlib . util . EventTarget
* /
var CSSLint = ( function ( ) {
var rules = [ ] ,
formatters = [ ] ,
api = new parserlib . util . EventTarget ( ) ;
api . version = "@VERSION@" ;
//-------------------------------------------------------------------------
// 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 = [ ] ;
} ;
//-------------------------------------------------------------------------
// 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 .
* @ return { String } A formatted string for the results .
* @ method format
* /
api . format = function ( results , filename , formatId ) {
var formatter = this . getFormatter ( formatId ) ,
result = null ;
if ( formatter ) {
result = formatter . startFormat ( ) ;
result += formatter . formatResults ( results , filename ) ;
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 .
* @ return { Object } Results of the verification .
* @ method verify
* /
api . verify = function ( text , ruleset ) {
var i = 0 ,
len = rules . length ,
reporter ,
lines ,
parser = new parserlib . css . Parser ( { starHack : true , ieFilters : true ,
underscoreHack : true , strict : false } ) ;
lines = text . split ( /\n\r?/g ) ;
reporter = new Reporter ( lines ) ;
if ( ! ruleset ) {
while ( i < len ) {
rules [ i ++ ] . init ( parser , reporter ) ;
}
} else {
ruleset . errors = 1 ; //always report parsing errors
for ( i in ruleset ) {
if ( ruleset . hasOwnProperty ( 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 ) ;
}
return {
messages : reporter . messages ,
stats : reporter . stats
} ;
} ;
//-------------------------------------------------------------------------
// 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 .
* /
function Reporter ( lines ) {
/ * *
* 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 ;
}
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 ) {
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
* /
warn : function ( message , line , col , rule ) {
this . messages . push ( {
type : "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 ) {
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 ) {
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 ) {
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 ) {
this . stats [ name ] = value ;
}
} ;
/ *
* Utility functions that make life easier .
* /
/ *
* Adds all properties from supplier onto receiver ,
* overwriting if the same name already exists on
* reciever .
* @ param { Object } The object to receive the properties .
* @ param { Object } The object to provide the properties .
* @ return { Object } The receiver
* /
function mix ( reciever , supplier ) {
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 .
* /
function indexOf ( values , value ) {
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 ;
}
}
/ *
* Rule : Don ' t use adjoining classes ( . foo . bar ) .
* /
CSSLint . addRule ( {
//rule information
id : "adjoining-classes" ,
name : "Adjoining Classes" ,
desc : "Don't use adjoining classes." ,
browsers : "IE6" ,
//initialization
init : function ( parser , reporter ) {
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 instanceof parserlib . css . SelectorPart ) {
classCount = 0 ;
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( modifier . type == "class" ) {
classCount ++ ;
}
if ( classCount > 1 ) {
reporter . warn ( "Don't use adjoining classes." , 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 : "Box Model" ,
desc : "Don't use width or height when using padding or border." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 ;
parser . addListener ( "startrule" , function ( ) {
properties = {
} ;
} ) ;
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 == "none" ) ) {
properties [ name ] = { line : event . property . line , col : event . property . col , value : event . value } ;
}
} else {
if ( name == "width" || name == "height" ) {
properties [ name ] = 1 ;
}
}
} ) ;
parser . addListener ( "endrule" , function ( ) {
var prop ;
if ( properties [ "height" ] ) {
for ( prop in heightProperties ) {
if ( heightProperties . hasOwnProperty ( prop ) && properties [ prop ] ) {
//special case for padding
if ( prop == "padding" && properties [ prop ] . value . parts . length == 2 && properties [ prop ] . value . parts [ 0 ] . value == 0 ) {
//noop
} else {
reporter . warn ( "Broken box model: using height with " + prop + "." , properties [ prop ] . line , properties [ prop ] . col , rule ) ;
}
}
}
}
if ( properties [ "width" ] ) {
for ( prop in widthProperties ) {
if ( widthProperties . hasOwnProperty ( prop ) && properties [ prop ] ) {
if ( prop == "padding" && properties [ prop ] . value . parts . length == 2 && properties [ prop ] . value . parts [ 1 ] . value == 0 ) {
//noop
} else {
reporter . warn ( "Broken box model: using width with " + prop + "." , properties [ prop ] . line , properties [ prop ] . col , rule ) ;
}
}
}
}
} ) ;
}
} ) ;
/ *
* Rule : Include all compatible vendor prefixes to reach a wider
* range of users .
* /
/*global CSSLint*/
CSSLint . addRule ( {
//rule information
id : "compatible-vendor-prefixes" ,
name : "Compatible Vendor Prefixes" ,
desc : "Include all compatible vendor prefixes to reach a wider range of users." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
var rule = this ,
compatiblePrefixes ,
properties ,
prop ,
variations ,
prefixed ,
i ,
len ,
arrayPush = Array . prototype . push ,
applyTo = [ ] ;
// See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
compatiblePrefixes = {
"animation" : "webkit moz" ,
"animation-delay" : "webkit moz" ,
"animation-direction" : "webkit moz" ,
"animation-duration" : "webkit moz" ,
"animation-fill-mode" : "webkit moz" ,
"animation-iteration-count" : "webkit moz" ,
"animation-name" : "webkit moz" ,
"animation-play-state" : "webkit moz" ,
"animation-timing-function" : "webkit moz" ,
"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 moz" ,
"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" : "webkit moz" ,
"box-shadow" : "webkit moz" ,
"column-count" : "webkit moz" ,
"column-gap" : "webkit moz" ,
"column-rule" : "webkit moz" ,
"column-rule-color" : "webkit moz" ,
"column-rule-style" : "webkit moz" ,
"column-rule-width" : "webkit moz" ,
"column-width" : "webkit moz" ,
"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 moz ms o" ,
"transform-origin" : "webkit moz ms o" ,
"transition" : "webkit moz o" ,
"transition-delay" : "webkit moz o" ,
"transition-duration" : "webkit moz o" ,
"transition-property" : "webkit moz o" ,
"transition-timing-function" : "webkit moz o" ,
"user-modify" : "webkit moz" ,
"user-select" : "webkit moz" ,
"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 ( "property" , function ( event ) {
var name = event . property . text ;
if ( applyTo . indexOf ( name ) > - 1 ) {
properties . push ( name ) ;
}
} ) ;
parser . addListener ( "endrule" , function ( event ) {
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 ( variations . indexOf ( name ) > - 1 ) {
if ( propertyGroups [ prop ] === undefined ) {
propertyGroups [ prop ] = {
full : variations . slice ( 0 ) ,
actual : [ ]
} ;
}
if ( propertyGroups [ prop ] . actual . indexOf ( name ) === - 1 ) {
propertyGroups [ prop ] . actual . 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 ( actual . indexOf ( item ) === - 1 ) {
propertiesSpecified = ( actual . length === 1 ) ? actual [ 0 ] : ( actual . length == 2 ) ? actual . join ( " and " ) : actual . join ( ", " ) ;
reporter . warn ( "The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well." , event . selectors [ 0 ] . line , event . selectors [ 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 : "Display Property Grouping" ,
desc : "Certain properties shouldn't be used with certain display property values." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 ;
parser . addListener ( "startrule" , function ( ) {
properties = { } ;
} ) ;
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" , function ( ) {
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
}
}
} ) ;
function reportProperty ( name , display , msg ) {
if ( properties [ name ] ) {
if ( ! ( typeof propertiesToCheck [ name ] == "string" ) || properties [ name ] . value . toLowerCase ( ) != propertiesToCheck [ name ] ) {
reporter . warn ( msg || name + " can't be used with display: " + display + "." , properties [ name ] . line , properties [ name ] . 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 : "Duplicate Properties" ,
desc : "Duplicate properties must appear one after the other." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
var rule = this ,
properties ,
lastProperty ;
function startRule ( event ) {
properties = { } ;
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , startRule ) ;
parser . addListener ( "startpage" , 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 . warn ( "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 : "Empty Rules" ,
desc : "Rules without any properties specified should be removed." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 . warn ( "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 ) {
var rule = this ;
parser . addListener ( "error" , function ( event ) {
reporter . error ( event . message , event . line , event . col , rule ) ;
} ) ;
}
} ) ;
/ *
* 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 : "Floats" ,
desc : "This rule tests if the float property is used too many times" ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 : "Font Faces" ,
desc : "Too many different web fonts in the same stylesheet." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 : "Font Sizes" ,
desc : "Checks the number of font-size declarations." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
var rule = this ,
count = 0 ;
//check for use of "font-size"
parser . addListener ( "property" , function ( event ) {
if ( event . property == "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 : "Gradients" ,
desc : "When using a vendor-prefixed gradient, make sure to use them all." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
var rule = this ,
gradients ;
parser . addListener ( "startrule" , function ( ) {
gradients = {
moz : 0 ,
webkit : 0 ,
ms : 0 ,
o : 0
} ;
} ) ;
parser . addListener ( "property" , function ( event ) {
if ( /\-(moz|ms|o|webkit)(?:\-(?:linear|radial))\-gradient/ . test ( event . value ) ) {
gradients [ RegExp . $1 ] = 1 ;
}
} ) ;
parser . addListener ( "endrule" , function ( event ) {
var missing = [ ] ;
if ( ! gradients . moz ) {
missing . push ( "Firefox 3.6+" ) ;
}
if ( ! gradients . webkit ) {
missing . push ( "Webkit (Safari, Chrome)" ) ;
}
if ( ! gradients . ms ) {
missing . push ( "Internet Explorer 10+" ) ;
}
if ( ! gradients . o ) {
missing . push ( "Opera 11.1+" ) ;
}
if ( missing . length && missing . length < 4 ) {
reporter . warn ( "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 : "IDs" ,
desc : "Selectors should not contain IDs." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 instanceof parserlib . css . SelectorPart ) {
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( modifier . type == "id" ) {
idCount ++ ;
}
}
}
}
if ( idCount == 1 ) {
reporter . warn ( "Don't use IDs in selectors." , selector . line , selector . col , rule ) ;
} else if ( idCount > 1 ) {
reporter . warn ( idCount + " IDs in the selector, really?" , selector . line , selector . col , rule ) ;
}
}
} ) ;
}
} ) ;
/ *
* Rule : Don ' t use @ import , use < link > instead .
* /
CSSLint . addRule ( {
//rule information
id : "import" ,
name : "@import" ,
desc : "Don't use @import, use <link> instead." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
var rule = this ;
parser . addListener ( "import" , function ( event ) {
reporter . warn ( "@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 : "Important" ,
desc : "Be careful when using !important declaration" ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 . warn ( "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 specifity 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 : "Known Properties" ,
desc : "Properties should be known (listed in CSS specification) or be a vendor-prefixed property." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
var rule = this ,
properties = {
"alignment-adjust" : 1 ,
"alignment-baseline" : 1 ,
"animation" : 1 ,
"animation-delay" : 1 ,
"animation-direction" : 1 ,
"animation-duration" : 1 ,
"animation-iteration-count" : 1 ,
"animation-name" : 1 ,
"animation-play-state" : 1 ,
"animation-timing-function" : 1 ,
"appearance" : 1 ,
"azimuth" : 1 ,
"backface-visibility" : 1 ,
"background" : 1 ,
"background-attachment" : 1 ,
"background-break" : 1 ,
"background-clip" : 1 ,
"background-color" : 1 ,
"background-image" : 1 ,
"background-origin" : 1 ,
"background-position" : 1 ,
"background-repeat" : 1 ,
"background-size" : 1 ,
"baseline-shift" : 1 ,
"binding" : 1 ,
"bleed" : 1 ,
"bookmark-label" : 1 ,
"bookmark-level" : 1 ,
"bookmark-state" : 1 ,
"bookmark-target" : 1 ,
"border" : 1 ,
"border-bottom" : 1 ,
"border-bottom-color" : 1 ,
"border-bottom-left-radius" : 1 ,
"border-bottom-right-radius" : 1 ,
"border-bottom-style" : 1 ,
"border-bottom-width" : 1 ,
"border-collapse" : 1 ,
"border-color" : 1 ,
"border-image" : 1 ,
"border-image-outset" : 1 ,
"border-image-repeat" : 1 ,
"border-image-slice" : 1 ,
"border-image-source" : 1 ,
"border-image-width" : 1 ,
"border-left" : 1 ,
"border-left-color" : 1 ,
"border-left-style" : 1 ,
"border-left-width" : 1 ,
"border-radius" : 1 ,
"border-right" : 1 ,
"border-right-color" : 1 ,
"border-right-style" : 1 ,
"border-right-width" : 1 ,
"border-spacing" : 1 ,
"border-style" : 1 ,
"border-top" : 1 ,
"border-top-color" : 1 ,
"border-top-left-radius" : 1 ,
"border-top-right-radius" : 1 ,
"border-top-style" : 1 ,
"border-top-width" : 1 ,
"border-width" : 1 ,
"bottom" : 1 ,
"box-align" : 1 ,
"box-decoration-break" : 1 ,
"box-direction" : 1 ,
"box-flex" : 1 ,
"box-flex-group" : 1 ,
"box-lines" : 1 ,
"box-ordinal-group" : 1 ,
"box-orient" : 1 ,
"box-pack" : 1 ,
"box-shadow" : 1 ,
"box-sizing" : 1 ,
"break-after" : 1 ,
"break-before" : 1 ,
"break-inside" : 1 ,
"caption-side" : 1 ,
"clear" : 1 ,
"clip" : 1 ,
"color" : 1 ,
"color-profile" : 1 ,
"column-count" : 1 ,
"column-fill" : 1 ,
"column-gap" : 1 ,
"column-rule" : 1 ,
"column-rule-color" : 1 ,
"column-rule-style" : 1 ,
"column-rule-width" : 1 ,
"column-span" : 1 ,
"column-width" : 1 ,
"columns" : 1 ,
"content" : 1 ,
"counter-increment" : 1 ,
"counter-reset" : 1 ,
"crop" : 1 ,
"cue" : 1 ,
"cue-after" : 1 ,
"cue-before" : 1 ,
"cursor" : 1 ,
"direction" : 1 ,
"display" : 1 ,
"dominant-baseline" : 1 ,
"drop-initial-after-adjust" : 1 ,
"drop-initial-after-align" : 1 ,
"drop-initial-before-adjust" : 1 ,
"drop-initial-before-align" : 1 ,
"drop-initial-size" : 1 ,
"drop-initial-value" : 1 ,
"elevation" : 1 ,
"empty-cells" : 1 ,
"fit" : 1 ,
"fit-position" : 1 ,
"float" : 1 ,
"float-offset" : 1 ,
"font" : 1 ,
"font-family" : 1 ,
"font-size" : 1 ,
"font-size-adjust" : 1 ,
"font-stretch" : 1 ,
"font-style" : 1 ,
"font-variant" : 1 ,
"font-weight" : 1 ,
"grid-columns" : 1 ,
"grid-rows" : 1 ,
"hanging-punctuation" : 1 ,
"height" : 1 ,
"hyphenate-after" : 1 ,
"hyphenate-before" : 1 ,
"hyphenate-character" : 1 ,
"hyphenate-lines" : 1 ,
"hyphenate-resource" : 1 ,
"hyphens" : 1 ,
"icon" : 1 ,
"image-orientation" : 1 ,
"image-rendering" : 1 ,
"image-resolution" : 1 ,
"inline-box-align" : 1 ,
"left" : 1 ,
"letter-spacing" : 1 ,
"line-height" : 1 ,
"line-stacking" : 1 ,
"line-stacking-ruby" : 1 ,
"line-stacking-shift" : 1 ,
"line-stacking-strategy" : 1 ,
"list-style" : 1 ,
"list-style-image" : 1 ,
"list-style-position" : 1 ,
"list-style-type" : 1 ,
"margin" : 1 ,
"margin-bottom" : 1 ,
"margin-left" : 1 ,
"margin-right" : 1 ,
"margin-top" : 1 ,
"mark" : 1 ,
"mark-after" : 1 ,
"mark-before" : 1 ,
"marks" : 1 ,
"marquee-direction" : 1 ,
"marquee-play-count" : 1 ,
"marquee-speed" : 1 ,
"marquee-style" : 1 ,
"max-height" : 1 ,
"max-width" : 1 ,
"min-height" : 1 ,
"min-width" : 1 ,
"move-to" : 1 ,
"nav-down" : 1 ,
"nav-index" : 1 ,
"nav-left" : 1 ,
"nav-right" : 1 ,
"nav-up" : 1 ,
"opacity" : 1 ,
"orphans" : 1 ,
"outline" : 1 ,
"outline-color" : 1 ,
"outline-offset" : 1 ,
"outline-style" : 1 ,
"outline-width" : 1 ,
"overflow" : 1 ,
"overflow-style" : 1 ,
"overflow-x" : 1 ,
"overflow-y" : 1 ,
"padding" : 1 ,
"padding-bottom" : 1 ,
"padding-left" : 1 ,
"padding-right" : 1 ,
"padding-top" : 1 ,
"page" : 1 ,
"page-break-after" : 1 ,
"page-break-before" : 1 ,
"page-break-inside" : 1 ,
"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 ,
"position" : 1 ,
"presentation-level" : 1 ,
"punctuation-trim" : 1 ,
"quotes" : 1 ,
"rendering-intent" : 1 ,
"resize" : 1 ,
"rest" : 1 ,
"rest-after" : 1 ,
"rest-before" : 1 ,
"richness" : 1 ,
"right" : 1 ,
"rotation" : 1 ,
"rotation-point" : 1 ,
"ruby-align" : 1 ,
"ruby-overhang" : 1 ,
"ruby-position" : 1 ,
"ruby-span" : 1 ,
"size" : 1 ,
"speak" : 1 ,
"speak-header" : 1 ,
"speak-numeral" : 1 ,
"speak-punctuation" : 1 ,
"speech-rate" : 1 ,
"stress" : 1 ,
"string-set" : 1 ,
"table-layout" : 1 ,
"target" : 1 ,
"target-name" : 1 ,
"target-new" : 1 ,
"target-position" : 1 ,
"text-align" : 1 ,
"text-align-last" : 1 ,
"text-decoration" : 1 ,
"text-emphasis" : 1 ,
"text-height" : 1 ,
"text-indent" : 1 ,
"text-justify" : 1 ,
"text-outline" : 1 ,
"text-shadow" : 1 ,
"text-transform" : 1 ,
"text-wrap" : 1 ,
"top" : 1 ,
"transform" : 1 ,
"transform-origin" : 1 ,
"transform-style" : 1 ,
"transition" : 1 ,
"transition-delay" : 1 ,
"transition-duration" : 1 ,
"transition-property" : 1 ,
"transition-timing-function" : 1 ,
"unicode-bidi" : 1 ,
"vertical-align" : 1 ,
"visibility" : 1 ,
"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 ,
"white-space" : 1 ,
"white-space-collapse" : 1 ,
"widows" : 1 ,
"width" : 1 ,
"word-break" : 1 ,
"word-spacing" : 1 ,
"word-wrap" : 1 ,
"z-index" : 1 ,
//IE
"filter" : 1 ,
"zoom" : 1
} ;
parser . addListener ( "property" , function ( event ) {
var name = event . property . text . toLowerCase ( ) ;
if ( ! properties [ name ] && name . charAt ( 0 ) != "-" ) {
reporter . error ( "Unknown property '" + event . property + "'." , event . line , event . col , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Don ' t use classes or IDs with elements ( a . foo or a # foo ) .
* /
CSSLint . addRule ( {
//rule information
id : "overqualified-elements" ,
name : "Overqualified Elements" ,
desc : "Don't use classes or IDs with elements (a.foo or a#foo)." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 instanceof parserlib . css . SelectorPart ) {
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( part . elementName && modifier . type == "id" ) {
reporter . warn ( "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 . warn ( "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 : "Qualified Headings" ,
desc : "Headings should not be qualified (namespaced)." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 instanceof parserlib . css . SelectorPart ) {
if ( part . elementName && /h[1-6]/ . test ( part . elementName . toString ( ) ) && j > 0 ) {
reporter . warn ( "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 : "Regex Selectors" ,
desc : "Selectors that look like regular expressions are slow and should be avoided." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 instanceof parserlib . css . SelectorPart ) {
for ( k = 0 ; k < part . modifiers . length ; k ++ ) {
modifier = part . modifiers [ k ] ;
if ( modifier . type == "attribute" ) {
if ( /([\~\|\^\$\*]=)/ . test ( modifier ) ) {
reporter . warn ( "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 ) {
var rule = this ,
count = 0 ;
//count each rule
parser . addListener ( "startrule" , function ( ) {
count ++ ;
} ) ;
parser . addListener ( "endstylesheet" , function ( ) {
reporter . stat ( "rule-count" , count ) ;
} ) ;
}
} ) ;
/ *
* Rule : Don ' t use text - indent for image replacement if you need to support rtl .
*
* /
/ *
* Should we be checking for rtl / ltr ?
* /
//Commented out due to lack of tests
CSSLint . addRule ( {
//rule information
id : "text-indent" ,
name : "Text Indent" ,
desc : "Checks for text indent less than -99px" ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
var rule = this ;
//check for use of "font-size"
parser . addListener ( "property" , function ( event ) {
var name = event . property ,
value = event . value . parts [ 0 ] . value ;
if ( name == "text-indent" && value < - 99 ) {
reporter . warn ( "Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set text-direction for that item to ltr." , name . line , name . col , rule ) ;
}
} ) ;
}
} ) ;
/ *
* Rule : Headings ( h1 - h6 ) should be defined only once .
* /
CSSLint . addRule ( {
//rule information
id : "unique-headings" ,
name : "Unique Headings" ,
desc : "Headings should be defined only once." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 ,
i ;
for ( i = 0 ; i < selectors . length ; i ++ ) {
selector = selectors [ i ] ;
part = selector . parts [ selector . parts . length - 1 ] ;
if ( part . elementName && /(h[1-6])/ . test ( part . elementName . toString ( ) ) ) {
headings [ RegExp . $1 ] ++ ;
if ( headings [ RegExp . $1 ] > 1 ) {
reporter . warn ( "Heading (" + part . elementName + ") has already been defined." , part . line , part . col , rule ) ;
}
}
}
} ) ;
}
} ) ;
/ *
* Rule : Don 't use universal selector because it' s slow .
* /
CSSLint . addRule ( {
//rule information
id : "universal-selector" ,
name : "Universal Selector" ,
desc : "The universal selector (*) is known to be slow." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 ] ;
part = selector . parts [ selector . parts . length - 1 ] ;
if ( part . elementName == "*" ) {
reporter . warn ( 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 : "Vendor Prefix" ,
desc : "When using a vendor-prefixed property, make sure to include the standard one." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
var rule = this ,
properties ,
num ,
propertiesToCheck = {
"-moz-border-radius" : "border-radius" ,
"-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" ,
"-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-box-shadow" : "box-shadow" ,
"-webkit-box-shadow" : "box-shadow" ,
"-moz-transform" : "transform" ,
"-webkit-transform" : "transform" ,
"-o-transform" : "transform" ,
"-ms-transform" : "transform" ,
"-moz-box-sizing" : "box-sizing" ,
"-webkit-box-sizing" : "box-sizing" ,
"-moz-user-select" : "user-select" ,
"-khtml-user-select" : "user-select" ,
"-webkit-user-select" : "user-select"
} ;
//event handler for beginning of rules
function startRule ( ) {
properties = { } ;
num = 1 ;
}
//event handler for end of rules
function endRule ( event ) {
var prop ,
i , len ,
standard ,
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 . warn ( "Missing standard property '" + needed + "' to go along with '" + actual + "'." , event . line , event . col , rule ) ;
} else {
//make sure standard property is last
if ( properties [ needed ] [ 0 ] . pos < properties [ actual ] [ 0 ] . pos ) {
reporter . warn ( "Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'." , event . line , event . col , rule ) ;
}
}
}
}
parser . addListener ( "startrule" , startRule ) ;
parser . addListener ( "startfontface" , 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 ) ;
}
} ) ;
/ *
* Rule : If an element has a width of 100 % , be careful when placing within
* an element that has padding . It may look strange .
* /
//Commented out pending further review.
/ * C S S L i n t . a d d R u l e ( {
//rule information
id : "width-100" ,
name : "Width 100%" ,
desc : "Be careful when using width: 100% on elements." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
var rule = this ,
width100 ,
boxsizing ;
parser . addListener ( "startrule" , function ( ) {
width100 = null ;
boxsizing = false ;
} ) ;
parser . addListener ( "property" , function ( event ) {
var name = event . property . text . toLowerCase ( ) ,
value = event . value ;
if ( name == "width" && value == "100%" ) {
width100 = event . property ;
} else if ( name == "box-sizing" || /\-(?:webkit|ms|moz)\-box-sizing/ . test ( name ) ) { //means you know what you're doing
boxsizing = true ;
}
} ) ;
parser . addListener ( "endrule" , function ( ) {
if ( width100 && ! boxsizing ) {
reporter . warn ( "Elements with a width of 100% may not appear as you expect inside of other elements." , width100 . line , width100 . col , rule ) ;
}
} ) ;
}
} ) ; * /
/ *
* Rule : You don ' t need to specify units when a value is 0.
* /
CSSLint . addRule ( {
//rule information
id : "zero-units" ,
name : "Zero Units" ,
desc : "You don't need to specify units when a value is 0." ,
browsers : "All" ,
//initialization
init : function ( parser , reporter ) {
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 ) {
reporter . warn ( "Values of 0 shouldn't have units specified." , parts [ i ] . line , parts [ i ] . col , rule ) ;
}
i ++ ;
}
} ) ;
}
} ) ;
exports . CSSLint = CSSLint ;
} ) ;