209 lines
6.1 KiB
TypeScript
209 lines
6.1 KiB
TypeScript
|
/**
|
||
|
* @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<T> {
|
||
|
/**
|
||
|
* Next predicate
|
||
|
*/
|
||
|
next: QueryPredicate<any>|null;
|
||
|
|
||
|
/**
|
||
|
* Destination to which the value should be added.
|
||
|
*/
|
||
|
list: QueryList<T>;
|
||
|
|
||
|
/**
|
||
|
* If looking for directives than it contains the directive type.
|
||
|
*/
|
||
|
type: Type<T>|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<any>|null = null;
|
||
|
deep: QueryPredicate<any>|null = null;
|
||
|
|
||
|
constructor(deep?: QueryPredicate<any>) { this.deep = deep == null ? null : deep; }
|
||
|
|
||
|
track<T>(queryList: IQueryList<T>, predicate: Type<T>|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<any>| 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<T>(
|
||
|
previous: QueryPredicate<any>| null, queryList: QueryList<T>,
|
||
|
predicate: Type<T>| any[]): QueryPredicate<T> {
|
||
|
const isArray = Array.isArray(predicate);
|
||
|
const values = <any>[];
|
||
|
if ((queryList as any as QueryList_<T>)._valuesTree === null) {
|
||
|
(queryList as any as QueryList_<T>)._valuesTree = values;
|
||
|
}
|
||
|
return {
|
||
|
next: previous,
|
||
|
list: queryList,
|
||
|
type: isArray ? null : predicate as Type<T>,
|
||
|
selector: isArray ? predicate as any[] : null,
|
||
|
values: values
|
||
|
};
|
||
|
}
|
||
|
|
||
|
class QueryList_<T>/* implements IQueryList<T> */ {
|
||
|
dirty: boolean = false;
|
||
|
changes: Observable<T>;
|
||
|
|
||
|
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<U>(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<U>(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<T> = IQueryList<T>;
|
||
|
export const QueryList: typeof IQueryList = QueryList_ as any;
|
||
|
|
||
|
export function refreshQuery(query: QueryList<any>): boolean {
|
||
|
return (query as any as QueryList_<any>)._refresh();
|
||
|
}
|