blob: 493a63ba6cd55e06ed1542ddc0b505b3d7e8c461 [file] [log] [blame]
//.CommonJS
var CSSOM = {
CSSStyleSheet: require("./CSSStyleSheet").CSSStyleSheet,
CSSStyleRule: require("./CSSStyleRule").CSSStyleRule,
CSSImportRule: require("./CSSImportRule").CSSImportRule,
CSSMediaRule: require("./CSSMediaRule").CSSMediaRule
};
///CommonJS
CSSOM.Parser = function Parser() {};
/**
* @param {string} cssText
* @param {Object} options
*/
CSSOM.Parser.prototype.parseStyleSheet = function(cssText, options) {
options = options || {};
var i = options.startIndex || 0;
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;
}
};
CSSOM.Parser.prototype.parse = function(token, options) {
options = options || {};
var i = options.startIndex || 0;
this.styleSheetStart(i);
/**
"before-selector" or
"selector" or
"atRule" or
"atBlock" or
"before-name" or
"name" or
"before-value" or
"value"
*/
var state = options.state || "before-selector";
var index;
var j = i;
var buffer = "";
var SIGNIFICANT_WHITESPACE = {
"selector": true,
"value": true,
"atRule": true,
"importRule-begin": true,
"importRule": true,
"atBlock": true
};
var styleSheet = new CSSOM.CSSStyleSheet;
// @type CSSStyleSheet|CSSMediaRule
var currentScope = styleSheet;
var selector, name, value, priority="", styleRule, mediaRule, importRule;
var declarationStarts;
var declarationEnds;
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 '"':
j = i + 1;
index = token.indexOf('"', j) + 1;
if (!index) {
throw '" is missing';
}
buffer += token.slice(i, index);
i = index - 1;
if (state == 'before-value') {
state = 'value';
}
break;
case "'":
j = i + 1;
index = token.indexOf("'", j) + 1;
if (!index) {
throw "' is missing";
}
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) {
throw SyntaxError("Missing */");
} else {
i = index + 1;
}
} else {
buffer += character;
}
if (state == "importRule-begin") {
buffer += " ";
state = "importRule";
}
break;
// At-rule
case "@":
if (token.indexOf("@media", i) == i) {
state = "atBlock";
mediaRule = new CSSOM.CSSMediaRule;
mediaRule.__starts = i;
i += "media".length;
buffer = "";
break;
} else if (token.indexOf("@import", i) == i) {
state = "importRule-begin";
i += "import".length;
buffer += "@import";
break;
} else if (state == "selector") {
state = "atRule";
}
buffer += character;
break;
case "{":
if (state == "selector" || state == "atRule") {
this.selectorEnd(i, buffer);
buffer = "";
state = "before-name";
} else if (state == "atBlock") {
mediaRule.media.mediaText = buffer.trim();
currentScope = mediaRule;
buffer = "";
state = "before-selector";
}
break;
case ":":
if (state == "name") {
name = buffer;
buffer = "";
state = "before-value";
} else {
buffer += character;
}
break;
case '(':
if (state == 'value') {
index = token.indexOf(')', i + 1);
if (index == -1) {
throw i + ': unclosed "("';
}
buffer += token.slice(i, index + 1);
i = index;
} else {
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":
this.declarationEnd(i, name, buffer, priority);
priority = "";
buffer = "";
state = "before-name";
break;
case "atRule":
buffer = "";
state = "before-selector";
break;
case "importRule":
importRule = new CSSOM.CSSImportRule;
importRule.cssText = buffer + character;
currentScope.cssRules.push(importRule);
buffer = "";
state = "before-selector";
break;
default:
buffer += character;
break;
}
break;
case "}":
switch (state) {
case "value":
this.declarationEnd(i, name, buffer, priority);
// fall down
case "before-name":
this.styleRuleEnd(i);
buffer = "";
break;
case "name":
throw i + ": Oops";
break;
case "before-selector":
case "selector":
// End of media rule.
// Nesting rules aren't supported yet
if (!mediaRule) {
throw "unexpected }";
}
mediaRule.__ends = i + 1;
styleSheet.cssRules.push(mediaRule);
currentScope = styleSheet;
buffer = "";
break;
}
state = "before-selector";
break;
default:
switch (state) {
case "before-selector":
this.styleRuleStart(i);
state = "selector";
break;
case "before-name":
state = "name";
break;
case "before-value":
state = "value";
break;
case "importRule-begin":
state = "importRule";
break;
}
buffer += character;
break;
}
}
return styleSheet;
};
CSSOM.Parser.prototype.compile = function() {
var handlers = {
styleSheetStart: this.styleSheetStart,
styleRuleStart: this.styleRuleStart,
selectorEnd: this.selectorEnd,
declarationEnd: this.declarationEnd,
styleRuleEnd: this.styleRuleEnd,
styleSheetEnd: this.styleSheetEnd
};
var parser = this.parse.toString();
for (var key in handlers) {
if (!handlers.hasOwnProperty(key)) {
continue;
}
parser = parser.replace(new RegExp('^.*' + key + '.*$', 'gm'), handlers[key].toString()
.replace(/^function.+$/m, '')
.replace(/^}/m, ''))
.replace(/this\.?/g, '');
}
return parser;
};
CSSOM.Parser.prototype.styleSheetStart = function(i) {
console.log('styleSheetStart', i);
this.styleSheet = new CSSOM.CSSStyleSheet;
this.scopeRules = this.styleSheet.cssRules;
};
CSSOM.Parser.prototype.styleRuleStart = function(i) {
console.log('styleRuleStart', i);
this.styleRule = new CSSOM.CSSStyleRule;
this.styleRule._start = i;
};
CSSOM.Parser.prototype.selectorEnd = function(i, buffer) {
this.styleRule.selectorText = buffer.trimRight();
this.styleRule.style._start = i;
};
CSSOM.Parser.prototype.declarationEnd = function(name, value, priority, startIndex, endIndex) {
console.log('declarationEnd', name, value, priority, startIndex, endIndex);
};
CSSOM.Parser.prototype.styleRuleEnd = function(i) {
this.styleRule._end = i;
this.scopeRules.push(this.styleRule);
};
CSSOM.Parser.prototype.styleSheetEnd = function(i) {
return this.styleSheet;
};
/*
Parser.prototype.nameStart = function(i) {
this.nameStartIndex = i;
};
Parser.prototype.nameEnd = function(i, buffer) {
this.name = buffer.trimRight();
this.nameEndIndex = this.nameStartIndex + this.name.length;
};
Parser.prototype.valueStart = function(i) {
this.valueStartIndex = i;
};
Parser.prototype.valueEnd = function(i, buffer) {
var value = buffer.trimRight();
this.styleRule.style.add(this.name, value, this.nameStartIndex, this.nameEndIndex, this.valueStartIndex, this.valueStartIndex + value.length);
};
*/
//.CommonJS
exports.Parser = CSSOM.Parser;
///CommonJS