362 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			362 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| //.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
 |