123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- //.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,
- "conditionBlock": true,
- 'documentRule-begin': true
- };
- var styleSheet = new CSSOM.CSSStyleSheet();
- // @type CSSStyleSheet|CSSMediaRule|CSSSupportsRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule
- var currentScope = styleSheet;
- // @type CSSMediaRule|CSSSupportsRule|CSSKeyframesRule|CSSDocumentRule
- var parentRule;
- var ancestorRules = [];
- var hasAncestors = false;
- var prevScope;
- var name, priority="", styleRule, mediaRule, supportsRule, 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("@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("@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 === "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 === "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"
- ) {
- 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.CSSMediaRule = require("./CSSMediaRule").CSSMediaRule;
- CSSOM.CSSSupportsRule = require("./CSSSupportsRule").CSSSupportsRule;
- 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
|