| //.CommonJS |
| var CSSOM = {}; |
| ///CommonJS |
| |
| |
| /** |
| * @param {string} token |
| */ |
| CSSOM.parse = function parse(token) { |
| |
| var i = 0; |
| |
| /** |
| "before-selector" or |
| "selector" or |
| "atRule" or |
| "atBlock" or |
| "before-name" or |
| "name" or |
| "before-value" or |
| "value" |
| */ |
| var state = "before-selector"; |
| |
| var index; |
| var buffer = ""; |
| var valueParenthesisDepth = 0; |
| |
| var SIGNIFICANT_WHITESPACE = { |
| "selector": true, |
| "value": true, |
| "value-parenthesis": true, |
| "atRule": true, |
| "importRule-begin": true, |
| "importRule": true, |
| "atBlock": true, |
| 'documentRule-begin': true |
| }; |
| |
| var styleSheet = new CSSOM.CSSStyleSheet(); |
| |
| // @type CSSStyleSheet|CSSMediaRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule |
| var currentScope = styleSheet; |
| |
| // @type CSSMediaRule|CSSKeyframesRule|CSSDocumentRule |
| var parentRule; |
| |
| var name, priority="", styleRule, mediaRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule; |
| |
| var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g; |
| |
| var parseError = function(message) { |
| var lines = token.substring(0, i).split('\n'); |
| var lineCount = lines.length; |
| var charCount = lines.pop().length + 1; |
| var error = new Error(message + ' (line ' + lineCount + ', char ' + charCount + ')'); |
| error.line = lineCount; |
| /* jshint sub : true */ |
| error['char'] = charCount; |
| error.styleSheet = styleSheet; |
| throw error; |
| }; |
| |
| for (var character; (character = token.charAt(i)); i++) { |
| |
| switch (character) { |
| |
| case " ": |
| case "\t": |
| case "\r": |
| case "\n": |
| case "\f": |
| if (SIGNIFICANT_WHITESPACE[state]) { |
| buffer += character; |
| } |
| break; |
| |
| // String |
| case '"': |
| index = i + 1; |
| do { |
| index = token.indexOf('"', index) + 1; |
| if (!index) { |
| parseError('Unmatched "'); |
| } |
| } while (token[index - 2] === '\\'); |
| buffer += token.slice(i, index); |
| i = index - 1; |
| switch (state) { |
| case 'before-value': |
| state = 'value'; |
| break; |
| case 'importRule-begin': |
| state = 'importRule'; |
| break; |
| } |
| break; |
| |
| case "'": |
| index = i + 1; |
| do { |
| index = token.indexOf("'", index) + 1; |
| if (!index) { |
| parseError("Unmatched '"); |
| } |
| } while (token[index - 2] === '\\'); |
| buffer += token.slice(i, index); |
| i = index - 1; |
| switch (state) { |
| case 'before-value': |
| state = 'value'; |
| break; |
| case 'importRule-begin': |
| state = 'importRule'; |
| break; |
| } |
| break; |
| |
| // Comment |
| case "/": |
| if (token.charAt(i + 1) === "*") { |
| i += 2; |
| index = token.indexOf("*/", i); |
| if (index === -1) { |
| parseError("Missing */"); |
| } else { |
| i = index + 1; |
| } |
| } else { |
| buffer += character; |
| } |
| if (state === "importRule-begin") { |
| buffer += " "; |
| state = "importRule"; |
| } |
| break; |
| |
| // At-rule |
| case "@": |
| if (token.indexOf("@-moz-document", i) === i) { |
| state = "documentRule-begin"; |
| documentRule = new CSSOM.CSSDocumentRule(); |
| documentRule.__starts = i; |
| i += "-moz-document".length; |
| buffer = ""; |
| break; |
| } else if (token.indexOf("@media", i) === i) { |
| state = "atBlock"; |
| mediaRule = new CSSOM.CSSMediaRule(); |
| mediaRule.__starts = i; |
| i += "media".length; |
| buffer = ""; |
| break; |
| } else if (token.indexOf("@host", i) === i) { |
| state = "hostRule-begin"; |
| i += "host".length; |
| hostRule = new CSSOM.CSSHostRule(); |
| hostRule.__starts = i; |
| buffer = ""; |
| break; |
| } else if (token.indexOf("@import", i) === i) { |
| state = "importRule-begin"; |
| i += "import".length; |
| buffer += "@import"; |
| break; |
| } else if (token.indexOf("@font-face", i) === i) { |
| state = "fontFaceRule-begin"; |
| i += "font-face".length; |
| fontFaceRule = new CSSOM.CSSFontFaceRule(); |
| fontFaceRule.__starts = i; |
| buffer = ""; |
| break; |
| } else { |
| atKeyframesRegExp.lastIndex = i; |
| var matchKeyframes = atKeyframesRegExp.exec(token); |
| if (matchKeyframes && matchKeyframes.index === i) { |
| state = "keyframesRule-begin"; |
| keyframesRule = new CSSOM.CSSKeyframesRule(); |
| keyframesRule.__starts = i; |
| keyframesRule._vendorPrefix = matchKeyframes[1]; // Will come out as undefined if no prefix was found |
| i += matchKeyframes[0].length - 1; |
| buffer = ""; |
| break; |
| } else if (state === "selector") { |
| state = "atRule"; |
| } |
| } |
| buffer += character; |
| break; |
| |
| case "{": |
| if (state === "selector" || state === "atRule") { |
| styleRule.selectorText = buffer.trim(); |
| styleRule.style.__starts = i; |
| buffer = ""; |
| state = "before-name"; |
| } else if (state === "atBlock") { |
| mediaRule.media.mediaText = buffer.trim(); |
| currentScope = parentRule = mediaRule; |
| mediaRule.parentStyleSheet = styleSheet; |
| buffer = ""; |
| state = "before-selector"; |
| } else if (state === "hostRule-begin") { |
| currentScope = parentRule = hostRule; |
| hostRule.parentStyleSheet = styleSheet; |
| buffer = ""; |
| state = "before-selector"; |
| } else if (state === "fontFaceRule-begin") { |
| if (parentRule) { |
| fontFaceRule.parentRule = parentRule; |
| } |
| fontFaceRule.parentStyleSheet = styleSheet; |
| styleRule = fontFaceRule; |
| buffer = ""; |
| state = "before-name"; |
| } else if (state === "keyframesRule-begin") { |
| keyframesRule.name = buffer.trim(); |
| if (parentRule) { |
| keyframesRule.parentRule = parentRule; |
| } |
| keyframesRule.parentStyleSheet = styleSheet; |
| currentScope = parentRule = keyframesRule; |
| buffer = ""; |
| state = "keyframeRule-begin"; |
| } else if (state === "keyframeRule-begin") { |
| styleRule = new CSSOM.CSSKeyframeRule(); |
| styleRule.keyText = buffer.trim(); |
| styleRule.__starts = i; |
| buffer = ""; |
| state = "before-name"; |
| } else if (state === "documentRule-begin") { |
| // FIXME: what if this '{' is in the url text of the match function? |
| documentRule.matcher.matcherText = buffer.trim(); |
| if (parentRule) { |
| documentRule.parentRule = parentRule; |
| } |
| currentScope = parentRule = documentRule; |
| documentRule.parentStyleSheet = styleSheet; |
| buffer = ""; |
| state = "before-selector"; |
| } |
| break; |
| |
| case ":": |
| if (state === "name") { |
| name = buffer.trim(); |
| buffer = ""; |
| state = "before-value"; |
| } else { |
| buffer += character; |
| } |
| break; |
| |
| case "(": |
| if (state === 'value') { |
| // ie css expression mode |
| if (buffer.trim() === 'expression') { |
| var info = (new CSSOM.CSSValueExpression(token, i)).parse(); |
| |
| if (info.error) { |
| parseError(info.error); |
| } else { |
| buffer += info.expression; |
| i = info.idx; |
| } |
| } else { |
| state = 'value-parenthesis'; |
| //always ensure this is reset to 1 on transition |
| //from value to value-parenthesis |
| valueParenthesisDepth = 1; |
| buffer += character; |
| } |
| } else if (state === 'value-parenthesis') { |
| valueParenthesisDepth++; |
| buffer += character; |
| } else { |
| buffer += character; |
| } |
| break; |
| |
| case ")": |
| if (state === 'value-parenthesis') { |
| valueParenthesisDepth--; |
| if (valueParenthesisDepth === 0) state = 'value'; |
| } |
| buffer += character; |
| break; |
| |
| case "!": |
| if (state === "value" && token.indexOf("!important", i) === i) { |
| priority = "important"; |
| i += "important".length; |
| } else { |
| buffer += character; |
| } |
| break; |
| |
| case ";": |
| switch (state) { |
| case "value": |
| styleRule.style.setProperty(name, buffer.trim(), priority); |
| priority = ""; |
| buffer = ""; |
| state = "before-name"; |
| break; |
| case "atRule": |
| buffer = ""; |
| state = "before-selector"; |
| break; |
| case "importRule": |
| importRule = new CSSOM.CSSImportRule(); |
| importRule.parentStyleSheet = importRule.styleSheet.parentStyleSheet = styleSheet; |
| importRule.cssText = buffer + character; |
| styleSheet.cssRules.push(importRule); |
| buffer = ""; |
| state = "before-selector"; |
| break; |
| default: |
| buffer += character; |
| break; |
| } |
| break; |
| |
| case "}": |
| switch (state) { |
| case "value": |
| styleRule.style.setProperty(name, buffer.trim(), priority); |
| priority = ""; |
| /* falls through */ |
| case "before-name": |
| case "name": |
| styleRule.__ends = i + 1; |
| if (parentRule) { |
| styleRule.parentRule = parentRule; |
| } |
| styleRule.parentStyleSheet = styleSheet; |
| currentScope.cssRules.push(styleRule); |
| buffer = ""; |
| if (currentScope.constructor === CSSOM.CSSKeyframesRule) { |
| state = "keyframeRule-begin"; |
| } else { |
| state = "before-selector"; |
| } |
| break; |
| case "keyframeRule-begin": |
| case "before-selector": |
| case "selector": |
| // End of media/document rule. |
| if (!parentRule) { |
| parseError("Unexpected }"); |
| } |
| currentScope.__ends = i + 1; |
| // Nesting rules aren't supported yet |
| styleSheet.cssRules.push(currentScope); |
| currentScope = styleSheet; |
| parentRule = null; |
| buffer = ""; |
| state = "before-selector"; |
| break; |
| } |
| break; |
| |
| default: |
| switch (state) { |
| case "before-selector": |
| state = "selector"; |
| styleRule = new CSSOM.CSSStyleRule(); |
| styleRule.__starts = i; |
| break; |
| case "before-name": |
| state = "name"; |
| break; |
| case "before-value": |
| state = "value"; |
| break; |
| case "importRule-begin": |
| state = "importRule"; |
| break; |
| } |
| buffer += character; |
| break; |
| } |
| } |
| |
| return styleSheet; |
| }; |
| |
| |
| //.CommonJS |
| exports.parse = CSSOM.parse; |
| // The following modules cannot be included sooner due to the mutual dependency with parse.js |
| CSSOM.CSSStyleSheet = require("./CSSStyleSheet").CSSStyleSheet; |
| CSSOM.CSSStyleRule = require("./CSSStyleRule").CSSStyleRule; |
| CSSOM.CSSImportRule = require("./CSSImportRule").CSSImportRule; |
| CSSOM.CSSMediaRule = require("./CSSMediaRule").CSSMediaRule; |
| CSSOM.CSSFontFaceRule = require("./CSSFontFaceRule").CSSFontFaceRule; |
| CSSOM.CSSHostRule = require("./CSSHostRule").CSSHostRule; |
| CSSOM.CSSStyleDeclaration = require('./CSSStyleDeclaration').CSSStyleDeclaration; |
| CSSOM.CSSKeyframeRule = require('./CSSKeyframeRule').CSSKeyframeRule; |
| CSSOM.CSSKeyframesRule = require('./CSSKeyframesRule').CSSKeyframesRule; |
| CSSOM.CSSValueExpression = require('./CSSValueExpression').CSSValueExpression; |
| CSSOM.CSSDocumentRule = require('./CSSDocumentRule').CSSDocumentRule; |
| ///CommonJS |