/** * @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 {Observable} from 'rxjs/Observable'; import {QueryList as IQueryList, Type} from '../core'; import {assertNotNull} from './assert'; import {LContainer, LNode, LNodeFlags, LView, QueryState} from './interfaces'; /** * A predicate which determines if a given element/directive should be included in the query */ export interface QueryPredicate { /** * Next predicate */ next: QueryPredicate|null; /** * Destination to which the value should be added. */ list: QueryList; /** * If looking for directives than it contains the directive type. */ type: Type|null; /** * If selector then contains the selector parts where: * - even index: * - `null`: represents a tag name * - `"#""`: represents a reference name * - `string`: name of the attribute * - odd index: * - `null`: any value will match * - `string`: the value which mast match. */ selector: any[]|null; /** * Values which have been located. * * this is what builds up the `QueryList._valuesTree`. */ values: any[]; } export class QueryState_ implements QueryState { shallow: QueryPredicate|null = null; deep: QueryPredicate|null = null; constructor(deep?: QueryPredicate) { this.deep = deep == null ? null : deep; } track(queryList: IQueryList, predicate: Type|any[], descend?: boolean): void { // TODO(misko): This is not right. In case of inherited state, a calling track will incorrectly // mutate parent. if (descend) { this.deep = createPredicate(this.deep, queryList, predicate); } else { this.shallow = createPredicate(this.shallow, queryList, predicate); } } child(): QueryState|null { if (this.deep === null) { // if we don't have any deep queries than no need to track anything more. return null; } if (this.shallow === null) { // DeepQuery: We can reuse the current state if the child state would be same as current // state. return this; } else { // We need to create new state return new QueryState_(this.deep); } } add(node: LNode): void { add(this.shallow, node); add(this.deep, node); } insert(container: LContainer, view: LView, index: number): void { throw new Error('Method not implemented.'); } remove(container: LContainer, view: LView, index: number): void { throw new Error('Method not implemented.'); } } function add(predicate: QueryPredicate| null, node: LNode) { while (predicate) { const type = predicate.type; if (type) { const directives = node.view.directives; const flags = node.flags; for (let i = flags >> LNodeFlags.INDX_SHIFT, ii = i + ((flags & LNodeFlags.SIZE_MASK) >> LNodeFlags.SIZE_SHIFT); i < ii; i++) { const def = directives[i << 1 | 1]; if (def.diPublic && def.type === type) { predicate.values.push(directives[i << 1]); } } } predicate = predicate.next; } } function createPredicate( previous: QueryPredicate| null, queryList: QueryList, predicate: Type| any[]): QueryPredicate { const isArray = Array.isArray(predicate); const values = []; if ((queryList as any as QueryList_)._valuesTree === null) { (queryList as any as QueryList_)._valuesTree = values; } return { next: previous, list: queryList, type: isArray ? null : predicate as Type, selector: isArray ? predicate as any[] : null, values: values }; } class QueryList_/* implements IQueryList */ { dirty: boolean = false; changes: Observable; get length(): number { ngDevMode && assertNotNull(this._values, 'refreshed'); return this._values !.length; } get first(): T|null { ngDevMode && assertNotNull(this._values, 'refreshed'); let values = this._values !; return values.length ? values[0] : null; } get last(): T|null { ngDevMode && assertNotNull(this._values, 'refreshed'); let values = this._values !; return values.length ? values[values.length - 1] : null; } /** @internal */ _valuesTree: any[]|null = null; /** @internal */ _values: T[]|null = null; /** @internal */ _refresh(): boolean { // TODO(misko): needs more logic to flatten tree. if (this._values === null) { this._values = this._valuesTree; return true; } return false; } map(fn: (item: T, index: number, array: T[]) => U): U[] { throw new Error('Method not implemented.'); } filter(fn: (item: T, index: number, array: T[]) => boolean): T[] { throw new Error('Method not implemented.'); } find(fn: (item: T, index: number, array: T[]) => boolean): T|undefined { throw new Error('Method not implemented.'); } reduce(fn: (prevValue: U, curValue: T, curIndex: number, array: T[]) => U, init: U): U { throw new Error('Method not implemented.'); } forEach(fn: (item: T, index: number, array: T[]) => void): void { throw new Error('Method not implemented.'); } some(fn: (value: T, index: number, array: T[]) => boolean): boolean { throw new Error('Method not implemented.'); } toArray(): T[] { ngDevMode && assertNotNull(this._values, 'refreshed'); return this._values !; } toString(): string { throw new Error('Method not implemented.'); } reset(res: (any[]|T)[]): void { throw new Error('Method not implemented.'); } notifyOnChanges(): void { throw new Error('Method not implemented.'); } setDirty(): void { throw new Error('Method not implemented.'); } destroy(): void { throw new Error('Method not implemented.'); } } // NOTE: this hack is here because IQueryList has private members and therefore // it can't be implemented only extended. export type QueryList = IQueryList; export const QueryList: typeof IQueryList = QueryList_ as any; export function refreshQuery(query: QueryList): boolean { return (query as any as QueryList_)._refresh(); }