Spaces:
Running
Running
//.CommonJS | |
var CSSOM = {}; | |
///CommonJS | |
/** | |
* @param {string} token | |
*/ | |
CSSOM.parse = function parse(token) { | |
var i = 0; | |
/** | |
"before-selector" or | |
"selector" or | |
"atRule" or | |
"atBlock" or | |
"conditionBlock" 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, | |
"containerBlock": true, | |
"conditionBlock": true, | |
'documentRule-begin': true | |
}; | |
var styleSheet = new CSSOM.CSSStyleSheet(); | |
// @type CSSStyleSheet|CSSMediaRule|CSSContainerRule|CSSSupportsRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule | |
var currentScope = styleSheet; | |
// @type CSSMediaRule|CSSContainerRule|CSSSupportsRule|CSSKeyframesRule|CSSDocumentRule | |
var parentRule; | |
var ancestorRules = []; | |
var hasAncestors = false; | |
var prevScope; | |
var name, priority="", styleRule, mediaRule, containerRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule; | |
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("@container", i) === i) { | |
state = "containerBlock"; | |
containerRule = new CSSOM.CSSContainerRule(); | |
containerRule.__starts = i; | |
i += "container".length; | |
buffer = ""; | |
break; | |
} else if (token.indexOf("@supports", i) === i) { | |
state = "conditionBlock"; | |
supportsRule = new CSSOM.CSSSupportsRule(); | |
supportsRule.__starts = i; | |
i += "supports".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("@starting-style", i) === i) { | |
state = "startingStyleRule-begin"; | |
i += "starting-style".length; | |
startingStyleRule = new CSSOM.CSSStartingStyleRule(); | |
startingStyleRule.__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(); | |
if (parentRule) { | |
ancestorRules.push(parentRule); | |
} | |
currentScope = parentRule = mediaRule; | |
mediaRule.parentStyleSheet = styleSheet; | |
buffer = ""; | |
state = "before-selector"; | |
} else if (state === "containerBlock") { | |
containerRule.containerText = buffer.trim(); | |
if (parentRule) { | |
ancestorRules.push(parentRule); | |
} | |
currentScope = parentRule = containerRule; | |
containerRule.parentStyleSheet = styleSheet; | |
buffer = ""; | |
state = "before-selector"; | |
} else if (state === "conditionBlock") { | |
supportsRule.conditionText = buffer.trim(); | |
if (parentRule) { | |
ancestorRules.push(parentRule); | |
} | |
currentScope = parentRule = supportsRule; | |
supportsRule.parentStyleSheet = styleSheet; | |
buffer = ""; | |
state = "before-selector"; | |
} else if (state === "hostRule-begin") { | |
if (parentRule) { | |
ancestorRules.push(parentRule); | |
} | |
currentScope = parentRule = hostRule; | |
hostRule.parentStyleSheet = styleSheet; | |
buffer = ""; | |
state = "before-selector"; | |
} else if (state === "startingStyleRule-begin") { | |
if (parentRule) { | |
ancestorRules.push(parentRule); | |
} | |
currentScope = parentRule = startingStyleRule; | |
startingStyleRule.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) { | |
ancestorRules.push(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) { | |
ancestorRules.push(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/supports/document rule. | |
if (!parentRule) { | |
parseError("Unexpected }"); | |
} | |
// Handle rules nested in @media or @supports | |
hasAncestors = ancestorRules.length > 0; | |
while (ancestorRules.length > 0) { | |
parentRule = ancestorRules.pop(); | |
if ( | |
parentRule.constructor.name === "CSSMediaRule" | |
|| parentRule.constructor.name === "CSSSupportsRule" | |
|| parentRule.constructor.name === "CSSContainerRule" | |
|| parentRule.constructor.name === "CSSStartingStyleRule" | |
) { | |
prevScope = currentScope; | |
currentScope = parentRule; | |
currentScope.cssRules.push(prevScope); | |
break; | |
} | |
if (ancestorRules.length === 0) { | |
hasAncestors = false; | |
} | |
} | |
if (!hasAncestors) { | |
currentScope.__ends = i + 1; | |
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.CSSGroupingRule = require("./CSSGroupingRule").CSSGroupingRule; | |
CSSOM.CSSMediaRule = require("./CSSMediaRule").CSSMediaRule; | |
CSSOM.CSSContainerRule = require("./CSSContainerRule").CSSContainerRule; | |
CSSOM.CSSConditionRule = require("./CSSConditionRule").CSSConditionRule; | |
CSSOM.CSSSupportsRule = require("./CSSSupportsRule").CSSSupportsRule; | |
CSSOM.CSSFontFaceRule = require("./CSSFontFaceRule").CSSFontFaceRule; | |
CSSOM.CSSHostRule = require("./CSSHostRule").CSSHostRule; | |
CSSOM.CSSStartingStyleRule = require("./CSSStartingStyleRule").CSSStartingStyleRule; | |
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 | |