PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Tour of Heroes/node_modules/@angular/compiler/src/selector.js

https://gitlab.com/courses-online/Angular2
JavaScript | 339 lines | 303 code | 0 blank | 36 comment | 77 complexity | 0c800c81e2cdc3b229b559a896396190 MD5 | raw file
  1. /**
  2. * @license
  3. * Copyright Google Inc. All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. import { getHtmlTagDefinition } from './ml_parser/html_tags';
  9. var _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' +
  10. '([-\\w]+)|' +
  11. '(?:\\.([-\\w]+))|' +
  12. '(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])|' +
  13. '(\\))|' +
  14. '(\\s*,\\s*)', // ","
  15. 'g');
  16. /**
  17. * A css selector contains an element name,
  18. * css classes and attribute/value pairs with the purpose
  19. * of selecting subsets out of them.
  20. */
  21. export var CssSelector = (function () {
  22. function CssSelector() {
  23. this.element = null;
  24. this.classNames = [];
  25. this.attrs = [];
  26. this.notSelectors = [];
  27. }
  28. CssSelector.parse = function (selector) {
  29. var results = [];
  30. var _addResult = function (res, cssSel) {
  31. if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 &&
  32. cssSel.attrs.length == 0) {
  33. cssSel.element = '*';
  34. }
  35. res.push(cssSel);
  36. };
  37. var cssSelector = new CssSelector();
  38. var match;
  39. var current = cssSelector;
  40. var inNot = false;
  41. _SELECTOR_REGEXP.lastIndex = 0;
  42. while (match = _SELECTOR_REGEXP.exec(selector)) {
  43. if (match[1]) {
  44. if (inNot) {
  45. throw new Error('Nesting :not is not allowed in a selector');
  46. }
  47. inNot = true;
  48. current = new CssSelector();
  49. cssSelector.notSelectors.push(current);
  50. }
  51. if (match[2]) {
  52. current.setElement(match[2]);
  53. }
  54. if (match[3]) {
  55. current.addClassName(match[3]);
  56. }
  57. if (match[4]) {
  58. current.addAttribute(match[4], match[5]);
  59. }
  60. if (match[6]) {
  61. inNot = false;
  62. current = cssSelector;
  63. }
  64. if (match[7]) {
  65. if (inNot) {
  66. throw new Error('Multiple selectors in :not are not supported');
  67. }
  68. _addResult(results, cssSelector);
  69. cssSelector = current = new CssSelector();
  70. }
  71. }
  72. _addResult(results, cssSelector);
  73. return results;
  74. };
  75. CssSelector.prototype.isElementSelector = function () {
  76. return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 &&
  77. this.notSelectors.length === 0;
  78. };
  79. CssSelector.prototype.hasElementSelector = function () { return !!this.element; };
  80. CssSelector.prototype.setElement = function (element) {
  81. if (element === void 0) { element = null; }
  82. this.element = element;
  83. };
  84. /** Gets a template string for an element that matches the selector. */
  85. CssSelector.prototype.getMatchingElementTemplate = function () {
  86. var tagName = this.element || 'div';
  87. var classAttr = this.classNames.length > 0 ? " class=\"" + this.classNames.join(' ') + "\"" : '';
  88. var attrs = '';
  89. for (var i = 0; i < this.attrs.length; i += 2) {
  90. var attrName = this.attrs[i];
  91. var attrValue = this.attrs[i + 1] !== '' ? "=\"" + this.attrs[i + 1] + "\"" : '';
  92. attrs += " " + attrName + attrValue;
  93. }
  94. return getHtmlTagDefinition(tagName).isVoid ? "<" + tagName + classAttr + attrs + "/>" :
  95. "<" + tagName + classAttr + attrs + "></" + tagName + ">";
  96. };
  97. CssSelector.prototype.addAttribute = function (name, value) {
  98. if (value === void 0) { value = ''; }
  99. this.attrs.push(name, value && value.toLowerCase() || '');
  100. };
  101. CssSelector.prototype.addClassName = function (name) { this.classNames.push(name.toLowerCase()); };
  102. CssSelector.prototype.toString = function () {
  103. var res = this.element || '';
  104. if (this.classNames) {
  105. this.classNames.forEach(function (klass) { return res += "." + klass; });
  106. }
  107. if (this.attrs) {
  108. for (var i = 0; i < this.attrs.length; i += 2) {
  109. var name_1 = this.attrs[i];
  110. var value = this.attrs[i + 1];
  111. res += "[" + name_1 + (value ? '=' + value : '') + "]";
  112. }
  113. }
  114. this.notSelectors.forEach(function (notSelector) { return res += ":not(" + notSelector + ")"; });
  115. return res;
  116. };
  117. return CssSelector;
  118. }());
  119. /**
  120. * Reads a list of CssSelectors and allows to calculate which ones
  121. * are contained in a given CssSelector.
  122. */
  123. export var SelectorMatcher = (function () {
  124. function SelectorMatcher() {
  125. this._elementMap = {};
  126. this._elementPartialMap = {};
  127. this._classMap = {};
  128. this._classPartialMap = {};
  129. this._attrValueMap = {};
  130. this._attrValuePartialMap = {};
  131. this._listContexts = [];
  132. }
  133. SelectorMatcher.createNotMatcher = function (notSelectors) {
  134. var notMatcher = new SelectorMatcher();
  135. notMatcher.addSelectables(notSelectors, null);
  136. return notMatcher;
  137. };
  138. SelectorMatcher.prototype.addSelectables = function (cssSelectors, callbackCtxt) {
  139. var listContext = null;
  140. if (cssSelectors.length > 1) {
  141. listContext = new SelectorListContext(cssSelectors);
  142. this._listContexts.push(listContext);
  143. }
  144. for (var i = 0; i < cssSelectors.length; i++) {
  145. this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
  146. }
  147. };
  148. /**
  149. * Add an object that can be found later on by calling `match`.
  150. * @param cssSelector A css selector
  151. * @param callbackCtxt An opaque object that will be given to the callback of the `match` function
  152. */
  153. SelectorMatcher.prototype._addSelectable = function (cssSelector, callbackCtxt, listContext) {
  154. var matcher = this;
  155. var element = cssSelector.element;
  156. var classNames = cssSelector.classNames;
  157. var attrs = cssSelector.attrs;
  158. var selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
  159. if (element) {
  160. var isTerminal = attrs.length === 0 && classNames.length === 0;
  161. if (isTerminal) {
  162. this._addTerminal(matcher._elementMap, element, selectable);
  163. }
  164. else {
  165. matcher = this._addPartial(matcher._elementPartialMap, element);
  166. }
  167. }
  168. if (classNames) {
  169. for (var i = 0; i < classNames.length; i++) {
  170. var isTerminal = attrs.length === 0 && i === classNames.length - 1;
  171. var className = classNames[i];
  172. if (isTerminal) {
  173. this._addTerminal(matcher._classMap, className, selectable);
  174. }
  175. else {
  176. matcher = this._addPartial(matcher._classPartialMap, className);
  177. }
  178. }
  179. }
  180. if (attrs) {
  181. for (var i = 0; i < attrs.length; i += 2) {
  182. var isTerminal = i === attrs.length - 2;
  183. var name_2 = attrs[i];
  184. var value = attrs[i + 1];
  185. if (isTerminal) {
  186. var terminalMap = matcher._attrValueMap;
  187. var terminalValuesMap = terminalMap[name_2];
  188. if (!terminalValuesMap) {
  189. terminalValuesMap = {};
  190. terminalMap[name_2] = terminalValuesMap;
  191. }
  192. this._addTerminal(terminalValuesMap, value, selectable);
  193. }
  194. else {
  195. var partialMap = matcher._attrValuePartialMap;
  196. var partialValuesMap = partialMap[name_2];
  197. if (!partialValuesMap) {
  198. partialValuesMap = {};
  199. partialMap[name_2] = partialValuesMap;
  200. }
  201. matcher = this._addPartial(partialValuesMap, value);
  202. }
  203. }
  204. }
  205. };
  206. SelectorMatcher.prototype._addTerminal = function (map, name, selectable) {
  207. var terminalList = map[name];
  208. if (!terminalList) {
  209. terminalList = [];
  210. map[name] = terminalList;
  211. }
  212. terminalList.push(selectable);
  213. };
  214. SelectorMatcher.prototype._addPartial = function (map, name) {
  215. var matcher = map[name];
  216. if (!matcher) {
  217. matcher = new SelectorMatcher();
  218. map[name] = matcher;
  219. }
  220. return matcher;
  221. };
  222. /**
  223. * Find the objects that have been added via `addSelectable`
  224. * whose css selector is contained in the given css selector.
  225. * @param cssSelector A css selector
  226. * @param matchedCallback This callback will be called with the object handed into `addSelectable`
  227. * @return boolean true if a match was found
  228. */
  229. SelectorMatcher.prototype.match = function (cssSelector, matchedCallback) {
  230. var result = false;
  231. var element = cssSelector.element;
  232. var classNames = cssSelector.classNames;
  233. var attrs = cssSelector.attrs;
  234. for (var i = 0; i < this._listContexts.length; i++) {
  235. this._listContexts[i].alreadyMatched = false;
  236. }
  237. result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
  238. result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) ||
  239. result;
  240. if (classNames) {
  241. for (var i = 0; i < classNames.length; i++) {
  242. var className = classNames[i];
  243. result =
  244. this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
  245. result =
  246. this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) ||
  247. result;
  248. }
  249. }
  250. if (attrs) {
  251. for (var i = 0; i < attrs.length; i += 2) {
  252. var name_3 = attrs[i];
  253. var value = attrs[i + 1];
  254. var terminalValuesMap = this._attrValueMap[name_3];
  255. if (value) {
  256. result =
  257. this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
  258. }
  259. result =
  260. this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
  261. var partialValuesMap = this._attrValuePartialMap[name_3];
  262. if (value) {
  263. result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
  264. }
  265. result =
  266. this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
  267. }
  268. }
  269. return result;
  270. };
  271. /** @internal */
  272. SelectorMatcher.prototype._matchTerminal = function (map, name, cssSelector, matchedCallback) {
  273. if (!map || typeof name !== 'string') {
  274. return false;
  275. }
  276. var selectables = map[name];
  277. var starSelectables = map['*'];
  278. if (starSelectables) {
  279. selectables = selectables.concat(starSelectables);
  280. }
  281. if (!selectables) {
  282. return false;
  283. }
  284. var selectable;
  285. var result = false;
  286. for (var i = 0; i < selectables.length; i++) {
  287. selectable = selectables[i];
  288. result = selectable.finalize(cssSelector, matchedCallback) || result;
  289. }
  290. return result;
  291. };
  292. /** @internal */
  293. SelectorMatcher.prototype._matchPartial = function (map, name, cssSelector, matchedCallback) {
  294. if (!map || typeof name !== 'string') {
  295. return false;
  296. }
  297. var nestedSelector = map[name];
  298. if (!nestedSelector) {
  299. return false;
  300. }
  301. // TODO(perf): get rid of recursion and measure again
  302. // TODO(perf): don't pass the whole selector into the recursion,
  303. // but only the not processed parts
  304. return nestedSelector.match(cssSelector, matchedCallback);
  305. };
  306. return SelectorMatcher;
  307. }());
  308. export var SelectorListContext = (function () {
  309. function SelectorListContext(selectors) {
  310. this.selectors = selectors;
  311. this.alreadyMatched = false;
  312. }
  313. return SelectorListContext;
  314. }());
  315. // Store context to pass back selector and context when a selector is matched
  316. export var SelectorContext = (function () {
  317. function SelectorContext(selector, cbContext, listContext) {
  318. this.selector = selector;
  319. this.cbContext = cbContext;
  320. this.listContext = listContext;
  321. this.notSelectors = selector.notSelectors;
  322. }
  323. SelectorContext.prototype.finalize = function (cssSelector, callback) {
  324. var result = true;
  325. if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
  326. var notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
  327. result = !notMatcher.match(cssSelector, null);
  328. }
  329. if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
  330. if (this.listContext) {
  331. this.listContext.alreadyMatched = true;
  332. }
  333. callback(this.selector, this.cbContext);
  334. }
  335. return result;
  336. };
  337. return SelectorContext;
  338. }());
  339. //# sourceMappingURL=selector.js.map