cssParser.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. "use strict";
  2. var __extends = (this && this.__extends) || (function () {
  3. var extendStatics = function (d, b) {
  4. extendStatics = Object.setPrototypeOf ||
  5. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  6. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  7. return extendStatics(d, b);
  8. };
  9. return function (d, b) {
  10. extendStatics(d, b);
  11. function __() { this.constructor = d; }
  12. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  13. };
  14. })();
  15. Object.defineProperty(exports, "__esModule", { value: true });
  16. var chars = require("./chars");
  17. var parseUtil_1 = require("./parseUtil");
  18. var cssAst_1 = require("./cssAst");
  19. var SPACE_OPERATOR = ' ';
  20. var cssLexer_1 = require("./cssLexer");
  21. var SLASH_CHARACTER = '/';
  22. var GT_CHARACTER = '>';
  23. var TRIPLE_GT_OPERATOR_STR = '>>>';
  24. var DEEP_OPERATOR_STR = '/deep/';
  25. var EOF_DELIM_FLAG = 1;
  26. var RBRACE_DELIM_FLAG = 2;
  27. var LBRACE_DELIM_FLAG = 4;
  28. var COMMA_DELIM_FLAG = 8;
  29. var COLON_DELIM_FLAG = 16;
  30. var SEMICOLON_DELIM_FLAG = 32;
  31. var NEWLINE_DELIM_FLAG = 64;
  32. var RPAREN_DELIM_FLAG = 128;
  33. var LPAREN_DELIM_FLAG = 256;
  34. var SPACE_DELIM_FLAG = 512;
  35. function _pseudoSelectorSupportsInnerSelectors(name) {
  36. return ['not', 'host', 'host-context'].indexOf(name) >= 0;
  37. }
  38. function isSelectorOperatorCharacter(code) {
  39. switch (code) {
  40. case chars.$SLASH:
  41. case chars.$TILDA:
  42. case chars.$PLUS:
  43. case chars.$GT:
  44. return true;
  45. default:
  46. return chars.isWhitespace(code);
  47. }
  48. }
  49. function getDelimFromCharacter(code) {
  50. switch (code) {
  51. case chars.$EOF:
  52. return EOF_DELIM_FLAG;
  53. case chars.$COMMA:
  54. return COMMA_DELIM_FLAG;
  55. case chars.$COLON:
  56. return COLON_DELIM_FLAG;
  57. case chars.$SEMICOLON:
  58. return SEMICOLON_DELIM_FLAG;
  59. case chars.$RBRACE:
  60. return RBRACE_DELIM_FLAG;
  61. case chars.$LBRACE:
  62. return LBRACE_DELIM_FLAG;
  63. case chars.$RPAREN:
  64. return RPAREN_DELIM_FLAG;
  65. case chars.$SPACE:
  66. case chars.$TAB:
  67. return SPACE_DELIM_FLAG;
  68. default:
  69. return cssLexer_1.isNewline(code) ? NEWLINE_DELIM_FLAG : 0;
  70. }
  71. }
  72. function characterContainsDelimiter(code, delimiters) {
  73. return (getDelimFromCharacter(code) & delimiters) > 0;
  74. }
  75. var ParsedCssResult = (function () {
  76. function ParsedCssResult(errors, ast) {
  77. this.errors = errors;
  78. this.ast = ast;
  79. }
  80. return ParsedCssResult;
  81. }());
  82. exports.ParsedCssResult = ParsedCssResult;
  83. var CssParser = (function () {
  84. function CssParser() {
  85. this._errors = [];
  86. }
  87. CssParser.prototype.parse = function (css, url) {
  88. var lexer = new cssLexer_1.CssLexer();
  89. this._file = new parseUtil_1.ParseSourceFile(css, url);
  90. this._scanner = lexer.scan(css, false);
  91. var ast = this._parseStyleSheet(EOF_DELIM_FLAG);
  92. var errors = this._errors;
  93. this._errors = [];
  94. var result = new ParsedCssResult(errors, ast);
  95. this._file = null;
  96. this._scanner = null;
  97. return result;
  98. };
  99. CssParser.prototype._parseStyleSheet = function (delimiters) {
  100. var results = [];
  101. this._scanner.consumeEmptyStatements();
  102. while (this._scanner.peek != chars.$EOF) {
  103. this._scanner.setMode(cssLexer_1.CssLexerMode.BLOCK);
  104. results.push(this._parseRule(delimiters));
  105. }
  106. var span = null;
  107. if (results.length > 0) {
  108. var firstRule = results[0];
  109. span = this._generateSourceSpan(firstRule, this._lastToken);
  110. }
  111. return new cssAst_1.CssStyleSheetAst(span, results);
  112. };
  113. CssParser.prototype._getSourceContent = function () {
  114. return this._scanner != null ? this._scanner.input : '';
  115. };
  116. CssParser.prototype._extractSourceContent = function (start, end) {
  117. return this._getSourceContent().substring(start, end + 1);
  118. };
  119. CssParser.prototype._generateSourceSpan = function (start, end) {
  120. if (end === void 0) { end = null; }
  121. var startLoc;
  122. if (start instanceof cssAst_1.CssAst) {
  123. startLoc = start.location.start;
  124. }
  125. else {
  126. var token = start;
  127. if (token == null) {
  128. token = this._lastToken;
  129. }
  130. startLoc = new parseUtil_1.ParseLocation(this._file, token.index, token.line, token.column);
  131. }
  132. if (end == null) {
  133. end = this._lastToken;
  134. }
  135. var endLine = -1;
  136. var endColumn = -1;
  137. var endIndex = -1;
  138. if (end instanceof cssAst_1.CssAst) {
  139. endLine = end.location.end.line;
  140. endColumn = end.location.end.col;
  141. endIndex = end.location.end.offset;
  142. }
  143. else if (end instanceof cssLexer_1.CssToken) {
  144. endLine = end.line;
  145. endColumn = end.column;
  146. endIndex = end.index;
  147. }
  148. var endLoc = new parseUtil_1.ParseLocation(this._file, endIndex, endLine, endColumn);
  149. return new parseUtil_1.ParseSourceSpan(startLoc, endLoc);
  150. };
  151. CssParser.prototype._resolveBlockType = function (token) {
  152. switch (token.strValue) {
  153. case '@-o-keyframes':
  154. case '@-moz-keyframes':
  155. case '@-webkit-keyframes':
  156. case '@keyframes':
  157. return cssAst_1.BlockType.Keyframes;
  158. case '@charset':
  159. return cssAst_1.BlockType.Charset;
  160. case '@import':
  161. return cssAst_1.BlockType.Import;
  162. case '@namespace':
  163. return cssAst_1.BlockType.Namespace;
  164. case '@page':
  165. return cssAst_1.BlockType.Page;
  166. case '@document':
  167. return cssAst_1.BlockType.Document;
  168. case '@media':
  169. return cssAst_1.BlockType.MediaQuery;
  170. case '@font-face':
  171. return cssAst_1.BlockType.FontFace;
  172. case '@viewport':
  173. return cssAst_1.BlockType.Viewport;
  174. case '@supports':
  175. return cssAst_1.BlockType.Supports;
  176. default:
  177. return cssAst_1.BlockType.Unsupported;
  178. }
  179. };
  180. CssParser.prototype._parseRule = function (delimiters) {
  181. if (this._scanner.peek == chars.$AT) {
  182. return this._parseAtRule(delimiters);
  183. }
  184. return this._parseSelectorRule(delimiters);
  185. };
  186. CssParser.prototype._parseAtRule = function (delimiters) {
  187. var start = this._getScannerIndex();
  188. this._scanner.setMode(cssLexer_1.CssLexerMode.BLOCK);
  189. var token = this._scan();
  190. var startToken = token;
  191. this._assertCondition(token.type == cssLexer_1.CssTokenType.AtKeyword, "The CSS Rule " + token.strValue + " is not a valid [@] rule.", token);
  192. var block;
  193. var type = this._resolveBlockType(token);
  194. var span;
  195. var tokens;
  196. var endToken;
  197. var end;
  198. var strValue;
  199. var query;
  200. switch (type) {
  201. case cssAst_1.BlockType.Charset:
  202. case cssAst_1.BlockType.Namespace:
  203. case cssAst_1.BlockType.Import:
  204. var value = this._parseValue(delimiters);
  205. this._scanner.setMode(cssLexer_1.CssLexerMode.BLOCK);
  206. this._scanner.consumeEmptyStatements();
  207. span = this._generateSourceSpan(startToken, value);
  208. return new cssAst_1.CssInlineRuleAst(span, type, value);
  209. case cssAst_1.BlockType.Viewport:
  210. case cssAst_1.BlockType.FontFace:
  211. block = this._parseStyleBlock(delimiters);
  212. span = this._generateSourceSpan(startToken, block);
  213. return new cssAst_1.CssBlockRuleAst(span, type, block);
  214. case cssAst_1.BlockType.Keyframes:
  215. tokens = this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG);
  216. var name_1 = tokens[0];
  217. block = this._parseKeyframeBlock(delimiters);
  218. span = this._generateSourceSpan(startToken, block);
  219. return new cssAst_1.CssKeyframeRuleAst(span, name_1, block);
  220. case cssAst_1.BlockType.MediaQuery:
  221. this._scanner.setMode(cssLexer_1.CssLexerMode.MEDIA_QUERY);
  222. tokens = this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG);
  223. endToken = tokens[tokens.length - 1];
  224. end = endToken.index + endToken.strValue.length - 1;
  225. strValue = this._extractSourceContent(start, end);
  226. span = this._generateSourceSpan(startToken, endToken);
  227. query = new cssAst_1.CssAtRulePredicateAst(span, strValue, tokens);
  228. block = this._parseBlock(delimiters);
  229. strValue = this._extractSourceContent(start, this._getScannerIndex() - 1);
  230. span = this._generateSourceSpan(startToken, block);
  231. return new cssAst_1.CssMediaQueryRuleAst(span, strValue, query, block);
  232. case cssAst_1.BlockType.Document:
  233. case cssAst_1.BlockType.Supports:
  234. case cssAst_1.BlockType.Page:
  235. this._scanner.setMode(cssLexer_1.CssLexerMode.AT_RULE_QUERY);
  236. tokens = this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG);
  237. endToken = tokens[tokens.length - 1];
  238. end = endToken.index + endToken.strValue.length - 1;
  239. strValue = this._extractSourceContent(start, end);
  240. span = this._generateSourceSpan(startToken, tokens[tokens.length - 1]);
  241. query = new cssAst_1.CssAtRulePredicateAst(span, strValue, tokens);
  242. block = this._parseBlock(delimiters);
  243. strValue = this._extractSourceContent(start, block.end.offset);
  244. span = this._generateSourceSpan(startToken, block);
  245. return new cssAst_1.CssBlockDefinitionRuleAst(span, strValue, type, query, block);
  246. default:
  247. var listOfTokens_1 = [];
  248. var tokenName = token.strValue;
  249. this._scanner.setMode(cssLexer_1.CssLexerMode.ALL);
  250. this._error(cssLexer_1.generateErrorMessage(this._getSourceContent(), "The CSS \"at\" rule \"" + tokenName + "\" is not allowed to used here", token.strValue, token.index, token.line, token.column), token);
  251. this._collectUntilDelim(delimiters | LBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG).forEach(function (token) {
  252. listOfTokens_1.push(token);
  253. });
  254. if (this._scanner.peek == chars.$LBRACE) {
  255. listOfTokens_1.push(this._consume(cssLexer_1.CssTokenType.Character, '{'));
  256. this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG).forEach(function (token) {
  257. listOfTokens_1.push(token);
  258. });
  259. listOfTokens_1.push(this._consume(cssLexer_1.CssTokenType.Character, '}'));
  260. }
  261. endToken = listOfTokens_1[listOfTokens_1.length - 1];
  262. span = this._generateSourceSpan(startToken, endToken);
  263. return new cssAst_1.CssUnknownRuleAst(span, tokenName, listOfTokens_1);
  264. }
  265. };
  266. CssParser.prototype._parseSelectorRule = function (delimiters) {
  267. var start = this._getScannerIndex();
  268. var selectors = this._parseSelectors(delimiters);
  269. var block = this._parseStyleBlock(delimiters);
  270. var ruleAst;
  271. var span;
  272. var startSelector = selectors[0];
  273. if (block != null) {
  274. span = this._generateSourceSpan(startSelector, block);
  275. ruleAst = new cssAst_1.CssSelectorRuleAst(span, selectors, block);
  276. }
  277. else {
  278. var name_2 = this._extractSourceContent(start, this._getScannerIndex() - 1);
  279. var innerTokens_1 = [];
  280. selectors.forEach(function (selector) {
  281. selector.selectorParts.forEach(function (part) {
  282. part.tokens.forEach(function (token) {
  283. innerTokens_1.push(token);
  284. });
  285. });
  286. });
  287. var endToken = innerTokens_1[innerTokens_1.length - 1];
  288. span = this._generateSourceSpan(startSelector, endToken);
  289. ruleAst = new cssAst_1.CssUnknownTokenListAst(span, name_2, innerTokens_1);
  290. }
  291. this._scanner.setMode(cssLexer_1.CssLexerMode.BLOCK);
  292. this._scanner.consumeEmptyStatements();
  293. return ruleAst;
  294. };
  295. CssParser.prototype._parseSelectors = function (delimiters) {
  296. delimiters |= LBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG;
  297. var selectors = [];
  298. var isParsingSelectors = true;
  299. while (isParsingSelectors) {
  300. selectors.push(this._parseSelector(delimiters));
  301. isParsingSelectors = !characterContainsDelimiter(this._scanner.peek, delimiters);
  302. if (isParsingSelectors) {
  303. this._consume(cssLexer_1.CssTokenType.Character, ',');
  304. isParsingSelectors = !characterContainsDelimiter(this._scanner.peek, delimiters);
  305. if (isParsingSelectors) {
  306. this._scanner.consumeWhitespace();
  307. }
  308. }
  309. }
  310. return selectors;
  311. };
  312. CssParser.prototype._scan = function () {
  313. var output = this._scanner.scan();
  314. var token = output.token;
  315. var error = output.error;
  316. if (error != null) {
  317. this._error(cssLexer_1.getRawMessage(error), token);
  318. }
  319. this._lastToken = token;
  320. return token;
  321. };
  322. CssParser.prototype._getScannerIndex = function () {
  323. return this._scanner.index;
  324. };
  325. CssParser.prototype._consume = function (type, value) {
  326. if (value === void 0) { value = null; }
  327. var output = this._scanner.consume(type, value);
  328. var token = output.token;
  329. var error = output.error;
  330. if (error != null) {
  331. this._error(cssLexer_1.getRawMessage(error), token);
  332. }
  333. this._lastToken = token;
  334. return token;
  335. };
  336. CssParser.prototype._parseKeyframeBlock = function (delimiters) {
  337. delimiters |= RBRACE_DELIM_FLAG;
  338. this._scanner.setMode(cssLexer_1.CssLexerMode.KEYFRAME_BLOCK);
  339. var startToken = this._consume(cssLexer_1.CssTokenType.Character, '{');
  340. var definitions = [];
  341. while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
  342. definitions.push(this._parseKeyframeDefinition(delimiters));
  343. }
  344. var endToken = this._consume(cssLexer_1.CssTokenType.Character, '}');
  345. var span = this._generateSourceSpan(startToken, endToken);
  346. return new cssAst_1.CssBlockAst(span, definitions);
  347. };
  348. CssParser.prototype._parseKeyframeDefinition = function (delimiters) {
  349. var stepTokens = [];
  350. delimiters |= LBRACE_DELIM_FLAG;
  351. while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
  352. stepTokens.push(this._parseKeyframeLabel(delimiters | COMMA_DELIM_FLAG));
  353. if (this._scanner.peek != chars.$LBRACE) {
  354. this._consume(cssLexer_1.CssTokenType.Character, ',');
  355. }
  356. }
  357. var stylesBlock = this._parseStyleBlock(delimiters | RBRACE_DELIM_FLAG);
  358. var span = this._generateSourceSpan(stepTokens[0], stylesBlock);
  359. var ast = new cssAst_1.CssKeyframeDefinitionAst(span, stepTokens, stylesBlock);
  360. this._scanner.setMode(cssLexer_1.CssLexerMode.BLOCK);
  361. return ast;
  362. };
  363. CssParser.prototype._parseKeyframeLabel = function (delimiters) {
  364. this._scanner.setMode(cssLexer_1.CssLexerMode.KEYFRAME_BLOCK);
  365. return cssAst_1.mergeTokens(this._collectUntilDelim(delimiters));
  366. };
  367. CssParser.prototype._parsePseudoSelector = function (delimiters) {
  368. var start = this._getScannerIndex();
  369. delimiters &= ~COMMA_DELIM_FLAG;
  370. var startingDelims = delimiters;
  371. var startToken = this._consume(cssLexer_1.CssTokenType.Character, ':');
  372. var tokens = [startToken];
  373. if (this._scanner.peek == chars.$COLON) {
  374. tokens.push(this._consume(cssLexer_1.CssTokenType.Character, ':'));
  375. }
  376. var innerSelectors = [];
  377. this._scanner.setMode(cssLexer_1.CssLexerMode.PSEUDO_SELECTOR);
  378. var pseudoSelectorToken = this._consume(cssLexer_1.CssTokenType.Identifier);
  379. var pseudoSelectorName = pseudoSelectorToken.strValue;
  380. tokens.push(pseudoSelectorToken);
  381. if (this._scanner.peek == chars.$LPAREN) {
  382. this._scanner.setMode(cssLexer_1.CssLexerMode.PSEUDO_SELECTOR_WITH_ARGUMENTS);
  383. var openParenToken = this._consume(cssLexer_1.CssTokenType.Character, '(');
  384. tokens.push(openParenToken);
  385. if (_pseudoSelectorSupportsInnerSelectors(pseudoSelectorName)) {
  386. var innerDelims = startingDelims | LPAREN_DELIM_FLAG | RPAREN_DELIM_FLAG;
  387. if (pseudoSelectorName == 'not') {
  388. innerDelims |= COMMA_DELIM_FLAG;
  389. }
  390. this._parseSelectors(innerDelims).forEach(function (selector) {
  391. innerSelectors.push(selector);
  392. });
  393. }
  394. else {
  395. var innerValueDelims = delimiters | LBRACE_DELIM_FLAG | COLON_DELIM_FLAG | RPAREN_DELIM_FLAG | LPAREN_DELIM_FLAG;
  396. while (!characterContainsDelimiter(this._scanner.peek, innerValueDelims)) {
  397. var token = this._scan();
  398. tokens.push(token);
  399. }
  400. }
  401. var closeParenToken = this._consume(cssLexer_1.CssTokenType.Character, ')');
  402. tokens.push(closeParenToken);
  403. }
  404. var end = this._getScannerIndex() - 1;
  405. var strValue = this._extractSourceContent(start, end);
  406. var endToken = tokens[tokens.length - 1];
  407. var span = this._generateSourceSpan(startToken, endToken);
  408. return new cssAst_1.CssPseudoSelectorAst(span, strValue, pseudoSelectorName, tokens, innerSelectors);
  409. };
  410. CssParser.prototype._parseSimpleSelector = function (delimiters) {
  411. var start = this._getScannerIndex();
  412. delimiters |= COMMA_DELIM_FLAG;
  413. this._scanner.setMode(cssLexer_1.CssLexerMode.SELECTOR);
  414. var selectorCssTokens = [];
  415. var pseudoSelectors = [];
  416. var previousToken = null;
  417. var selectorPartDelimiters = delimiters | SPACE_DELIM_FLAG;
  418. var loopOverSelector = !characterContainsDelimiter(this._scanner.peek, selectorPartDelimiters);
  419. var hasAttributeError = false;
  420. while (loopOverSelector) {
  421. var peek = this._scanner.peek;
  422. switch (peek) {
  423. case chars.$COLON:
  424. var innerPseudo = this._parsePseudoSelector(delimiters);
  425. pseudoSelectors.push(innerPseudo);
  426. this._scanner.setMode(cssLexer_1.CssLexerMode.SELECTOR);
  427. break;
  428. case chars.$LBRACKET:
  429. selectorCssTokens.push(this._scan());
  430. this._scanner.setMode(cssLexer_1.CssLexerMode.ATTRIBUTE_SELECTOR);
  431. break;
  432. case chars.$RBRACKET:
  433. if (this._scanner.getMode() != cssLexer_1.CssLexerMode.ATTRIBUTE_SELECTOR) {
  434. hasAttributeError = true;
  435. }
  436. this._scanner.setMode(cssLexer_1.CssLexerMode.SELECTOR);
  437. selectorCssTokens.push(this._scan());
  438. break;
  439. default:
  440. if (isSelectorOperatorCharacter(peek)) {
  441. loopOverSelector = false;
  442. continue;
  443. }
  444. var token = this._scan();
  445. previousToken = token;
  446. selectorCssTokens.push(token);
  447. break;
  448. }
  449. loopOverSelector = !characterContainsDelimiter(this._scanner.peek, selectorPartDelimiters);
  450. }
  451. hasAttributeError = hasAttributeError || this._scanner.getMode() == cssLexer_1.CssLexerMode.ATTRIBUTE_SELECTOR;
  452. if (hasAttributeError) {
  453. this._error("Unbalanced CSS attribute selector at column " + previousToken.line + ":" + previousToken.column, previousToken);
  454. }
  455. var end = this._getScannerIndex() - 1;
  456. var operator = null;
  457. var operatorScanCount = 0;
  458. var lastOperatorToken = null;
  459. if (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
  460. while (operator == null &&
  461. !characterContainsDelimiter(this._scanner.peek, delimiters) &&
  462. isSelectorOperatorCharacter(this._scanner.peek)) {
  463. var token = this._scan();
  464. var tokenOperator = token.strValue;
  465. operatorScanCount++;
  466. lastOperatorToken = token;
  467. if (tokenOperator != SPACE_OPERATOR) {
  468. switch (tokenOperator) {
  469. case SLASH_CHARACTER:
  470. var deepToken = this._consume(cssLexer_1.CssTokenType.Identifier);
  471. var deepSlash = this._consume(cssLexer_1.CssTokenType.Character);
  472. var index = lastOperatorToken.index;
  473. var line = lastOperatorToken.line;
  474. var column = lastOperatorToken.column;
  475. if (deepToken != null && deepToken.strValue.toLowerCase() == 'deep' && deepSlash.strValue == SLASH_CHARACTER) {
  476. token = new cssLexer_1.CssToken(lastOperatorToken.index, lastOperatorToken.column, lastOperatorToken.line, cssLexer_1.CssTokenType.Identifier, DEEP_OPERATOR_STR);
  477. }
  478. else {
  479. var text = SLASH_CHARACTER + deepToken.strValue + deepSlash.strValue;
  480. this._error(cssLexer_1.generateErrorMessage(this._getSourceContent(), text + " is an invalid CSS operator", text, index, line, column), lastOperatorToken);
  481. token = new cssLexer_1.CssToken(index, column, line, cssLexer_1.CssTokenType.Invalid, text);
  482. }
  483. break;
  484. case GT_CHARACTER:
  485. if (this._scanner.peek == chars.$GT && this._scanner.peekPeek == chars.$GT) {
  486. this._consume(cssLexer_1.CssTokenType.Character, GT_CHARACTER);
  487. this._consume(cssLexer_1.CssTokenType.Character, GT_CHARACTER);
  488. token = new cssLexer_1.CssToken(lastOperatorToken.index, lastOperatorToken.column, lastOperatorToken.line, cssLexer_1.CssTokenType.Identifier, TRIPLE_GT_OPERATOR_STR);
  489. }
  490. break;
  491. }
  492. operator = token;
  493. }
  494. }
  495. if (operator != null) {
  496. end = operator.index;
  497. }
  498. }
  499. this._scanner.consumeWhitespace();
  500. var strValue = this._extractSourceContent(start, end);
  501. if (operator == null && operatorScanCount > 0 && this._scanner.peek != chars.$LBRACE) {
  502. operator = lastOperatorToken;
  503. }
  504. var startTokenOrAst = null;
  505. var endTokenOrAst = null;
  506. if (selectorCssTokens.length > 0) {
  507. startTokenOrAst = startTokenOrAst || selectorCssTokens[0];
  508. endTokenOrAst = selectorCssTokens[selectorCssTokens.length - 1];
  509. }
  510. if (pseudoSelectors.length > 0) {
  511. startTokenOrAst = startTokenOrAst || pseudoSelectors[0];
  512. endTokenOrAst = pseudoSelectors[pseudoSelectors.length - 1];
  513. }
  514. if (operator != null) {
  515. startTokenOrAst = startTokenOrAst || operator;
  516. endTokenOrAst = operator;
  517. }
  518. var span = this._generateSourceSpan(startTokenOrAst, endTokenOrAst);
  519. return new cssAst_1.CssSimpleSelectorAst(span, selectorCssTokens, strValue, pseudoSelectors, operator);
  520. };
  521. CssParser.prototype._parseSelector = function (delimiters) {
  522. delimiters |= COMMA_DELIM_FLAG;
  523. this._scanner.setMode(cssLexer_1.CssLexerMode.SELECTOR);
  524. var simpleSelectors = [];
  525. while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
  526. simpleSelectors.push(this._parseSimpleSelector(delimiters));
  527. this._scanner.consumeWhitespace();
  528. }
  529. var firstSelector = simpleSelectors[0];
  530. var lastSelector = simpleSelectors[simpleSelectors.length - 1];
  531. var span = this._generateSourceSpan(firstSelector, lastSelector);
  532. return new cssAst_1.CssSelectorAst(span, simpleSelectors);
  533. };
  534. CssParser.prototype._parseValue = function (delimiters) {
  535. delimiters |= RBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG | NEWLINE_DELIM_FLAG;
  536. this._scanner.setMode(cssLexer_1.CssLexerMode.STYLE_VALUE);
  537. var start = this._getScannerIndex();
  538. var tokens = [];
  539. var previous = null;
  540. while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
  541. var token = void 0;
  542. if (previous != null && previous.type == cssLexer_1.CssTokenType.Identifier && this._scanner.peek == chars.$LPAREN) {
  543. token = this._consume(cssLexer_1.CssTokenType.Character, '(');
  544. tokens.push(token);
  545. this._scanner.setMode(cssLexer_1.CssLexerMode.STYLE_VALUE_FUNCTION);
  546. token = this._scan();
  547. tokens.push(token);
  548. this._scanner.setMode(cssLexer_1.CssLexerMode.STYLE_VALUE);
  549. token = this._consume(cssLexer_1.CssTokenType.Character, ')');
  550. tokens.push(token);
  551. }
  552. else {
  553. token = this._scan();
  554. if (token.type != cssLexer_1.CssTokenType.Whitespace) {
  555. tokens.push(token);
  556. }
  557. }
  558. previous = token;
  559. }
  560. var end = this._getScannerIndex() - 1;
  561. this._scanner.consumeWhitespace();
  562. var code = this._scanner.peek;
  563. if (code == chars.$SEMICOLON) {
  564. this._consume(cssLexer_1.CssTokenType.Character, ';');
  565. }
  566. else if (code != chars.$RBRACE) {
  567. this._error(cssLexer_1.generateErrorMessage(this._getSourceContent(), "The CSS key/value definition did not end with a semicolon", previous.strValue, previous.index, previous.line, previous.column), previous);
  568. }
  569. var strValue = this._extractSourceContent(start, end);
  570. var startToken = tokens[0];
  571. var endToken = tokens[tokens.length - 1];
  572. var span = this._generateSourceSpan(startToken, endToken);
  573. return new cssAst_1.CssStyleValueAst(span, tokens, strValue);
  574. };
  575. CssParser.prototype._collectUntilDelim = function (delimiters, assertType) {
  576. if (assertType === void 0) { assertType = null; }
  577. var tokens = [];
  578. while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
  579. var val = assertType != null ? this._consume(assertType) : this._scan();
  580. tokens.push(val);
  581. }
  582. return tokens;
  583. };
  584. CssParser.prototype._parseBlock = function (delimiters) {
  585. delimiters |= RBRACE_DELIM_FLAG;
  586. this._scanner.setMode(cssLexer_1.CssLexerMode.BLOCK);
  587. var startToken = this._consume(cssLexer_1.CssTokenType.Character, '{');
  588. this._scanner.consumeEmptyStatements();
  589. var results = [];
  590. while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
  591. results.push(this._parseRule(delimiters));
  592. }
  593. var endToken = this._consume(cssLexer_1.CssTokenType.Character, '}');
  594. this._scanner.setMode(cssLexer_1.CssLexerMode.BLOCK);
  595. this._scanner.consumeEmptyStatements();
  596. var span = this._generateSourceSpan(startToken, endToken);
  597. return new cssAst_1.CssBlockAst(span, results);
  598. };
  599. CssParser.prototype._parseStyleBlock = function (delimiters) {
  600. delimiters |= RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG;
  601. this._scanner.setMode(cssLexer_1.CssLexerMode.STYLE_BLOCK);
  602. var startToken = this._consume(cssLexer_1.CssTokenType.Character, '{');
  603. if (startToken.numValue != chars.$LBRACE) {
  604. return null;
  605. }
  606. var definitions = [];
  607. this._scanner.consumeEmptyStatements();
  608. while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
  609. definitions.push(this._parseDefinition(delimiters));
  610. this._scanner.consumeEmptyStatements();
  611. }
  612. var endToken = this._consume(cssLexer_1.CssTokenType.Character, '}');
  613. this._scanner.setMode(cssLexer_1.CssLexerMode.STYLE_BLOCK);
  614. this._scanner.consumeEmptyStatements();
  615. var span = this._generateSourceSpan(startToken, endToken);
  616. return new cssAst_1.CssStylesBlockAst(span, definitions);
  617. };
  618. CssParser.prototype._parseDefinition = function (delimiters) {
  619. this._scanner.setMode(cssLexer_1.CssLexerMode.STYLE_BLOCK);
  620. var prop = this._consume(cssLexer_1.CssTokenType.Identifier);
  621. var parseValue = false;
  622. var value = null;
  623. var endToken = prop;
  624. switch (this._scanner.peek) {
  625. case chars.$SEMICOLON:
  626. case chars.$RBRACE:
  627. case chars.$EOF:
  628. parseValue = false;
  629. break;
  630. default:
  631. var propStr_1 = [prop.strValue];
  632. if (this._scanner.peek != chars.$COLON) {
  633. var nextValue = this._consume(cssLexer_1.CssTokenType.Character, ':');
  634. propStr_1.push(nextValue.strValue);
  635. var remainingTokens = this._collectUntilDelim(delimiters | COLON_DELIM_FLAG | SEMICOLON_DELIM_FLAG, cssLexer_1.CssTokenType.Identifier);
  636. if (remainingTokens.length > 0) {
  637. remainingTokens.forEach(function (token) {
  638. propStr_1.push(token.strValue);
  639. });
  640. }
  641. endToken = prop = new cssLexer_1.CssToken(prop.index, prop.column, prop.line, prop.type, propStr_1.join(' '));
  642. }
  643. if (this._scanner.peek == chars.$COLON) {
  644. this._consume(cssLexer_1.CssTokenType.Character, ':');
  645. parseValue = true;
  646. }
  647. break;
  648. }
  649. if (parseValue) {
  650. value = this._parseValue(delimiters);
  651. endToken = value;
  652. }
  653. else {
  654. this._error(cssLexer_1.generateErrorMessage(this._getSourceContent(), "The CSS property was not paired with a style value", prop.strValue, prop.index, prop.line, prop.column), prop);
  655. }
  656. var span = this._generateSourceSpan(prop, endToken);
  657. return new cssAst_1.CssDefinitionAst(span, prop, value);
  658. };
  659. CssParser.prototype._assertCondition = function (status, errorMessage, problemToken) {
  660. if (!status) {
  661. this._error(errorMessage, problemToken);
  662. return true;
  663. }
  664. return false;
  665. };
  666. CssParser.prototype._error = function (message, problemToken) {
  667. var length = problemToken.strValue.length;
  668. var error = CssParseError.create(this._file, 0, problemToken.line, problemToken.column, length, message);
  669. this._errors.push(error);
  670. };
  671. return CssParser;
  672. }());
  673. exports.CssParser = CssParser;
  674. var CssParseError = (function (_super) {
  675. __extends(CssParseError, _super);
  676. function CssParseError(span, message) {
  677. return _super.call(this, span, message) || this;
  678. }
  679. CssParseError.create = function (file, offset, line, col, length, errMsg) {
  680. var start = new parseUtil_1.ParseLocation(file, offset, line, col);
  681. var end = new parseUtil_1.ParseLocation(file, offset, line, col + length);
  682. var span = new parseUtil_1.ParseSourceSpan(start, end);
  683. return new CssParseError(span, 'CSS Parse Error: ' + errMsg);
  684. };
  685. return CssParseError;
  686. }(parseUtil_1.ParseError));
  687. exports.CssParseError = CssParseError;