diff --git a/packages/upgrade/src/common/compiler_helpers/README.md b/packages/upgrade/src/common/compiler_helpers/README.md deleted file mode 100644 index 7c53b572b3..0000000000 --- a/packages/upgrade/src/common/compiler_helpers/README.md +++ /dev/null @@ -1,7 +0,0 @@ -The following code has been copied from the Angular compiler to be used in the upgrade library without -the need to import the entire compiler: - -* `selector.ts` -* `ml_parser/html_tags.ts` -* `ml_parser/tags.ts` -* `createElementCssSelector.ts` \ No newline at end of file diff --git a/packages/upgrade/src/common/compiler_helpers/createElementCssSelector.ts b/packages/upgrade/src/common/compiler_helpers/createElementCssSelector.ts deleted file mode 100644 index e964ac5b27..0000000000 --- a/packages/upgrade/src/common/compiler_helpers/createElementCssSelector.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {CssSelector} from './selector'; - -/* - * The following items are copied from the Angular Compiler to be used here - * without the need to import the entire compiler into the build - */ - -const CLASS_ATTR = 'class'; - -export function createElementCssSelector( - elementName: string, attributes: [string, string][]): CssSelector { - const cssSelector = new CssSelector(); - const elNameNoNs = splitNsName(elementName)[1]; - - cssSelector.setElement(elNameNoNs); - - for (let i = 0; i < attributes.length; i++) { - const attrName = attributes[i][0]; - const attrNameNoNs = splitNsName(attrName)[1]; - const attrValue = attributes[i][1]; - - cssSelector.addAttribute(attrNameNoNs, attrValue); - if (attrName.toLowerCase() == CLASS_ATTR) { - const classes = splitClasses(attrValue); - classes.forEach(className => cssSelector.addClassName(className)); - } - } - return cssSelector; -} - -export function splitNsName(elementName: string): [string, string] { - if (elementName[0] != ':') { - return [null, elementName]; - } - - const colonIndex = elementName.indexOf(':', 1); - - if (colonIndex == -1) { - throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`); - } - - return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)]; -} - -export function splitClasses(classAttrValue: string): string[] { - return classAttrValue.trim().split(/\s+/g); -} diff --git a/packages/upgrade/src/common/compiler_helpers/ml_parser/html_tags.ts b/packages/upgrade/src/common/compiler_helpers/ml_parser/html_tags.ts deleted file mode 100644 index 65a3cc8f6b..0000000000 --- a/packages/upgrade/src/common/compiler_helpers/ml_parser/html_tags.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {TagContentType, TagDefinition} from './tags'; - -export class HtmlTagDefinition implements TagDefinition { - private closedByChildren: {[key: string]: boolean} = {}; - - closedByParent: boolean = false; - requiredParents: {[key: string]: boolean}; - parentToAdd: string; - implicitNamespacePrefix: string; - contentType: TagContentType; - isVoid: boolean; - ignoreFirstLf: boolean; - canSelfClose: boolean = false; - - constructor( - {closedByChildren, requiredParents, implicitNamespacePrefix, - contentType = TagContentType.PARSABLE_DATA, closedByParent = false, isVoid = false, - ignoreFirstLf = false}: { - closedByChildren?: string[], - closedByParent?: boolean, - requiredParents?: string[], - implicitNamespacePrefix?: string, - contentType?: TagContentType, - isVoid?: boolean, - ignoreFirstLf?: boolean - } = {}) { - if (closedByChildren && closedByChildren.length > 0) { - closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true); - } - this.isVoid = isVoid; - this.closedByParent = closedByParent || isVoid; - if (requiredParents && requiredParents.length > 0) { - this.requiredParents = {}; - // The first parent is the list is automatically when none of the listed parents are present - this.parentToAdd = requiredParents[0]; - requiredParents.forEach(tagName => this.requiredParents[tagName] = true); - } - this.implicitNamespacePrefix = implicitNamespacePrefix; - this.contentType = contentType; - this.ignoreFirstLf = ignoreFirstLf; - } - - requireExtraParent(currentParent: string): boolean { - if (!this.requiredParents) { - return false; - } - - if (!currentParent) { - return true; - } - - const lcParent = currentParent.toLowerCase(); - const isParentTemplate = lcParent === 'template' || currentParent === 'ng-template'; - return !isParentTemplate && this.requiredParents[lcParent] != true; - } - - isClosedByChild(name: string): boolean { - return this.isVoid || name.toLowerCase() in this.closedByChildren; - } -} - -// see http://www.w3.org/TR/html51/syntax.html#optional-tags -// This implementation does not fully conform to the HTML5 spec. -const TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = { - 'base': new HtmlTagDefinition({isVoid: true}), - 'meta': new HtmlTagDefinition({isVoid: true}), - 'area': new HtmlTagDefinition({isVoid: true}), - 'embed': new HtmlTagDefinition({isVoid: true}), - 'link': new HtmlTagDefinition({isVoid: true}), - 'img': new HtmlTagDefinition({isVoid: true}), - 'input': new HtmlTagDefinition({isVoid: true}), - 'param': new HtmlTagDefinition({isVoid: true}), - 'hr': new HtmlTagDefinition({isVoid: true}), - 'br': new HtmlTagDefinition({isVoid: true}), - 'source': new HtmlTagDefinition({isVoid: true}), - 'track': new HtmlTagDefinition({isVoid: true}), - 'wbr': new HtmlTagDefinition({isVoid: true}), - 'p': new HtmlTagDefinition({ - closedByChildren: [ - 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset', 'footer', 'form', - 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', - 'main', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul' - ], - closedByParent: true - }), - 'thead': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot']}), - 'tbody': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot'], closedByParent: true}), - 'tfoot': new HtmlTagDefinition({closedByChildren: ['tbody'], closedByParent: true}), - 'tr': new HtmlTagDefinition({ - closedByChildren: ['tr'], - requiredParents: ['tbody', 'tfoot', 'thead'], - closedByParent: true - }), - 'td': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}), - 'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}), - 'col': new HtmlTagDefinition({requiredParents: ['colgroup'], isVoid: true}), - 'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}), - 'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}), - 'li': new HtmlTagDefinition({closedByChildren: ['li'], closedByParent: true}), - 'dt': new HtmlTagDefinition({closedByChildren: ['dt', 'dd']}), - 'dd': new HtmlTagDefinition({closedByChildren: ['dt', 'dd'], closedByParent: true}), - 'rb': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}), - 'rt': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}), - 'rtc': new HtmlTagDefinition({closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true}), - 'rp': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}), - 'optgroup': new HtmlTagDefinition({closedByChildren: ['optgroup'], closedByParent: true}), - 'option': new HtmlTagDefinition({closedByChildren: ['option', 'optgroup'], closedByParent: true}), - 'pre': new HtmlTagDefinition({ignoreFirstLf: true}), - 'listing': new HtmlTagDefinition({ignoreFirstLf: true}), - 'style': new HtmlTagDefinition({contentType: TagContentType.RAW_TEXT}), - 'script': new HtmlTagDefinition({contentType: TagContentType.RAW_TEXT}), - 'title': new HtmlTagDefinition({contentType: TagContentType.ESCAPABLE_RAW_TEXT}), - 'textarea': - new HtmlTagDefinition({contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true}), -}; - -const _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition(); - -export function getHtmlTagDefinition(tagName: string): HtmlTagDefinition { - return TAG_DEFINITIONS[tagName.toLowerCase()] || _DEFAULT_TAG_DEFINITION; -} \ No newline at end of file diff --git a/packages/upgrade/src/common/compiler_helpers/ml_parser/tags.ts b/packages/upgrade/src/common/compiler_helpers/ml_parser/tags.ts deleted file mode 100644 index f83c8fd848..0000000000 --- a/packages/upgrade/src/common/compiler_helpers/ml_parser/tags.ts +++ /dev/null @@ -1,310 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -export enum TagContentType { - RAW_TEXT, - ESCAPABLE_RAW_TEXT, - PARSABLE_DATA -} - -// TODO(vicb): read-only when TS supports it -export interface TagDefinition { - closedByParent: boolean; - requiredParents: {[key: string]: boolean}; - parentToAdd: string; - implicitNamespacePrefix: string; - contentType: TagContentType; - isVoid: boolean; - ignoreFirstLf: boolean; - canSelfClose: boolean; - - requireExtraParent(currentParent: string): boolean; - - isClosedByChild(name: string): boolean; -} - -export function splitNsName(elementName: string): [string, string] { - if (elementName[0] != ':') { - return [null, elementName]; - } - - const colonIndex = elementName.indexOf(':', 1); - - if (colonIndex == -1) { - throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`); - } - - return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)]; -} - -export function getNsPrefix(fullName: string): string { - return fullName === null ? null : splitNsName(fullName)[0]; -} - -export function mergeNsAndName(prefix: string, localName: string): string { - return prefix ? `:${prefix}:${localName}` : localName; -} - -// see http://www.w3.org/TR/html51/syntax.html#named-character-references -// see https://html.spec.whatwg.org/multipage/entities.json -// This list is not exhaustive to keep the compiler footprint low. -// The `{` / `ƫ` syntax should be used when the named character reference does not exist. -export const NAMED_ENTITIES: {[k: string]: string} = { - 'Aacute': '\u00C1', - 'aacute': '\u00E1', - 'Acirc': '\u00C2', - 'acirc': '\u00E2', - 'acute': '\u00B4', - 'AElig': '\u00C6', - 'aelig': '\u00E6', - 'Agrave': '\u00C0', - 'agrave': '\u00E0', - 'alefsym': '\u2135', - 'Alpha': '\u0391', - 'alpha': '\u03B1', - 'amp': '&', - 'and': '\u2227', - 'ang': '\u2220', - 'apos': '\u0027', - 'Aring': '\u00C5', - 'aring': '\u00E5', - 'asymp': '\u2248', - 'Atilde': '\u00C3', - 'atilde': '\u00E3', - 'Auml': '\u00C4', - 'auml': '\u00E4', - 'bdquo': '\u201E', - 'Beta': '\u0392', - 'beta': '\u03B2', - 'brvbar': '\u00A6', - 'bull': '\u2022', - 'cap': '\u2229', - 'Ccedil': '\u00C7', - 'ccedil': '\u00E7', - 'cedil': '\u00B8', - 'cent': '\u00A2', - 'Chi': '\u03A7', - 'chi': '\u03C7', - 'circ': '\u02C6', - 'clubs': '\u2663', - 'cong': '\u2245', - 'copy': '\u00A9', - 'crarr': '\u21B5', - 'cup': '\u222A', - 'curren': '\u00A4', - 'dagger': '\u2020', - 'Dagger': '\u2021', - 'darr': '\u2193', - 'dArr': '\u21D3', - 'deg': '\u00B0', - 'Delta': '\u0394', - 'delta': '\u03B4', - 'diams': '\u2666', - 'divide': '\u00F7', - 'Eacute': '\u00C9', - 'eacute': '\u00E9', - 'Ecirc': '\u00CA', - 'ecirc': '\u00EA', - 'Egrave': '\u00C8', - 'egrave': '\u00E8', - 'empty': '\u2205', - 'emsp': '\u2003', - 'ensp': '\u2002', - 'Epsilon': '\u0395', - 'epsilon': '\u03B5', - 'equiv': '\u2261', - 'Eta': '\u0397', - 'eta': '\u03B7', - 'ETH': '\u00D0', - 'eth': '\u00F0', - 'Euml': '\u00CB', - 'euml': '\u00EB', - 'euro': '\u20AC', - 'exist': '\u2203', - 'fnof': '\u0192', - 'forall': '\u2200', - 'frac12': '\u00BD', - 'frac14': '\u00BC', - 'frac34': '\u00BE', - 'frasl': '\u2044', - 'Gamma': '\u0393', - 'gamma': '\u03B3', - 'ge': '\u2265', - 'gt': '>', - 'harr': '\u2194', - 'hArr': '\u21D4', - 'hearts': '\u2665', - 'hellip': '\u2026', - 'Iacute': '\u00CD', - 'iacute': '\u00ED', - 'Icirc': '\u00CE', - 'icirc': '\u00EE', - 'iexcl': '\u00A1', - 'Igrave': '\u00CC', - 'igrave': '\u00EC', - 'image': '\u2111', - 'infin': '\u221E', - 'int': '\u222B', - 'Iota': '\u0399', - 'iota': '\u03B9', - 'iquest': '\u00BF', - 'isin': '\u2208', - 'Iuml': '\u00CF', - 'iuml': '\u00EF', - 'Kappa': '\u039A', - 'kappa': '\u03BA', - 'Lambda': '\u039B', - 'lambda': '\u03BB', - 'lang': '\u27E8', - 'laquo': '\u00AB', - 'larr': '\u2190', - 'lArr': '\u21D0', - 'lceil': '\u2308', - 'ldquo': '\u201C', - 'le': '\u2264', - 'lfloor': '\u230A', - 'lowast': '\u2217', - 'loz': '\u25CA', - 'lrm': '\u200E', - 'lsaquo': '\u2039', - 'lsquo': '\u2018', - 'lt': '<', - 'macr': '\u00AF', - 'mdash': '\u2014', - 'micro': '\u00B5', - 'middot': '\u00B7', - 'minus': '\u2212', - 'Mu': '\u039C', - 'mu': '\u03BC', - 'nabla': '\u2207', - 'nbsp': '\u00A0', - 'ndash': '\u2013', - 'ne': '\u2260', - 'ni': '\u220B', - 'not': '\u00AC', - 'notin': '\u2209', - 'nsub': '\u2284', - 'Ntilde': '\u00D1', - 'ntilde': '\u00F1', - 'Nu': '\u039D', - 'nu': '\u03BD', - 'Oacute': '\u00D3', - 'oacute': '\u00F3', - 'Ocirc': '\u00D4', - 'ocirc': '\u00F4', - 'OElig': '\u0152', - 'oelig': '\u0153', - 'Ograve': '\u00D2', - 'ograve': '\u00F2', - 'oline': '\u203E', - 'Omega': '\u03A9', - 'omega': '\u03C9', - 'Omicron': '\u039F', - 'omicron': '\u03BF', - 'oplus': '\u2295', - 'or': '\u2228', - 'ordf': '\u00AA', - 'ordm': '\u00BA', - 'Oslash': '\u00D8', - 'oslash': '\u00F8', - 'Otilde': '\u00D5', - 'otilde': '\u00F5', - 'otimes': '\u2297', - 'Ouml': '\u00D6', - 'ouml': '\u00F6', - 'para': '\u00B6', - 'permil': '\u2030', - 'perp': '\u22A5', - 'Phi': '\u03A6', - 'phi': '\u03C6', - 'Pi': '\u03A0', - 'pi': '\u03C0', - 'piv': '\u03D6', - 'plusmn': '\u00B1', - 'pound': '\u00A3', - 'prime': '\u2032', - 'Prime': '\u2033', - 'prod': '\u220F', - 'prop': '\u221D', - 'Psi': '\u03A8', - 'psi': '\u03C8', - 'quot': '\u0022', - 'radic': '\u221A', - 'rang': '\u27E9', - 'raquo': '\u00BB', - 'rarr': '\u2192', - 'rArr': '\u21D2', - 'rceil': '\u2309', - 'rdquo': '\u201D', - 'real': '\u211C', - 'reg': '\u00AE', - 'rfloor': '\u230B', - 'Rho': '\u03A1', - 'rho': '\u03C1', - 'rlm': '\u200F', - 'rsaquo': '\u203A', - 'rsquo': '\u2019', - 'sbquo': '\u201A', - 'Scaron': '\u0160', - 'scaron': '\u0161', - 'sdot': '\u22C5', - 'sect': '\u00A7', - 'shy': '\u00AD', - 'Sigma': '\u03A3', - 'sigma': '\u03C3', - 'sigmaf': '\u03C2', - 'sim': '\u223C', - 'spades': '\u2660', - 'sub': '\u2282', - 'sube': '\u2286', - 'sum': '\u2211', - 'sup': '\u2283', - 'sup1': '\u00B9', - 'sup2': '\u00B2', - 'sup3': '\u00B3', - 'supe': '\u2287', - 'szlig': '\u00DF', - 'Tau': '\u03A4', - 'tau': '\u03C4', - 'there4': '\u2234', - 'Theta': '\u0398', - 'theta': '\u03B8', - 'thetasym': '\u03D1', - 'thinsp': '\u2009', - 'THORN': '\u00DE', - 'thorn': '\u00FE', - 'tilde': '\u02DC', - 'times': '\u00D7', - 'trade': '\u2122', - 'Uacute': '\u00DA', - 'uacute': '\u00FA', - 'uarr': '\u2191', - 'uArr': '\u21D1', - 'Ucirc': '\u00DB', - 'ucirc': '\u00FB', - 'Ugrave': '\u00D9', - 'ugrave': '\u00F9', - 'uml': '\u00A8', - 'upsih': '\u03D2', - 'Upsilon': '\u03A5', - 'upsilon': '\u03C5', - 'Uuml': '\u00DC', - 'uuml': '\u00FC', - 'weierp': '\u2118', - 'Xi': '\u039E', - 'xi': '\u03BE', - 'Yacute': '\u00DD', - 'yacute': '\u00FD', - 'yen': '\u00A5', - 'yuml': '\u00FF', - 'Yuml': '\u0178', - 'Zeta': '\u0396', - 'zeta': '\u03B6', - 'zwj': '\u200D', - 'zwnj': '\u200C', -}; diff --git a/packages/upgrade/src/common/compiler_helpers/selector.ts b/packages/upgrade/src/common/compiler_helpers/selector.ts deleted file mode 100644 index 62a9c028a5..0000000000 --- a/packages/upgrade/src/common/compiler_helpers/selector.ts +++ /dev/null @@ -1,368 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {getHtmlTagDefinition} from './ml_parser/html_tags'; - -const _SELECTOR_REGEXP = new RegExp( - '(\\:not\\()|' + //":not(" - '([-\\w]+)|' + // "tag" - '(?:\\.([-\\w]+))|' + // ".class" - // "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range - '(?:\\[([-.\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]" - '(\\))|' + // ")" - '(\\s*,\\s*)', // "," - 'g'); - -/** - * A css selector contains an element name, - * css classes and attribute/value pairs with the purpose - * of selecting subsets out of them. - */ -export class CssSelector { - element: string = null; - classNames: string[] = []; - attrs: string[] = []; - notSelectors: CssSelector[] = []; - - static parse(selector: string): CssSelector[] { - const results: CssSelector[] = []; - const _addResult = (res: CssSelector[], cssSel: CssSelector) => { - if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 && - cssSel.attrs.length == 0) { - cssSel.element = '*'; - } - res.push(cssSel); - }; - let cssSelector = new CssSelector(); - let match: string[]; - let current = cssSelector; - let inNot = false; - _SELECTOR_REGEXP.lastIndex = 0; - while (match = _SELECTOR_REGEXP.exec(selector)) { - if (match[1]) { - if (inNot) { - throw new Error('Nesting :not is not allowed in a selector'); - } - inNot = true; - current = new CssSelector(); - cssSelector.notSelectors.push(current); - } - if (match[2]) { - current.setElement(match[2]); - } - if (match[3]) { - current.addClassName(match[3]); - } - if (match[4]) { - current.addAttribute(match[4], match[5]); - } - if (match[6]) { - inNot = false; - current = cssSelector; - } - if (match[7]) { - if (inNot) { - throw new Error('Multiple selectors in :not are not supported'); - } - _addResult(results, cssSelector); - cssSelector = current = new CssSelector(); - } - } - _addResult(results, cssSelector); - return results; - } - - isElementSelector(): boolean { - return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 && - this.notSelectors.length === 0; - } - - hasElementSelector(): boolean { return !!this.element; } - - setElement(element: string = null) { this.element = element; } - - /** Gets a template string for an element that matches the selector. */ - getMatchingElementTemplate(): string { - const tagName = this.element || 'div'; - const classAttr = this.classNames.length > 0 ? ` class="${this.classNames.join(' ')}"` : ''; - - let attrs = ''; - for (let i = 0; i < this.attrs.length; i += 2) { - const attrName = this.attrs[i]; - const attrValue = this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : ''; - attrs += ` ${attrName}${attrValue}`; - } - - return getHtmlTagDefinition(tagName).isVoid ? `<${tagName}${classAttr}${attrs}/>` : - `<${tagName}${classAttr}${attrs}>`; - } - - addAttribute(name: string, value: string = '') { - this.attrs.push(name, value && value.toLowerCase() || ''); - } - - addClassName(name: string) { this.classNames.push(name.toLowerCase()); } - - toString(): string { - let res: string = this.element || ''; - if (this.classNames) { - this.classNames.forEach(klass => res += `.${klass}`); - } - if (this.attrs) { - for (let i = 0; i < this.attrs.length; i += 2) { - const name = this.attrs[i]; - const value = this.attrs[i + 1]; - res += `[${name}${value ? '=' + value : ''}]`; - } - } - this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`); - return res; - } -} - -/** - * Reads a list of CssSelectors and allows to calculate which ones - * are contained in a given CssSelector. - */ -export class SelectorMatcher { - static createNotMatcher(notSelectors: CssSelector[]): SelectorMatcher { - const notMatcher = new SelectorMatcher(); - notMatcher.addSelectables(notSelectors, null); - return notMatcher; - } - - private _elementMap = new Map(); - private _elementPartialMap = new Map(); - private _classMap = new Map(); - private _classPartialMap = new Map(); - private _attrValueMap = new Map>(); - private _attrValuePartialMap = new Map>(); - private _listContexts: SelectorListContext[] = []; - - addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) { - let listContext: SelectorListContext = null; - if (cssSelectors.length > 1) { - listContext = new SelectorListContext(cssSelectors); - this._listContexts.push(listContext); - } - for (let i = 0; i < cssSelectors.length; i++) { - this._addSelectable(cssSelectors[i], callbackCtxt, listContext); - } - } - - /** - * Add an object that can be found later on by calling `match`. - * @param cssSelector A css selector - * @param callbackCtxt An opaque object that will be given to the callback of the `match` function - */ - private _addSelectable( - cssSelector: CssSelector, callbackCtxt: any, listContext: SelectorListContext) { - let matcher: SelectorMatcher = this; - const element = cssSelector.element; - const classNames = cssSelector.classNames; - const attrs = cssSelector.attrs; - const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext); - - if (element) { - const isTerminal = attrs.length === 0 && classNames.length === 0; - if (isTerminal) { - this._addTerminal(matcher._elementMap, element, selectable); - } else { - matcher = this._addPartial(matcher._elementPartialMap, element); - } - } - - if (classNames) { - for (let i = 0; i < classNames.length; i++) { - const isTerminal = attrs.length === 0 && i === classNames.length - 1; - const className = classNames[i]; - if (isTerminal) { - this._addTerminal(matcher._classMap, className, selectable); - } else { - matcher = this._addPartial(matcher._classPartialMap, className); - } - } - } - - if (attrs) { - for (let i = 0; i < attrs.length; i += 2) { - const isTerminal = i === attrs.length - 2; - const name = attrs[i]; - const value = attrs[i + 1]; - if (isTerminal) { - const terminalMap = matcher._attrValueMap; - let terminalValuesMap = terminalMap.get(name); - if (!terminalValuesMap) { - terminalValuesMap = new Map(); - terminalMap.set(name, terminalValuesMap); - } - this._addTerminal(terminalValuesMap, value, selectable); - } else { - const partialMap = matcher._attrValuePartialMap; - let partialValuesMap = partialMap.get(name); - if (!partialValuesMap) { - partialValuesMap = new Map(); - partialMap.set(name, partialValuesMap); - } - matcher = this._addPartial(partialValuesMap, value); - } - } - } - } - - private _addTerminal( - map: Map, name: string, selectable: SelectorContext) { - let terminalList = map.get(name); - if (!terminalList) { - terminalList = []; - map.set(name, terminalList); - } - terminalList.push(selectable); - } - - private _addPartial(map: Map, name: string): SelectorMatcher { - let matcher = map.get(name); - if (!matcher) { - matcher = new SelectorMatcher(); - map.set(name, matcher); - } - return matcher; - } - - /** - * Find the objects that have been added via `addSelectable` - * whose css selector is contained in the given css selector. - * @param cssSelector A css selector - * @param matchedCallback This callback will be called with the object handed into `addSelectable` - * @return boolean true if a match was found - */ - match(cssSelector: CssSelector, matchedCallback: (c: CssSelector, a: any) => void): boolean { - let result = false; - const element = cssSelector.element; - const classNames = cssSelector.classNames; - const attrs = cssSelector.attrs; - - for (let i = 0; i < this._listContexts.length; i++) { - this._listContexts[i].alreadyMatched = false; - } - - result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result; - result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || - result; - - if (classNames) { - for (let i = 0; i < classNames.length; i++) { - const className = classNames[i]; - result = - this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result; - result = - this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) || - result; - } - } - - if (attrs) { - for (let i = 0; i < attrs.length; i += 2) { - const name = attrs[i]; - const value = attrs[i + 1]; - - const terminalValuesMap = this._attrValueMap.get(name); - if (value) { - result = - this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result; - } - result = - this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result; - - const partialValuesMap = this._attrValuePartialMap.get(name); - if (value) { - result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result; - } - result = - this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result; - } - } - return result; - } - - /** @internal */ - _matchTerminal( - map: Map, name: string, cssSelector: CssSelector, - matchedCallback: (c: CssSelector, a: any) => void): boolean { - if (!map || typeof name !== 'string') { - return false; - } - - let selectables: SelectorContext[] = map.get(name) || []; - const starSelectables: SelectorContext[] = map.get('*'); - if (starSelectables) { - selectables = selectables.concat(starSelectables); - } - if (selectables.length === 0) { - return false; - } - let selectable: SelectorContext; - let result = false; - for (let i = 0; i < selectables.length; i++) { - selectable = selectables[i]; - result = selectable.finalize(cssSelector, matchedCallback) || result; - } - return result; - } - - /** @internal */ - _matchPartial( - map: Map, name: string, cssSelector: CssSelector, - matchedCallback: (c: CssSelector, a: any) => void): boolean { - if (!map || typeof name !== 'string') { - return false; - } - - const nestedSelector = map.get(name); - if (!nestedSelector) { - return false; - } - // TODO(perf): get rid of recursion and measure again - // TODO(perf): don't pass the whole selector into the recursion, - // but only the not processed parts - return nestedSelector.match(cssSelector, matchedCallback); - } -} - - -export class SelectorListContext { - alreadyMatched: boolean = false; - - constructor(public selectors: CssSelector[]) {} -} - -// Store context to pass back selector and context when a selector is matched -export class SelectorContext { - notSelectors: CssSelector[]; - - constructor( - public selector: CssSelector, public cbContext: any, - public listContext: SelectorListContext) { - this.notSelectors = selector.notSelectors; - } - - finalize(cssSelector: CssSelector, callback: (c: CssSelector, a: any) => void): boolean { - let result = true; - if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) { - const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors); - result = !notMatcher.match(cssSelector, null); - } - if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) { - if (this.listContext) { - this.listContext.alreadyMatched = true; - } - callback(this.selector, this.cbContext); - } - return result; - } -} diff --git a/packages/upgrade/src/common/downgrade_component_adapter.ts b/packages/upgrade/src/common/downgrade_component_adapter.ts index b926c13474..36f9e0e808 100644 --- a/packages/upgrade/src/common/downgrade_component_adapter.ts +++ b/packages/upgrade/src/common/downgrade_component_adapter.ts @@ -9,8 +9,6 @@ import {ChangeDetectorRef, ComponentFactory, ComponentRef, EventEmitter, Injector, OnChanges, ReflectiveInjector, SimpleChange, SimpleChanges, Type} from '@angular/core'; import * as angular from './angular1'; -import {createElementCssSelector} from './compiler_helpers/createElementCssSelector'; -import {CssSelector, SelectorMatcher} from './compiler_helpers/selector'; import {ComponentInfo, PropertyBinding} from './component_info'; import {$SCOPE} from './constants'; import {NgContentSelectorHelper} from './ng_content_selector_helper'; @@ -201,38 +199,52 @@ export class DowngradeComponentAdapter { */ private _groupNodesBySelector(ngContentSelectors: string[], nodes: Node[]): Node[][] { const projectableNodes: Node[][] = []; - let matcher = new SelectorMatcher(); let wildcardNgContentIndex: number; for (let i = 0, ii = ngContentSelectors.length; i < ii; ++i) { projectableNodes[i] = []; - - const selector = ngContentSelectors[i]; - if (selector === '*') { - wildcardNgContentIndex = i; - } else { - matcher.addSelectables(CssSelector.parse(selector), i); - } } for (let j = 0, jj = nodes.length; j < jj; ++j) { - const ngContentIndices: number[] = []; const node = nodes[j]; - const selector = - createElementCssSelector(node.nodeName.toLowerCase(), getAttributesAsArray(node)); - - matcher.match(selector, (_, index) => ngContentIndices.push(index)); - ngContentIndices.sort(); - - if (wildcardNgContentIndex !== undefined) { - ngContentIndices.push(wildcardNgContentIndex); - } - - if (ngContentIndices.length) { - projectableNodes[ngContentIndices[0]].push(node); + const ngContentIndex = findMatchingNgContentIndex(node, ngContentSelectors); + if (ngContentIndex != null) { + projectableNodes[ngContentIndex].push(node); } } return projectableNodes; } } + +let _matches: (this: any, selector: string) => boolean; + +function matchesSelector(el: any, selector: string): boolean { + if (!_matches) { + const elProto = Element.prototype; + _matches = elProto.matchesSelector || elProto.mozMatchesSelector || elProto.msMatchesSelector || + elProto.oMatchesSelector || elProto.webkitMatchesSelector; + } + return _matches.call(el, selector); +} + +function findMatchingNgContentIndex(element: any, ngContentSelectors: string[]): number { + const ngContentIndices: number[] = []; + let wildcardNgContentIndex: number; + for (let i = 0; i < ngContentSelectors.length; i++) { + const selector = ngContentSelectors[i]; + if (selector === '*') { + wildcardNgContentIndex = i; + } else { + if (matchesSelector(element, selector)) { + ngContentIndices.push(i); + } + } + } + ngContentIndices.sort(); + + if (wildcardNgContentIndex !== undefined) { + ngContentIndices.push(wildcardNgContentIndex); + } + return ngContentIndices.length ? ngContentIndices[0] : null; +} \ No newline at end of file