/* Simple parser for CSS */ var CSSParser = Editor.Parser = (function() { var tokenizeCSS = (function() { function normal(source, setState) { var ch = source.next(); if (ch == "@") { source.nextWhileMatches(/\w/); return "css-at"; } else if (ch == "/" && source.equals("*")) { setState(inCComment); return null; } else if (ch == "<" && source.equals("!")) { setState(inSGMLComment); return null; } else if (ch == "=") { return "css-compare"; } else if (source.equals("=") && (ch == "~" || ch == "|")) { source.next(); return "css-compare"; } else if (ch == "\"" || ch == "'") { setState(inString(ch)); return null; } else if (ch == "#") { source.nextWhileMatches(/\w/); return "css-hash"; } else if (ch == "!") { source.nextWhileMatches(/[ \t]/); source.nextWhileMatches(/\w/); return "css-important"; } else if (/\d/.test(ch)) { source.nextWhileMatches(/[\w.%]/); return "css-unit"; } else if (/[,.+>*\/]/.test(ch)) { return "css-select-op"; } else if (/[;{}:\[\]]/.test(ch)) { return "css-punctuation"; } else { source.nextWhileMatches(/[\w\\\-_]/); return "css-identifier"; } } function inCComment(source, setState) { var maybeEnd = false; while (!source.endOfLine()) { var ch = source.next(); if (maybeEnd && ch == "/") { setState(normal); break; } maybeEnd = (ch == "*"); } return "css-comment"; } function inSGMLComment(source, setState) { var dashes = 0; while (!source.endOfLine()) { var ch = source.next(); if (dashes >= 2 && ch == ">") { setState(normal); break; } dashes = (ch == "-") ? dashes + 1 : 0; } return "css-comment"; } function inString(quote) { return function(source, setState) { var escaped = false; while (!source.endOfLine()) { var ch = source.next(); if (ch == quote && !escaped) break; escaped = !escaped && ch == "\\"; } if (!escaped) setState(normal); return "css-string"; }; } return function(source, startState) { return tokenizer(source, startState || normal); }; })(); function indentCSS(inBraces, inRule, base) { return function(nextChars) { if (!inBraces || /^\}/.test(nextChars)) return base; else if (inRule) return base + indentUnit * 2; else return base + indentUnit; }; } // This is a very simplistic parser -- since CSS does not really // nest, it works acceptably well, but some nicer colouroing could // be provided with a more complicated parser. function parseCSS(source, basecolumn) { basecolumn = basecolumn || 0; var tokens = tokenizeCSS(source); var inBraces = false, inRule = false; var iter = { next: function() { var token = tokens.next(), style = token.style, content = token.content; if (style == "css-identifier" && inRule) token.style = "css-value"; if (style == "css-hash") token.style = inRule ? "css-colorcode" : "css-identifier"; if (content == "\n") token.indentation = indentCSS(inBraces, inRule, basecolumn); if (content == "{") inBraces = true; else if (content == "}") inBraces = inRule = false; else if (inBraces && content == ";") inRule = false; else if (inBraces && style != "css-comment" && style != "whitespace") inRule = true; return token; }, copy: function() { var _inBraces = inBraces, _inRule = inRule, _tokenState = tokens.state; return function(source) { tokens = tokenizeCSS(source, _tokenState); inBraces = _inBraces; inRule = _inRule; return iter; }; } }; return iter; } return {make: parseCSS, electricChars: "}"}; })();