123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- /*
- Slick Parser
- - originally created by the almighty Thomas Aylott <@subtlegradient> (http://subtlegradient.com)
- */"use strict"
-
- // Notable changes from Slick.Parser 1.0.x
-
- // The parser now uses 2 classes: Expressions and Expression
- // `new Expressions` produces an array-like object containing a list of Expression objects
- // - Expressions::toString() produces a cleaned up expressions string
- // `new Expression` produces an array-like object
- // - Expression::toString() produces a cleaned up expression string
- // The only exposed method is parse, which produces a (cached) `new Expressions` instance
- // parsed.raw is no longer present, use .toString()
- // parsed.expression is now useless, just use the indices
- // parsed.reverse() has been removed for now, due to its apparent uselessness
- // Other changes in the Expressions object:
- // - classNames are now unique, and save both escaped and unescaped values
- // - attributes now save both escaped and unescaped values
- // - pseudos now save both escaped and unescaped values
-
- var escapeRe = /([-.*+?^${}()|[\]\/\\])/g,
- unescapeRe = /\\/g
-
- var escape = function(string){
- // XRegExp v2.0.0-beta-3
- // « https://github.com/slevithan/XRegExp/blob/master/src/xregexp.js
- return (string + "").replace(escapeRe, '\\$1')
- }
-
- var unescape = function(string){
- return (string + "").replace(unescapeRe, '')
- }
-
- var slickRe = RegExp(
- /*
- #!/usr/bin/env ruby
- puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
- __END__
- "(?x)^(?:\
- \\s* ( , ) \\s* # Separator \n\
- | \\s* ( <combinator>+ ) \\s* # Combinator \n\
- | ( \\s+ ) # CombinatorChildren \n\
- | ( <unicode>+ | \\* ) # Tag \n\
- | \\# ( <unicode>+ ) # ID \n\
- | \\. ( <unicode>+ ) # ClassName \n\
- | # Attribute \n\
- \\[ \
- \\s* (<unicode1>+) (?: \
- \\s* ([*^$!~|]?=) (?: \
- \\s* (?:\
- ([\"']?)(.*?)\\9 \
- )\
- ) \
- )? \\s* \
- \\](?!\\]) \n\
- | :+ ( <unicode>+ )(?:\
- \\( (?:\
- (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
- ) \\)\
- )?\
- )"
- */
- "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
- .replace(/<combinator>/, '[' + escape(">+~`!@$%^&={}\\;</") + ']')
- .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
- .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
- )
-
- // Part
-
- var Part = function Part(combinator){
- this.combinator = combinator || " "
- this.tag = "*"
- }
-
- Part.prototype.toString = function(){
-
- if (!this.raw){
-
- var xpr = "", k, part
-
- xpr += this.tag || "*"
- if (this.id) xpr += "#" + this.id
- if (this.classes) xpr += "." + this.classList.join(".")
- if (this.attributes) for (k = 0; part = this.attributes[k++];){
- xpr += "[" + part.name + (part.operator ? part.operator + '"' + part.value + '"' : '') + "]"
- }
- if (this.pseudos) for (k = 0; part = this.pseudos[k++];){
- xpr += ":" + part.name
- if (part.value) xpr += "(" + part.value + ")"
- }
-
- this.raw = xpr
-
- }
-
- return this.raw
- }
-
- // Expression
-
- var Expression = function Expression(){
- this.length = 0
- }
-
- Expression.prototype.toString = function(){
-
- if (!this.raw){
-
- var xpr = ""
-
- for (var j = 0, bit; bit = this[j++];){
- if (j !== 1) xpr += " "
- if (bit.combinator !== " ") xpr += bit.combinator + " "
- xpr += bit
- }
-
- this.raw = xpr
-
- }
-
- return this.raw
- }
-
- var replacer = function(
- rawMatch,
-
- separator,
- combinator,
- combinatorChildren,
-
- tagName,
- id,
- className,
-
- attributeKey,
- attributeOperator,
- attributeQuote,
- attributeValue,
-
- pseudoMarker,
- pseudoClass,
- pseudoQuote,
- pseudoClassQuotedValue,
- pseudoClassValue
- ){
-
- var expression, current
-
- if (separator || !this.length){
- expression = this[this.length++] = new Expression
- if (separator) return ''
- }
-
- if (!expression) expression = this[this.length - 1]
-
- if (combinator || combinatorChildren || !expression.length){
- current = expression[expression.length++] = new Part(combinator)
- }
-
- if (!current) current = expression[expression.length - 1]
-
- if (tagName){
-
- current.tag = unescape(tagName)
-
- } else if (id){
-
- current.id = unescape(id)
-
- } else if (className){
-
- var unescaped = unescape(className)
-
- var classes = current.classes || (current.classes = {})
- if (!classes[unescaped]){
- classes[unescaped] = escape(className)
- var classList = current.classList || (current.classList = [])
- classList.push(unescaped)
- classList.sort()
- }
-
- } else if (pseudoClass){
-
- pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue
-
- ;(current.pseudos || (current.pseudos = [])).push({
- type : pseudoMarker.length == 1 ? 'class' : 'element',
- name : unescape(pseudoClass),
- escapedName : escape(pseudoClass),
- value : pseudoClassValue ? unescape(pseudoClassValue) : null,
- escapedValue : pseudoClassValue ? escape(pseudoClassValue) : null
- })
-
- } else if (attributeKey){
-
- attributeValue = attributeValue ? escape(attributeValue) : null
-
- ;(current.attributes || (current.attributes = [])).push({
- operator : attributeOperator,
- name : unescape(attributeKey),
- escapedName : escape(attributeKey),
- value : attributeValue ? unescape(attributeValue) : null,
- escapedValue : attributeValue ? escape(attributeValue) : null
- })
-
- }
-
- return ''
-
- }
-
- // Expressions
-
- var Expressions = function Expressions(expression){
- this.length = 0
-
- var self = this
-
- var original = expression, replaced
-
- while (expression){
- replaced = expression.replace(slickRe, function(){
- return replacer.apply(self, arguments)
- })
- if (replaced === expression) throw new Error(original + ' is an invalid expression')
- expression = replaced
- }
- }
-
- Expressions.prototype.toString = function(){
- if (!this.raw){
- var expressions = []
- for (var i = 0, expression; expression = this[i++];) expressions.push(expression)
- this.raw = expressions.join(", ")
- }
-
- return this.raw
- }
-
- var cache = {}
-
- var parse = function(expression){
- if (expression == null) return null
- expression = ('' + expression).replace(/^\s+|\s+$/g, '')
- return cache[expression] || (cache[expression] = new Expressions(expression))
- }
-
- module.exports = parse
|