//.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