build(typescript): Migrated change detection to typescript
This commit is contained in:
parent
f0ef72d6cc
commit
fa28b28d0a
|
@ -6,17 +6,40 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ASTWithSource, AST, AstTransformer, AccessMember, LiteralArray, ImplicitReceiver
|
ASTWithSource,
|
||||||
|
AST,
|
||||||
|
AstTransformer,
|
||||||
|
AccessMember,
|
||||||
|
LiteralArray,
|
||||||
|
ImplicitReceiver
|
||||||
} from './src/change_detection/parser/ast';
|
} from './src/change_detection/parser/ast';
|
||||||
|
|
||||||
export {Lexer} from './src/change_detection/parser/lexer';
|
export {Lexer} from './src/change_detection/parser/lexer';
|
||||||
export {Parser} from './src/change_detection/parser/parser';
|
export {Parser} from './src/change_detection/parser/parser';
|
||||||
export {Locals} from './src/change_detection/parser/locals';
|
export {Locals} from './src/change_detection/parser/locals';
|
||||||
|
|
||||||
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './src/change_detection/exceptions';
|
export {
|
||||||
export {ProtoChangeDetector, ChangeDispatcher, ChangeDetector, ChangeDetection} from './src/change_detection/interfaces';
|
ExpressionChangedAfterItHasBeenChecked,
|
||||||
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './src/change_detection/constants';
|
ChangeDetectionError
|
||||||
export {DynamicProtoChangeDetector, JitProtoChangeDetector} from './src/change_detection/proto_change_detector';
|
} from './src/change_detection/exceptions';
|
||||||
|
export {
|
||||||
|
ProtoChangeDetector,
|
||||||
|
ChangeDispatcher,
|
||||||
|
ChangeDetector,
|
||||||
|
ChangeDetection
|
||||||
|
} from './src/change_detection/interfaces';
|
||||||
|
export {
|
||||||
|
CHECK_ONCE,
|
||||||
|
CHECK_ALWAYS,
|
||||||
|
DETACHED,
|
||||||
|
CHECKED,
|
||||||
|
ON_PUSH,
|
||||||
|
DEFAULT
|
||||||
|
} from './src/change_detection/constants';
|
||||||
|
export {
|
||||||
|
DynamicProtoChangeDetector,
|
||||||
|
JitProtoChangeDetector
|
||||||
|
} from './src/change_detection/proto_change_detector';
|
||||||
export {BindingRecord} from './src/change_detection/binding_record';
|
export {BindingRecord} from './src/change_detection/binding_record';
|
||||||
export {DirectiveIndex, DirectiveRecord} from './src/change_detection/directive_record';
|
export {DirectiveIndex, DirectiveRecord} from './src/change_detection/directive_record';
|
||||||
export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector';
|
export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector';
|
||||||
|
@ -25,5 +48,13 @@ export {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
|
||||||
export {uninitialized} from './src/change_detection/change_detection_util';
|
export {uninitialized} from './src/change_detection/change_detection_util';
|
||||||
export {WrappedValue, Pipe} from './src/change_detection/pipes/pipe';
|
export {WrappedValue, Pipe} from './src/change_detection/pipes/pipe';
|
||||||
export {
|
export {
|
||||||
defaultPipes, DynamicChangeDetection, JitChangeDetection, defaultPipeRegistry
|
defaultPipes,
|
||||||
|
DynamicChangeDetection,
|
||||||
|
JitChangeDetection,
|
||||||
|
defaultPipeRegistry
|
||||||
} from './src/change_detection/change_detection';
|
} from './src/change_detection/change_detection';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
|
@ -4,12 +4,17 @@ import {ChangeDetectorRef} from './change_detector_ref';
|
||||||
import {ChangeDetector} from './interfaces';
|
import {ChangeDetector} from './interfaces';
|
||||||
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
|
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
export class AbstractChangeDetector extends ChangeDetector {
|
export class AbstractChangeDetector extends ChangeDetector {
|
||||||
lightDomChildren:List;
|
lightDomChildren: List<any>;
|
||||||
shadowDomChildren:List;
|
shadowDomChildren: List<any>;
|
||||||
parent:ChangeDetector;
|
parent: ChangeDetector;
|
||||||
mode:string;
|
mode: string;
|
||||||
ref:ChangeDetectorRef;
|
ref: ChangeDetectorRef;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -19,37 +24,27 @@ export class AbstractChangeDetector extends ChangeDetector {
|
||||||
this.mode = null;
|
this.mode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
addChild(cd:ChangeDetector) {
|
addChild(cd: ChangeDetector) {
|
||||||
ListWrapper.push(this.lightDomChildren, cd);
|
ListWrapper.push(this.lightDomChildren, cd);
|
||||||
cd.parent = this;
|
cd.parent = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeChild(cd:ChangeDetector) {
|
removeChild(cd: ChangeDetector) { ListWrapper.remove(this.lightDomChildren, cd); }
|
||||||
ListWrapper.remove(this.lightDomChildren, cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
addShadowDomChild(cd:ChangeDetector) {
|
addShadowDomChild(cd: ChangeDetector) {
|
||||||
ListWrapper.push(this.shadowDomChildren, cd);
|
ListWrapper.push(this.shadowDomChildren, cd);
|
||||||
cd.parent = this;
|
cd.parent = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeShadowDomChild(cd:ChangeDetector) {
|
removeShadowDomChild(cd: ChangeDetector) { ListWrapper.remove(this.shadowDomChildren, cd); }
|
||||||
ListWrapper.remove(this.shadowDomChildren, cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove() {
|
remove() { this.parent.removeChild(this); }
|
||||||
this.parent.removeChild(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
detectChanges() {
|
detectChanges() { this._detectChanges(false); }
|
||||||
this._detectChanges(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNoChanges() {
|
checkNoChanges() { this._detectChanges(true); }
|
||||||
this._detectChanges(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
_detectChanges(throwOnChange:boolean) {
|
_detectChanges(throwOnChange: boolean) {
|
||||||
if (this.mode === DETACHED || this.mode === CHECKED) return;
|
if (this.mode === DETACHED || this.mode === CHECKED) return;
|
||||||
|
|
||||||
this.detectChangesInRecords(throwOnChange);
|
this.detectChangesInRecords(throwOnChange);
|
||||||
|
@ -63,30 +58,28 @@ export class AbstractChangeDetector extends ChangeDetector {
|
||||||
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
|
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
detectChangesInRecords(throwOnChange:boolean){}
|
detectChangesInRecords(throwOnChange: boolean) {}
|
||||||
callOnAllChangesDone(){}
|
callOnAllChangesDone() {}
|
||||||
|
|
||||||
_detectChangesInLightDomChildren(throwOnChange:boolean) {
|
_detectChangesInLightDomChildren(throwOnChange: boolean) {
|
||||||
var c = this.lightDomChildren;
|
var c = this.lightDomChildren;
|
||||||
for(var i = 0; i < c.length; ++i) {
|
for (var i = 0; i < c.length; ++i) {
|
||||||
c[i]._detectChanges(throwOnChange);
|
c[i]._detectChanges(throwOnChange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_detectChangesInShadowDomChildren(throwOnChange:boolean) {
|
_detectChangesInShadowDomChildren(throwOnChange: boolean) {
|
||||||
var c = this.shadowDomChildren;
|
var c = this.shadowDomChildren;
|
||||||
for(var i = 0; i < c.length; ++i) {
|
for (var i = 0; i < c.length; ++i) {
|
||||||
c[i]._detectChanges(throwOnChange);
|
c[i]._detectChanges(throwOnChange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
markAsCheckOnce() {
|
markAsCheckOnce() { this.mode = CHECK_ONCE; }
|
||||||
this.mode = CHECK_ONCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
markPathToRootAsCheckOnce() {
|
markPathToRootAsCheckOnce() {
|
||||||
var c = this;
|
var c: ChangeDetector = this;
|
||||||
while(isPresent(c) && c.mode != DETACHED) {
|
while (isPresent(c) && c.mode != DETACHED) {
|
||||||
if (c.mode === CHECKED) c.mode = CHECK_ONCE;
|
if (c.mode === CHECKED) c.mode = CHECK_ONCE;
|
||||||
c = c.parent;
|
c = c.parent;
|
||||||
}
|
}
|
|
@ -1,68 +0,0 @@
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
|
||||||
import {SetterFn} from 'angular2/src/reflection/types';
|
|
||||||
import {AST} from './parser/ast';
|
|
||||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
|
||||||
|
|
||||||
const DIRECTIVE="directive";
|
|
||||||
const ELEMENT="element";
|
|
||||||
const TEXT_NODE="textNode";
|
|
||||||
|
|
||||||
export class BindingRecord {
|
|
||||||
mode:string;
|
|
||||||
ast:AST;
|
|
||||||
|
|
||||||
implicitReceiver:any; //number | DirectiveIndex
|
|
||||||
elementIndex:number;
|
|
||||||
propertyName:string;
|
|
||||||
setter:SetterFn;
|
|
||||||
|
|
||||||
directiveRecord:DirectiveRecord;
|
|
||||||
|
|
||||||
constructor(mode:string, implicitReceiver:any, ast:AST, elementIndex:number, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
|
|
||||||
this.mode = mode;
|
|
||||||
this.implicitReceiver = implicitReceiver;
|
|
||||||
this.ast = ast;
|
|
||||||
|
|
||||||
this.elementIndex = elementIndex;
|
|
||||||
this.propertyName = propertyName;
|
|
||||||
this.setter = setter;
|
|
||||||
|
|
||||||
this.directiveRecord = directiveRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
callOnChange() {
|
|
||||||
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
isOnPushChangeDetection() {
|
|
||||||
return isPresent(this.directiveRecord) && this.directiveRecord.isOnPushChangeDetection();
|
|
||||||
}
|
|
||||||
|
|
||||||
isDirective() {
|
|
||||||
return this.mode === DIRECTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
isElement() {
|
|
||||||
return this.mode === ELEMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
isTextNode() {
|
|
||||||
return this.mode === TEXT_NODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForDirective(ast:AST, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
|
|
||||||
return new BindingRecord(DIRECTIVE, 0, ast, 0, propertyName, setter, directiveRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForElement(ast:AST, elementIndex:number, propertyName:string) {
|
|
||||||
return new BindingRecord(ELEMENT, 0, ast, elementIndex, propertyName, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForHostProperty(directiveIndex:DirectiveIndex, ast:AST, propertyName:string) {
|
|
||||||
return new BindingRecord(ELEMENT, directiveIndex, ast, directiveIndex.elementIndex, propertyName, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForTextNode(ast:AST, elementIndex:number) {
|
|
||||||
return new BindingRecord(TEXT_NODE, 0, ast, elementIndex, null, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {SetterFn} from 'angular2/src/reflection/types';
|
||||||
|
import {AST} from './parser/ast';
|
||||||
|
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
const DIRECTIVE = "directive";
|
||||||
|
const ELEMENT = "element";
|
||||||
|
const TEXT_NODE = "textNode";
|
||||||
|
|
||||||
|
export class BindingRecord {
|
||||||
|
constructor(public mode: string, public implicitReceiver: any, public ast: AST,
|
||||||
|
public elementIndex: number, public propertyName: string, public setter: SetterFn,
|
||||||
|
public directiveRecord: DirectiveRecord) {}
|
||||||
|
|
||||||
|
callOnChange() { return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange; }
|
||||||
|
|
||||||
|
isOnPushChangeDetection() {
|
||||||
|
return isPresent(this.directiveRecord) && this.directiveRecord.isOnPushChangeDetection();
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirective() { return this.mode === DIRECTIVE; }
|
||||||
|
|
||||||
|
isElement() { return this.mode === ELEMENT; }
|
||||||
|
|
||||||
|
isTextNode() { return this.mode === TEXT_NODE; }
|
||||||
|
|
||||||
|
static createForDirective(ast: AST, propertyName: string, setter: SetterFn,
|
||||||
|
directiveRecord: DirectiveRecord) {
|
||||||
|
return new BindingRecord(DIRECTIVE, 0, ast, 0, propertyName, setter, directiveRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createForElement(ast: AST, elementIndex: number, propertyName: string) {
|
||||||
|
return new BindingRecord(ELEMENT, 0, ast, elementIndex, propertyName, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createForHostProperty(directiveIndex: DirectiveIndex, ast: AST, propertyName: string) {
|
||||||
|
return new BindingRecord(ELEMENT, directiveIndex, ast, directiveIndex.elementIndex,
|
||||||
|
propertyName, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createForTextNode(ast: AST, elementIndex: number) {
|
||||||
|
return new BindingRecord(TEXT_NODE, 0, ast, elementIndex, null, null, null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,96 +0,0 @@
|
||||||
import {DynamicProtoChangeDetector, JitProtoChangeDetector} from './proto_change_detector';
|
|
||||||
import {PipeFactory} from './pipes/pipe';
|
|
||||||
import {PipeRegistry} from './pipes/pipe_registry';
|
|
||||||
import {IterableChangesFactory} from './pipes/iterable_changes';
|
|
||||||
import {KeyValueChangesFactory} from './pipes/keyvalue_changes';
|
|
||||||
import {AsyncPipeFactory} from './pipes/async_pipe';
|
|
||||||
import {NullPipeFactory} from './pipes/null_pipe';
|
|
||||||
import {BindingRecord} from './binding_record';
|
|
||||||
import {DirectiveRecord} from './directive_record';
|
|
||||||
import {DEFAULT} from './constants';
|
|
||||||
import {ChangeDetection, ProtoChangeDetector} from './interfaces';
|
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
|
||||||
import {List} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Structural diffing for `Object`s and `Map`s.
|
|
||||||
*
|
|
||||||
* @exportedAs angular2/pipes
|
|
||||||
*/
|
|
||||||
export var keyValDiff:List<PipeFactory> = [
|
|
||||||
new KeyValueChangesFactory(),
|
|
||||||
new NullPipeFactory()
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Structural diffing for `Iterable` types such as `Array`s.
|
|
||||||
*
|
|
||||||
* @exportedAs angular2/pipes
|
|
||||||
*/
|
|
||||||
export var iterableDiff:List<PipeFactory> = [
|
|
||||||
new IterableChangesFactory(),
|
|
||||||
new NullPipeFactory()
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Async binding to such types as Observable.
|
|
||||||
*
|
|
||||||
* @exportedAs angular2/pipes
|
|
||||||
*/
|
|
||||||
export var async:List<PipeFactory> = [
|
|
||||||
new AsyncPipeFactory(),
|
|
||||||
new NullPipeFactory()
|
|
||||||
];
|
|
||||||
|
|
||||||
export var defaultPipes:Map<String, List<PipeFactory>> = {
|
|
||||||
"iterableDiff" : iterableDiff,
|
|
||||||
"keyValDiff" : keyValDiff,
|
|
||||||
"async" : async
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements change detection that does not require `eval()`.
|
|
||||||
*
|
|
||||||
* This is slower than {@link JitChangeDetection}.
|
|
||||||
*
|
|
||||||
* @exportedAs angular2/change_detection
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class DynamicChangeDetection extends ChangeDetection {
|
|
||||||
registry:PipeRegistry;
|
|
||||||
|
|
||||||
constructor(registry:PipeRegistry) {
|
|
||||||
super();
|
|
||||||
this.registry = registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
createProtoChangeDetector(name:string, bindingRecords:List<BindingRecord>, variableBindings:List<string>,
|
|
||||||
directiveRecords:List<DirectiveRecord>, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
|
|
||||||
return new DynamicProtoChangeDetector(this.registry, bindingRecords, variableBindings, directiveRecords, changeControlStrategy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements faster change detection, by generating source code.
|
|
||||||
*
|
|
||||||
* This requires `eval()`. For change detection that does not require `eval()`, see {@link DynamicChangeDetection}.
|
|
||||||
*
|
|
||||||
* @exportedAs angular2/change_detection
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class JitChangeDetection extends ChangeDetection {
|
|
||||||
registry:PipeRegistry;
|
|
||||||
|
|
||||||
constructor(registry:PipeRegistry) {
|
|
||||||
super();
|
|
||||||
this.registry = registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
createProtoChangeDetector(name:string, bindingRecords:List<BindingRecord>, variableBindings:List<string>,
|
|
||||||
directiveRecords:List<DirectiveRecord>, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
|
|
||||||
return new JitProtoChangeDetector(this.registry, bindingRecords, variableBindings, directiveRecords, changeControlStrategy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export var defaultPipeRegistry:PipeRegistry = new PipeRegistry(defaultPipes);
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
import {DynamicProtoChangeDetector, JitProtoChangeDetector} from './proto_change_detector';
|
||||||
|
import {PipeFactory} from './pipes/pipe';
|
||||||
|
import {PipeRegistry} from './pipes/pipe_registry';
|
||||||
|
import {IterableChangesFactory} from './pipes/iterable_changes';
|
||||||
|
import {KeyValueChangesFactory} from './pipes/keyvalue_changes';
|
||||||
|
import {AsyncPipeFactory} from './pipes/async_pipe';
|
||||||
|
import {NullPipeFactory} from './pipes/null_pipe';
|
||||||
|
import {BindingRecord} from './binding_record';
|
||||||
|
import {DirectiveRecord} from './directive_record';
|
||||||
|
import {DEFAULT} from './constants';
|
||||||
|
import {ChangeDetection, ProtoChangeDetector} from './interfaces';
|
||||||
|
import {Injectable} from 'angular2/src/di/decorators';
|
||||||
|
import {List} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structural diffing for `Object`s and `Map`s.
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/pipes
|
||||||
|
*/
|
||||||
|
export var keyValDiff: List < PipeFactory >= [new KeyValueChangesFactory(), new NullPipeFactory()];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structural diffing for `Iterable` types such as `Array`s.
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/pipes
|
||||||
|
*/
|
||||||
|
export var iterableDiff: List <
|
||||||
|
PipeFactory >= [new IterableChangesFactory(), new NullPipeFactory()];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async binding to such types as Observable.
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/pipes
|
||||||
|
*/
|
||||||
|
export var async: List < PipeFactory >= [new AsyncPipeFactory(), new NullPipeFactory()];
|
||||||
|
|
||||||
|
export var defaultPipes = {
|
||||||
|
"iterableDiff": iterableDiff,
|
||||||
|
"keyValDiff": keyValDiff,
|
||||||
|
"async": async
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements change detection that does not require `eval()`.
|
||||||
|
*
|
||||||
|
* This is slower than {@link JitChangeDetection}.
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/change_detection
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class DynamicChangeDetection extends ChangeDetection {
|
||||||
|
constructor(public registry: PipeRegistry) { super(); }
|
||||||
|
|
||||||
|
createProtoChangeDetector(name: string, bindingRecords: List<BindingRecord>,
|
||||||
|
variableBindings: List<string>, directiveRecords: List<DirectiveRecord>,
|
||||||
|
changeControlStrategy: string = DEFAULT): ProtoChangeDetector {
|
||||||
|
return new DynamicProtoChangeDetector(this.registry, bindingRecords, variableBindings,
|
||||||
|
directiveRecords, changeControlStrategy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements faster change detection, by generating source code.
|
||||||
|
*
|
||||||
|
* This requires `eval()`. For change detection that does not require `eval()`, see {@link
|
||||||
|
*DynamicChangeDetection}.
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/change_detection
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class JitChangeDetection extends ChangeDetection {
|
||||||
|
constructor(public registry: PipeRegistry) { super(); }
|
||||||
|
|
||||||
|
createProtoChangeDetector(name: string, bindingRecords: List<BindingRecord>,
|
||||||
|
variableBindings: List<string>, directiveRecords: List<DirectiveRecord>,
|
||||||
|
changeControlStrategy: string = DEFAULT): ProtoChangeDetector {
|
||||||
|
return new JitProtoChangeDetector(this.registry, bindingRecords, variableBindings,
|
||||||
|
directiveRecords, changeControlStrategy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export var defaultPipeRegistry: PipeRegistry = new PipeRegistry(defaultPipes);
|
|
@ -18,7 +18,13 @@ import {
|
||||||
RECORD_TYPE_PIPE,
|
RECORD_TYPE_PIPE,
|
||||||
RECORD_TYPE_BINDING_PIPE,
|
RECORD_TYPE_BINDING_PIPE,
|
||||||
RECORD_TYPE_INTERPOLATE
|
RECORD_TYPE_INTERPOLATE
|
||||||
} from './proto_record';
|
} from './proto_record';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The code generator takes a list of proto records and creates a function/class
|
* The code generator takes a list of proto records and creates a function/class
|
||||||
|
@ -43,8 +49,8 @@ var MODE_ACCESSOR = "this.mode";
|
||||||
var TEMP_LOCAL = "temp";
|
var TEMP_LOCAL = "temp";
|
||||||
var CURRENT_PROTO = "currentProto";
|
var CURRENT_PROTO = "currentProto";
|
||||||
|
|
||||||
function typeTemplate(type:string, cons:string, detectChanges:string,
|
function typeTemplate(type: string, cons: string, detectChanges: string,
|
||||||
notifyOnAllChangesDone:string, setContext:string):string {
|
notifyOnAllChangesDone: string, setContext: string): string {
|
||||||
return `
|
return `
|
||||||
${cons}
|
${cons}
|
||||||
${detectChanges}
|
${detectChanges}
|
||||||
|
@ -57,7 +63,7 @@ return function(dispatcher, pipeRegistry) {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
function constructorTemplate(type: string, fieldsDefinitions: string): string {
|
||||||
return `
|
return `
|
||||||
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveRecords) {
|
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveRecords) {
|
||||||
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
||||||
|
@ -73,20 +79,23 @@ ${type}.prototype = Object.create(${ABSTRACT_CHANGE_DETECTOR}.prototype);
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pipeOnDestroyTemplate(pipeNames:List) {
|
function pipeOnDestroyTemplate(pipeNames: List<any>) {
|
||||||
return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
|
return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function hydrateTemplate(type:string, mode:string, fieldDefinitions:string, pipeOnDestroy:string,
|
function hydrateTemplate(type: string, mode: string, fieldDefinitions: string,
|
||||||
directiveFieldNames:List<String>, detectorFieldNames:List<String>):string {
|
pipeOnDestroy: string, directiveFieldNames: List<String>,
|
||||||
|
detectorFieldNames: List<String>): string {
|
||||||
var directiveInit = "";
|
var directiveInit = "";
|
||||||
for(var i = 0; i < directiveFieldNames.length; ++i) {
|
for (var i = 0; i < directiveFieldNames.length; ++i) {
|
||||||
directiveInit += `${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
directiveInit +=
|
||||||
|
`${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
var detectorInit = "";
|
var detectorInit = "";
|
||||||
for(var i = 0; i < detectorFieldNames.length; ++i) {
|
for (var i = 0; i < detectorFieldNames.length; ++i) {
|
||||||
detectorInit += `${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
detectorInit +=
|
||||||
|
`${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
|
@ -108,7 +117,7 @@ ${type}.prototype.hydrated = function() {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectChangesTemplate(type:string, body:string):string {
|
function detectChangesTemplate(type: string, body: string): string {
|
||||||
return `
|
return `
|
||||||
${type}.prototype.detectChangesInRecords = function(throwOnChange) {
|
${type}.prototype.detectChangesInRecords = function(throwOnChange) {
|
||||||
${body}
|
${body}
|
||||||
|
@ -116,7 +125,7 @@ ${type}.prototype.detectChangesInRecords = function(throwOnChange) {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function callOnAllChangesDoneTemplate(type:string, body:string):string {
|
function callOnAllChangesDoneTemplate(type: string, body: string): string {
|
||||||
return `
|
return `
|
||||||
${type}.prototype.callOnAllChangesDone = function() {
|
${type}.prototype.callOnAllChangesDone = function() {
|
||||||
${body}
|
${body}
|
||||||
|
@ -124,12 +133,13 @@ ${type}.prototype.callOnAllChangesDone = function() {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAllChangesDoneTemplate(directive:string):string {
|
function onAllChangesDoneTemplate(directive: string): string {
|
||||||
return `${directive}.onAllChangesDone();`;
|
return `${directive}.onAllChangesDone();`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function detectChangesBodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
|
function detectChangesBodyTemplate(localDefinitions: string, changeDefinitions: string,
|
||||||
|
records: string): string {
|
||||||
return `
|
return `
|
||||||
${localDefinitions}
|
${localDefinitions}
|
||||||
${changeDefinitions}
|
${changeDefinitions}
|
||||||
|
@ -143,9 +153,10 @@ ${records}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pipeCheckTemplate(protoIndex:number, context:string, bindingPropagationConfig:string, pipe:string, pipeType:string,
|
function pipeCheckTemplate(protoIndex: number, context: string, bindingPropagationConfig: string,
|
||||||
oldValue:string, newValue:string, change:string, update:string,
|
pipe: string, pipeType: string, oldValue: string, newValue: string,
|
||||||
addToChanges, lastInDirective:string):string{
|
change: string, update: string, addToChanges,
|
||||||
|
lastInDirective: string): string {
|
||||||
return `
|
return `
|
||||||
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
|
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
|
||||||
if (${pipe} === ${UTIL}.unitialized()) {
|
if (${pipe} === ${UTIL}.unitialized()) {
|
||||||
|
@ -167,8 +178,9 @@ ${lastInDirective}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function referenceCheckTemplate(protoIndex:number, assignment:string, oldValue:string, newValue:string, change:string,
|
function referenceCheckTemplate(protoIndex: number, assignment: string, oldValue: string,
|
||||||
update:string, addToChanges:string, lastInDirective:string):string {
|
newValue: string, change: string, update: string,
|
||||||
|
addToChanges: string, lastInDirective: string): string {
|
||||||
return `
|
return `
|
||||||
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
|
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
|
||||||
${assignment}
|
${assignment}
|
||||||
|
@ -182,23 +194,23 @@ ${lastInDirective}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function assignmentTemplate(field:string, value:string) {
|
function assignmentTemplate(field: string, value: string) {
|
||||||
return `${field} = ${value};`;
|
return `${field} = ${value};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function localDefinitionsTemplate(names:List):string {
|
function localDefinitionsTemplate(names: List<any>): string {
|
||||||
return names.map((n) => `var ${n};`).join("\n");
|
return names.map((n) => `var ${n};`).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeDefinitionsTemplate(names:List):string {
|
function changeDefinitionsTemplate(names: List<any>): string {
|
||||||
return names.map((n) => `var ${n} = false;`).join("\n");
|
return names.map((n) => `var ${n} = false;`).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function fieldDefinitionsTemplate(names:List):string {
|
function fieldDefinitionsTemplate(names: List<any>): string {
|
||||||
return names.map((n) => `${n} = ${UTIL}.unitialized();`).join("\n");
|
return names.map((n) => `${n} = ${UTIL}.unitialized();`).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function ifChangedGuardTemplate(changeNames:List, body:string):string {
|
function ifChangedGuardTemplate(changeNames: List<any>, body: string): string {
|
||||||
var cond = changeNames.join(" || ");
|
var cond = changeNames.join(" || ");
|
||||||
return `
|
return `
|
||||||
if (${cond}) {
|
if (${cond}) {
|
||||||
|
@ -207,11 +219,12 @@ if (${cond}) {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToChangesTemplate(oldValue:string, newValue:string):string {
|
function addToChangesTemplate(oldValue: string, newValue: string): string {
|
||||||
return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingRecord.propertyName, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`;
|
return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingRecord.propertyName, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDirectiveTemplate(oldValue:string, newValue:string, directiveProperty:string):string {
|
function updateDirectiveTemplate(oldValue: string, newValue: string,
|
||||||
|
directiveProperty: string): string {
|
||||||
return `
|
return `
|
||||||
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
|
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
|
||||||
${directiveProperty} = ${newValue};
|
${directiveProperty} = ${newValue};
|
||||||
|
@ -219,15 +232,15 @@ ${IS_CHANGED_LOCAL} = true;
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateElementTemplate(oldValue:string, newValue:string):string {
|
function updateElementTemplate(oldValue: string, newValue: string): string {
|
||||||
return `
|
return `
|
||||||
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
|
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
|
||||||
${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
|
${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function notifyOnChangesTemplate(directive:string):string{
|
function notifyOnChangesTemplate(directive: string): string {
|
||||||
return `
|
return `
|
||||||
if(${CHANGES_LOCAL}) {
|
if(${CHANGES_LOCAL}) {
|
||||||
${directive}.onChange(${CHANGES_LOCAL});
|
${directive}.onChange(${CHANGES_LOCAL});
|
||||||
${CHANGES_LOCAL} = null;
|
${CHANGES_LOCAL} = null;
|
||||||
|
@ -235,16 +248,16 @@ if(${CHANGES_LOCAL}) {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function notifyOnPushDetectorsTemplate(detector:string):string{
|
function notifyOnPushDetectorsTemplate(detector: string): string {
|
||||||
return `
|
return `
|
||||||
if(${IS_CHANGED_LOCAL}) {
|
if(${IS_CHANGED_LOCAL}) {
|
||||||
${detector}.markAsCheckOnce();
|
${detector}.markAsCheckOnce();
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function lastInDirectiveTemplate(notifyOnChanges:string, notifyOnPush:string):string{
|
function lastInDirectiveTemplate(notifyOnChanges: string, notifyOnPush: string): string {
|
||||||
return `
|
return `
|
||||||
${notifyOnChanges}
|
${notifyOnChanges}
|
||||||
${notifyOnPush}
|
${notifyOnPush}
|
||||||
${IS_CHANGED_LOCAL} = false;
|
${IS_CHANGED_LOCAL} = false;
|
||||||
|
@ -253,28 +266,20 @@ ${IS_CHANGED_LOCAL} = false;
|
||||||
|
|
||||||
|
|
||||||
export class ChangeDetectorJITGenerator {
|
export class ChangeDetectorJITGenerator {
|
||||||
typeName:string;
|
localNames: List<string>;
|
||||||
records:List<ProtoRecord>;
|
changeNames: List<string>;
|
||||||
directiveRecords:List;
|
fieldNames: List<string>;
|
||||||
localNames:List<string>;
|
pipeNames: List<string>;
|
||||||
changeNames:List<string>;
|
|
||||||
fieldNames:List<string>;
|
|
||||||
pipeNames:List<string>;
|
|
||||||
changeDetectionStrategy:stirng;
|
|
||||||
|
|
||||||
constructor(typeName:string, changeDetectionStrategy:string, records:List<ProtoRecord>, directiveRecords:List) {
|
|
||||||
this.typeName = typeName;
|
|
||||||
this.changeDetectionStrategy = changeDetectionStrategy;
|
|
||||||
this.records = records;
|
|
||||||
this.directiveRecords = directiveRecords;
|
|
||||||
|
|
||||||
|
constructor(public typeName: string, public changeDetectionStrategy: string,
|
||||||
|
public records: List<ProtoRecord>, public directiveRecords: List<any>) {
|
||||||
this.localNames = this.getLocalNames(records);
|
this.localNames = this.getLocalNames(records);
|
||||||
this.changeNames = this.getChangeNames(this.localNames);
|
this.changeNames = this.getChangeNames(this.localNames);
|
||||||
this.fieldNames = this.getFieldNames(this.localNames);
|
this.fieldNames = this.getFieldNames(this.localNames);
|
||||||
this.pipeNames = this.getPipeNames(this.localNames);
|
this.pipeNames = this.getPipeNames(this.localNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLocalNames(records:List<ProtoRecord>):List<string> {
|
getLocalNames(records: List<ProtoRecord>): List<string> {
|
||||||
var index = 0;
|
var index = 0;
|
||||||
var names = records.map((r) => {
|
var names = records.map((r) => {
|
||||||
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
|
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
|
||||||
|
@ -283,51 +288,49 @@ export class ChangeDetectorJITGenerator {
|
||||||
return ["context"].concat(names);
|
return ["context"].concat(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
getChangeNames(localNames:List<string>):List<string> {
|
getChangeNames(localNames: List<string>): List<string> {
|
||||||
return localNames.map((n) => `change_${n}`);
|
return localNames.map((n) => `change_${n}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFieldNames(localNames:List<string>):List<string> {
|
getFieldNames(localNames: List<string>): List<string> {
|
||||||
return localNames.map((n) => `this.${n}`);
|
return localNames.map((n) => `this.${n}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPipeNames(localNames:List<string>):List<string> {
|
getPipeNames(localNames: List<string>): List<string> {
|
||||||
return localNames.map((n) => `this.${n}_pipe`);
|
return localNames.map((n) => `this.${n}_pipe`);
|
||||||
}
|
}
|
||||||
|
|
||||||
generate():Function {
|
generate(): Function {
|
||||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
|
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
|
||||||
this.genCallOnAllChangesDone(), this.genHydrate());
|
this.genCallOnAllChangesDone(), this.genHydrate());
|
||||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveRecords', text)
|
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos',
|
||||||
(AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
|
'directiveRecords', text)(AbstractChangeDetector, ChangeDetectionUtil,
|
||||||
|
this.records, this.directiveRecords);
|
||||||
}
|
}
|
||||||
|
|
||||||
genConstructor():string {
|
genConstructor(): string {
|
||||||
return constructorTemplate(this.typeName, this.genFieldDefinitions());
|
return constructorTemplate(this.typeName, this.genFieldDefinitions());
|
||||||
}
|
}
|
||||||
|
|
||||||
genHydrate():string {
|
genHydrate(): string {
|
||||||
var mode = ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy);
|
var mode = ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy);
|
||||||
return hydrateTemplate(this.typeName, mode, this.genFieldDefinitions(),
|
return hydrateTemplate(this.typeName, mode, this.genFieldDefinitions(),
|
||||||
pipeOnDestroyTemplate(this.getNonNullPipeNames()),
|
pipeOnDestroyTemplate(this.getNonNullPipeNames()),
|
||||||
this.getDirectiveFieldNames(), this.getDetectorFieldNames());
|
this.getDirectiveFieldNames(), this.getDetectorFieldNames());
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveFieldNames():List<string> {
|
getDirectiveFieldNames(): List<string> {
|
||||||
return this.directiveRecords.map((d) => this.getDirective(d.directiveIndex));
|
return this.directiveRecords.map((d) => this.getDirective(d.directiveIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
getDetectorFieldNames():List<string> {
|
getDetectorFieldNames(): List<string> {
|
||||||
return this.directiveRecords.filter(r => r.isOnPushChangeDetection()).map((d) => this.getDetector(d.directiveIndex));
|
return this.directiveRecords.filter(r => r.isOnPushChangeDetection())
|
||||||
|
.map((d) => this.getDetector(d.directiveIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirective(d:DirectiveIndex) {
|
getDirective(d: DirectiveIndex) { return `this.directive_${d.name}`; }
|
||||||
return `this.directive_${d.name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDetector(d:DirectiveIndex) {
|
getDetector(d: DirectiveIndex) { return `this.detector_${d.name}`; }
|
||||||
return `this.detector_${d.name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
genFieldDefinitions() {
|
genFieldDefinitions() {
|
||||||
var fields = [];
|
var fields = [];
|
||||||
|
@ -338,7 +341,7 @@ export class ChangeDetectorJITGenerator {
|
||||||
return fieldDefinitionsTemplate(fields);
|
return fieldDefinitionsTemplate(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNonNullPipeNames():List<string> {
|
getNonNullPipeNames(): List<string> {
|
||||||
var pipes = [];
|
var pipes = [];
|
||||||
this.records.forEach((r) => {
|
this.records.forEach((r) => {
|
||||||
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||||
|
@ -348,12 +351,12 @@ export class ChangeDetectorJITGenerator {
|
||||||
return pipes;
|
return pipes;
|
||||||
}
|
}
|
||||||
|
|
||||||
genDetectChanges():string {
|
genDetectChanges(): string {
|
||||||
var body = this.genDetectChangesBody();
|
var body = this.genDetectChangesBody();
|
||||||
return detectChangesTemplate(this.typeName, body);
|
return detectChangesTemplate(this.typeName, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
genCallOnAllChangesDone():string {
|
genCallOnAllChangesDone(): string {
|
||||||
var notifications = [];
|
var notifications = [];
|
||||||
var dirs = this.directiveRecords;
|
var dirs = this.directiveRecords;
|
||||||
|
|
||||||
|
@ -368,28 +371,24 @@ export class ChangeDetectorJITGenerator {
|
||||||
return callOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
|
return callOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
genDetectChangesBody():string {
|
genDetectChangesBody(): string {
|
||||||
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
|
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
|
||||||
return detectChangesBodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
|
return detectChangesBodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
|
||||||
}
|
}
|
||||||
|
|
||||||
genLocalDefinitions():string {
|
genLocalDefinitions(): string { return localDefinitionsTemplate(this.localNames); }
|
||||||
return localDefinitionsTemplate(this.localNames);
|
|
||||||
}
|
|
||||||
|
|
||||||
genChangeDefinitions():string {
|
genChangeDefinitions(): string { return changeDefinitionsTemplate(this.changeNames); }
|
||||||
return changeDefinitionsTemplate(this.changeNames);
|
|
||||||
}
|
|
||||||
|
|
||||||
genRecord(r:ProtoRecord):string {
|
genRecord(r: ProtoRecord): string {
|
||||||
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||||
return this.genPipeCheck (r);
|
return this.genPipeCheck(r);
|
||||||
} else {
|
} else {
|
||||||
return this.genReferenceCheck(r);
|
return this.genReferenceCheck(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genPipeCheck(r:ProtoRecord):string {
|
genPipeCheck(r: ProtoRecord): string {
|
||||||
var context = this.localNames[r.contextIndex];
|
var context = this.localNames[r.contextIndex];
|
||||||
var oldValue = this.fieldNames[r.selfIndex];
|
var oldValue = this.fieldNames[r.selfIndex];
|
||||||
var newValue = this.localNames[r.selfIndex];
|
var newValue = this.localNames[r.selfIndex];
|
||||||
|
@ -402,11 +401,11 @@ export class ChangeDetectorJITGenerator {
|
||||||
var addToChanges = this.genAddToChanges(r);
|
var addToChanges = this.genAddToChanges(r);
|
||||||
var lastInDirective = this.genLastInDirective(r);
|
var lastInDirective = this.genLastInDirective(r);
|
||||||
|
|
||||||
return pipeCheckTemplate(r.selfIndex - 1, context, cdRef, pipe, r.name, oldValue, newValue, change,
|
return pipeCheckTemplate(r.selfIndex - 1, context, cdRef, pipe, r.name, oldValue, newValue,
|
||||||
update, addToChanges, lastInDirective);
|
change, update, addToChanges, lastInDirective);
|
||||||
}
|
}
|
||||||
|
|
||||||
genReferenceCheck(r:ProtoRecord):string {
|
genReferenceCheck(r: ProtoRecord): string {
|
||||||
var oldValue = this.fieldNames[r.selfIndex];
|
var oldValue = this.fieldNames[r.selfIndex];
|
||||||
var newValue = this.localNames[r.selfIndex];
|
var newValue = this.localNames[r.selfIndex];
|
||||||
var change = this.changeNames[r.selfIndex];
|
var change = this.changeNames[r.selfIndex];
|
||||||
|
@ -417,7 +416,7 @@ export class ChangeDetectorJITGenerator {
|
||||||
var lastInDirective = this.genLastInDirective(r);
|
var lastInDirective = this.genLastInDirective(r);
|
||||||
|
|
||||||
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
|
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
|
||||||
update, addToChanges, lastInDirective);
|
update, addToChanges, lastInDirective);
|
||||||
if (r.isPureFunction()) {
|
if (r.isPureFunction()) {
|
||||||
return this.ifChangedGuard(r, check);
|
return this.ifChangedGuard(r, check);
|
||||||
} else {
|
} else {
|
||||||
|
@ -425,7 +424,7 @@ export class ChangeDetectorJITGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genUpdateCurrentValue(r:ProtoRecord):string {
|
genUpdateCurrentValue(r: ProtoRecord): string {
|
||||||
var context = this.getContext(r);
|
var context = this.getContext(r);
|
||||||
var newValue = this.localNames[r.selfIndex];
|
var newValue = this.localNames[r.selfIndex];
|
||||||
var args = this.genArgs(r);
|
var args = this.genArgs(r);
|
||||||
|
@ -464,7 +463,7 @@ export class ChangeDetectorJITGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getContext(r:ProtoRecord):string {
|
getContext(r: ProtoRecord): string {
|
||||||
if (r.contextIndex == -1) {
|
if (r.contextIndex == -1) {
|
||||||
return this.getDirective(r.directiveIndex);
|
return this.getDirective(r.directiveIndex);
|
||||||
} else {
|
} else {
|
||||||
|
@ -472,11 +471,11 @@ export class ChangeDetectorJITGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ifChangedGuard(r:ProtoRecord, body:string):string {
|
ifChangedGuard(r: ProtoRecord, body: string): string {
|
||||||
return ifChangedGuardTemplate(r.args.map((a) => this.changeNames[a]), body);
|
return ifChangedGuardTemplate(r.args.map((a) => this.changeNames[a]), body);
|
||||||
}
|
}
|
||||||
|
|
||||||
genInterpolation(r:ProtoRecord):string{
|
genInterpolation(r: ProtoRecord): string {
|
||||||
var res = "";
|
var res = "";
|
||||||
for (var i = 0; i < r.args.length; ++i) {
|
for (var i = 0; i < r.args.length; ++i) {
|
||||||
res += this.genLiteral(r.fixedArgs[i]);
|
res += this.genLiteral(r.fixedArgs[i]);
|
||||||
|
@ -488,38 +487,37 @@ export class ChangeDetectorJITGenerator {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
genLiteral(value):string {
|
genLiteral(value): string { return JSON.stringify(value); }
|
||||||
return JSON.stringify(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
genUpdateDirectiveOrElement(r:ProtoRecord):string {
|
genUpdateDirectiveOrElement(r: ProtoRecord): string {
|
||||||
if (! r.lastInBinding) return "";
|
if (!r.lastInBinding) return "";
|
||||||
|
|
||||||
var newValue = this.localNames[r.selfIndex];
|
var newValue = this.localNames[r.selfIndex];
|
||||||
var oldValue = this.fieldNames[r.selfIndex];
|
var oldValue = this.fieldNames[r.selfIndex];
|
||||||
|
|
||||||
var br = r.bindingRecord;
|
var br = r.bindingRecord;
|
||||||
if (br.isDirective()) {
|
if (br.isDirective()) {
|
||||||
var directiveProperty = `${this.getDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
|
var directiveProperty =
|
||||||
|
`${this.getDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
|
||||||
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
|
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
|
||||||
} else {
|
} else {
|
||||||
return updateElementTemplate(oldValue, newValue);
|
return updateElementTemplate(oldValue, newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genAddToChanges(r:ProtoRecord):string {
|
genAddToChanges(r: ProtoRecord): string {
|
||||||
var newValue = this.localNames[r.selfIndex];
|
var newValue = this.localNames[r.selfIndex];
|
||||||
var oldValue = this.fieldNames[r.selfIndex];
|
var oldValue = this.fieldNames[r.selfIndex];
|
||||||
return r.bindingRecord.callOnChange() ? addToChangesTemplate(oldValue, newValue) : "";
|
return r.bindingRecord.callOnChange() ? addToChangesTemplate(oldValue, newValue) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
genLastInDirective(r:ProtoRecord):string{
|
genLastInDirective(r: ProtoRecord): string {
|
||||||
var onChanges = this.genNotifyOnChanges(r);
|
var onChanges = this.genNotifyOnChanges(r);
|
||||||
var onPush = this.genNotifyOnPushDetectors(r);
|
var onPush = this.genNotifyOnPushDetectors(r);
|
||||||
return lastInDirectiveTemplate(onChanges, onPush);
|
return lastInDirectiveTemplate(onChanges, onPush);
|
||||||
}
|
}
|
||||||
|
|
||||||
genNotifyOnChanges(r:ProtoRecord):string{
|
genNotifyOnChanges(r: ProtoRecord): string {
|
||||||
var br = r.bindingRecord;
|
var br = r.bindingRecord;
|
||||||
if (r.lastInDirective && br.callOnChange()) {
|
if (r.lastInDirective && br.callOnChange()) {
|
||||||
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord.directiveIndex));
|
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord.directiveIndex));
|
||||||
|
@ -528,7 +526,7 @@ export class ChangeDetectorJITGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genNotifyOnPushDetectors(r:ProtoRecord):string{
|
genNotifyOnPushDetectors(r: ProtoRecord): string {
|
||||||
var br = r.bindingRecord;
|
var br = r.bindingRecord;
|
||||||
if (r.lastInDirective && br.isOnPushChangeDetection()) {
|
if (r.lastInDirective && br.isOnPushChangeDetection()) {
|
||||||
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord.directiveIndex));
|
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord.directiveIndex));
|
||||||
|
@ -537,11 +535,5 @@ export class ChangeDetectorJITGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genArgs(r:ProtoRecord):string {
|
genArgs(r: ProtoRecord): string { return r.args.map((arg) => this.localNames[arg]).join(", "); }
|
||||||
return r.args.map((arg) => this.localNames[arg]).join(", ");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,139 +0,0 @@
|
||||||
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
|
|
||||||
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {ProtoRecord} from './proto_record';
|
|
||||||
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
|
|
||||||
import {WrappedValue} from './pipes/pipe';
|
|
||||||
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
|
|
||||||
|
|
||||||
export var uninitialized = new Object();
|
|
||||||
|
|
||||||
export class SimpleChange {
|
|
||||||
previousValue:any;
|
|
||||||
currentValue:any;
|
|
||||||
|
|
||||||
constructor(previousValue:any, currentValue:any) {
|
|
||||||
this.previousValue = previousValue;
|
|
||||||
this.currentValue = currentValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _simpleChangesIndex = 0;
|
|
||||||
var _simpleChanges = [
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null),
|
|
||||||
new SimpleChange(null, null)
|
|
||||||
];
|
|
||||||
|
|
||||||
function _simpleChange(previousValue, currentValue) {
|
|
||||||
var index = _simpleChangesIndex++ % 20;
|
|
||||||
var s = _simpleChanges[index];
|
|
||||||
s.previousValue = previousValue;
|
|
||||||
s.currentValue = currentValue;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ChangeDetectionUtil {
|
|
||||||
static unitialized() {
|
|
||||||
return uninitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
static arrayFn0() { return []; }
|
|
||||||
static arrayFn1(a1) { return [a1]; }
|
|
||||||
static arrayFn2(a1, a2) { return [a1, a2]; }
|
|
||||||
static arrayFn3(a1, a2, a3) { return [a1, a2, a3]; }
|
|
||||||
static arrayFn4(a1, a2, a3, a4) { return [a1, a2, a3, a4]; }
|
|
||||||
static arrayFn5(a1, a2, a3, a4, a5) { return [a1, a2, a3, a4, a5]; }
|
|
||||||
static arrayFn6(a1, a2, a3, a4, a5, a6) { return [a1, a2, a3, a4, a5, a6]; }
|
|
||||||
static arrayFn7(a1, a2, a3, a4, a5, a6, a7) { return [a1, a2, a3, a4, a5, a6, a7]; }
|
|
||||||
static arrayFn8(a1, a2, a3, a4, a5, a6, a7, a8) { return [a1, a2, a3, a4, a5, a6, a7, a8]; }
|
|
||||||
static arrayFn9(a1, a2, a3, a4, a5, a6, a7, a8, a9) { return [a1, a2, a3, a4, a5, a6, a7, a8, a9]; }
|
|
||||||
|
|
||||||
static operation_negate(value) {return !value;}
|
|
||||||
static operation_add(left, right) {return left + right;}
|
|
||||||
static operation_subtract(left, right) {return left - right;}
|
|
||||||
static operation_multiply(left, right) {return left * right;}
|
|
||||||
static operation_divide(left, right) {return left / right;}
|
|
||||||
static operation_remainder(left, right) {return left % right;}
|
|
||||||
static operation_equals(left, right) {return left == right;}
|
|
||||||
static operation_not_equals(left, right) {return left != right;}
|
|
||||||
static operation_less_then(left, right) {return left < right;}
|
|
||||||
static operation_greater_then(left, right) {return left > right;}
|
|
||||||
static operation_less_or_equals_then(left, right) {return left <= right;}
|
|
||||||
static operation_greater_or_equals_then(left, right) {return left >= right;}
|
|
||||||
static operation_logical_and(left, right) {return left && right;}
|
|
||||||
static operation_logical_or(left, right) {return left || right;}
|
|
||||||
static cond(cond, trueVal, falseVal) {return cond ? trueVal : falseVal;}
|
|
||||||
|
|
||||||
static mapFn(keys:List) {
|
|
||||||
function buildMap(values) {
|
|
||||||
var res = StringMapWrapper.create();
|
|
||||||
for(var i = 0; i < keys.length; ++i) {
|
|
||||||
StringMapWrapper.set(res, keys[i], values[i]);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (keys.length) {
|
|
||||||
case 0: return () => [];
|
|
||||||
case 1: return (a1) => buildMap([a1]);
|
|
||||||
case 2: return (a1, a2) => buildMap([a1, a2]);
|
|
||||||
case 3: return (a1, a2, a3) => buildMap([a1, a2, a3]);
|
|
||||||
case 4: return (a1, a2, a3, a4) => buildMap([a1, a2, a3, a4]);
|
|
||||||
case 5: return (a1, a2, a3, a4, a5) => buildMap([a1, a2, a3, a4, a5]);
|
|
||||||
case 6: return (a1, a2, a3, a4, a5, a6) => buildMap([a1, a2, a3, a4, a5, a6]);
|
|
||||||
case 7: return (a1, a2, a3, a4, a5, a6, a7) => buildMap([a1, a2, a3, a4, a5, a6, a7]);
|
|
||||||
case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) => buildMap([a1, a2, a3, a4, a5, a6, a7, a8]);
|
|
||||||
case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) => buildMap([a1, a2, a3, a4, a5, a6, a7, a8, a9]);
|
|
||||||
default: throw new BaseException(`Does not support literal maps with more than 9 elements`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static keyedAccess(obj, args) {
|
|
||||||
return obj[args[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
static unwrapValue(value:any):any {
|
|
||||||
if (value instanceof WrappedValue) {
|
|
||||||
return value.wrapped;
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static throwOnChange(proto:ProtoRecord, change) {
|
|
||||||
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
|
|
||||||
}
|
|
||||||
|
|
||||||
static changeDetectionMode(strategy:string) {
|
|
||||||
return strategy == ON_PUSH ? CHECK_ONCE : CHECK_ALWAYS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static simpleChange(previousValue:any, currentValue:any):SimpleChange {
|
|
||||||
return _simpleChange(previousValue, currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
static addChange(changes, propertyName:string, change){
|
|
||||||
if (isBlank(changes)) {
|
|
||||||
changes = {};
|
|
||||||
}
|
|
||||||
changes[propertyName] = change;
|
|
||||||
return changes;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
|
||||||
|
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {ProtoRecord} from './proto_record';
|
||||||
|
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
|
||||||
|
import {WrappedValue} from './pipes/pipe';
|
||||||
|
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
export var uninitialized = new Object();
|
||||||
|
|
||||||
|
export class SimpleChange {
|
||||||
|
constructor(public previousValue: any, public currentValue: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _simpleChangesIndex = 0;
|
||||||
|
var _simpleChanges = [
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null),
|
||||||
|
new SimpleChange(null, null)
|
||||||
|
];
|
||||||
|
|
||||||
|
function _simpleChange(previousValue, currentValue) {
|
||||||
|
var index = _simpleChangesIndex++ % 20;
|
||||||
|
var s = _simpleChanges[index];
|
||||||
|
s.previousValue = previousValue;
|
||||||
|
s.currentValue = currentValue;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeDetectionUtil {
|
||||||
|
static unitialized() { return uninitialized; }
|
||||||
|
|
||||||
|
static arrayFn0() { return []; }
|
||||||
|
static arrayFn1(a1) { return [a1]; }
|
||||||
|
static arrayFn2(a1, a2) { return [a1, a2]; }
|
||||||
|
static arrayFn3(a1, a2, a3) { return [a1, a2, a3]; }
|
||||||
|
static arrayFn4(a1, a2, a3, a4) { return [a1, a2, a3, a4]; }
|
||||||
|
static arrayFn5(a1, a2, a3, a4, a5) { return [a1, a2, a3, a4, a5]; }
|
||||||
|
static arrayFn6(a1, a2, a3, a4, a5, a6) { return [a1, a2, a3, a4, a5, a6]; }
|
||||||
|
static arrayFn7(a1, a2, a3, a4, a5, a6, a7) { return [a1, a2, a3, a4, a5, a6, a7]; }
|
||||||
|
static arrayFn8(a1, a2, a3, a4, a5, a6, a7, a8) { return [a1, a2, a3, a4, a5, a6, a7, a8]; }
|
||||||
|
static arrayFn9(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
||||||
|
return [a1, a2, a3, a4, a5, a6, a7, a8, a9];
|
||||||
|
}
|
||||||
|
|
||||||
|
static operation_negate(value) { return !value; }
|
||||||
|
static operation_add(left, right) { return left + right; }
|
||||||
|
static operation_subtract(left, right) { return left - right; }
|
||||||
|
static operation_multiply(left, right) { return left * right; }
|
||||||
|
static operation_divide(left, right) { return left / right; }
|
||||||
|
static operation_remainder(left, right) { return left % right; }
|
||||||
|
static operation_equals(left, right) { return left == right; }
|
||||||
|
static operation_not_equals(left, right) { return left != right; }
|
||||||
|
static operation_less_then(left, right) { return left < right; }
|
||||||
|
static operation_greater_then(left, right) { return left > right; }
|
||||||
|
static operation_less_or_equals_then(left, right) { return left <= right; }
|
||||||
|
static operation_greater_or_equals_then(left, right) { return left >= right; }
|
||||||
|
static operation_logical_and(left, right) { return left && right; }
|
||||||
|
static operation_logical_or(left, right) { return left || right; }
|
||||||
|
static cond(cond, trueVal, falseVal) { return cond ? trueVal : falseVal; }
|
||||||
|
|
||||||
|
static mapFn(keys: List<any>) {
|
||||||
|
function buildMap(values) {
|
||||||
|
var res = StringMapWrapper.create();
|
||||||
|
for (var i = 0; i < keys.length; ++i) {
|
||||||
|
StringMapWrapper.set(res, keys[i], values[i]);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (keys.length) {
|
||||||
|
case 0:
|
||||||
|
return () => [];
|
||||||
|
case 1:
|
||||||
|
return (a1) => buildMap([a1]);
|
||||||
|
case 2:
|
||||||
|
return (a1, a2) => buildMap([a1, a2]);
|
||||||
|
case 3:
|
||||||
|
return (a1, a2, a3) => buildMap([a1, a2, a3]);
|
||||||
|
case 4:
|
||||||
|
return (a1, a2, a3, a4) => buildMap([a1, a2, a3, a4]);
|
||||||
|
case 5:
|
||||||
|
return (a1, a2, a3, a4, a5) => buildMap([a1, a2, a3, a4, a5]);
|
||||||
|
case 6:
|
||||||
|
return (a1, a2, a3, a4, a5, a6) => buildMap([a1, a2, a3, a4, a5, a6]);
|
||||||
|
case 7:
|
||||||
|
return (a1, a2, a3, a4, a5, a6, a7) => buildMap([a1, a2, a3, a4, a5, a6, a7]);
|
||||||
|
case 8:
|
||||||
|
return (a1, a2, a3, a4, a5, a6, a7, a8) => buildMap([a1, a2, a3, a4, a5, a6, a7, a8]);
|
||||||
|
case 9:
|
||||||
|
return (a1, a2, a3, a4, a5, a6, a7, a8, a9) =>
|
||||||
|
buildMap([a1, a2, a3, a4, a5, a6, a7, a8, a9]);
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Does not support literal maps with more than 9 elements`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static keyedAccess(obj, args) { return obj[args[0]]; }
|
||||||
|
|
||||||
|
static unwrapValue(value: any): any {
|
||||||
|
if (value instanceof WrappedValue) {
|
||||||
|
return value.wrapped;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static throwOnChange(proto: ProtoRecord, change) {
|
||||||
|
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
|
||||||
|
}
|
||||||
|
|
||||||
|
static changeDetectionMode(strategy: string) {
|
||||||
|
return strategy == ON_PUSH ? CHECK_ONCE : CHECK_ALWAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static simpleChange(previousValue: any, currentValue: any): SimpleChange {
|
||||||
|
return _simpleChange(previousValue, currentValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static addChange(changes, propertyName: string, change) {
|
||||||
|
if (isBlank(changes)) {
|
||||||
|
changes = {};
|
||||||
|
}
|
||||||
|
changes[propertyName] = change;
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,41 +1,40 @@
|
||||||
import {ChangeDetector} from './interfaces';
|
import {ChangeDetector} from './interfaces';
|
||||||
import {CHECK_ONCE, DETACHED, CHECK_ALWAYS} from './constants';
|
import {CHECK_ONCE, DETACHED, CHECK_ALWAYS} from './constants';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controls change detection.
|
* Controls change detection.
|
||||||
*
|
*
|
||||||
* {@link ChangeDetectorRef} allows requesting checks for detectors that rely on observables. It also allows detaching and
|
* {@link ChangeDetectorRef} allows requesting checks for detectors that rely on observables. It
|
||||||
|
*also allows detaching and
|
||||||
* attaching change detector subtrees.
|
* attaching change detector subtrees.
|
||||||
*
|
*
|
||||||
* @exportedAs angular2/change_detection
|
* @exportedAs angular2/change_detection
|
||||||
*/
|
*/
|
||||||
export class ChangeDetectorRef {
|
export class ChangeDetectorRef {
|
||||||
_cd:ChangeDetector;
|
constructor(private _cd: ChangeDetector) {}
|
||||||
|
|
||||||
constructor(cd:ChangeDetector) {
|
|
||||||
this._cd = cd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request to check all ON_PUSH ancestors.
|
* Request to check all ON_PUSH ancestors.
|
||||||
*/
|
*/
|
||||||
requestCheck() {
|
requestCheck() { this._cd.markPathToRootAsCheckOnce(); }
|
||||||
this._cd.markPathToRootAsCheckOnce();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detaches the change detector from the change detector tree.
|
* Detaches the change detector from the change detector tree.
|
||||||
*
|
*
|
||||||
* The detached change detector will not be checked until it is reattached.
|
* The detached change detector will not be checked until it is reattached.
|
||||||
*/
|
*/
|
||||||
detach() {
|
detach() { this._cd.mode = DETACHED; }
|
||||||
this._cd.mode = DETACHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reattach the change detector to the change detector tree.
|
* Reattach the change detector to the change detector tree.
|
||||||
*
|
*
|
||||||
* This also requests a check of this change detector. This reattached change detector will be checked during the
|
* This also requests a check of this change detector. This reattached change detector will be
|
||||||
|
*checked during the
|
||||||
* next change detection run.
|
* next change detection run.
|
||||||
*/
|
*/
|
||||||
reattach() {
|
reattach() {
|
|
@ -2,6 +2,11 @@ import {isPresent} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {RECORD_TYPE_SELF, ProtoRecord} from './proto_record';
|
import {RECORD_TYPE_SELF, ProtoRecord} from './proto_record';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes "duplicate" records. It assuming that record evaluation does not
|
* Removes "duplicate" records. It assuming that record evaluation does not
|
||||||
* have side-effects.
|
* have side-effects.
|
||||||
|
@ -12,7 +17,7 @@ import {RECORD_TYPE_SELF, ProtoRecord} from './proto_record';
|
||||||
* Records that are last in bindings CANNOT be removed, and instead are
|
* Records that are last in bindings CANNOT be removed, and instead are
|
||||||
* replaced with very cheap SELF records.
|
* replaced with very cheap SELF records.
|
||||||
*/
|
*/
|
||||||
export function coalesce(records:List<ProtoRecord>):List<ProtoRecord> {
|
export function coalesce(records: List<ProtoRecord>): List<ProtoRecord> {
|
||||||
var res = ListWrapper.create();
|
var res = ListWrapper.create();
|
||||||
var indexMap = MapWrapper.create();
|
var indexMap = MapWrapper.create();
|
||||||
|
|
||||||
|
@ -37,52 +42,27 @@ export function coalesce(records:List<ProtoRecord>):List<ProtoRecord> {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):ProtoRecord {
|
function _selfRecord(r: ProtoRecord, contextIndex: number, selfIndex: number): ProtoRecord {
|
||||||
return new ProtoRecord(
|
return new ProtoRecord(RECORD_TYPE_SELF, "self", null, [], r.fixedArgs, contextIndex,
|
||||||
RECORD_TYPE_SELF,
|
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString,
|
||||||
"self",
|
r.lastInBinding, r.lastInDirective);
|
||||||
null,
|
|
||||||
[],
|
|
||||||
r.fixedArgs,
|
|
||||||
contextIndex,
|
|
||||||
r.directiveIndex,
|
|
||||||
selfIndex,
|
|
||||||
r.bindingRecord,
|
|
||||||
r.expressionAsString,
|
|
||||||
r.lastInBinding,
|
|
||||||
r.lastInDirective
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _findMatching(r:ProtoRecord, rs:List<ProtoRecord>){
|
function _findMatching(r: ProtoRecord, rs: List<ProtoRecord>) {
|
||||||
return ListWrapper.find(rs, (rr) =>
|
return ListWrapper.find(rs, (rr) => rr.mode === r.mode && rr.funcOrValue === r.funcOrValue &&
|
||||||
rr.mode === r.mode &&
|
rr.contextIndex === r.contextIndex &&
|
||||||
rr.funcOrValue === r.funcOrValue &&
|
ListWrapper.equals(rr.args, r.args));
|
||||||
rr.contextIndex === r.contextIndex &&
|
|
||||||
ListWrapper.equals(rr.args, r.args)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
|
function _replaceIndices(r: ProtoRecord, selfIndex: number, indexMap: Map<any, any>) {
|
||||||
var args = ListWrapper.map(r.args, (a) => _map(indexMap, a));
|
var args = ListWrapper.map(r.args, (a) => _map(indexMap, a));
|
||||||
var contextIndex = _map(indexMap, r.contextIndex);
|
var contextIndex = _map(indexMap, r.contextIndex);
|
||||||
return new ProtoRecord(
|
return new ProtoRecord(r.mode, r.name, r.funcOrValue, args, r.fixedArgs, contextIndex,
|
||||||
r.mode,
|
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString,
|
||||||
r.name,
|
r.lastInBinding, r.lastInDirective);
|
||||||
r.funcOrValue,
|
|
||||||
args,
|
|
||||||
r.fixedArgs,
|
|
||||||
contextIndex,
|
|
||||||
r.directiveIndex,
|
|
||||||
selfIndex,
|
|
||||||
r.bindingRecord,
|
|
||||||
r.expressionAsString,
|
|
||||||
r.lastInBinding,
|
|
||||||
r.lastInDirective
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _map(indexMap:Map, value:number) {
|
function _map(indexMap: Map<any, any>, value: number) {
|
||||||
var r = MapWrapper.get(indexMap, value)
|
var r = MapWrapper.get(indexMap, value);
|
||||||
return isPresent(r) ? r : value;
|
return isPresent(r) ? r : value;
|
||||||
}
|
}
|
|
@ -1,28 +1,33 @@
|
||||||
//TODO:vsavkin Use enums after switching to TypeScript
|
// TODO:vsavkin Use enums after switching to TypeScript
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
|
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
|
||||||
* will become CHECKED.
|
* will become CHECKED.
|
||||||
*/
|
*/
|
||||||
export const CHECK_ONCE="CHECK_ONCE";
|
export const CHECK_ONCE = "CHECK_ONCE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CHECKED means that the change detector should be skipped until its mode changes to
|
* CHECKED means that the change detector should be skipped until its mode changes to
|
||||||
* CHECK_ONCE or CHECK_ALWAYS.
|
* CHECK_ONCE or CHECK_ALWAYS.
|
||||||
*/
|
*/
|
||||||
export const CHECKED="CHECKED";
|
export const CHECKED = "CHECKED";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CHECK_ALWAYS means that after calling detectChanges the mode of the change detector
|
* CHECK_ALWAYS means that after calling detectChanges the mode of the change detector
|
||||||
* will remain CHECK_ALWAYS.
|
* will remain CHECK_ALWAYS.
|
||||||
*/
|
*/
|
||||||
export const CHECK_ALWAYS="ALWAYS_CHECK";
|
export const CHECK_ALWAYS = "ALWAYS_CHECK";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DETACHED means that the change detector sub tree is not a part of the main tree and
|
* DETACHED means that the change detector sub tree is not a part of the main tree and
|
||||||
* should be skipped.
|
* should be skipped.
|
||||||
*/
|
*/
|
||||||
export const DETACHED="DETACHED";
|
export const DETACHED = "DETACHED";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ON_PUSH means that the change detector's mode will be set to CHECK_ONCE during hydration.
|
* ON_PUSH means that the change detector's mode will be set to CHECK_ONCE during hydration.
|
|
@ -1,34 +0,0 @@
|
||||||
import {ON_PUSH} from './constants';
|
|
||||||
import {StringWrapper} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
export class DirectiveIndex {
|
|
||||||
elementIndex:number;
|
|
||||||
directiveIndex:number;
|
|
||||||
|
|
||||||
constructor(elementIndex:number, directiveIndex:number) {
|
|
||||||
this.elementIndex = elementIndex;
|
|
||||||
this.directiveIndex = directiveIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return `${this.elementIndex}_${this.directiveIndex}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DirectiveRecord {
|
|
||||||
directiveIndex:DirectiveIndex;
|
|
||||||
callOnAllChangesDone:boolean;
|
|
||||||
callOnChange:boolean;
|
|
||||||
changeDetection:string;
|
|
||||||
|
|
||||||
constructor(directiveIndex:DirectiveIndex, callOnAllChangesDone:boolean, callOnChange:boolean, changeDetection:string) {
|
|
||||||
this.directiveIndex = directiveIndex;
|
|
||||||
this.callOnAllChangesDone = callOnAllChangesDone;
|
|
||||||
this.callOnChange = callOnChange;
|
|
||||||
this.changeDetection = changeDetection;
|
|
||||||
}
|
|
||||||
|
|
||||||
isOnPushChangeDetection():boolean {
|
|
||||||
return StringWrapper.equals(this.changeDetection, ON_PUSH);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import {ON_PUSH} from './constants';
|
||||||
|
import {StringWrapper} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
export class DirectiveIndex {
|
||||||
|
constructor(public elementIndex: number, public directiveIndex: number) {}
|
||||||
|
|
||||||
|
get name() { return `${this.elementIndex}_${this.directiveIndex}`; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DirectiveRecord {
|
||||||
|
constructor(public directiveIndex: DirectiveIndex, public callOnAllChangesDone: boolean,
|
||||||
|
public callOnChange: boolean, public changeDetection: string) {}
|
||||||
|
|
||||||
|
isOnPushChangeDetection(): boolean { return StringWrapper.equals(this.changeDetection, ON_PUSH); }
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
|
||||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
import {AbstractChangeDetector} from './abstract_change_detector';
|
||||||
import {BindingRecord} from './binding_record';
|
import {BindingRecord} from './binding_record';
|
||||||
import {PipeRegistry} from './pipes/pipe_registry';
|
import {PipeRegistry} from './pipes/pipe_registry';
|
||||||
import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
|
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
|
||||||
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -20,35 +20,31 @@ import {
|
||||||
RECORD_TYPE_PIPE,
|
RECORD_TYPE_PIPE,
|
||||||
RECORD_TYPE_BINDING_PIPE,
|
RECORD_TYPE_BINDING_PIPE,
|
||||||
RECORD_TYPE_INTERPOLATE
|
RECORD_TYPE_INTERPOLATE
|
||||||
} from './proto_record';
|
} from './proto_record';
|
||||||
|
|
||||||
import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './exceptions';
|
import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './exceptions';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
export class DynamicChangeDetector extends AbstractChangeDetector {
|
export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
dispatcher:any;
|
locals: any;
|
||||||
pipeRegistry;
|
values: List<any>;
|
||||||
|
changes: List<any>;
|
||||||
|
pipes: List<any>;
|
||||||
|
prevContexts: List<any>;
|
||||||
|
directives: any;
|
||||||
|
|
||||||
locals:any;
|
constructor(private changeControlStrategy: string, private dispatcher: any,
|
||||||
values:List;
|
private pipeRegistry: PipeRegistry, private protos: List<ProtoRecord>,
|
||||||
changes:List;
|
private directiveRecords: List<any>) {
|
||||||
pipes:List;
|
|
||||||
prevContexts:List;
|
|
||||||
|
|
||||||
protos:List<ProtoRecord>;
|
|
||||||
directives:any;
|
|
||||||
directiveRecords:List;
|
|
||||||
changeControlStrategy:string;
|
|
||||||
|
|
||||||
constructor(changeControlStrategy:string, dispatcher:any, pipeRegistry:PipeRegistry,
|
|
||||||
protoRecords:List<ProtoRecord>, directiveRecords:List) {
|
|
||||||
super();
|
super();
|
||||||
this.dispatcher = dispatcher;
|
this.values = ListWrapper.createFixedSize(protos.length + 1);
|
||||||
this.pipeRegistry = pipeRegistry;
|
this.pipes = ListWrapper.createFixedSize(protos.length + 1);
|
||||||
|
this.prevContexts = ListWrapper.createFixedSize(protos.length + 1);
|
||||||
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
|
this.changes = ListWrapper.createFixedSize(protos.length + 1);
|
||||||
this.pipes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
|
||||||
this.prevContexts = ListWrapper.createFixedSize(protoRecords.length + 1);
|
|
||||||
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
|
||||||
|
|
||||||
ListWrapper.fill(this.values, uninitialized);
|
ListWrapper.fill(this.values, uninitialized);
|
||||||
ListWrapper.fill(this.pipes, null);
|
ListWrapper.fill(this.pipes, null);
|
||||||
|
@ -56,13 +52,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
ListWrapper.fill(this.changes, false);
|
ListWrapper.fill(this.changes, false);
|
||||||
this.locals = null;
|
this.locals = null;
|
||||||
this.directives = null;
|
this.directives = null;
|
||||||
|
|
||||||
this.protos = protoRecords;
|
|
||||||
this.directiveRecords = directiveRecords;
|
|
||||||
this.changeControlStrategy = changeControlStrategy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrate(context:any, locals:any, directives:any) {
|
hydrate(context: any, locals: any, directives: any) {
|
||||||
this.mode = ChangeDetectionUtil.changeDetectionMode(this.changeControlStrategy);
|
this.mode = ChangeDetectionUtil.changeDetectionMode(this.changeControlStrategy);
|
||||||
this.values[0] = context;
|
this.values[0] = context;
|
||||||
this.locals = locals;
|
this.locals = locals;
|
||||||
|
@ -79,24 +71,22 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
}
|
}
|
||||||
|
|
||||||
_destroyPipes() {
|
_destroyPipes() {
|
||||||
for(var i = 0; i < this.pipes.length; ++i) {
|
for (var i = 0; i < this.pipes.length; ++i) {
|
||||||
if (isPresent(this.pipes[i])) {
|
if (isPresent(this.pipes[i])) {
|
||||||
this.pipes[i].onDestroy();
|
this.pipes[i].onDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrated():boolean {
|
hydrated(): boolean { return this.values[0] !== uninitialized; }
|
||||||
return this.values[0] !== uninitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
detectChangesInRecords(throwOnChange:boolean) {
|
detectChangesInRecords(throwOnChange: boolean) {
|
||||||
var protos:List<ProtoRecord> = this.protos;
|
var protos: List < ProtoRecord >= this.protos;
|
||||||
|
|
||||||
var changes = null;
|
var changes = null;
|
||||||
var isChanged = false;
|
var isChanged = false;
|
||||||
for (var i = 0; i < protos.length; ++i) {
|
for (var i = 0; i < protos.length; ++i) {
|
||||||
var proto:ProtoRecord = protos[i];
|
var proto: ProtoRecord = protos[i];
|
||||||
var bindingRecord = proto.bindingRecord;
|
var bindingRecord = proto.bindingRecord;
|
||||||
var directiveRecord = bindingRecord.directiveRecord;
|
var directiveRecord = bindingRecord.directiveRecord;
|
||||||
|
|
||||||
|
@ -142,7 +132,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_addChange(bindingRecord:BindingRecord, change, changes) {
|
_addChange(bindingRecord: BindingRecord, change, changes) {
|
||||||
if (bindingRecord.callOnChange()) {
|
if (bindingRecord.callOnChange()) {
|
||||||
return ChangeDetectionUtil.addChange(changes, bindingRecord.propertyName, change);
|
return ChangeDetectionUtil.addChange(changes, bindingRecord.propertyName, change);
|
||||||
} else {
|
} else {
|
||||||
|
@ -150,15 +140,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDirectiveFor(directiveIndex) {
|
_getDirectiveFor(directiveIndex) { return this.directives.getDirectiveFor(directiveIndex); }
|
||||||
return this.directives.getDirectiveFor(directiveIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
_getDetectorFor(directiveIndex) {
|
_getDetectorFor(directiveIndex) { return this.directives.getDetectorFor(directiveIndex); }
|
||||||
return this.directives.getDetectorFor(directiveIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
_check(proto:ProtoRecord) {
|
_check(proto: ProtoRecord): SimpleChange {
|
||||||
try {
|
try {
|
||||||
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {
|
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||||
return this._pipeCheck(proto);
|
return this._pipeCheck(proto);
|
||||||
|
@ -170,7 +156,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_referenceCheck(proto:ProtoRecord) {
|
_referenceCheck(proto: ProtoRecord) {
|
||||||
if (this._pureFuncAndArgsDidNotChange(proto)) {
|
if (this._pureFuncAndArgsDidNotChange(proto)) {
|
||||||
this._setChanged(proto, false);
|
this._setChanged(proto, false);
|
||||||
return null;
|
return null;
|
||||||
|
@ -194,7 +180,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_calculateCurrValue(proto:ProtoRecord) {
|
_calculateCurrValue(proto: ProtoRecord) {
|
||||||
switch (proto.mode) {
|
switch (proto.mode) {
|
||||||
case RECORD_TYPE_SELF:
|
case RECORD_TYPE_SELF:
|
||||||
return this._readContext(proto);
|
return this._readContext(proto);
|
||||||
|
@ -230,7 +216,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_pipeCheck(proto:ProtoRecord) {
|
_pipeCheck(proto: ProtoRecord) {
|
||||||
var context = this._readContext(proto);
|
var context = this._readContext(proto);
|
||||||
var pipe = this._pipeFor(proto, context);
|
var pipe = this._pipeFor(proto, context);
|
||||||
var prevValue = this._readSelf(proto);
|
var prevValue = this._readSelf(proto);
|
||||||
|
@ -254,7 +240,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_pipeFor(proto:ProtoRecord, context) {
|
_pipeFor(proto: ProtoRecord, context) {
|
||||||
var storedPipe = this._readPipe(proto);
|
var storedPipe = this._readPipe(proto);
|
||||||
if (isPresent(storedPipe) && storedPipe.supports(context)) {
|
if (isPresent(storedPipe) && storedPipe.supports(context)) {
|
||||||
return storedPipe;
|
return storedPipe;
|
||||||
|
@ -274,7 +260,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
return pipe;
|
return pipe;
|
||||||
}
|
}
|
||||||
|
|
||||||
_readContext(proto:ProtoRecord) {
|
_readContext(proto: ProtoRecord) {
|
||||||
if (proto.contextIndex == -1) {
|
if (proto.contextIndex == -1) {
|
||||||
return this._getDirectiveFor(proto.directiveIndex);
|
return this._getDirectiveFor(proto.directiveIndex);
|
||||||
} else {
|
} else {
|
||||||
|
@ -284,33 +270,23 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
return this.values[proto.contextIndex];
|
return this.values[proto.contextIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
_readSelf(proto:ProtoRecord) {
|
_readSelf(proto: ProtoRecord) { return this.values[proto.selfIndex]; }
|
||||||
return this.values[proto.selfIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
_writeSelf(proto:ProtoRecord, value) {
|
_writeSelf(proto: ProtoRecord, value) { this.values[proto.selfIndex] = value; }
|
||||||
this.values[proto.selfIndex] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
_readPipe(proto:ProtoRecord) {
|
_readPipe(proto: ProtoRecord) { return this.pipes[proto.selfIndex]; }
|
||||||
return this.pipes[proto.selfIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
_writePipe(proto:ProtoRecord, value) {
|
_writePipe(proto: ProtoRecord, value) { this.pipes[proto.selfIndex] = value; }
|
||||||
this.pipes[proto.selfIndex] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setChanged(proto:ProtoRecord, value:boolean) {
|
_setChanged(proto: ProtoRecord, value: boolean) { this.changes[proto.selfIndex] = value; }
|
||||||
this.changes[proto.selfIndex] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
_pureFuncAndArgsDidNotChange(proto:ProtoRecord):boolean {
|
_pureFuncAndArgsDidNotChange(proto: ProtoRecord): boolean {
|
||||||
return proto.isPureFunction() && !this._argsChanged(proto);
|
return proto.isPureFunction() && !this._argsChanged(proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
_argsChanged(proto:ProtoRecord):boolean {
|
_argsChanged(proto: ProtoRecord): boolean {
|
||||||
var args = proto.args;
|
var args = proto.args;
|
||||||
for(var i = 0; i < args.length; ++i) {
|
for (var i = 0; i < args.length; ++i) {
|
||||||
if (this.changes[args[i]]) {
|
if (this.changes[args[i]]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -318,7 +294,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_readArgs(proto:ProtoRecord) {
|
_readArgs(proto: ProtoRecord) {
|
||||||
var res = ListWrapper.createFixedSize(proto.args.length);
|
var res = ListWrapper.createFixedSize(proto.args.length);
|
||||||
var args = proto.args;
|
var args = proto.args;
|
||||||
for (var i = 0; i < args.length; ++i) {
|
for (var i = 0; i < args.length; ++i) {
|
||||||
|
@ -334,6 +310,3 @@ function isSame(a, b) {
|
||||||
if ((a !== a) && (b !== b)) return true;
|
if ((a !== a) && (b !== b)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
import {ProtoRecord} from './proto_record';
|
|
||||||
import {BaseException} from "angular2/src/facade/lang";
|
|
||||||
|
|
||||||
export class ExpressionChangedAfterItHasBeenChecked extends BaseException {
|
|
||||||
message:string;
|
|
||||||
|
|
||||||
constructor(proto:ProtoRecord, change:any) {
|
|
||||||
super();
|
|
||||||
this.message = `Expression '${proto.expressionAsString}' has changed after it was checked. ` +
|
|
||||||
`Previous value: '${change.previousValue}'. Current value: '${change.currentValue}'`;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString():string {
|
|
||||||
return this.message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ChangeDetectionError extends BaseException {
|
|
||||||
message:string;
|
|
||||||
originalException:any;
|
|
||||||
location:string;
|
|
||||||
|
|
||||||
constructor(proto:ProtoRecord, originalException:any) {
|
|
||||||
super();
|
|
||||||
this.originalException = originalException;
|
|
||||||
this.location = proto.expressionAsString;
|
|
||||||
this.message = `${this.originalException} in [${this.location}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString():string {
|
|
||||||
return this.message;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import {ProtoRecord} from './proto_record';
|
||||||
|
import {BaseException} from "angular2/src/facade/lang";
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
|
||||||
|
export class ExpressionChangedAfterItHasBeenChecked extends BaseException {
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
constructor(proto: ProtoRecord, change: any) {
|
||||||
|
super();
|
||||||
|
this.message =
|
||||||
|
`Expression '${proto.expressionAsString}' has changed after it was checked. ` +
|
||||||
|
`Previous value: '${change.previousValue}'. Current value: '${change.currentValue}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string { return this.message; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeDetectionError extends BaseException {
|
||||||
|
message: string;
|
||||||
|
originalException: any;
|
||||||
|
location: string;
|
||||||
|
|
||||||
|
constructor(proto: ProtoRecord, originalException: any) {
|
||||||
|
super();
|
||||||
|
this.originalException = originalException;
|
||||||
|
this.location = proto.expressionAsString;
|
||||||
|
this.message = `${this.originalException} in [${this.location}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string { return this.message; }
|
||||||
|
}
|
|
@ -3,10 +3,13 @@ import {Locals} from './parser/locals';
|
||||||
import {DEFAULT} from './constants';
|
import {DEFAULT} from './constants';
|
||||||
import {BindingRecord} from './binding_record';
|
import {BindingRecord} from './binding_record';
|
||||||
|
|
||||||
export class ProtoChangeDetector {
|
// HACK: workaround for Traceur behavior.
|
||||||
instantiate(dispatcher:any):ChangeDetector{
|
// It expects all transpiled modules to contain this marker.
|
||||||
return null;
|
// TODO: remove this when we no longer use traceur
|
||||||
}
|
export var __esModule = true;
|
||||||
|
|
||||||
|
export class ProtoChangeDetector {
|
||||||
|
instantiate(dispatcher: any): ChangeDetector { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,10 +20,12 @@ export class ProtoChangeDetector {
|
||||||
* - {@link DynamicChangeDetection}: slower, but does not require `eval()`.
|
* - {@link DynamicChangeDetection}: slower, but does not require `eval()`.
|
||||||
* - {@link JitChangeDetection}: faster, but requires `eval()`.
|
* - {@link JitChangeDetection}: faster, but requires `eval()`.
|
||||||
*
|
*
|
||||||
* In JavaScript, you should always use `JitChangeDetection`, unless you are in an environment that has
|
* In JavaScript, you should always use `JitChangeDetection`, unless you are in an environment that
|
||||||
|
*has
|
||||||
* [CSP](https://developer.mozilla.org/en-US/docs/Web/Security/CSP), such as a Chrome Extension.
|
* [CSP](https://developer.mozilla.org/en-US/docs/Web/Security/CSP), such as a Chrome Extension.
|
||||||
*
|
*
|
||||||
* In Dart, use `DynamicChangeDetection` during development. The Angular transformer generates an analog to the
|
* In Dart, use `DynamicChangeDetection` during development. The Angular transformer generates an
|
||||||
|
*analog to the
|
||||||
* `JitChangeDetection` strategy at compile time.
|
* `JitChangeDetection` strategy at compile time.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -33,26 +38,27 @@ export class ProtoChangeDetector {
|
||||||
* @exportedAs angular2/change_detection
|
* @exportedAs angular2/change_detection
|
||||||
*/
|
*/
|
||||||
export class ChangeDetection {
|
export class ChangeDetection {
|
||||||
createProtoChangeDetector(name:string, bindingRecords:List, variableBindings:List, directiveRecords:List,
|
createProtoChangeDetector(name: string, bindingRecords: List<any>, variableBindings: List<any>,
|
||||||
changeControlStrategy:string=DEFAULT):ProtoChangeDetector{
|
directiveRecords: List<any>,
|
||||||
|
changeControlStrategy: string = DEFAULT): ProtoChangeDetector {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChangeDispatcher {
|
export class ChangeDispatcher {
|
||||||
notifyOnBinding(bindingRecord:BindingRecord, value:any) {}
|
notifyOnBinding(bindingRecord: BindingRecord, value: any) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChangeDetector {
|
export class ChangeDetector {
|
||||||
parent:ChangeDetector;
|
parent: ChangeDetector;
|
||||||
mode:string;
|
mode: string;
|
||||||
|
|
||||||
addChild(cd:ChangeDetector) {}
|
addChild(cd: ChangeDetector) {}
|
||||||
addShadowDomChild(cd:ChangeDetector) {}
|
addShadowDomChild(cd: ChangeDetector) {}
|
||||||
removeChild(cd:ChangeDetector) {}
|
removeChild(cd: ChangeDetector) {}
|
||||||
removeShadowDomChild(cd:ChangeDetector) {}
|
removeShadowDomChild(cd: ChangeDetector) {}
|
||||||
remove() {}
|
remove() {}
|
||||||
hydrate(context:any, locals:Locals, directives:any) {}
|
hydrate(context: any, locals: Locals, directives: any) {}
|
||||||
dehydrate() {}
|
dehydrate() {}
|
||||||
markPathToRootAsCheckOnce() {}
|
markPathToRootAsCheckOnce() {}
|
||||||
|
|
|
@ -1,532 +0,0 @@
|
||||||
import {autoConvertAdd, isBlank, isPresent, FunctionWrapper, BaseException} from "angular2/src/facade/lang";
|
|
||||||
import {List, Map, ListWrapper, StringMapWrapper} from "angular2/src/facade/collection";
|
|
||||||
|
|
||||||
export class AST {
|
|
||||||
eval(context, locals) {
|
|
||||||
throw new BaseException("Not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
get isAssignable():boolean {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
assign(context, locals, value) {
|
|
||||||
throw new BaseException("Not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
}
|
|
||||||
|
|
||||||
toString():string {
|
|
||||||
return "AST";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EmptyExpr extends AST {
|
|
||||||
eval(context, locals) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
//do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ImplicitReceiver extends AST {
|
|
||||||
eval(context, locals) {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitImplicitReceiver(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Multiple expressions separated by a semicolon.
|
|
||||||
*/
|
|
||||||
export class Chain extends AST {
|
|
||||||
expressions:List;
|
|
||||||
constructor(expressions:List) {
|
|
||||||
super();
|
|
||||||
this.expressions = expressions;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
var result;
|
|
||||||
for (var i = 0; i < this.expressions.length; i++) {
|
|
||||||
var last = this.expressions[i].eval(context, locals);
|
|
||||||
if (isPresent(last)) result = last;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitChain(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Conditional extends AST {
|
|
||||||
condition:AST;
|
|
||||||
trueExp:AST;
|
|
||||||
falseExp:AST;
|
|
||||||
constructor(condition:AST, trueExp:AST, falseExp:AST){
|
|
||||||
super();
|
|
||||||
this.condition = condition;
|
|
||||||
this.trueExp = trueExp;
|
|
||||||
this.falseExp = falseExp;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
if(this.condition.eval(context, locals)) {
|
|
||||||
return this.trueExp.eval(context, locals);
|
|
||||||
} else {
|
|
||||||
return this.falseExp.eval(context, locals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitConditional(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AccessMember extends AST {
|
|
||||||
receiver:AST;
|
|
||||||
name:string;
|
|
||||||
getter:Function;
|
|
||||||
setter:Function;
|
|
||||||
constructor(receiver:AST, name:string, getter:Function, setter:Function) {
|
|
||||||
super();
|
|
||||||
this.receiver = receiver;
|
|
||||||
this.name = name;
|
|
||||||
this.getter = getter;
|
|
||||||
this.setter = setter;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
if (this.receiver instanceof ImplicitReceiver &&
|
|
||||||
isPresent(locals) && locals.contains(this.name)) {
|
|
||||||
return locals.get(this.name);
|
|
||||||
} else {
|
|
||||||
var evaluatedReceiver = this.receiver.eval(context, locals);
|
|
||||||
return this.getter(evaluatedReceiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get isAssignable():boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
assign(context, locals, value) {
|
|
||||||
var evaluatedContext = this.receiver.eval(context, locals);
|
|
||||||
|
|
||||||
if (this.receiver instanceof ImplicitReceiver &&
|
|
||||||
isPresent(locals) && locals.contains(this.name)) {
|
|
||||||
throw new BaseException(`Cannot reassign a variable binding ${this.name}`);
|
|
||||||
} else {
|
|
||||||
return this.setter(evaluatedContext, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitAccessMember(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class KeyedAccess extends AST {
|
|
||||||
obj:AST;
|
|
||||||
key:AST;
|
|
||||||
constructor(obj:AST, key:AST) {
|
|
||||||
super();
|
|
||||||
this.obj = obj;
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
var obj = this.obj.eval(context, locals);
|
|
||||||
var key = this.key.eval(context, locals);
|
|
||||||
return obj[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isAssignable():boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
assign(context, locals, value) {
|
|
||||||
var obj = this.obj.eval(context, locals);
|
|
||||||
var key = this.key.eval(context, locals);
|
|
||||||
obj[key] = value;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitKeyedAccess(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Pipe extends AST {
|
|
||||||
exp:AST;
|
|
||||||
name:string;
|
|
||||||
args:List<AST>;
|
|
||||||
inBinding:boolean;
|
|
||||||
constructor(exp:AST, name:string, args:List, inBinding:boolean) {
|
|
||||||
super();
|
|
||||||
this.exp = exp;
|
|
||||||
this.name = name;
|
|
||||||
this.args = args;
|
|
||||||
this.inBinding = inBinding;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitPipe(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LiteralPrimitive extends AST {
|
|
||||||
value;
|
|
||||||
constructor(value) {
|
|
||||||
super();
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitLiteralPrimitive(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LiteralArray extends AST {
|
|
||||||
expressions:List;
|
|
||||||
constructor(expressions:List) {
|
|
||||||
super();
|
|
||||||
this.expressions = expressions;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
return ListWrapper.map(this.expressions, (e) => e.eval(context, locals));
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitLiteralArray(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LiteralMap extends AST {
|
|
||||||
keys:List;
|
|
||||||
values:List;
|
|
||||||
constructor(keys:List, values:List) {
|
|
||||||
super();
|
|
||||||
this.keys = keys;
|
|
||||||
this.values = values;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
var res = StringMapWrapper.create();
|
|
||||||
for(var i = 0; i < this.keys.length; ++i) {
|
|
||||||
StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context, locals));
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitLiteralMap(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Interpolation extends AST {
|
|
||||||
strings:List;
|
|
||||||
expressions:List;
|
|
||||||
constructor(strings:List, expressions:List) {
|
|
||||||
super();
|
|
||||||
this.strings = strings;
|
|
||||||
this.expressions = expressions;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
throw new BaseException("evaluating an Interpolation is not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
visitor.visitInterpolation(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Binary extends AST {
|
|
||||||
operation:string;
|
|
||||||
left:AST;
|
|
||||||
right:AST;
|
|
||||||
constructor(operation:string, left:AST, right:AST) {
|
|
||||||
super();
|
|
||||||
this.operation = operation;
|
|
||||||
this.left = left;
|
|
||||||
this.right = right;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
var left = this.left.eval(context, locals);
|
|
||||||
switch (this.operation) {
|
|
||||||
case '&&': return left && this.right.eval(context, locals);
|
|
||||||
case '||': return left || this.right.eval(context, locals);
|
|
||||||
}
|
|
||||||
var right = this.right.eval(context, locals);
|
|
||||||
|
|
||||||
switch (this.operation) {
|
|
||||||
case '+' : return left + right;
|
|
||||||
case '-' : return left - right;
|
|
||||||
case '*' : return left * right;
|
|
||||||
case '/' : return left / right;
|
|
||||||
case '%' : return left % right;
|
|
||||||
case '==' : return left == right;
|
|
||||||
case '!=' : return left != right;
|
|
||||||
case '===' : return left === right;
|
|
||||||
case '!==' : return left !== right;
|
|
||||||
case '<' : return left < right;
|
|
||||||
case '>' : return left > right;
|
|
||||||
case '<=' : return left <= right;
|
|
||||||
case '>=' : return left >= right;
|
|
||||||
case '^' : return left ^ right;
|
|
||||||
case '&' : return left & right;
|
|
||||||
}
|
|
||||||
throw 'Internal error [$operation] not handled';
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitBinary(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PrefixNot extends AST {
|
|
||||||
expression:AST;
|
|
||||||
constructor(expression:AST) {
|
|
||||||
super();
|
|
||||||
this.expression = expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
return !this.expression.eval(context, locals);
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitPrefixNot(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Assignment extends AST {
|
|
||||||
target:AST;
|
|
||||||
value:AST;
|
|
||||||
constructor(target:AST, value:AST) {
|
|
||||||
super();
|
|
||||||
this.target = target;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
return this.target.assign(context, locals, this.value.eval(context, locals));
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitAssignment(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MethodCall extends AST {
|
|
||||||
receiver:AST;
|
|
||||||
fn:Function;
|
|
||||||
args:List;
|
|
||||||
name:string;
|
|
||||||
constructor(receiver:AST, name:string, fn:Function, args:List) {
|
|
||||||
super();
|
|
||||||
this.receiver = receiver;
|
|
||||||
this.fn = fn;
|
|
||||||
this.args = args;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
var evaluatedArgs = evalList(context, locals, this.args);
|
|
||||||
if (this.receiver instanceof ImplicitReceiver &&
|
|
||||||
isPresent(locals) && locals.contains(this.name)) {
|
|
||||||
var fn = locals.get(this.name);
|
|
||||||
return FunctionWrapper.apply(fn, evaluatedArgs);
|
|
||||||
} else {
|
|
||||||
var evaluatedReceiver = this.receiver.eval(context, locals);
|
|
||||||
return this.fn(evaluatedReceiver, evaluatedArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitMethodCall(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FunctionCall extends AST {
|
|
||||||
target:AST;
|
|
||||||
args:List;
|
|
||||||
constructor(target:AST, args:List) {
|
|
||||||
super();
|
|
||||||
this.target = target;
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
var obj = this.target.eval(context, locals);
|
|
||||||
if (! (obj instanceof Function)) {
|
|
||||||
throw new BaseException(`${obj} is not a function`);
|
|
||||||
}
|
|
||||||
return FunctionWrapper.apply(obj, evalList(context, locals, this.args));
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitFunctionCall(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ASTWithSource extends AST {
|
|
||||||
ast:AST;
|
|
||||||
source:string;
|
|
||||||
location:string;
|
|
||||||
constructor(ast:AST, source:string, location:string) {
|
|
||||||
super();
|
|
||||||
this.source = source;
|
|
||||||
this.location = location;
|
|
||||||
this.ast = ast;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(context, locals) {
|
|
||||||
return this.ast.eval(context, locals);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isAssignable():boolean {
|
|
||||||
return this.ast.isAssignable;
|
|
||||||
}
|
|
||||||
|
|
||||||
assign(context, locals, value) {
|
|
||||||
return this.ast.assign(context, locals, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return this.ast.visit(visitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
toString():string {
|
|
||||||
return `${this.source} in ${this.location}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TemplateBinding {
|
|
||||||
key:string;
|
|
||||||
keyIsVar:boolean;
|
|
||||||
name:string;
|
|
||||||
expression:ASTWithSource;
|
|
||||||
constructor(key:string, keyIsVar:boolean, name:string, expression:ASTWithSource) {
|
|
||||||
this.key = key;
|
|
||||||
this.keyIsVar = keyIsVar;
|
|
||||||
// only either name or expression will be filled.
|
|
||||||
this.name = name;
|
|
||||||
this.expression = expression;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//INTERFACE
|
|
||||||
export class AstVisitor {
|
|
||||||
visitAccessMember(ast:AccessMember) {}
|
|
||||||
visitAssignment(ast:Assignment) {}
|
|
||||||
visitBinary(ast:Binary) {}
|
|
||||||
visitChain(ast:Chain){}
|
|
||||||
visitConditional(ast:Conditional) {}
|
|
||||||
visitPipe(ast:Pipe) {}
|
|
||||||
visitFunctionCall(ast:FunctionCall) {}
|
|
||||||
visitImplicitReceiver(ast:ImplicitReceiver) {}
|
|
||||||
visitKeyedAccess(ast:KeyedAccess) {}
|
|
||||||
visitLiteralArray(ast:LiteralArray) {}
|
|
||||||
visitLiteralMap(ast:LiteralMap) {}
|
|
||||||
visitLiteralPrimitive(ast:LiteralPrimitive) {}
|
|
||||||
visitMethodCall(ast:MethodCall) {}
|
|
||||||
visitPrefixNot(ast:PrefixNot) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AstTransformer {
|
|
||||||
visitImplicitReceiver(ast:ImplicitReceiver) {
|
|
||||||
return ast;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitInterpolation(ast:Interpolation) {
|
|
||||||
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralPrimitive(ast:LiteralPrimitive) {
|
|
||||||
return new LiteralPrimitive(ast.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitAccessMember(ast:AccessMember) {
|
|
||||||
return new AccessMember(ast.receiver.visit(this), ast.name, ast.getter, ast.setter);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitMethodCall(ast:MethodCall) {
|
|
||||||
return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitFunctionCall(ast:FunctionCall) {
|
|
||||||
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralArray(ast:LiteralArray) {
|
|
||||||
return new LiteralArray(this.visitAll(ast.expressions));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralMap(ast:LiteralMap) {
|
|
||||||
return new LiteralMap(ast.keys, this.visitAll(ast.values));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitBinary(ast:Binary) {
|
|
||||||
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPrefixNot(ast:PrefixNot) {
|
|
||||||
return new PrefixNot(ast.expression.visit(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitConditional(ast:Conditional) {
|
|
||||||
return new Conditional(
|
|
||||||
ast.condition.visit(this),
|
|
||||||
ast.trueExp.visit(this),
|
|
||||||
ast.falseExp.visit(this)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPipe(ast:Pipe) {
|
|
||||||
return new Pipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.inBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitKeyedAccess(ast:KeyedAccess) {
|
|
||||||
return new KeyedAccess(ast.obj.visit(this), ast.key.visit(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitAll(asts:List) {
|
|
||||||
var res = ListWrapper.createFixedSize(asts.length);
|
|
||||||
for (var i = 0; i < asts.length; ++i) {
|
|
||||||
res[i] = asts[i].visit(this);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _evalListCache = [[],[0],[0,0],[0,0,0],[0,0,0,0],[0,0,0,0,0],
|
|
||||||
[0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0],
|
|
||||||
[0,0,0,0,0,0,0,0,0]];
|
|
||||||
|
|
||||||
function evalList(context, locals, exps:List){
|
|
||||||
var length = exps.length;
|
|
||||||
if (length > 10) {
|
|
||||||
throw new BaseException("Cannot have more than 10 argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = _evalListCache[length];
|
|
||||||
for (var i = 0; i < length; i++) {
|
|
||||||
result[i] = exps[i].eval(context, locals);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -0,0 +1,388 @@
|
||||||
|
import {isBlank, isPresent, FunctionWrapper, BaseException} from "angular2/src/facade/lang";
|
||||||
|
import {List, Map, ListWrapper, StringMapWrapper} from "angular2/src/facade/collection";
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
export class AST {
|
||||||
|
eval(context, locals) { throw new BaseException("Not supported"); }
|
||||||
|
|
||||||
|
get isAssignable(): boolean { return false; }
|
||||||
|
|
||||||
|
assign(context, locals, value) { throw new BaseException("Not supported"); }
|
||||||
|
|
||||||
|
visit(visitor): any { return null; }
|
||||||
|
|
||||||
|
toString(): string { return "AST"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EmptyExpr extends AST {
|
||||||
|
eval(context, locals) { return null; }
|
||||||
|
|
||||||
|
visit(visitor) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ImplicitReceiver extends AST {
|
||||||
|
eval(context, locals) { return context; }
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitImplicitReceiver(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiple expressions separated by a semicolon.
|
||||||
|
*/
|
||||||
|
export class Chain extends AST {
|
||||||
|
constructor(public expressions: List<any>) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
var result;
|
||||||
|
for (var i = 0; i < this.expressions.length; i++) {
|
||||||
|
var last = this.expressions[i].eval(context, locals);
|
||||||
|
if (isPresent(last)) result = last;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitChain(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Conditional extends AST {
|
||||||
|
constructor(public condition: AST, public trueExp: AST, public falseExp: AST) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
if (this.condition.eval(context, locals)) {
|
||||||
|
return this.trueExp.eval(context, locals);
|
||||||
|
} else {
|
||||||
|
return this.falseExp.eval(context, locals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitConditional(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AccessMember extends AST {
|
||||||
|
constructor(public receiver: AST, public name: string, public getter: Function,
|
||||||
|
public setter: Function) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
if (this.receiver instanceof ImplicitReceiver && isPresent(locals) &&
|
||||||
|
locals.contains(this.name)) {
|
||||||
|
return locals.get(this.name);
|
||||||
|
} else {
|
||||||
|
var evaluatedReceiver = this.receiver.eval(context, locals);
|
||||||
|
return this.getter(evaluatedReceiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get isAssignable(): boolean { return true; }
|
||||||
|
|
||||||
|
assign(context, locals, value) {
|
||||||
|
var evaluatedContext = this.receiver.eval(context, locals);
|
||||||
|
|
||||||
|
if (this.receiver instanceof ImplicitReceiver && isPresent(locals) &&
|
||||||
|
locals.contains(this.name)) {
|
||||||
|
throw new BaseException(`Cannot reassign a variable binding ${this.name}`);
|
||||||
|
} else {
|
||||||
|
return this.setter(evaluatedContext, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitAccessMember(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KeyedAccess extends AST {
|
||||||
|
constructor(public obj: AST, public key: AST) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
var obj: any = this.obj.eval(context, locals);
|
||||||
|
var key: any = this.key.eval(context, locals);
|
||||||
|
return obj[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
get isAssignable(): boolean { return true; }
|
||||||
|
|
||||||
|
assign(context, locals, value) {
|
||||||
|
var obj: any = this.obj.eval(context, locals);
|
||||||
|
var key: any = this.key.eval(context, locals);
|
||||||
|
obj[key] = value;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitKeyedAccess(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Pipe extends AST {
|
||||||
|
constructor(public exp: AST, public name: string, public args: List<any>,
|
||||||
|
public inBinding: boolean) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitPipe(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LiteralPrimitive extends AST {
|
||||||
|
constructor(public value) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) { return this.value; }
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitLiteralPrimitive(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LiteralArray extends AST {
|
||||||
|
constructor(public expressions: List<any>) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
return ListWrapper.map(this.expressions, (e) => e.eval(context, locals));
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitLiteralArray(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LiteralMap extends AST {
|
||||||
|
constructor(public keys: List<any>, public values: List<any>) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
var res = StringMapWrapper.create();
|
||||||
|
for (var i = 0; i < this.keys.length; ++i) {
|
||||||
|
StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context, locals));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitLiteralMap(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Interpolation extends AST {
|
||||||
|
constructor(public strings: List<any>, public expressions: List<any>) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) { throw new BaseException("evaluating an Interpolation is not supported"); }
|
||||||
|
|
||||||
|
visit(visitor) { visitor.visitInterpolation(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Binary extends AST {
|
||||||
|
constructor(public operation: string, public left: AST, public right: AST) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
var left: any = this.left.eval(context, locals);
|
||||||
|
switch (this.operation) {
|
||||||
|
case '&&':
|
||||||
|
return left && this.right.eval(context, locals);
|
||||||
|
case '||':
|
||||||
|
return left || this.right.eval(context, locals);
|
||||||
|
}
|
||||||
|
var right: any = this.right.eval(context, locals);
|
||||||
|
|
||||||
|
switch (this.operation) {
|
||||||
|
case '+':
|
||||||
|
return left + right;
|
||||||
|
case '-':
|
||||||
|
return left - right;
|
||||||
|
case '*':
|
||||||
|
return left * right;
|
||||||
|
case '/':
|
||||||
|
return left / right;
|
||||||
|
case '%':
|
||||||
|
return left % right;
|
||||||
|
case '==':
|
||||||
|
return left == right;
|
||||||
|
case '!=':
|
||||||
|
return left != right;
|
||||||
|
case '===':
|
||||||
|
return left === right;
|
||||||
|
case '!==':
|
||||||
|
return left !== right;
|
||||||
|
case '<':
|
||||||
|
return left < right;
|
||||||
|
case '>':
|
||||||
|
return left > right;
|
||||||
|
case '<=':
|
||||||
|
return left <= right;
|
||||||
|
case '>=':
|
||||||
|
return left >= right;
|
||||||
|
case '^':
|
||||||
|
return left ^ right;
|
||||||
|
case '&':
|
||||||
|
return left & right;
|
||||||
|
}
|
||||||
|
throw 'Internal error [$operation] not handled';
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitBinary(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PrefixNot extends AST {
|
||||||
|
constructor(public expression: AST) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) { return !this.expression.eval(context, locals); }
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitPrefixNot(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Assignment extends AST {
|
||||||
|
constructor(public target: AST, public value: AST) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
return this.target.assign(context, locals, this.value.eval(context, locals));
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitAssignment(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MethodCall extends AST {
|
||||||
|
constructor(public receiver: AST, public name: string, public fn: Function,
|
||||||
|
public args: List<any>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
var evaluatedArgs = evalList(context, locals, this.args);
|
||||||
|
if (this.receiver instanceof ImplicitReceiver && isPresent(locals) &&
|
||||||
|
locals.contains(this.name)) {
|
||||||
|
var fn = locals.get(this.name);
|
||||||
|
return FunctionWrapper.apply(fn, evaluatedArgs);
|
||||||
|
} else {
|
||||||
|
var evaluatedReceiver = this.receiver.eval(context, locals);
|
||||||
|
return this.fn(evaluatedReceiver, evaluatedArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitMethodCall(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FunctionCall extends AST {
|
||||||
|
constructor(public target: AST, public args: List<any>) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
var obj: any = this.target.eval(context, locals);
|
||||||
|
if (!(obj instanceof Function)) {
|
||||||
|
throw new BaseException(`${obj} is not a function`);
|
||||||
|
}
|
||||||
|
return FunctionWrapper.apply(obj, evalList(context, locals, this.args));
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) { return visitor.visitFunctionCall(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ASTWithSource extends AST {
|
||||||
|
constructor(public ast: AST, public source: string, public location: string) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) { return this.ast.eval(context, locals); }
|
||||||
|
|
||||||
|
get isAssignable(): boolean { return this.ast.isAssignable; }
|
||||||
|
|
||||||
|
assign(context, locals, value) { return this.ast.assign(context, locals, value); }
|
||||||
|
|
||||||
|
visit(visitor) { return this.ast.visit(visitor); }
|
||||||
|
|
||||||
|
toString(): string { return `${this.source} in ${this.location}`; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TemplateBinding {
|
||||||
|
constructor(public key: string, public keyIsVar: boolean, public name: string,
|
||||||
|
public expression: ASTWithSource) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// INTERFACE
|
||||||
|
export class AstVisitor {
|
||||||
|
visitAccessMember(ast: AccessMember) {}
|
||||||
|
visitAssignment(ast: Assignment) {}
|
||||||
|
visitBinary(ast: Binary) {}
|
||||||
|
visitChain(ast: Chain) {}
|
||||||
|
visitConditional(ast: Conditional) {}
|
||||||
|
visitPipe(ast: Pipe) {}
|
||||||
|
visitFunctionCall(ast: FunctionCall) {}
|
||||||
|
visitImplicitReceiver(ast: ImplicitReceiver) {}
|
||||||
|
visitKeyedAccess(ast: KeyedAccess) {}
|
||||||
|
visitLiteralArray(ast: LiteralArray) {}
|
||||||
|
visitLiteralMap(ast: LiteralMap) {}
|
||||||
|
visitLiteralPrimitive(ast: LiteralPrimitive) {}
|
||||||
|
visitMethodCall(ast: MethodCall) {}
|
||||||
|
visitPrefixNot(ast: PrefixNot) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AstTransformer {
|
||||||
|
visitImplicitReceiver(ast: ImplicitReceiver) { return ast; }
|
||||||
|
|
||||||
|
visitInterpolation(ast: Interpolation) {
|
||||||
|
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitLiteralPrimitive(ast: LiteralPrimitive) { return new LiteralPrimitive(ast.value); }
|
||||||
|
|
||||||
|
visitAccessMember(ast: AccessMember) {
|
||||||
|
return new AccessMember(ast.receiver.visit(this), ast.name, ast.getter, ast.setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitMethodCall(ast: MethodCall) {
|
||||||
|
return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitFunctionCall(ast: FunctionCall) {
|
||||||
|
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitLiteralArray(ast: LiteralArray) { return new LiteralArray(this.visitAll(ast.expressions)); }
|
||||||
|
|
||||||
|
visitLiteralMap(ast: LiteralMap) { return new LiteralMap(ast.keys, this.visitAll(ast.values)); }
|
||||||
|
|
||||||
|
visitBinary(ast: Binary) {
|
||||||
|
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPrefixNot(ast: PrefixNot) { return new PrefixNot(ast.expression.visit(this)); }
|
||||||
|
|
||||||
|
visitConditional(ast: Conditional) {
|
||||||
|
return new Conditional(ast.condition.visit(this), ast.trueExp.visit(this),
|
||||||
|
ast.falseExp.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPipe(ast: Pipe) {
|
||||||
|
return new Pipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.inBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitKeyedAccess(ast: KeyedAccess) {
|
||||||
|
return new KeyedAccess(ast.obj.visit(this), ast.key.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAll(asts: List<any>) {
|
||||||
|
var res = ListWrapper.createFixedSize(asts.length);
|
||||||
|
for (var i = 0; i < asts.length; ++i) {
|
||||||
|
res[i] = asts[i].visit(this);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _evalListCache = [
|
||||||
|
[],
|
||||||
|
[0],
|
||||||
|
[0, 0],
|
||||||
|
[0, 0, 0],
|
||||||
|
[0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
];
|
||||||
|
|
||||||
|
function evalList(context, locals, exps: List<any>) {
|
||||||
|
var length = exps.length;
|
||||||
|
if (length > 10) {
|
||||||
|
throw new BaseException("Cannot have more than 10 argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _evalListCache[length];
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
result[i] = exps[i].eval(context, locals);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -1,476 +0,0 @@
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
|
||||||
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
|
|
||||||
import {int, NumberWrapper, StringJoiner, StringWrapper, BaseException} from "angular2/src/facade/lang";
|
|
||||||
|
|
||||||
export const TOKEN_TYPE_CHARACTER = 1;
|
|
||||||
export const TOKEN_TYPE_IDENTIFIER = 2;
|
|
||||||
export const TOKEN_TYPE_KEYWORD = 3;
|
|
||||||
export const TOKEN_TYPE_STRING = 4;
|
|
||||||
export const TOKEN_TYPE_OPERATOR = 5;
|
|
||||||
export const TOKEN_TYPE_NUMBER = 6;
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class Lexer {
|
|
||||||
text:string;
|
|
||||||
tokenize(text:string):List {
|
|
||||||
var scanner = new _Scanner(text);
|
|
||||||
var tokens = [];
|
|
||||||
var token = scanner.scanToken();
|
|
||||||
while (token != null) {
|
|
||||||
ListWrapper.push(tokens, token);
|
|
||||||
token = scanner.scanToken();
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Token {
|
|
||||||
index:int;
|
|
||||||
type:int;
|
|
||||||
_numValue:number;
|
|
||||||
_strValue:string;
|
|
||||||
constructor(index:int, type:int, numValue:number, strValue:string) {
|
|
||||||
/**
|
|
||||||
* NOTE: To ensure that this constructor creates the same hidden class each time, ensure that
|
|
||||||
* all the fields are assigned to in the exact same order in each run of this constructor.
|
|
||||||
*/
|
|
||||||
this.index = index;
|
|
||||||
this.type = type;
|
|
||||||
this._numValue = numValue;
|
|
||||||
this._strValue = strValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
isCharacter(code:int):boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_CHARACTER && this._numValue == code);
|
|
||||||
}
|
|
||||||
|
|
||||||
isNumber():boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_NUMBER);
|
|
||||||
}
|
|
||||||
|
|
||||||
isString():boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_STRING);
|
|
||||||
}
|
|
||||||
|
|
||||||
isOperator(operater:string):boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_OPERATOR && this._strValue == operater);
|
|
||||||
}
|
|
||||||
|
|
||||||
isIdentifier():boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_IDENTIFIER);
|
|
||||||
}
|
|
||||||
|
|
||||||
isKeyword():boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_KEYWORD);
|
|
||||||
}
|
|
||||||
|
|
||||||
isKeywordVar():boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "var");
|
|
||||||
}
|
|
||||||
|
|
||||||
isKeywordNull():boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "null");
|
|
||||||
}
|
|
||||||
|
|
||||||
isKeywordUndefined():boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "undefined");
|
|
||||||
}
|
|
||||||
|
|
||||||
isKeywordTrue():boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
isKeywordFalse():boolean {
|
|
||||||
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
toNumber():number {
|
|
||||||
// -1 instead of NULL ok?
|
|
||||||
return (this.type == TOKEN_TYPE_NUMBER) ? this._numValue : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString():string {
|
|
||||||
var type:int = this.type;
|
|
||||||
if (type >= TOKEN_TYPE_CHARACTER && type <= TOKEN_TYPE_STRING) {
|
|
||||||
return this._strValue;
|
|
||||||
} else if (type == TOKEN_TYPE_NUMBER) {
|
|
||||||
return this._numValue.toString();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function newCharacterToken(index:int, code:int):Token {
|
|
||||||
return new Token(index, TOKEN_TYPE_CHARACTER, code, StringWrapper.fromCharCode(code));
|
|
||||||
}
|
|
||||||
|
|
||||||
function newIdentifierToken(index:int, text:string):Token {
|
|
||||||
return new Token(index, TOKEN_TYPE_IDENTIFIER, 0, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function newKeywordToken(index:int, text:string):Token {
|
|
||||||
return new Token(index, TOKEN_TYPE_KEYWORD, 0, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function newOperatorToken(index:int, text:string):Token {
|
|
||||||
return new Token(index, TOKEN_TYPE_OPERATOR, 0, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function newStringToken(index:int, text:string):Token {
|
|
||||||
return new Token(index, TOKEN_TYPE_STRING, 0, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function newNumberToken(index:int, n:number):Token {
|
|
||||||
return new Token(index, TOKEN_TYPE_NUMBER, n, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export var EOF:Token = new Token(-1, 0, 0, "");
|
|
||||||
|
|
||||||
export const $EOF = 0;
|
|
||||||
export const $TAB = 9;
|
|
||||||
export const $LF = 10;
|
|
||||||
export const $VTAB = 11;
|
|
||||||
export const $FF = 12;
|
|
||||||
export const $CR = 13;
|
|
||||||
export const $SPACE = 32;
|
|
||||||
export const $BANG = 33;
|
|
||||||
export const $DQ = 34;
|
|
||||||
export const $HASH = 35;
|
|
||||||
export const $$ = 36;
|
|
||||||
export const $PERCENT = 37;
|
|
||||||
export const $AMPERSAND = 38;
|
|
||||||
export const $SQ = 39;
|
|
||||||
export const $LPAREN = 40;
|
|
||||||
export const $RPAREN = 41;
|
|
||||||
export const $STAR = 42;
|
|
||||||
export const $PLUS = 43;
|
|
||||||
export const $COMMA = 44;
|
|
||||||
export const $MINUS = 45;
|
|
||||||
export const $PERIOD = 46;
|
|
||||||
export const $SLASH = 47;
|
|
||||||
export const $COLON = 58;
|
|
||||||
export const $SEMICOLON = 59;
|
|
||||||
export const $LT = 60;
|
|
||||||
export const $EQ = 61;
|
|
||||||
export const $GT = 62;
|
|
||||||
export const $QUESTION = 63;
|
|
||||||
|
|
||||||
const $0 = 48;
|
|
||||||
const $9 = 57;
|
|
||||||
|
|
||||||
const $A = 65, $E = 69, $Z = 90;
|
|
||||||
|
|
||||||
export const $LBRACKET = 91;
|
|
||||||
export const $BACKSLASH = 92;
|
|
||||||
export const $RBRACKET = 93;
|
|
||||||
const $CARET = 94;
|
|
||||||
const $_ = 95;
|
|
||||||
|
|
||||||
const $a = 97, $e = 101, $f = 102, $n = 110, $r = 114, $t = 116, $u = 117, $v = 118, $z = 122;
|
|
||||||
|
|
||||||
export const $LBRACE = 123;
|
|
||||||
export const $BAR = 124;
|
|
||||||
export const $RBRACE = 125;
|
|
||||||
const $NBSP = 160;
|
|
||||||
|
|
||||||
|
|
||||||
export class ScannerError extends BaseException {
|
|
||||||
message:string;
|
|
||||||
constructor(message) {
|
|
||||||
super();
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
|
||||||
return this.message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Scanner {
|
|
||||||
input:string;
|
|
||||||
length:int;
|
|
||||||
peek:int;
|
|
||||||
index:int;
|
|
||||||
|
|
||||||
constructor(input:string) {
|
|
||||||
this.input = input;
|
|
||||||
this.length = input.length;
|
|
||||||
this.peek = 0;
|
|
||||||
this.index = -1;
|
|
||||||
this.advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
advance() {
|
|
||||||
this.peek = ++this.index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, this.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
scanToken():Token {
|
|
||||||
var input = this.input,
|
|
||||||
length = this.length,
|
|
||||||
peek = this.peek,
|
|
||||||
index = this.index;
|
|
||||||
|
|
||||||
// Skip whitespace.
|
|
||||||
while (peek <= $SPACE) {
|
|
||||||
if (++index >= length) {
|
|
||||||
peek = $EOF;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
peek = StringWrapper.charCodeAt(input, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.peek = peek;
|
|
||||||
this.index = index;
|
|
||||||
|
|
||||||
if (index >= length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle identifiers and numbers.
|
|
||||||
if (isIdentifierStart(peek)) return this.scanIdentifier();
|
|
||||||
if (isDigit(peek)) return this.scanNumber(index);
|
|
||||||
|
|
||||||
var start:int = index;
|
|
||||||
switch (peek) {
|
|
||||||
case $PERIOD:
|
|
||||||
this.advance();
|
|
||||||
return isDigit(this.peek) ? this.scanNumber(start) :
|
|
||||||
newCharacterToken(start, $PERIOD);
|
|
||||||
case $LPAREN: case $RPAREN:
|
|
||||||
case $LBRACE: case $RBRACE:
|
|
||||||
case $LBRACKET: case $RBRACKET:
|
|
||||||
case $COMMA:
|
|
||||||
case $COLON:
|
|
||||||
case $SEMICOLON:
|
|
||||||
return this.scanCharacter(start, peek);
|
|
||||||
case $SQ:
|
|
||||||
case $DQ:
|
|
||||||
return this.scanString();
|
|
||||||
case $HASH:
|
|
||||||
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
|
|
||||||
case $PLUS:
|
|
||||||
case $MINUS:
|
|
||||||
case $STAR:
|
|
||||||
case $SLASH:
|
|
||||||
case $PERCENT:
|
|
||||||
case $CARET:
|
|
||||||
case $QUESTION:
|
|
||||||
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
|
|
||||||
case $LT:
|
|
||||||
case $GT:
|
|
||||||
case $BANG:
|
|
||||||
case $EQ:
|
|
||||||
return this.scanComplexOperator(start, $EQ, StringWrapper.fromCharCode(peek), '=');
|
|
||||||
case $AMPERSAND:
|
|
||||||
return this.scanComplexOperator(start, $AMPERSAND, '&', '&');
|
|
||||||
case $BAR:
|
|
||||||
return this.scanComplexOperator(start, $BAR, '|', '|');
|
|
||||||
case $NBSP:
|
|
||||||
while (isWhitespace(this.peek)) this.advance();
|
|
||||||
return this.scanToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`, 0);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
scanCharacter(start:int, code:int):Token {
|
|
||||||
assert(this.peek == code);
|
|
||||||
this.advance();
|
|
||||||
return newCharacterToken(start, code);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
scanOperator(start:int, str:string):Token {
|
|
||||||
assert(this.peek == StringWrapper.charCodeAt(str, 0));
|
|
||||||
assert(SetWrapper.has(OPERATORS, str));
|
|
||||||
this.advance();
|
|
||||||
return newOperatorToken(start, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
scanComplexOperator(start:int, code:int, one:string, two:string):Token {
|
|
||||||
assert(this.peek == StringWrapper.charCodeAt(one, 0));
|
|
||||||
this.advance();
|
|
||||||
var str:string = one;
|
|
||||||
while (this.peek == code) {
|
|
||||||
this.advance();
|
|
||||||
str += two;
|
|
||||||
}
|
|
||||||
assert(SetWrapper.has(OPERATORS, str));
|
|
||||||
return newOperatorToken(start, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
scanIdentifier():Token {
|
|
||||||
assert(isIdentifierStart(this.peek));
|
|
||||||
var start:int = this.index;
|
|
||||||
this.advance();
|
|
||||||
while (isIdentifierPart(this.peek)) this.advance();
|
|
||||||
var str:string = this.input.substring(start, this.index);
|
|
||||||
if (SetWrapper.has(KEYWORDS, str)) {
|
|
||||||
return newKeywordToken(start, str);
|
|
||||||
} else {
|
|
||||||
return newIdentifierToken(start, str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scanNumber(start:int):Token {
|
|
||||||
assert(isDigit(this.peek));
|
|
||||||
var simple:boolean = (this.index === start);
|
|
||||||
this.advance(); // Skip initial digit.
|
|
||||||
while (true) {
|
|
||||||
if (isDigit(this.peek)) {
|
|
||||||
// Do nothing.
|
|
||||||
} else if (this.peek == $PERIOD) {
|
|
||||||
simple = false;
|
|
||||||
} else if (isExponentStart(this.peek)) {
|
|
||||||
this.advance();
|
|
||||||
if (isExponentSign(this.peek)) this.advance();
|
|
||||||
if (!isDigit(this.peek)) this.error('Invalid exponent', -1);
|
|
||||||
simple = false;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.advance();
|
|
||||||
}
|
|
||||||
var str:string = this.input.substring(start, this.index);
|
|
||||||
// TODO
|
|
||||||
var value:number = simple ? NumberWrapper.parseIntAutoRadix(str) : NumberWrapper.parseFloat(str);
|
|
||||||
return newNumberToken(start, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
scanString():Token {
|
|
||||||
assert(this.peek == $SQ || this.peek == $DQ);
|
|
||||||
var start:int = this.index;
|
|
||||||
var quote:int = this.peek;
|
|
||||||
this.advance(); // Skip initial quote.
|
|
||||||
|
|
||||||
var buffer:StringJoiner;
|
|
||||||
var marker:int = this.index;
|
|
||||||
var input:string = this.input;
|
|
||||||
|
|
||||||
while (this.peek != quote) {
|
|
||||||
if (this.peek == $BACKSLASH) {
|
|
||||||
if (buffer == null) buffer = new StringJoiner();
|
|
||||||
buffer.add(input.substring(marker, this.index));
|
|
||||||
this.advance();
|
|
||||||
var unescapedCode:int;
|
|
||||||
if (this.peek == $u) {
|
|
||||||
// 4 character hex code for unicode character.
|
|
||||||
var hex:string = input.substring(this.index + 1, this.index + 5);
|
|
||||||
try {
|
|
||||||
unescapedCode = NumberWrapper.parseInt(hex, 16);
|
|
||||||
} catch (e) {
|
|
||||||
this.error(`Invalid unicode escape [\\u${hex}]`, 0);
|
|
||||||
}
|
|
||||||
for (var i:int = 0; i < 5; i++) {
|
|
||||||
this.advance();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unescapedCode = unescape(this.peek);
|
|
||||||
this.advance();
|
|
||||||
}
|
|
||||||
buffer.add(StringWrapper.fromCharCode(unescapedCode));
|
|
||||||
marker = this.index;
|
|
||||||
} else if (this.peek == $EOF) {
|
|
||||||
this.error('Unterminated quote', 0);
|
|
||||||
} else {
|
|
||||||
this.advance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var last:string = input.substring(marker, this.index);
|
|
||||||
this.advance(); // Skip terminating quote.
|
|
||||||
|
|
||||||
// Compute the unescaped string value.
|
|
||||||
var unescaped:string = last;
|
|
||||||
if (buffer != null) {
|
|
||||||
buffer.add(last);
|
|
||||||
unescaped = buffer.toString();
|
|
||||||
}
|
|
||||||
return newStringToken(start, unescaped);
|
|
||||||
}
|
|
||||||
|
|
||||||
error(message:string, offset:int) {
|
|
||||||
var position:int = this.index + offset;
|
|
||||||
throw new ScannerError(`Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isWhitespace(code:int):boolean {
|
|
||||||
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isIdentifierStart(code:int):boolean {
|
|
||||||
return ($a <= code && code <= $z) ||
|
|
||||||
($A <= code && code <= $Z) ||
|
|
||||||
(code == $_) ||
|
|
||||||
(code == $$);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isIdentifierPart(code:int):boolean {
|
|
||||||
return ($a <= code && code <= $z) ||
|
|
||||||
($A <= code && code <= $Z) ||
|
|
||||||
($0 <= code && code <= $9) ||
|
|
||||||
(code == $_) ||
|
|
||||||
(code == $$);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDigit(code:int):boolean {
|
|
||||||
return $0 <= code && code <= $9;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isExponentStart(code:int):boolean {
|
|
||||||
return code == $e || code == $E;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isExponentSign(code:int):boolean {
|
|
||||||
return code == $MINUS || code == $PLUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
function unescape(code:int):int {
|
|
||||||
switch(code) {
|
|
||||||
case $n: return $LF;
|
|
||||||
case $f: return $FF;
|
|
||||||
case $r: return $CR;
|
|
||||||
case $t: return $TAB;
|
|
||||||
case $v: return $VTAB;
|
|
||||||
default: return code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var OPERATORS = SetWrapper.createFromList([
|
|
||||||
'+',
|
|
||||||
'-',
|
|
||||||
'*',
|
|
||||||
'/',
|
|
||||||
'%',
|
|
||||||
'^',
|
|
||||||
'=',
|
|
||||||
'==',
|
|
||||||
'!=',
|
|
||||||
'===',
|
|
||||||
'!==',
|
|
||||||
'<',
|
|
||||||
'>',
|
|
||||||
'<=',
|
|
||||||
'>=',
|
|
||||||
'&&',
|
|
||||||
'||',
|
|
||||||
'&',
|
|
||||||
'|',
|
|
||||||
'!',
|
|
||||||
'?',
|
|
||||||
'#'
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
var KEYWORDS = SetWrapper.createFromList([
|
|
||||||
'var',
|
|
||||||
'null',
|
|
||||||
'undefined',
|
|
||||||
'true',
|
|
||||||
'false'
|
|
||||||
]);
|
|
|
@ -0,0 +1,452 @@
|
||||||
|
import {Injectable} from 'angular2/src/di/decorators';
|
||||||
|
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
|
||||||
|
import {
|
||||||
|
int,
|
||||||
|
NumberWrapper,
|
||||||
|
StringJoiner,
|
||||||
|
StringWrapper,
|
||||||
|
BaseException
|
||||||
|
} from "angular2/src/facade/lang";
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
export const TOKEN_TYPE_CHARACTER = 1;
|
||||||
|
export const TOKEN_TYPE_IDENTIFIER = 2;
|
||||||
|
export const TOKEN_TYPE_KEYWORD = 3;
|
||||||
|
export const TOKEN_TYPE_STRING = 4;
|
||||||
|
export const TOKEN_TYPE_OPERATOR = 5;
|
||||||
|
export const TOKEN_TYPE_NUMBER = 6;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class Lexer {
|
||||||
|
tokenize(text: string): List<any> {
|
||||||
|
var scanner = new _Scanner(text);
|
||||||
|
var tokens = [];
|
||||||
|
var token = scanner.scanToken();
|
||||||
|
while (token != null) {
|
||||||
|
ListWrapper.push(tokens, token);
|
||||||
|
token = scanner.scanToken();
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Token {
|
||||||
|
constructor(public index: int, public type: int, public numValue: number,
|
||||||
|
public strValue: string) {}
|
||||||
|
|
||||||
|
isCharacter(code: int): boolean {
|
||||||
|
return (this.type == TOKEN_TYPE_CHARACTER && this.numValue == code);
|
||||||
|
}
|
||||||
|
|
||||||
|
isNumber(): boolean { return (this.type == TOKEN_TYPE_NUMBER); }
|
||||||
|
|
||||||
|
isString(): boolean { return (this.type == TOKEN_TYPE_STRING); }
|
||||||
|
|
||||||
|
isOperator(operater: string): boolean {
|
||||||
|
return (this.type == TOKEN_TYPE_OPERATOR && this.strValue == operater);
|
||||||
|
}
|
||||||
|
|
||||||
|
isIdentifier(): boolean { return (this.type == TOKEN_TYPE_IDENTIFIER); }
|
||||||
|
|
||||||
|
isKeyword(): boolean { return (this.type == TOKEN_TYPE_KEYWORD); }
|
||||||
|
|
||||||
|
isKeywordVar(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "var"); }
|
||||||
|
|
||||||
|
isKeywordNull(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "null"); }
|
||||||
|
|
||||||
|
isKeywordUndefined(): boolean {
|
||||||
|
return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
isKeywordTrue(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "true"); }
|
||||||
|
|
||||||
|
isKeywordFalse(): boolean {
|
||||||
|
return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
toNumber(): number {
|
||||||
|
// -1 instead of NULL ok?
|
||||||
|
return (this.type == TOKEN_TYPE_NUMBER) ? this.numValue : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
var t: int = this.type;
|
||||||
|
if (t >= TOKEN_TYPE_CHARACTER && t <= TOKEN_TYPE_STRING) {
|
||||||
|
return this.strValue;
|
||||||
|
} else if (t == TOKEN_TYPE_NUMBER) {
|
||||||
|
return this.numValue.toString();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function newCharacterToken(index: int, code: int): Token {
|
||||||
|
return new Token(index, TOKEN_TYPE_CHARACTER, code, StringWrapper.fromCharCode(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
function newIdentifierToken(index: int, text: string): Token {
|
||||||
|
return new Token(index, TOKEN_TYPE_IDENTIFIER, 0, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function newKeywordToken(index: int, text: string): Token {
|
||||||
|
return new Token(index, TOKEN_TYPE_KEYWORD, 0, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function newOperatorToken(index: int, text: string): Token {
|
||||||
|
return new Token(index, TOKEN_TYPE_OPERATOR, 0, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function newStringToken(index: int, text: string): Token {
|
||||||
|
return new Token(index, TOKEN_TYPE_STRING, 0, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function newNumberToken(index: int, n: number): Token {
|
||||||
|
return new Token(index, TOKEN_TYPE_NUMBER, n, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export var EOF: Token = new Token(-1, 0, 0, "");
|
||||||
|
|
||||||
|
export const $EOF = 0;
|
||||||
|
export const $TAB = 9;
|
||||||
|
export const $LF = 10;
|
||||||
|
export const $VTAB = 11;
|
||||||
|
export const $FF = 12;
|
||||||
|
export const $CR = 13;
|
||||||
|
export const $SPACE = 32;
|
||||||
|
export const $BANG = 33;
|
||||||
|
export const $DQ = 34;
|
||||||
|
export const $HASH = 35;
|
||||||
|
export const $$ = 36;
|
||||||
|
export const $PERCENT = 37;
|
||||||
|
export const $AMPERSAND = 38;
|
||||||
|
export const $SQ = 39;
|
||||||
|
export const $LPAREN = 40;
|
||||||
|
export const $RPAREN = 41;
|
||||||
|
export const $STAR = 42;
|
||||||
|
export const $PLUS = 43;
|
||||||
|
export const $COMMA = 44;
|
||||||
|
export const $MINUS = 45;
|
||||||
|
export const $PERIOD = 46;
|
||||||
|
export const $SLASH = 47;
|
||||||
|
export const $COLON = 58;
|
||||||
|
export const $SEMICOLON = 59;
|
||||||
|
export const $LT = 60;
|
||||||
|
export const $EQ = 61;
|
||||||
|
export const $GT = 62;
|
||||||
|
export const $QUESTION = 63;
|
||||||
|
|
||||||
|
const $0 = 48;
|
||||||
|
const $9 = 57;
|
||||||
|
|
||||||
|
const $A = 65, $E = 69, $Z = 90;
|
||||||
|
|
||||||
|
export const $LBRACKET = 91;
|
||||||
|
export const $BACKSLASH = 92;
|
||||||
|
export const $RBRACKET = 93;
|
||||||
|
const $CARET = 94;
|
||||||
|
const $_ = 95;
|
||||||
|
|
||||||
|
const $a = 97, $e = 101, $f = 102, $n = 110, $r = 114, $t = 116, $u = 117, $v = 118, $z = 122;
|
||||||
|
|
||||||
|
export const $LBRACE = 123;
|
||||||
|
export const $BAR = 124;
|
||||||
|
export const $RBRACE = 125;
|
||||||
|
const $NBSP = 160;
|
||||||
|
|
||||||
|
|
||||||
|
export class ScannerError extends BaseException {
|
||||||
|
message: string;
|
||||||
|
constructor(message) {
|
||||||
|
super();
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() { return this.message; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Scanner {
|
||||||
|
length: int;
|
||||||
|
peek: int;
|
||||||
|
index: int;
|
||||||
|
|
||||||
|
constructor(public input: string) {
|
||||||
|
this.length = input.length;
|
||||||
|
this.peek = 0;
|
||||||
|
this.index = -1;
|
||||||
|
this.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
advance() {
|
||||||
|
this.peek =
|
||||||
|
++this.index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, this.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanToken(): Token {
|
||||||
|
var input = this.input, length = this.length, peek = this.peek, index = this.index;
|
||||||
|
|
||||||
|
// Skip whitespace.
|
||||||
|
while (peek <= $SPACE) {
|
||||||
|
if (++index >= length) {
|
||||||
|
peek = $EOF;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
peek = StringWrapper.charCodeAt(input, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.peek = peek;
|
||||||
|
this.index = index;
|
||||||
|
|
||||||
|
if (index >= length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle identifiers and numbers.
|
||||||
|
if (isIdentifierStart(peek)) return this.scanIdentifier();
|
||||||
|
if (isDigit(peek)) return this.scanNumber(index);
|
||||||
|
|
||||||
|
var start: int = index;
|
||||||
|
switch (peek) {
|
||||||
|
case $PERIOD:
|
||||||
|
this.advance();
|
||||||
|
return isDigit(this.peek) ? this.scanNumber(start) : newCharacterToken(start, $PERIOD);
|
||||||
|
case $LPAREN:
|
||||||
|
case $RPAREN:
|
||||||
|
case $LBRACE:
|
||||||
|
case $RBRACE:
|
||||||
|
case $LBRACKET:
|
||||||
|
case $RBRACKET:
|
||||||
|
case $COMMA:
|
||||||
|
case $COLON:
|
||||||
|
case $SEMICOLON:
|
||||||
|
return this.scanCharacter(start, peek);
|
||||||
|
case $SQ:
|
||||||
|
case $DQ:
|
||||||
|
return this.scanString();
|
||||||
|
case $HASH:
|
||||||
|
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
|
||||||
|
case $PLUS:
|
||||||
|
case $MINUS:
|
||||||
|
case $STAR:
|
||||||
|
case $SLASH:
|
||||||
|
case $PERCENT:
|
||||||
|
case $CARET:
|
||||||
|
case $QUESTION:
|
||||||
|
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
|
||||||
|
case $LT:
|
||||||
|
case $GT:
|
||||||
|
case $BANG:
|
||||||
|
case $EQ:
|
||||||
|
return this.scanComplexOperator(start, $EQ, StringWrapper.fromCharCode(peek), '=');
|
||||||
|
case $AMPERSAND:
|
||||||
|
return this.scanComplexOperator(start, $AMPERSAND, '&', '&');
|
||||||
|
case $BAR:
|
||||||
|
return this.scanComplexOperator(start, $BAR, '|', '|');
|
||||||
|
case $NBSP:
|
||||||
|
while (isWhitespace(this.peek)) this.advance();
|
||||||
|
return this.scanToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`, 0);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
scanCharacter(start: int, code: int): Token {
|
||||||
|
assert(this.peek == code);
|
||||||
|
this.advance();
|
||||||
|
return newCharacterToken(start, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
scanOperator(start: int, str: string): Token {
|
||||||
|
assert(this.peek == StringWrapper.charCodeAt(str, 0));
|
||||||
|
assert(SetWrapper.has(OPERATORS, str));
|
||||||
|
this.advance();
|
||||||
|
return newOperatorToken(start, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanComplexOperator(start: int, code: int, one: string, two: string): Token {
|
||||||
|
assert(this.peek == StringWrapper.charCodeAt(one, 0));
|
||||||
|
this.advance();
|
||||||
|
var str: string = one;
|
||||||
|
while (this.peek == code) {
|
||||||
|
this.advance();
|
||||||
|
str += two;
|
||||||
|
}
|
||||||
|
assert(SetWrapper.has(OPERATORS, str));
|
||||||
|
return newOperatorToken(start, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanIdentifier(): Token {
|
||||||
|
assert(isIdentifierStart(this.peek));
|
||||||
|
var start: int = this.index;
|
||||||
|
this.advance();
|
||||||
|
while (isIdentifierPart(this.peek)) this.advance();
|
||||||
|
var str: string = this.input.substring(start, this.index);
|
||||||
|
if (SetWrapper.has(KEYWORDS, str)) {
|
||||||
|
return newKeywordToken(start, str);
|
||||||
|
} else {
|
||||||
|
return newIdentifierToken(start, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scanNumber(start: int): Token {
|
||||||
|
assert(isDigit(this.peek));
|
||||||
|
var simple: boolean = (this.index === start);
|
||||||
|
this.advance(); // Skip initial digit.
|
||||||
|
while (true) {
|
||||||
|
if (isDigit(this.peek)) {
|
||||||
|
// Do nothing.
|
||||||
|
} else if (this.peek == $PERIOD) {
|
||||||
|
simple = false;
|
||||||
|
} else if (isExponentStart(this.peek)) {
|
||||||
|
this.advance();
|
||||||
|
if (isExponentSign(this.peek)) this.advance();
|
||||||
|
if (!isDigit(this.peek)) this.error('Invalid exponent', -1);
|
||||||
|
simple = false;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.advance();
|
||||||
|
}
|
||||||
|
var str: string = this.input.substring(start, this.index);
|
||||||
|
// TODO
|
||||||
|
var value: number =
|
||||||
|
simple ? NumberWrapper.parseIntAutoRadix(str) : NumberWrapper.parseFloat(str);
|
||||||
|
return newNumberToken(start, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanString(): Token {
|
||||||
|
assert(this.peek == $SQ || this.peek == $DQ);
|
||||||
|
var start: int = this.index;
|
||||||
|
var quote: int = this.peek;
|
||||||
|
this.advance(); // Skip initial quote.
|
||||||
|
|
||||||
|
var buffer: StringJoiner;
|
||||||
|
var marker: int = this.index;
|
||||||
|
var input: string = this.input;
|
||||||
|
|
||||||
|
while (this.peek != quote) {
|
||||||
|
if (this.peek == $BACKSLASH) {
|
||||||
|
if (buffer == null) buffer = new StringJoiner();
|
||||||
|
buffer.add(input.substring(marker, this.index));
|
||||||
|
this.advance();
|
||||||
|
var unescapedCode: int;
|
||||||
|
if (this.peek == $u) {
|
||||||
|
// 4 character hex code for unicode character.
|
||||||
|
var hex: string = input.substring(this.index + 1, this.index + 5);
|
||||||
|
try {
|
||||||
|
unescapedCode = NumberWrapper.parseInt(hex, 16);
|
||||||
|
} catch (e) {
|
||||||
|
this.error(`Invalid unicode escape [\\u${hex}]`, 0);
|
||||||
|
}
|
||||||
|
for (var i: int = 0; i < 5; i++) {
|
||||||
|
this.advance();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unescapedCode = unescape(this.peek);
|
||||||
|
this.advance();
|
||||||
|
}
|
||||||
|
buffer.add(StringWrapper.fromCharCode(unescapedCode));
|
||||||
|
marker = this.index;
|
||||||
|
} else if (this.peek == $EOF) {
|
||||||
|
this.error('Unterminated quote', 0);
|
||||||
|
} else {
|
||||||
|
this.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var last: string = input.substring(marker, this.index);
|
||||||
|
this.advance(); // Skip terminating quote.
|
||||||
|
|
||||||
|
// Compute the unescaped string value.
|
||||||
|
var unescaped: string = last;
|
||||||
|
if (buffer != null) {
|
||||||
|
buffer.add(last);
|
||||||
|
unescaped = buffer.toString();
|
||||||
|
}
|
||||||
|
return newStringToken(start, unescaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(message: string, offset: int) {
|
||||||
|
var position: int = this.index + offset;
|
||||||
|
throw new ScannerError(
|
||||||
|
`Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWhitespace(code: int): boolean {
|
||||||
|
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIdentifierStart(code: int): boolean {
|
||||||
|
return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || (code == $_) || (code == $$);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIdentifierPart(code: int): boolean {
|
||||||
|
return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || ($0 <= code && code <= $9) ||
|
||||||
|
(code == $_) || (code == $$);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDigit(code: int): boolean {
|
||||||
|
return $0 <= code && code <= $9;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExponentStart(code: int): boolean {
|
||||||
|
return code == $e || code == $E;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExponentSign(code: int): boolean {
|
||||||
|
return code == $MINUS || code == $PLUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unescape(code: int): int {
|
||||||
|
switch (code) {
|
||||||
|
case $n:
|
||||||
|
return $LF;
|
||||||
|
case $f:
|
||||||
|
return $FF;
|
||||||
|
case $r:
|
||||||
|
return $CR;
|
||||||
|
case $t:
|
||||||
|
return $TAB;
|
||||||
|
case $v:
|
||||||
|
return $VTAB;
|
||||||
|
default:
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var OPERATORS = SetWrapper.createFromList([
|
||||||
|
'+',
|
||||||
|
'-',
|
||||||
|
'*',
|
||||||
|
'/',
|
||||||
|
'%',
|
||||||
|
'^',
|
||||||
|
'=',
|
||||||
|
'==',
|
||||||
|
'!=',
|
||||||
|
'===',
|
||||||
|
'!==',
|
||||||
|
'<',
|
||||||
|
'>',
|
||||||
|
'<=',
|
||||||
|
'>=',
|
||||||
|
'&&',
|
||||||
|
'||',
|
||||||
|
'&',
|
||||||
|
'|',
|
||||||
|
'!',
|
||||||
|
'?',
|
||||||
|
'#'
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
var KEYWORDS = SetWrapper.createFromList(['var', 'null', 'undefined', 'true', 'false']);
|
|
@ -1,16 +1,15 @@
|
||||||
import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
export class Locals {
|
export class Locals {
|
||||||
parent:Locals;
|
constructor(public parent: Locals, public current: Map<any, any>) {}
|
||||||
current:Map;
|
|
||||||
|
|
||||||
constructor(parent:Locals, current:Map) {
|
contains(name: string): boolean {
|
||||||
this.parent = parent;
|
|
||||||
this.current = current;
|
|
||||||
}
|
|
||||||
|
|
||||||
contains(name:string):boolean {
|
|
||||||
if (MapWrapper.contains(this.current, name)) {
|
if (MapWrapper.contains(this.current, name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +21,7 @@ export class Locals {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(name:string) {
|
get(name: string) {
|
||||||
if (MapWrapper.contains(this.current, name)) {
|
if (MapWrapper.contains(this.current, name)) {
|
||||||
return MapWrapper.get(this.current, name);
|
return MapWrapper.get(this.current, name);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +33,7 @@ export class Locals {
|
||||||
throw new BaseException(`Cannot find '${name}'`);
|
throw new BaseException(`Cannot find '${name}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(name:string, value):void {
|
set(name: string, value): void {
|
||||||
// TODO(rado): consider removing this check if we can guarantee this is not
|
// TODO(rado): consider removing this check if we can guarantee this is not
|
||||||
// exposed to the public API.
|
// exposed to the public API.
|
||||||
// TODO: vsavkin maybe it should check only the local map
|
// TODO: vsavkin maybe it should check only the local map
|
||||||
|
@ -45,7 +44,5 @@ export class Locals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearValues():void {
|
clearValues(): void { MapWrapper.clearValues(this.current); }
|
||||||
MapWrapper.clearValues(this.current);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,8 +1,28 @@
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {Injectable} from 'angular2/src/di/decorators';
|
||||||
import {int, isBlank, isPresent, BaseException, StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
|
import {
|
||||||
|
int,
|
||||||
|
isBlank,
|
||||||
|
isPresent,
|
||||||
|
BaseException,
|
||||||
|
StringWrapper,
|
||||||
|
RegExpWrapper
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||||
import {Lexer, EOF, Token, $PERIOD, $COLON, $SEMICOLON, $LBRACKET, $RBRACKET,
|
import {
|
||||||
$COMMA, $LBRACE, $RBRACE, $LPAREN, $RPAREN} from './lexer';
|
Lexer,
|
||||||
|
EOF,
|
||||||
|
Token,
|
||||||
|
$PERIOD,
|
||||||
|
$COLON,
|
||||||
|
$SEMICOLON,
|
||||||
|
$LBRACKET,
|
||||||
|
$RBRACKET,
|
||||||
|
$COMMA,
|
||||||
|
$LBRACE,
|
||||||
|
$RBRACE,
|
||||||
|
$LPAREN,
|
||||||
|
$RPAREN
|
||||||
|
} from './lexer';
|
||||||
import {reflector, Reflector} from 'angular2/src/reflection/reflection';
|
import {reflector, Reflector} from 'angular2/src/reflection/reflection';
|
||||||
import {
|
import {
|
||||||
AST,
|
AST,
|
||||||
|
@ -10,7 +30,6 @@ import {
|
||||||
ImplicitReceiver,
|
ImplicitReceiver,
|
||||||
AccessMember,
|
AccessMember,
|
||||||
LiteralPrimitive,
|
LiteralPrimitive,
|
||||||
Expression,
|
|
||||||
Binary,
|
Binary,
|
||||||
PrefixNot,
|
PrefixNot,
|
||||||
Conditional,
|
Conditional,
|
||||||
|
@ -23,10 +42,15 @@ import {
|
||||||
Interpolation,
|
Interpolation,
|
||||||
MethodCall,
|
MethodCall,
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
TemplateBindings,
|
|
||||||
TemplateBinding,
|
TemplateBinding,
|
||||||
ASTWithSource
|
ASTWithSource
|
||||||
} from './ast';
|
} from './ast';
|
||||||
|
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
var _implicitReceiver = new ImplicitReceiver();
|
var _implicitReceiver = new ImplicitReceiver();
|
||||||
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
|
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
|
||||||
|
@ -34,40 +58,40 @@ var INTERPOLATION_REGEXP = RegExpWrapper.create('\\{\\{(.*?)\\}\\}');
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Parser {
|
export class Parser {
|
||||||
_lexer:Lexer;
|
_lexer: Lexer;
|
||||||
_reflector:Reflector;
|
_reflector: Reflector;
|
||||||
constructor(lexer:Lexer, providedReflector:Reflector = null){
|
constructor(lexer: Lexer, providedReflector: Reflector = null) {
|
||||||
this._lexer = lexer;
|
this._lexer = lexer;
|
||||||
this._reflector = isPresent(providedReflector) ? providedReflector : reflector;
|
this._reflector = isPresent(providedReflector) ? providedReflector : reflector;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAction(input:string, location:any):ASTWithSource {
|
parseAction(input: string, location: any): ASTWithSource {
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
var ast = new _ParseAST(input, location, tokens, this._reflector, true).parseChain();
|
var ast = new _ParseAST(input, location, tokens, this._reflector, true).parseChain();
|
||||||
return new ASTWithSource(ast, input, location);
|
return new ASTWithSource(ast, input, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseBinding(input:string, location:any):ASTWithSource {
|
parseBinding(input: string, location: any): ASTWithSource {
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
|
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
|
||||||
return new ASTWithSource(ast, input, location);
|
return new ASTWithSource(ast, input, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
addPipes(bindingAst:ASTWithSource, pipes:List<String>):ASTWithSource {
|
addPipes(bindingAst: ASTWithSource, pipes: List<string>): ASTWithSource {
|
||||||
if (ListWrapper.isEmpty(pipes)) return bindingAst;
|
if (ListWrapper.isEmpty(pipes)) return bindingAst;
|
||||||
|
|
||||||
var res = ListWrapper.reduce(pipes,
|
var res: AST = ListWrapper.reduce(
|
||||||
(result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
|
pipes, (result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
|
||||||
bindingAst.ast);
|
bindingAst.ast);
|
||||||
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
|
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseTemplateBindings(input:string, location:any):List<TemplateBinding> {
|
parseTemplateBindings(input: string, location: any): List<TemplateBinding> {
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
return new _ParseAST(input, location, tokens, this._reflector, false).parseTemplateBindings();
|
return new _ParseAST(input, location, tokens, this._reflector, false).parseTemplateBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
parseInterpolation(input:string, location:any):ASTWithSource {
|
parseInterpolation(input: string, location: any): ASTWithSource {
|
||||||
var parts = StringWrapper.split(input, INTERPOLATION_REGEXP);
|
var parts = StringWrapper.split(input, INTERPOLATION_REGEXP);
|
||||||
if (parts.length <= 1) {
|
if (parts.length <= 1) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -75,9 +99,9 @@ export class Parser {
|
||||||
var strings = [];
|
var strings = [];
|
||||||
var expressions = [];
|
var expressions = [];
|
||||||
|
|
||||||
for (var i=0; i<parts.length; i++) {
|
for (var i = 0; i < parts.length; i++) {
|
||||||
var part = parts[i];
|
var part = parts[i];
|
||||||
if (i%2 === 0) {
|
if (i % 2 === 0) {
|
||||||
// fixed string
|
// fixed string
|
||||||
ListWrapper.push(strings, part);
|
ListWrapper.push(strings, part);
|
||||||
} else {
|
} else {
|
||||||
|
@ -89,46 +113,32 @@ export class Parser {
|
||||||
return new ASTWithSource(new Interpolation(strings, expressions), input, location);
|
return new ASTWithSource(new Interpolation(strings, expressions), input, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapLiteralPrimitive(input:string, location:any):ASTWithSource {
|
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
|
||||||
return new ASTWithSource(new LiteralPrimitive(input), input, location);
|
return new ASTWithSource(new LiteralPrimitive(input), input, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ParseAST {
|
class _ParseAST {
|
||||||
input:string;
|
index: int;
|
||||||
location:any;
|
constructor(public input: string, public location: any, public tokens: List<any>,
|
||||||
tokens:List<Token>;
|
public reflector: Reflector, public parseAction: boolean) {
|
||||||
reflector:Reflector;
|
|
||||||
parseAction:boolean;
|
|
||||||
index:int;
|
|
||||||
constructor(input:string, location:any, tokens:List, reflector:Reflector, parseAction:boolean) {
|
|
||||||
this.input = input;
|
|
||||||
this.location = location;
|
|
||||||
this.tokens = tokens;
|
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.reflector = reflector;
|
|
||||||
this.parseAction = parseAction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
peek(offset:int):Token {
|
peek(offset: int): Token {
|
||||||
var i = this.index + offset;
|
var i = this.index + offset;
|
||||||
return i < this.tokens.length ? this.tokens[i] : EOF;
|
return i < this.tokens.length ? this.tokens[i] : EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
get next():Token {
|
get next(): Token { return this.peek(0); }
|
||||||
return this.peek(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
get inputIndex():int {
|
get inputIndex(): int {
|
||||||
return (this.index < this.tokens.length) ? this.next.index : this.input.length;
|
return (this.index < this.tokens.length) ? this.next.index : this.input.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
advance() {
|
advance() { this.index++; }
|
||||||
this.index ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
optionalCharacter(code:int):boolean {
|
optionalCharacter(code: int): boolean {
|
||||||
if (this.next.isCharacter(code)) {
|
if (this.next.isCharacter(code)) {
|
||||||
this.advance();
|
this.advance();
|
||||||
return true;
|
return true;
|
||||||
|
@ -137,7 +147,7 @@ class _ParseAST {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
optionalKeywordVar():boolean {
|
optionalKeywordVar(): boolean {
|
||||||
if (this.peekKeywordVar()) {
|
if (this.peekKeywordVar()) {
|
||||||
this.advance();
|
this.advance();
|
||||||
return true;
|
return true;
|
||||||
|
@ -146,17 +156,15 @@ class _ParseAST {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
peekKeywordVar():boolean {
|
peekKeywordVar(): boolean { return this.next.isKeywordVar() || this.next.isOperator('#'); }
|
||||||
return this.next.isKeywordVar() || this.next.isOperator('#');
|
|
||||||
}
|
|
||||||
|
|
||||||
expectCharacter(code:int) {
|
expectCharacter(code: int) {
|
||||||
if (this.optionalCharacter(code)) return;
|
if (this.optionalCharacter(code)) return;
|
||||||
this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
|
this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
optionalOperator(op:string):boolean {
|
optionalOperator(op: string): boolean {
|
||||||
if (this.next.isOperator(op)) {
|
if (this.next.isOperator(op)) {
|
||||||
this.advance();
|
this.advance();
|
||||||
return true;
|
return true;
|
||||||
|
@ -165,12 +173,12 @@ class _ParseAST {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expectOperator(operator:string) {
|
expectOperator(operator: string) {
|
||||||
if (this.optionalOperator(operator)) return;
|
if (this.optionalOperator(operator)) return;
|
||||||
this.error(`Missing expected operator ${operator}`);
|
this.error(`Missing expected operator ${operator}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
expectIdentifierOrKeyword():string {
|
expectIdentifierOrKeyword(): string {
|
||||||
var n = this.next;
|
var n = this.next;
|
||||||
if (!n.isIdentifier() && !n.isKeyword()) {
|
if (!n.isIdentifier() && !n.isKeyword()) {
|
||||||
this.error(`Unexpected token ${n}, expected identifier or keyword`)
|
this.error(`Unexpected token ${n}, expected identifier or keyword`)
|
||||||
|
@ -179,7 +187,7 @@ class _ParseAST {
|
||||||
return n.toString();
|
return n.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
expectIdentifierOrKeywordOrString():string {
|
expectIdentifierOrKeywordOrString(): string {
|
||||||
var n = this.next;
|
var n = this.next;
|
||||||
if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
|
if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
|
||||||
this.error(`Unexpected token ${n}, expected identifier, keyword, or string`)
|
this.error(`Unexpected token ${n}, expected identifier, keyword, or string`)
|
||||||
|
@ -188,17 +196,18 @@ class _ParseAST {
|
||||||
return n.toString();
|
return n.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
parseChain():AST {
|
parseChain(): AST {
|
||||||
var exprs = [];
|
var exprs = [];
|
||||||
while (this.index < this.tokens.length) {
|
while (this.index < this.tokens.length) {
|
||||||
var expr = this.parsePipe();
|
var expr = this.parsePipe();
|
||||||
ListWrapper.push(exprs, expr);
|
ListWrapper.push(exprs, expr);
|
||||||
|
|
||||||
if (this.optionalCharacter($SEMICOLON)) {
|
if (this.optionalCharacter($SEMICOLON)) {
|
||||||
if (! this.parseAction) {
|
if (!this.parseAction) {
|
||||||
this.error("Binding expression cannot contain chained expression");
|
this.error("Binding expression cannot contain chained expression");
|
||||||
}
|
}
|
||||||
while (this.optionalCharacter($SEMICOLON)){} //read all semicolons
|
while (this.optionalCharacter($SEMICOLON)) {
|
||||||
|
} // read all semicolons
|
||||||
} else if (this.index < this.tokens.length) {
|
} else if (this.index < this.tokens.length) {
|
||||||
this.error(`Unexpected token '${this.next}'`);
|
this.error(`Unexpected token '${this.next}'`);
|
||||||
}
|
}
|
||||||
|
@ -353,7 +362,7 @@ class _ParseAST {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseCallChain():AST {
|
parseCallChain(): AST {
|
||||||
var result = this.parsePrimary();
|
var result = this.parsePrimary();
|
||||||
while (true) {
|
while (true) {
|
||||||
if (this.optionalCharacter($PERIOD)) {
|
if (this.optionalCharacter($PERIOD)) {
|
||||||
|
@ -410,9 +419,9 @@ class _ParseAST {
|
||||||
return new LiteralPrimitive(value);
|
return new LiteralPrimitive(value);
|
||||||
|
|
||||||
} else if (this.next.isString()) {
|
} else if (this.next.isString()) {
|
||||||
var value = this.next.toString();
|
var literalValue = this.next.toString();
|
||||||
this.advance();
|
this.advance();
|
||||||
return new LiteralPrimitive(value);
|
return new LiteralPrimitive(literalValue);
|
||||||
|
|
||||||
} else if (this.index >= this.tokens.length) {
|
} else if (this.index >= this.tokens.length) {
|
||||||
this.error(`Unexpected end of expression: ${this.input}`);
|
this.error(`Unexpected end of expression: ${this.input}`);
|
||||||
|
@ -422,7 +431,7 @@ class _ParseAST {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseExpressionList(terminator:int):List {
|
parseExpressionList(terminator: int): List<any> {
|
||||||
var result = [];
|
var result = [];
|
||||||
if (!this.next.isCharacter(terminator)) {
|
if (!this.next.isCharacter(terminator)) {
|
||||||
do {
|
do {
|
||||||
|
@ -448,7 +457,7 @@ class _ParseAST {
|
||||||
return new LiteralMap(keys, values);
|
return new LiteralMap(keys, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAccessMemberOrMethodCall(receiver):AST {
|
parseAccessMemberOrMethodCall(receiver): AST {
|
||||||
var id = this.expectIdentifierOrKeyword();
|
var id = this.expectIdentifierOrKeyword();
|
||||||
|
|
||||||
if (this.optionalCharacter($LPAREN)) {
|
if (this.optionalCharacter($LPAREN)) {
|
||||||
|
@ -471,7 +480,7 @@ class _ParseAST {
|
||||||
}
|
}
|
||||||
|
|
||||||
parseInlinedPipe(result) {
|
parseInlinedPipe(result) {
|
||||||
do {
|
do {
|
||||||
if (this.parseAction) {
|
if (this.parseAction) {
|
||||||
this.error("Cannot have a pipe in an action expression");
|
this.error("Cannot have a pipe in an action expression");
|
||||||
}
|
}
|
||||||
|
@ -481,7 +490,7 @@ class _ParseAST {
|
||||||
ListWrapper.push(args, this.parseExpression());
|
ListWrapper.push(args, this.parseExpression());
|
||||||
}
|
}
|
||||||
result = new Pipe(result, name, args, true);
|
result = new Pipe(result, name, args, true);
|
||||||
} while(this.optionalOperator("|"));
|
} while (this.optionalOperator("|"));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -515,7 +524,7 @@ class _ParseAST {
|
||||||
parseTemplateBindings() {
|
parseTemplateBindings() {
|
||||||
var bindings = [];
|
var bindings = [];
|
||||||
while (this.index < this.tokens.length) {
|
while (this.index < this.tokens.length) {
|
||||||
var keyIsVar:boolean = this.optionalKeywordVar();
|
var keyIsVar: boolean = this.optionalKeywordVar();
|
||||||
var key = this.expectTemplateBindingKey();
|
var key = this.expectTemplateBindingKey();
|
||||||
this.optionalCharacter($COLON);
|
this.optionalCharacter($COLON);
|
||||||
var name = null;
|
var name = null;
|
||||||
|
@ -542,13 +551,13 @@ class _ParseAST {
|
||||||
return bindings;
|
return bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
error(message:string, index:int = null) {
|
error(message: string, index: int = null) {
|
||||||
if (isBlank(index)) index = this.index;
|
if (isBlank(index)) index = this.index;
|
||||||
|
|
||||||
var location = (index < this.tokens.length)
|
var location = (index < this.tokens.length) ? `at column ${this.tokens[index].index + 1} in` :
|
||||||
? `at column ${this.tokens[index].index + 1} in`
|
`at the end of the expression`;
|
||||||
: `at the end of the expression`;
|
|
||||||
|
|
||||||
throw new BaseException(`Parser Error: ${message} ${location} [${this.input}] in ${this.location}`);
|
throw new BaseException(
|
||||||
|
`Parser Error: ${message} ${location} [${this.input}] in ${this.location}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,13 +3,21 @@ import {isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
|
||||||
import {Pipe, WrappedValue, PipeFactory} from './pipe';
|
import {Pipe, WrappedValue, PipeFactory} from './pipe';
|
||||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements async bindings to Observable.
|
* Implements async bindings to Observable.
|
||||||
*
|
*
|
||||||
* # Example
|
* # Example
|
||||||
*
|
*
|
||||||
* In this example we bind the description observable to the DOM. The async pipe will convert an observable to the
|
* In this example we bind the description observable to the DOM. The async pipe will convert an
|
||||||
* latest value it emitted. It will also request a change detection check when a new value is emitted.
|
*observable to the
|
||||||
|
* latest value it emitted. It will also request a change detection check when a new value is
|
||||||
|
*emitted.
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* @Component({
|
* @Component({
|
||||||
|
@ -28,15 +36,15 @@ import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class AsyncPipe extends Pipe {
|
export class AsyncPipe extends Pipe {
|
||||||
_ref:ChangeDetectorRef;
|
_ref: ChangeDetectorRef;
|
||||||
|
|
||||||
_latestValue:Object;
|
_latestValue: Object;
|
||||||
_latestReturnedValue:Object;
|
_latestReturnedValue: Object;
|
||||||
|
|
||||||
_subscription:Object;
|
_subscription: Object;
|
||||||
_observable:Observable;
|
_observable: Observable;
|
||||||
|
|
||||||
constructor(ref:ChangeDetectorRef) {
|
constructor(ref: ChangeDetectorRef) {
|
||||||
super();
|
super();
|
||||||
this._ref = ref;
|
this._ref = ref;
|
||||||
this._latestValue = null;
|
this._latestValue = null;
|
||||||
|
@ -45,17 +53,15 @@ export class AsyncPipe extends Pipe {
|
||||||
this._observable = null;
|
this._observable = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
supports(obs):boolean {
|
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
|
||||||
return ObservableWrapper.isObservable(obs);
|
|
||||||
}
|
|
||||||
|
|
||||||
onDestroy():void {
|
onDestroy(): void {
|
||||||
if (isPresent(this._subscription)) {
|
if (isPresent(this._subscription)) {
|
||||||
this._dispose();
|
this._dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transform(obs:Observable):any {
|
transform(obs: Observable): any {
|
||||||
if (isBlank(this._subscription)) {
|
if (isBlank(this._subscription)) {
|
||||||
this._subscribe(obs);
|
this._subscribe(obs);
|
||||||
return null;
|
return null;
|
||||||
|
@ -74,15 +80,13 @@ export class AsyncPipe extends Pipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_subscribe(obs:Observable):void {
|
_subscribe(obs: Observable): void {
|
||||||
this._observable = obs;
|
this._observable = obs;
|
||||||
this._subscription = ObservableWrapper.subscribe(obs,
|
this._subscription = ObservableWrapper.subscribe(obs, value => {this._updateLatestValue(value)},
|
||||||
value => this._updateLatestValue(value),
|
e => { throw e; });
|
||||||
e => {throw e;}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_dispose():void {
|
_dispose(): void {
|
||||||
ObservableWrapper.dispose(this._subscription);
|
ObservableWrapper.dispose(this._subscription);
|
||||||
this._latestValue = null;
|
this._latestValue = null;
|
||||||
this._latestReturnedValue = null;
|
this._latestReturnedValue = null;
|
||||||
|
@ -90,7 +94,7 @@ export class AsyncPipe extends Pipe {
|
||||||
this._observable = null;
|
this._observable = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateLatestValue(value:Object) {
|
_updateLatestValue(value: Object) {
|
||||||
this._latestValue = value;
|
this._latestValue = value;
|
||||||
this._ref.requestCheck();
|
this._ref.requestCheck();
|
||||||
}
|
}
|
||||||
|
@ -101,17 +105,11 @@ export class AsyncPipe extends Pipe {
|
||||||
*
|
*
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class AsyncPipeFactory extends PipeFactory {
|
export class AsyncPipeFactory extends PipeFactory {
|
||||||
@CONST()
|
constructor() { super(); }
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(obs):boolean {
|
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
|
||||||
return ObservableWrapper.isObservable(obs);
|
|
||||||
}
|
|
||||||
|
|
||||||
create(cdRef):Pipe {
|
create(cdRef): Pipe { return new AsyncPipe(cdRef); }
|
||||||
return new AsyncPipe(cdRef);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -17,38 +17,37 @@ import {
|
||||||
|
|
||||||
import {WrappedValue, Pipe, PipeFactory} from './pipe';
|
import {WrappedValue, Pipe, PipeFactory} from './pipe';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
@CONST()
|
||||||
export class IterableChangesFactory extends PipeFactory {
|
export class IterableChangesFactory extends PipeFactory {
|
||||||
@CONST()
|
constructor() { super(); }
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(obj):boolean {
|
supports(obj): boolean { return IterableChanges.supportsObj(obj); }
|
||||||
return IterableChanges.supportsObj(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
create(cdRef):Pipe {
|
create(cdRef): Pipe { return new IterableChanges(); }
|
||||||
return new IterableChanges();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class IterableChanges extends Pipe {
|
export class IterableChanges extends Pipe {
|
||||||
_collection;
|
private _collection;
|
||||||
_length:int;
|
private _length: int;
|
||||||
_linkedRecords:_DuplicateMap;
|
private _linkedRecords: _DuplicateMap;
|
||||||
_unlinkedRecords:_DuplicateMap;
|
private _unlinkedRecords: _DuplicateMap;
|
||||||
_previousItHead:CollectionChangeRecord;
|
private _previousItHead: CollectionChangeRecord;
|
||||||
_itHead:CollectionChangeRecord;
|
private _itHead: CollectionChangeRecord;
|
||||||
_itTail:CollectionChangeRecord;
|
private _itTail: CollectionChangeRecord;
|
||||||
_additionsHead:CollectionChangeRecord;
|
private _additionsHead: CollectionChangeRecord;
|
||||||
_additionsTail:CollectionChangeRecord;
|
private _additionsTail: CollectionChangeRecord;
|
||||||
_movesHead:CollectionChangeRecord;
|
private _movesHead: CollectionChangeRecord;
|
||||||
_movesTail:CollectionChangeRecord;
|
private _movesTail: CollectionChangeRecord;
|
||||||
_removalsHead:CollectionChangeRecord;
|
private _removalsHead: CollectionChangeRecord;
|
||||||
_removalsTail:CollectionChangeRecord;
|
private _removalsTail: CollectionChangeRecord;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -70,58 +69,50 @@ export class IterableChanges extends Pipe {
|
||||||
this._removalsTail = null;
|
this._removalsTail = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static supportsObj(obj):boolean {
|
static supportsObj(obj): boolean { return isListLikeIterable(obj); }
|
||||||
return isListLikeIterable(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(obj):boolean {
|
supports(obj): boolean { return IterableChanges.supportsObj(obj); }
|
||||||
return IterableChanges.supportsObj(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
get collection() {
|
get collection() { return this._collection; }
|
||||||
return this._collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
get length():int {
|
get length(): int { return this._length; }
|
||||||
return this._length;
|
|
||||||
}
|
|
||||||
|
|
||||||
forEachItem(fn:Function) {
|
forEachItem(fn: Function) {
|
||||||
var record:CollectionChangeRecord;
|
var record: CollectionChangeRecord;
|
||||||
for (record = this._itHead; record !== null; record = record._next) {
|
for (record = this._itHead; record !== null; record = record._next) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachPreviousItem(fn:Function) {
|
forEachPreviousItem(fn: Function) {
|
||||||
var record:CollectionChangeRecord;
|
var record: CollectionChangeRecord;
|
||||||
for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachAddedItem(fn:Function){
|
forEachAddedItem(fn: Function) {
|
||||||
var record:CollectionChangeRecord;
|
var record: CollectionChangeRecord;
|
||||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachMovedItem(fn:Function) {
|
forEachMovedItem(fn: Function) {
|
||||||
var record:CollectionChangeRecord;
|
var record: CollectionChangeRecord;
|
||||||
for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachRemovedItem(fn:Function){
|
forEachRemovedItem(fn: Function) {
|
||||||
var record:CollectionChangeRecord;
|
var record: CollectionChangeRecord;
|
||||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transform(collection){
|
transform(collection): any {
|
||||||
if (this.check(collection)) {
|
if (this.check(collection)) {
|
||||||
return WrappedValue.wrap(this);
|
return WrappedValue.wrap(this);
|
||||||
} else {
|
} else {
|
||||||
|
@ -130,12 +121,12 @@ export class IterableChanges extends Pipe {
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
||||||
check(collection):boolean {
|
check(collection): boolean {
|
||||||
this._reset();
|
this._reset();
|
||||||
|
|
||||||
var record:CollectionChangeRecord = this._itHead;
|
var record: CollectionChangeRecord = this._itHead;
|
||||||
var mayBeDirty:boolean = false;
|
var mayBeDirty: boolean = false;
|
||||||
var index:int;
|
var index: int;
|
||||||
var item;
|
var item;
|
||||||
|
|
||||||
if (ListWrapper.isList(collection)) {
|
if (ListWrapper.isList(collection)) {
|
||||||
|
@ -175,10 +166,8 @@ export class IterableChanges extends Pipe {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CollectionChanges is considered dirty if it has any additions, moves or removals.
|
// CollectionChanges is considered dirty if it has any additions, moves or removals.
|
||||||
get isDirty():boolean {
|
get isDirty(): boolean {
|
||||||
return this._additionsHead !== null ||
|
return this._additionsHead !== null || this._movesHead !== null || this._removalsHead !== null;
|
||||||
this._movesHead !== null ||
|
|
||||||
this._removalsHead !== null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,8 +178,8 @@ export class IterableChanges extends Pipe {
|
||||||
*/
|
*/
|
||||||
_reset() {
|
_reset() {
|
||||||
if (this.isDirty) {
|
if (this.isDirty) {
|
||||||
var record:CollectionChangeRecord;
|
var record: CollectionChangeRecord;
|
||||||
var nextRecord:CollectionChangeRecord;
|
var nextRecord: CollectionChangeRecord;
|
||||||
|
|
||||||
for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
|
for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
|
||||||
record._nextPrevious = record._next;
|
record._nextPrevious = record._next;
|
||||||
|
@ -221,9 +210,9 @@ export class IterableChanges extends Pipe {
|
||||||
* - `item` is the current item in the collection
|
* - `item` is the current item in the collection
|
||||||
* - `index` is the position of the item in the collection
|
* - `index` is the position of the item in the collection
|
||||||
*/
|
*/
|
||||||
_mismatch(record:CollectionChangeRecord, item, index:int):CollectionChangeRecord {
|
_mismatch(record: CollectionChangeRecord, item, index: int): CollectionChangeRecord {
|
||||||
// The previous record after which we will append the current one.
|
// The previous record after which we will append the current one.
|
||||||
var previousRecord:CollectionChangeRecord;
|
var previousRecord: CollectionChangeRecord;
|
||||||
|
|
||||||
if (record === null) {
|
if (record === null) {
|
||||||
previousRecord = this._itTail;
|
previousRecord = this._itTail;
|
||||||
|
@ -277,9 +266,9 @@ export class IterableChanges extends Pipe {
|
||||||
* better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a'
|
* better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a'
|
||||||
* at the end.
|
* at the end.
|
||||||
*/
|
*/
|
||||||
_verifyReinsertion(record:CollectionChangeRecord, item, index:int):CollectionChangeRecord {
|
_verifyReinsertion(record: CollectionChangeRecord, item, index: int): CollectionChangeRecord {
|
||||||
var reinsertRecord:CollectionChangeRecord = this._unlinkedRecords === null ?
|
var reinsertRecord: CollectionChangeRecord =
|
||||||
null : this._unlinkedRecords.get(item);
|
this._unlinkedRecords === null ? null : this._unlinkedRecords.get(item);
|
||||||
if (reinsertRecord !== null) {
|
if (reinsertRecord !== null) {
|
||||||
record = this._reinsertAfter(reinsertRecord, record._prev, index);
|
record = this._reinsertAfter(reinsertRecord, record._prev, index);
|
||||||
} else if (record.currentIndex != index) {
|
} else if (record.currentIndex != index) {
|
||||||
|
@ -294,10 +283,10 @@ export class IterableChanges extends Pipe {
|
||||||
*
|
*
|
||||||
* - `record` The first excess {@link CollectionChangeRecord}.
|
* - `record` The first excess {@link CollectionChangeRecord}.
|
||||||
*/
|
*/
|
||||||
_truncate(record:CollectionChangeRecord) {
|
_truncate(record: CollectionChangeRecord) {
|
||||||
// Anything after that needs to be removed;
|
// Anything after that needs to be removed;
|
||||||
while (record !== null) {
|
while (record !== null) {
|
||||||
var nextRecord:CollectionChangeRecord = record._next;
|
var nextRecord: CollectionChangeRecord = record._next;
|
||||||
this._addToRemovals(this._unlink(record));
|
this._addToRemovals(this._unlink(record));
|
||||||
record = nextRecord;
|
record = nextRecord;
|
||||||
}
|
}
|
||||||
|
@ -319,8 +308,8 @@ export class IterableChanges extends Pipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_reinsertAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
|
_reinsertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||||
index:int):CollectionChangeRecord {
|
index: int): CollectionChangeRecord {
|
||||||
if (this._unlinkedRecords !== null) {
|
if (this._unlinkedRecords !== null) {
|
||||||
this._unlinkedRecords.remove(record);
|
this._unlinkedRecords.remove(record);
|
||||||
}
|
}
|
||||||
|
@ -343,42 +332,42 @@ export class IterableChanges extends Pipe {
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
_moveAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
|
_moveAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||||
index:int):CollectionChangeRecord {
|
index: int): CollectionChangeRecord {
|
||||||
this._unlink(record);
|
this._unlink(record);
|
||||||
this._insertAfter(record, prevRecord, index);
|
this._insertAfter(record, prevRecord, index);
|
||||||
this._addToMoves(record, index);
|
this._addToMoves(record, index);
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
_addAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
|
_addAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||||
index:int):CollectionChangeRecord {
|
index: int): CollectionChangeRecord {
|
||||||
this._insertAfter(record, prevRecord, index);
|
this._insertAfter(record, prevRecord, index);
|
||||||
|
|
||||||
if (this._additionsTail === null) {
|
if (this._additionsTail === null) {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(this._additionsHead === null);
|
// assert(this._additionsHead === null);
|
||||||
this._additionsTail = this._additionsHead = record;
|
this._additionsTail = this._additionsHead = record;
|
||||||
} else {
|
} else {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(_additionsTail._nextAdded === null);
|
// assert(_additionsTail._nextAdded === null);
|
||||||
//assert(record._nextAdded === null);
|
// assert(record._nextAdded === null);
|
||||||
this._additionsTail = this._additionsTail._nextAdded = record;
|
this._additionsTail = this._additionsTail._nextAdded = record;
|
||||||
}
|
}
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
_insertAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
|
_insertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||||
index:int):CollectionChangeRecord {
|
index: int): CollectionChangeRecord {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(record != prevRecord);
|
// assert(record != prevRecord);
|
||||||
//assert(record._next === null);
|
// assert(record._next === null);
|
||||||
//assert(record._prev === null);
|
// assert(record._prev === null);
|
||||||
|
|
||||||
var next:CollectionChangeRecord = prevRecord === null ? this._itHead :prevRecord._next;
|
var next: CollectionChangeRecord = prevRecord === null ? this._itHead : prevRecord._next;
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(next != record);
|
// assert(next != record);
|
||||||
//assert(prevRecord != record);
|
// assert(prevRecord != record);
|
||||||
record._next = next;
|
record._next = next;
|
||||||
record._prev = prevRecord;
|
record._prev = prevRecord;
|
||||||
if (next === null) {
|
if (next === null) {
|
||||||
|
@ -401,11 +390,11 @@ export class IterableChanges extends Pipe {
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
_remove(record:CollectionChangeRecord):CollectionChangeRecord {
|
_remove(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||||
return this._addToRemovals(this._unlink(record));
|
return this._addToRemovals(this._unlink(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
_unlink(record:CollectionChangeRecord):CollectionChangeRecord {
|
_unlink(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||||
if (this._linkedRecords !== null) {
|
if (this._linkedRecords !== null) {
|
||||||
this._linkedRecords.remove(record);
|
this._linkedRecords.remove(record);
|
||||||
}
|
}
|
||||||
|
@ -414,8 +403,8 @@ export class IterableChanges extends Pipe {
|
||||||
var next = record._next;
|
var next = record._next;
|
||||||
|
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert((record._prev = null) === null);
|
// assert((record._prev = null) === null);
|
||||||
//assert((record._next = null) === null);
|
// assert((record._next = null) === null);
|
||||||
|
|
||||||
if (prev === null) {
|
if (prev === null) {
|
||||||
this._itHead = next;
|
this._itHead = next;
|
||||||
|
@ -431,9 +420,9 @@ export class IterableChanges extends Pipe {
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
_addToMoves(record:CollectionChangeRecord, toIndex:int):CollectionChangeRecord {
|
_addToMoves(record: CollectionChangeRecord, toIndex: int): CollectionChangeRecord {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(record._nextMoved === null);
|
// assert(record._nextMoved === null);
|
||||||
|
|
||||||
if (record.previousIndex === toIndex) {
|
if (record.previousIndex === toIndex) {
|
||||||
return record;
|
return record;
|
||||||
|
@ -441,18 +430,18 @@ export class IterableChanges extends Pipe {
|
||||||
|
|
||||||
if (this._movesTail === null) {
|
if (this._movesTail === null) {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(_movesHead === null);
|
// assert(_movesHead === null);
|
||||||
this._movesTail = this._movesHead = record;
|
this._movesTail = this._movesHead = record;
|
||||||
} else {
|
} else {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(_movesTail._nextMoved === null);
|
// assert(_movesTail._nextMoved === null);
|
||||||
this._movesTail = this._movesTail._nextMoved = record;
|
this._movesTail = this._movesTail._nextMoved = record;
|
||||||
}
|
}
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
_addToRemovals(record:CollectionChangeRecord):CollectionChangeRecord {
|
_addToRemovals(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||||
if (this._unlinkedRecords === null) {
|
if (this._unlinkedRecords === null) {
|
||||||
this._unlinkedRecords = new _DuplicateMap();
|
this._unlinkedRecords = new _DuplicateMap();
|
||||||
}
|
}
|
||||||
|
@ -462,21 +451,21 @@ export class IterableChanges extends Pipe {
|
||||||
|
|
||||||
if (this._removalsTail === null) {
|
if (this._removalsTail === null) {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(_removalsHead === null);
|
// assert(_removalsHead === null);
|
||||||
this._removalsTail = this._removalsHead = record;
|
this._removalsTail = this._removalsHead = record;
|
||||||
record._prevRemoved = null;
|
record._prevRemoved = null;
|
||||||
} else {
|
} else {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(_removalsTail._nextRemoved === null);
|
// assert(_removalsTail._nextRemoved === null);
|
||||||
//assert(record._nextRemoved === null);
|
// assert(record._nextRemoved === null);
|
||||||
record._prevRemoved = this._removalsTail;
|
record._prevRemoved = this._removalsTail;
|
||||||
this._removalsTail = this._removalsTail._nextRemoved = record;
|
this._removalsTail = this._removalsTail._nextRemoved = record;
|
||||||
}
|
}
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
toString():string {
|
toString(): string {
|
||||||
var record:CollectionChangeRecord;
|
var record: CollectionChangeRecord;
|
||||||
|
|
||||||
var list = [];
|
var list = [];
|
||||||
for (record = this._itHead; record !== null; record = record._next) {
|
for (record = this._itHead; record !== null; record = record._next) {
|
||||||
|
@ -502,10 +491,8 @@ export class IterableChanges extends Pipe {
|
||||||
ListWrapper.push(removals, record);
|
ListWrapper.push(removals, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
return "collection: " + list.join(', ') + "\n" +
|
return "collection: " + list.join(', ') + "\n" + "previous: " + previous.join(', ') + "\n" +
|
||||||
"previous: " + previous.join(', ') + "\n" +
|
"additions: " + additions.join(', ') + "\n" + "moves: " + moves.join(', ') + "\n" +
|
||||||
"additions: " + additions.join(', ') + "\n" +
|
|
||||||
"moves: " + moves.join(', ') + "\n" +
|
|
||||||
"removals: " + removals.join(', ') + "\n";
|
"removals: " + removals.join(', ') + "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -513,17 +500,20 @@ export class IterableChanges extends Pipe {
|
||||||
/**
|
/**
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class CollectionChangeRecord {
|
export class CollectionChangeRecord {
|
||||||
currentIndex:int;
|
currentIndex: int;
|
||||||
previousIndex:int;
|
previousIndex: int;
|
||||||
item;
|
item;
|
||||||
|
|
||||||
_nextPrevious:CollectionChangeRecord;
|
_nextPrevious: CollectionChangeRecord;
|
||||||
_prev:CollectionChangeRecord; _next:CollectionChangeRecord;
|
_prev: CollectionChangeRecord;
|
||||||
_prevDup:CollectionChangeRecord; _nextDup:CollectionChangeRecord;
|
_next: CollectionChangeRecord;
|
||||||
_prevRemoved:CollectionChangeRecord; _nextRemoved:CollectionChangeRecord;
|
_prevDup: CollectionChangeRecord;
|
||||||
_nextAdded:CollectionChangeRecord;
|
_nextDup: CollectionChangeRecord;
|
||||||
_nextMoved:CollectionChangeRecord;
|
_prevRemoved: CollectionChangeRecord;
|
||||||
|
_nextRemoved: CollectionChangeRecord;
|
||||||
|
_nextAdded: CollectionChangeRecord;
|
||||||
|
_nextMoved: CollectionChangeRecord;
|
||||||
|
|
||||||
constructor(item) {
|
constructor(item) {
|
||||||
this.currentIndex = null;
|
this.currentIndex = null;
|
||||||
|
@ -541,18 +531,18 @@ export class CollectionChangeRecord {
|
||||||
this._nextMoved = null;
|
this._nextMoved = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
toString():string {
|
toString(): string {
|
||||||
return this.previousIndex === this.currentIndex ?
|
return this.previousIndex === this.currentIndex ?
|
||||||
stringify(this.item) :
|
stringify(this.item) :
|
||||||
stringify(this.item) + '[' + stringify(this.previousIndex) + '->' +
|
stringify(this.item) + '[' + stringify(this.previousIndex) + '->' +
|
||||||
stringify(this.currentIndex) + ']';
|
stringify(this.currentIndex) + ']';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A linked list of CollectionChangeRecords with the same CollectionChangeRecord.item
|
// A linked list of CollectionChangeRecords with the same CollectionChangeRecord.item
|
||||||
class _DuplicateItemRecordList {
|
class _DuplicateItemRecordList {
|
||||||
_head:CollectionChangeRecord;
|
_head: CollectionChangeRecord;
|
||||||
_tail:CollectionChangeRecord;
|
_tail: CollectionChangeRecord;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._head = null;
|
this._head = null;
|
||||||
|
@ -564,14 +554,14 @@ class _DuplicateItemRecordList {
|
||||||
*
|
*
|
||||||
* Note: by design all records in the list of duplicates hold the same value in record.item.
|
* Note: by design all records in the list of duplicates hold the same value in record.item.
|
||||||
*/
|
*/
|
||||||
add(record:CollectionChangeRecord) {
|
add(record: CollectionChangeRecord) {
|
||||||
if (this._head === null) {
|
if (this._head === null) {
|
||||||
this._head = this._tail = record;
|
this._head = this._tail = record;
|
||||||
record._nextDup = null;
|
record._nextDup = null;
|
||||||
record._prevDup = null;
|
record._prevDup = null;
|
||||||
} else {
|
} else {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(record.item == _head.item ||
|
// assert(record.item == _head.item ||
|
||||||
// record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
|
// record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
|
||||||
this._tail._nextDup = record;
|
this._tail._nextDup = record;
|
||||||
record._prevDup = this._tail;
|
record._prevDup = this._tail;
|
||||||
|
@ -582,14 +572,14 @@ class _DuplicateItemRecordList {
|
||||||
|
|
||||||
// Returns a CollectionChangeRecord having CollectionChangeRecord.item == item and
|
// Returns a CollectionChangeRecord having CollectionChangeRecord.item == item and
|
||||||
// CollectionChangeRecord.currentIndex >= afterIndex
|
// CollectionChangeRecord.currentIndex >= afterIndex
|
||||||
get(item, afterIndex:int):CollectionChangeRecord {
|
get(item, afterIndex: int): CollectionChangeRecord {
|
||||||
var record:CollectionChangeRecord;
|
var record: CollectionChangeRecord;
|
||||||
for (record = this._head; record !== null; record = record._nextDup) {
|
for (record = this._head; record !== null; record = record._nextDup) {
|
||||||
if ((afterIndex === null || afterIndex < record.currentIndex) &&
|
if ((afterIndex === null || afterIndex < record.currentIndex) &&
|
||||||
looseIdentical(record.item, item)) {
|
looseIdentical(record.item, item)) {
|
||||||
return record;
|
return record;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,9 +588,9 @@ class _DuplicateItemRecordList {
|
||||||
*
|
*
|
||||||
* Returns whether the list of duplicates is empty.
|
* Returns whether the list of duplicates is empty.
|
||||||
*/
|
*/
|
||||||
remove(record:CollectionChangeRecord):boolean {
|
remove(record: CollectionChangeRecord): boolean {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(() {
|
// assert(() {
|
||||||
// // verify that the record being removed is in the list.
|
// // verify that the record being removed is in the list.
|
||||||
// for (CollectionChangeRecord cursor = _head; cursor != null; cursor = cursor._nextDup) {
|
// for (CollectionChangeRecord cursor = _head; cursor != null; cursor = cursor._nextDup) {
|
||||||
// if (identical(cursor, record)) return true;
|
// if (identical(cursor, record)) return true;
|
||||||
|
@ -608,8 +598,8 @@ class _DuplicateItemRecordList {
|
||||||
// return false;
|
// return false;
|
||||||
//});
|
//});
|
||||||
|
|
||||||
var prev:CollectionChangeRecord = record._prevDup;
|
var prev: CollectionChangeRecord = record._prevDup;
|
||||||
var next:CollectionChangeRecord = record._nextDup;
|
var next: CollectionChangeRecord = record._nextDup;
|
||||||
if (prev === null) {
|
if (prev === null) {
|
||||||
this._head = next;
|
this._head = next;
|
||||||
} else {
|
} else {
|
||||||
|
@ -625,12 +615,10 @@ class _DuplicateItemRecordList {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DuplicateMap {
|
class _DuplicateMap {
|
||||||
map:Map;
|
map: Map<any, any>;
|
||||||
constructor() {
|
constructor() { this.map = MapWrapper.create(); }
|
||||||
this.map = MapWrapper.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
put(record:CollectionChangeRecord) {
|
put(record: CollectionChangeRecord) {
|
||||||
// todo(vicb) handle corner cases
|
// todo(vicb) handle corner cases
|
||||||
var key = getMapKey(record.item);
|
var key = getMapKey(record.item);
|
||||||
|
|
||||||
|
@ -649,7 +637,7 @@ class _DuplicateMap {
|
||||||
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
|
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
|
||||||
* have any more `a`s needs to return the last `a` not the first or second.
|
* have any more `a`s needs to return the last `a` not the first or second.
|
||||||
*/
|
*/
|
||||||
get(value, afterIndex = null):CollectionChangeRecord {
|
get(value, afterIndex = null): CollectionChangeRecord {
|
||||||
var key = getMapKey(value);
|
var key = getMapKey(value);
|
||||||
|
|
||||||
var recordList = MapWrapper.get(this.map, key);
|
var recordList = MapWrapper.get(this.map, key);
|
||||||
|
@ -661,11 +649,11 @@ class _DuplicateMap {
|
||||||
*
|
*
|
||||||
* The list of duplicates also is removed from the map if it gets empty.
|
* The list of duplicates also is removed from the map if it gets empty.
|
||||||
*/
|
*/
|
||||||
remove(record:CollectionChangeRecord):CollectionChangeRecord {
|
remove(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||||
var key = getMapKey(record.item);
|
var key = getMapKey(record.item);
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
//assert(this.map.containsKey(key));
|
// assert(this.map.containsKey(key));
|
||||||
var recordList:_DuplicateItemRecordList = MapWrapper.get(this.map, key);
|
var recordList: _DuplicateItemRecordList = MapWrapper.get(this.map, key);
|
||||||
// Remove the list of duplicates when it gets empty
|
// Remove the list of duplicates when it gets empty
|
||||||
if (recordList.remove(record)) {
|
if (recordList.remove(record)) {
|
||||||
MapWrapper.delete(this.map, key);
|
MapWrapper.delete(this.map, key);
|
||||||
|
@ -673,15 +661,9 @@ class _DuplicateMap {
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isEmpty():boolean {
|
get isEmpty(): boolean { return MapWrapper.size(this.map) === 0; }
|
||||||
return MapWrapper.size(this.map) === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
clear() { MapWrapper.clear(this.map); }
|
||||||
MapWrapper.clear(this.map);
|
|
||||||
}
|
|
||||||
|
|
||||||
toString():string {
|
toString(): string { return '_DuplicateMap(' + stringify(this.map) + ')'; }
|
||||||
return '_DuplicateMap(' + stringify(this.map) + ')';
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -3,38 +3,36 @@ import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/
|
||||||
|
|
||||||
import {WrappedValue, Pipe, PipeFactory} from './pipe';
|
import {WrappedValue, Pipe, PipeFactory} from './pipe';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class KeyValueChangesFactory extends PipeFactory {
|
export class KeyValueChangesFactory extends PipeFactory {
|
||||||
@CONST()
|
constructor() { super(); }
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(obj):boolean {
|
supports(obj): boolean { return KeyValueChanges.supportsObj(obj); }
|
||||||
return KeyValueChanges.supportsObj(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
create(cdRef):Pipe {
|
create(cdRef): Pipe { return new KeyValueChanges(); }
|
||||||
return new KeyValueChanges();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class KeyValueChanges extends Pipe {
|
export class KeyValueChanges extends Pipe {
|
||||||
_records:Map;
|
private _records: Map<any, any>;
|
||||||
|
private _mapHead: KVChangeRecord;
|
||||||
_mapHead:KVChangeRecord;
|
private _previousMapHead: KVChangeRecord;
|
||||||
_previousMapHead:KVChangeRecord;
|
private _changesHead: KVChangeRecord;
|
||||||
_changesHead:KVChangeRecord;
|
private _changesTail: KVChangeRecord;
|
||||||
_changesTail:KVChangeRecord;
|
private _additionsHead: KVChangeRecord;
|
||||||
_additionsHead:KVChangeRecord;
|
private _additionsTail: KVChangeRecord;
|
||||||
_additionsTail:KVChangeRecord;
|
private _removalsHead: KVChangeRecord;
|
||||||
_removalsHead:KVChangeRecord;
|
private _removalsTail: KVChangeRecord;
|
||||||
_removalsTail:KVChangeRecord;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -49,15 +47,11 @@ export class KeyValueChanges extends Pipe {
|
||||||
this._removalsTail = null;
|
this._removalsTail = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static supportsObj(obj):boolean {
|
static supportsObj(obj): boolean { return obj instanceof Map || isJsObject(obj); }
|
||||||
return obj instanceof Map || isJsObject(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(obj):boolean {
|
supports(obj): boolean { return KeyValueChanges.supportsObj(obj); }
|
||||||
return KeyValueChanges.supportsObj(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
transform(map){
|
transform(map): any {
|
||||||
if (this.check(map)) {
|
if (this.check(map)) {
|
||||||
return WrappedValue.wrap(this);
|
return WrappedValue.wrap(this);
|
||||||
} else {
|
} else {
|
||||||
|
@ -65,54 +59,53 @@ export class KeyValueChanges extends Pipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDirty():boolean {
|
get isDirty(): boolean {
|
||||||
return this._additionsHead !== null ||
|
return this._additionsHead !== null || this._changesHead !== null ||
|
||||||
this._changesHead !== null ||
|
|
||||||
this._removalsHead !== null;
|
this._removalsHead !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachItem(fn:Function) {
|
forEachItem(fn: Function) {
|
||||||
var record:KVChangeRecord;
|
var record: KVChangeRecord;
|
||||||
for (record = this._mapHead; record !== null; record = record._next) {
|
for (record = this._mapHead; record !== null; record = record._next) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachPreviousItem(fn:Function) {
|
forEachPreviousItem(fn: Function) {
|
||||||
var record:KVChangeRecord;
|
var record: KVChangeRecord;
|
||||||
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachChangedItem(fn:Function) {
|
forEachChangedItem(fn: Function) {
|
||||||
var record:KVChangeRecord;
|
var record: KVChangeRecord;
|
||||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachAddedItem(fn:Function){
|
forEachAddedItem(fn: Function) {
|
||||||
var record:KVChangeRecord;
|
var record: KVChangeRecord;
|
||||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachRemovedItem(fn:Function){
|
forEachRemovedItem(fn: Function) {
|
||||||
var record:KVChangeRecord;
|
var record: KVChangeRecord;
|
||||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
check(map):boolean {
|
check(map): boolean {
|
||||||
this._reset();
|
this._reset();
|
||||||
var records = this._records;
|
var records = this._records;
|
||||||
var oldSeqRecord:KVChangeRecord = this._mapHead;
|
var oldSeqRecord: KVChangeRecord = this._mapHead;
|
||||||
var lastOldSeqRecord:KVChangeRecord = null;
|
var lastOldSeqRecord: KVChangeRecord = null;
|
||||||
var lastNewSeqRecord:KVChangeRecord = null;
|
var lastNewSeqRecord: KVChangeRecord = null;
|
||||||
var seqChanged:boolean = false;
|
var seqChanged: boolean = false;
|
||||||
|
|
||||||
this._forEach(map, (value, key) => {
|
this._forEach(map, (value, key) => {
|
||||||
var newSeqRecord;
|
var newSeqRecord;
|
||||||
|
@ -160,11 +153,9 @@ export class KeyValueChanges extends Pipe {
|
||||||
|
|
||||||
_reset() {
|
_reset() {
|
||||||
if (this.isDirty) {
|
if (this.isDirty) {
|
||||||
var record:KVChangeRecord;
|
var record: KVChangeRecord;
|
||||||
// Record the state of the mapping
|
// Record the state of the mapping
|
||||||
for (record = this._previousMapHead = this._mapHead;
|
for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) {
|
||||||
record !== null;
|
|
||||||
record = record._next) {
|
|
||||||
record._nextPrevious = record._next;
|
record._nextPrevious = record._next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +168,7 @@ export class KeyValueChanges extends Pipe {
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(vicb) once assert is supported
|
// todo(vicb) once assert is supported
|
||||||
//assert(() {
|
// assert(() {
|
||||||
// var r = _changesHead;
|
// var r = _changesHead;
|
||||||
// while (r != null) {
|
// while (r != null) {
|
||||||
// var nextRecord = r._nextChanged;
|
// var nextRecord = r._nextChanged;
|
||||||
|
@ -207,7 +198,7 @@ export class KeyValueChanges extends Pipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_truncate(lastRecord:KVChangeRecord, record:KVChangeRecord) {
|
_truncate(lastRecord: KVChangeRecord, record: KVChangeRecord) {
|
||||||
while (record !== null) {
|
while (record !== null) {
|
||||||
if (lastRecord === null) {
|
if (lastRecord === null) {
|
||||||
this._mapHead = null;
|
this._mapHead = null;
|
||||||
|
@ -216,7 +207,7 @@ export class KeyValueChanges extends Pipe {
|
||||||
}
|
}
|
||||||
var nextRecord = record._next;
|
var nextRecord = record._next;
|
||||||
// todo(vicb) assert
|
// todo(vicb) assert
|
||||||
//assert((() {
|
// assert((() {
|
||||||
// record._next = null;
|
// record._next = null;
|
||||||
// return true;
|
// return true;
|
||||||
//}));
|
//}));
|
||||||
|
@ -225,26 +216,25 @@ export class KeyValueChanges extends Pipe {
|
||||||
record = nextRecord;
|
record = nextRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var rec:KVChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
|
for (var rec: KVChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
|
||||||
rec.previousValue = rec.currentValue;
|
rec.previousValue = rec.currentValue;
|
||||||
rec.currentValue = null;
|
rec.currentValue = null;
|
||||||
MapWrapper.delete(this._records, rec.key);
|
MapWrapper.delete(this._records, rec.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isInRemovals(record:KVChangeRecord) {
|
_isInRemovals(record: KVChangeRecord) {
|
||||||
return record === this._removalsHead ||
|
return record === this._removalsHead || record._nextRemoved !== null ||
|
||||||
record._nextRemoved !== null ||
|
|
||||||
record._prevRemoved !== null;
|
record._prevRemoved !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_addToRemovals(record:KVChangeRecord) {
|
_addToRemovals(record: KVChangeRecord) {
|
||||||
// todo(vicb) assert
|
// todo(vicb) assert
|
||||||
//assert(record._next == null);
|
// assert(record._next == null);
|
||||||
//assert(record._nextAdded == null);
|
// assert(record._nextAdded == null);
|
||||||
//assert(record._nextChanged == null);
|
// assert(record._nextChanged == null);
|
||||||
//assert(record._nextRemoved == null);
|
// assert(record._nextRemoved == null);
|
||||||
//assert(record._prevRemoved == null);
|
// assert(record._prevRemoved == null);
|
||||||
if (this._removalsHead === null) {
|
if (this._removalsHead === null) {
|
||||||
this._removalsHead = this._removalsTail = record;
|
this._removalsHead = this._removalsTail = record;
|
||||||
} else {
|
} else {
|
||||||
|
@ -254,7 +244,7 @@ export class KeyValueChanges extends Pipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeFromSeq(prev:KVChangeRecord, record:KVChangeRecord) {
|
_removeFromSeq(prev: KVChangeRecord, record: KVChangeRecord) {
|
||||||
var next = record._next;
|
var next = record._next;
|
||||||
if (prev === null) {
|
if (prev === null) {
|
||||||
this._mapHead = next;
|
this._mapHead = next;
|
||||||
|
@ -262,17 +252,17 @@ export class KeyValueChanges extends Pipe {
|
||||||
prev._next = next;
|
prev._next = next;
|
||||||
}
|
}
|
||||||
// todo(vicb) assert
|
// todo(vicb) assert
|
||||||
//assert((() {
|
// assert((() {
|
||||||
// record._next = null;
|
// record._next = null;
|
||||||
// return true;
|
// return true;
|
||||||
//})());
|
//})());
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeFromRemovals(record:KVChangeRecord) {
|
_removeFromRemovals(record: KVChangeRecord) {
|
||||||
// todo(vicb) assert
|
// todo(vicb) assert
|
||||||
//assert(record._next == null);
|
// assert(record._next == null);
|
||||||
//assert(record._nextAdded == null);
|
// assert(record._nextAdded == null);
|
||||||
//assert(record._nextChanged == null);
|
// assert(record._nextChanged == null);
|
||||||
|
|
||||||
var prev = record._prevRemoved;
|
var prev = record._prevRemoved;
|
||||||
var next = record._nextRemoved;
|
var next = record._nextRemoved;
|
||||||
|
@ -289,13 +279,13 @@ export class KeyValueChanges extends Pipe {
|
||||||
record._prevRemoved = record._nextRemoved = null;
|
record._prevRemoved = record._nextRemoved = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_addToAdditions(record:KVChangeRecord) {
|
_addToAdditions(record: KVChangeRecord) {
|
||||||
// todo(vicb): assert
|
// todo(vicb): assert
|
||||||
//assert(record._next == null);
|
// assert(record._next == null);
|
||||||
//assert(record._nextAdded == null);
|
// assert(record._nextAdded == null);
|
||||||
//assert(record._nextChanged == null);
|
// assert(record._nextChanged == null);
|
||||||
//assert(record._nextRemoved == null);
|
// assert(record._nextRemoved == null);
|
||||||
//assert(record._prevRemoved == null);
|
// assert(record._prevRemoved == null);
|
||||||
if (this._additionsHead === null) {
|
if (this._additionsHead === null) {
|
||||||
this._additionsHead = this._additionsTail = record;
|
this._additionsHead = this._additionsTail = record;
|
||||||
} else {
|
} else {
|
||||||
|
@ -304,12 +294,12 @@ export class KeyValueChanges extends Pipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_addToChanges(record:KVChangeRecord) {
|
_addToChanges(record: KVChangeRecord) {
|
||||||
// todo(vicb) assert
|
// todo(vicb) assert
|
||||||
//assert(record._nextAdded == null);
|
// assert(record._nextAdded == null);
|
||||||
//assert(record._nextChanged == null);
|
// assert(record._nextChanged == null);
|
||||||
//assert(record._nextRemoved == null);
|
// assert(record._nextRemoved == null);
|
||||||
//assert(record._prevRemoved == null);
|
// assert(record._prevRemoved == null);
|
||||||
if (this._changesHead === null) {
|
if (this._changesHead === null) {
|
||||||
this._changesHead = this._changesTail = record;
|
this._changesHead = this._changesTail = record;
|
||||||
} else {
|
} else {
|
||||||
|
@ -318,13 +308,13 @@ export class KeyValueChanges extends Pipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toString():string {
|
toString(): string {
|
||||||
var items = [];
|
var items = [];
|
||||||
var previous = [];
|
var previous = [];
|
||||||
var changes = [];
|
var changes = [];
|
||||||
var additions = [];
|
var additions = [];
|
||||||
var removals = [];
|
var removals = [];
|
||||||
var record:KVChangeRecord;
|
var record: KVChangeRecord;
|
||||||
|
|
||||||
for (record = this._mapHead; record !== null; record = record._next) {
|
for (record = this._mapHead; record !== null; record = record._next) {
|
||||||
ListWrapper.push(items, stringify(record));
|
ListWrapper.push(items, stringify(record));
|
||||||
|
@ -342,14 +332,12 @@ export class KeyValueChanges extends Pipe {
|
||||||
ListWrapper.push(removals, stringify(record));
|
ListWrapper.push(removals, stringify(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
return "map: " + items.join(', ') + "\n" +
|
return "map: " + items.join(', ') + "\n" + "previous: " + previous.join(', ') + "\n" +
|
||||||
"previous: " + previous.join(', ') + "\n" +
|
"additions: " + additions.join(', ') + "\n" + "changes: " + changes.join(', ') + "\n" +
|
||||||
"additions: " + additions.join(', ') + "\n" +
|
|
||||||
"changes: " + changes.join(', ') + "\n" +
|
|
||||||
"removals: " + removals.join(', ') + "\n";
|
"removals: " + removals.join(', ') + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
_forEach(obj, fn:Function) {
|
_forEach(obj, fn: Function) {
|
||||||
if (obj instanceof Map) {
|
if (obj instanceof Map) {
|
||||||
MapWrapper.forEach(obj, fn);
|
MapWrapper.forEach(obj, fn);
|
||||||
} else {
|
} else {
|
||||||
|
@ -368,12 +356,12 @@ export class KVChangeRecord {
|
||||||
previousValue;
|
previousValue;
|
||||||
currentValue;
|
currentValue;
|
||||||
|
|
||||||
_nextPrevious:KVChangeRecord;
|
_nextPrevious: KVChangeRecord;
|
||||||
_next:KVChangeRecord;
|
_next: KVChangeRecord;
|
||||||
_nextAdded:KVChangeRecord;
|
_nextAdded: KVChangeRecord;
|
||||||
_nextRemoved:KVChangeRecord;
|
_nextRemoved: KVChangeRecord;
|
||||||
_prevRemoved:KVChangeRecord;
|
_prevRemoved: KVChangeRecord;
|
||||||
_nextChanged:KVChangeRecord;
|
_nextChanged: KVChangeRecord;
|
||||||
|
|
||||||
constructor(key) {
|
constructor(key) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
@ -388,10 +376,10 @@ export class KVChangeRecord {
|
||||||
this._nextChanged = null;
|
this._nextChanged = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
toString():string {
|
toString(): string {
|
||||||
return looseIdentical(this.previousValue, this.currentValue) ?
|
return looseIdentical(this.previousValue, this.currentValue) ?
|
||||||
stringify(this.key) :
|
stringify(this.key) :
|
||||||
(stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
|
(stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
|
||||||
stringify(this.currentValue) + ']');
|
stringify(this.currentValue) + ']');
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,48 +0,0 @@
|
||||||
import {isBlank, CONST} from 'angular2/src/facade/lang';
|
|
||||||
import {Pipe, WrappedValue, PipeFactory} from './pipe';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @exportedAs angular2/pipes
|
|
||||||
*/
|
|
||||||
export class NullPipeFactory extends PipeFactory {
|
|
||||||
@CONST()
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(obj):boolean {
|
|
||||||
return NullPipe.supportsObj(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
create(cdRef):Pipe {
|
|
||||||
return new NullPipe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @exportedAs angular2/pipes
|
|
||||||
*/
|
|
||||||
export class NullPipe extends Pipe {
|
|
||||||
called:boolean;
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.called = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static supportsObj(obj):boolean {
|
|
||||||
return isBlank(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(obj) {
|
|
||||||
return NullPipe.supportsObj(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
transform(value) {
|
|
||||||
if (! this.called) {
|
|
||||||
this.called = true;
|
|
||||||
return WrappedValue.wrap(null);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
import {isBlank, CONST} from 'angular2/src/facade/lang';
|
||||||
|
import {Pipe, WrappedValue, PipeFactory} from './pipe';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @exportedAs angular2/pipes
|
||||||
|
*/
|
||||||
|
@CONST()
|
||||||
|
export class NullPipeFactory extends PipeFactory {
|
||||||
|
constructor() { super(); }
|
||||||
|
|
||||||
|
supports(obj): boolean { return NullPipe.supportsObj(obj); }
|
||||||
|
|
||||||
|
create(cdRef): Pipe { return new NullPipe(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @exportedAs angular2/pipes
|
||||||
|
*/
|
||||||
|
export class NullPipe extends Pipe {
|
||||||
|
called: boolean;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.called = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static supportsObj(obj): boolean { return isBlank(obj); }
|
||||||
|
|
||||||
|
supports(obj) { return NullPipe.supportsObj(obj); }
|
||||||
|
|
||||||
|
transform(value) {
|
||||||
|
if (!this.called) {
|
||||||
|
this.called = true;
|
||||||
|
return WrappedValue.wrap(null);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,22 @@
|
||||||
import {ABSTRACT, BaseException, CONST} from 'angular2/src/facade/lang';
|
import {ABSTRACT, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that the result of a {@link Pipe} transformation has changed even though the reference has not changed.
|
* Indicates that the result of a {@link Pipe} transformation has changed even though the reference
|
||||||
|
*has not changed.
|
||||||
*
|
*
|
||||||
* The wrapped value will be unwrapped by change detection, and the unwrapped value will be stored.
|
* The wrapped value will be unwrapped by change detection, and the unwrapped value will be stored.
|
||||||
*
|
*
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class WrappedValue {
|
export class WrappedValue {
|
||||||
wrapped:any;
|
constructor(public wrapped: any) {}
|
||||||
|
|
||||||
constructor(wrapped:any) {
|
static wrap(value: any): WrappedValue {
|
||||||
this.wrapped = wrapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
static wrap(value:any):WrappedValue {
|
|
||||||
var w = _wrappedValues[_wrappedIndex++ % 5];
|
var w = _wrappedValues[_wrappedIndex++ % 5];
|
||||||
w.wrapped = value;
|
w.wrapped = value;
|
||||||
return w;
|
return w;
|
||||||
|
@ -53,26 +55,25 @@ var _wrappedIndex = 0;
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class Pipe {
|
export class Pipe {
|
||||||
supports(obj):boolean {return false;}
|
supports(obj): boolean { return false; }
|
||||||
onDestroy() {}
|
onDestroy() {}
|
||||||
transform(value:any):any {return null;}
|
transform(value: any): any { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ABSTRACT()
|
// TODO: vsavkin: make it an interface
|
||||||
|
@CONST()
|
||||||
export class PipeFactory {
|
export class PipeFactory {
|
||||||
@CONST()
|
supports(obs): boolean {
|
||||||
constructor() {
|
_abstract();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
supports(obs):boolean {
|
create(cdRef): Pipe {
|
||||||
return _abstract();
|
_abstract();
|
||||||
}
|
return null;
|
||||||
|
|
||||||
create(cdRef):Pipe {
|
|
||||||
return _abstract();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _abstract() {
|
function _abstract() {
|
||||||
return new BaseException('This method is abstract');
|
throw new BaseException('This method is abstract');
|
||||||
}
|
}
|
|
@ -1,25 +1,25 @@
|
||||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||||
import {Pipe} from './pipe';
|
import {Pipe} from './pipe';
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {Injectable} from 'angular2/src/di/decorators';
|
||||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PipeRegistry {
|
export class PipeRegistry {
|
||||||
config;
|
constructor(public config) {}
|
||||||
|
|
||||||
constructor(config){
|
get(type: string, obj, cdRef: ChangeDetectorRef): Pipe {
|
||||||
this.config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
get(type:string, obj, cdRef:ChangeDetectorRef):Pipe {
|
|
||||||
var listOfConfigs = this.config[type];
|
var listOfConfigs = this.config[type];
|
||||||
if (isBlank(listOfConfigs)) {
|
if (isBlank(listOfConfigs)) {
|
||||||
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchingConfig = ListWrapper.find(listOfConfigs,
|
var matchingConfig = ListWrapper.find(listOfConfigs, (pipeConfig) => pipeConfig.supports(obj));
|
||||||
(pipeConfig) => pipeConfig.supports(obj));
|
|
||||||
|
|
||||||
if (isBlank(matchingConfig)) {
|
if (isBlank(matchingConfig)) {
|
||||||
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
|
@ -1,368 +0,0 @@
|
||||||
import {isPresent, isBlank, BaseException, Type, isString} from 'angular2/src/facade/lang';
|
|
||||||
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
import {
|
|
||||||
AccessMember,
|
|
||||||
Assignment,
|
|
||||||
AST,
|
|
||||||
ASTWithSource,
|
|
||||||
AstVisitor,
|
|
||||||
Binary,
|
|
||||||
Chain,
|
|
||||||
Conditional,
|
|
||||||
Pipe,
|
|
||||||
FunctionCall,
|
|
||||||
ImplicitReceiver,
|
|
||||||
Interpolation,
|
|
||||||
KeyedAccess,
|
|
||||||
LiteralArray,
|
|
||||||
LiteralMap,
|
|
||||||
LiteralPrimitive,
|
|
||||||
MethodCall,
|
|
||||||
PrefixNot
|
|
||||||
} from './parser/ast';
|
|
||||||
|
|
||||||
import {ChangeDispatcher, ChangeDetector, ProtoChangeDetector} from './interfaces';
|
|
||||||
import {ChangeDetectionUtil} from './change_detection_util';
|
|
||||||
import {DynamicChangeDetector} from './dynamic_change_detector';
|
|
||||||
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
|
||||||
import {PipeRegistry} from './pipes/pipe_registry';
|
|
||||||
import {BindingRecord} from './binding_record';
|
|
||||||
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
|
||||||
|
|
||||||
import {coalesce} from './coalesce';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ProtoRecord,
|
|
||||||
RECORD_TYPE_SELF,
|
|
||||||
RECORD_TYPE_PROPERTY,
|
|
||||||
RECORD_TYPE_LOCAL,
|
|
||||||
RECORD_TYPE_INVOKE_METHOD,
|
|
||||||
RECORD_TYPE_CONST,
|
|
||||||
RECORD_TYPE_INVOKE_CLOSURE,
|
|
||||||
RECORD_TYPE_PRIMITIVE_OP,
|
|
||||||
RECORD_TYPE_KEYED_ACCESS,
|
|
||||||
RECORD_TYPE_PIPE,
|
|
||||||
RECORD_TYPE_BINDING_PIPE,
|
|
||||||
RECORD_TYPE_INTERPOLATE
|
|
||||||
} from './proto_record';
|
|
||||||
|
|
||||||
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
|
||||||
_pipeRegistry:PipeRegistry;
|
|
||||||
_records:List<ProtoRecord>;
|
|
||||||
_bindingRecords:List<BindingRecord>;
|
|
||||||
_variableBindings:List<string>;
|
|
||||||
_directiveRecords:List<DirectiveRecord>;
|
|
||||||
_changeControlStrategy:string;
|
|
||||||
|
|
||||||
constructor(pipeRegistry:PipeRegistry, bindingRecords:List, variableBindings:List, directiveRecords:List, changeControlStrategy:string) {
|
|
||||||
super();
|
|
||||||
this._pipeRegistry = pipeRegistry;
|
|
||||||
this._bindingRecords = bindingRecords;
|
|
||||||
this._variableBindings = variableBindings;
|
|
||||||
this._directiveRecords = directiveRecords;
|
|
||||||
this._changeControlStrategy = changeControlStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
instantiate(dispatcher:any) {
|
|
||||||
this._createRecordsIfNecessary();
|
|
||||||
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher,
|
|
||||||
this._pipeRegistry, this._records, this._directiveRecords);
|
|
||||||
}
|
|
||||||
|
|
||||||
_createRecordsIfNecessary() {
|
|
||||||
if (isBlank(this._records)) {
|
|
||||||
var recordBuilder = new ProtoRecordBuilder();
|
|
||||||
ListWrapper.forEach(this._bindingRecords, (b) => {
|
|
||||||
recordBuilder.addAst(b, this._variableBindings);
|
|
||||||
});
|
|
||||||
this._records = coalesce(recordBuilder.records);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _jitProtoChangeDetectorClassCounter:number = 0;
|
|
||||||
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
|
||||||
_factory:Function;
|
|
||||||
_pipeRegistry;
|
|
||||||
_bindingRecords:List<BindingRecord>;
|
|
||||||
_variableBindings:List<string>;
|
|
||||||
_directiveRecords:List<DirectiveRecord>;
|
|
||||||
_changeControlStrategy:string;
|
|
||||||
|
|
||||||
constructor(pipeRegistry, bindingRecords:List, variableBindings:List, directiveRecords:List, changeControlStrategy:string) {
|
|
||||||
super();
|
|
||||||
this._pipeRegistry = pipeRegistry;
|
|
||||||
this._factory = null;
|
|
||||||
this._bindingRecords = bindingRecords;
|
|
||||||
this._variableBindings = variableBindings;
|
|
||||||
this._directiveRecords = directiveRecords;
|
|
||||||
this._changeControlStrategy = changeControlStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
instantiate(dispatcher:any) {
|
|
||||||
this._createFactoryIfNecessary();
|
|
||||||
return this._factory(dispatcher, this._pipeRegistry);
|
|
||||||
}
|
|
||||||
|
|
||||||
_createFactoryIfNecessary() {
|
|
||||||
if (isBlank(this._factory)) {
|
|
||||||
var recordBuilder = new ProtoRecordBuilder();
|
|
||||||
ListWrapper.forEach(this._bindingRecords, (b) => {
|
|
||||||
recordBuilder.addAst(b, this._variableBindings);
|
|
||||||
});
|
|
||||||
var c = _jitProtoChangeDetectorClassCounter++;
|
|
||||||
var records = coalesce(recordBuilder.records);
|
|
||||||
var typeName = `ChangeDetector${c}`;
|
|
||||||
this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records,
|
|
||||||
this._directiveRecords).generate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ProtoRecordBuilder {
|
|
||||||
records:List<ProtoRecord>;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.records = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
addAst(b:BindingRecord, variableBindings:List = null) {
|
|
||||||
var last = ListWrapper.last(this.records);
|
|
||||||
if (isPresent(last) && last.bindingRecord.directiveRecord == b.directiveRecord) {
|
|
||||||
last.lastInDirective = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pr = _ConvertAstIntoProtoRecords.convert(b, this.records.length, variableBindings);
|
|
||||||
if (! ListWrapper.isEmpty(pr)) {
|
|
||||||
var last = ListWrapper.last(pr);
|
|
||||||
last.lastInBinding = true;
|
|
||||||
last.lastInDirective = true;
|
|
||||||
|
|
||||||
this.records = ListWrapper.concat(this.records, pr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConvertAstIntoProtoRecords {
|
|
||||||
protoRecords:List;
|
|
||||||
bindingRecord:BindingRecord;
|
|
||||||
variableBindings:List;
|
|
||||||
contextIndex:number;
|
|
||||||
expressionAsString:string;
|
|
||||||
|
|
||||||
constructor(bindingRecord:BindingRecord, contextIndex:number, expressionAsString:string, variableBindings:List) {
|
|
||||||
this.protoRecords = [];
|
|
||||||
this.bindingRecord = bindingRecord;
|
|
||||||
this.contextIndex = contextIndex;
|
|
||||||
this.expressionAsString = expressionAsString;
|
|
||||||
this.variableBindings = variableBindings;
|
|
||||||
}
|
|
||||||
|
|
||||||
static convert(b:BindingRecord, contextIndex:number, variableBindings:List) {
|
|
||||||
var c = new _ConvertAstIntoProtoRecords(b, contextIndex, b.ast.toString(), variableBindings);
|
|
||||||
b.ast.visit(c);
|
|
||||||
return c.protoRecords;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitImplicitReceiver(ast:ImplicitReceiver) {
|
|
||||||
return this.bindingRecord.implicitReceiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitInterpolation(ast:Interpolation) {
|
|
||||||
var args = this._visitAll(ast.expressions);
|
|
||||||
return this._addRecord(RECORD_TYPE_INTERPOLATE, "interpolate", _interpolationFn(ast.strings),
|
|
||||||
args, ast.strings, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralPrimitive(ast:LiteralPrimitive) {
|
|
||||||
return this._addRecord(RECORD_TYPE_CONST, "literal", ast.value, [], null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitAccessMember(ast:AccessMember) {
|
|
||||||
var receiver = ast.receiver.visit(this);
|
|
||||||
if (isPresent(this.variableBindings) &&
|
|
||||||
ListWrapper.contains(this.variableBindings, ast.name) &&
|
|
||||||
ast.receiver instanceof ImplicitReceiver) {
|
|
||||||
return this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
|
|
||||||
} else {
|
|
||||||
return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visitMethodCall(ast:MethodCall) {;
|
|
||||||
var receiver = ast.receiver.visit(this);
|
|
||||||
var args = this._visitAll(ast.args);
|
|
||||||
if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name)) {
|
|
||||||
var target = this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
|
|
||||||
return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
|
|
||||||
} else {
|
|
||||||
return this._addRecord(RECORD_TYPE_INVOKE_METHOD, ast.name, ast.fn, args, null, receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visitFunctionCall(ast:FunctionCall) {
|
|
||||||
var target = ast.target.visit(this);
|
|
||||||
var args = this._visitAll(ast.args);
|
|
||||||
return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralArray(ast:LiteralArray) {
|
|
||||||
var primitiveName = `arrayFn${ast.expressions.length}`;
|
|
||||||
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, primitiveName, _arrayFn(ast.expressions.length),
|
|
||||||
this._visitAll(ast.expressions), null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralMap(ast:LiteralMap) {
|
|
||||||
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _mapPrimitiveName(ast.keys),
|
|
||||||
ChangeDetectionUtil.mapFn(ast.keys), this._visitAll(ast.values), null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitBinary(ast:Binary) {
|
|
||||||
var left = ast.left.visit(this);
|
|
||||||
var right = ast.right.visit(this);
|
|
||||||
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _operationToPrimitiveName(ast.operation),
|
|
||||||
_operationToFunction(ast.operation), [left, right], null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPrefixNot(ast:PrefixNot) {
|
|
||||||
var exp = ast.expression.visit(this)
|
|
||||||
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "operation_negate",
|
|
||||||
ChangeDetectionUtil.operation_negate, [exp], null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitConditional(ast:Conditional) {
|
|
||||||
var c = ast.condition.visit(this);
|
|
||||||
var t = ast.trueExp.visit(this);
|
|
||||||
var f = ast.falseExp.visit(this);
|
|
||||||
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "cond",
|
|
||||||
ChangeDetectionUtil.cond, [c,t,f], null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPipe(ast:Pipe) {
|
|
||||||
var value = ast.exp.visit(this);
|
|
||||||
var type = ast.inBinding ? RECORD_TYPE_BINDING_PIPE : RECORD_TYPE_PIPE;
|
|
||||||
return this._addRecord(type, ast.name, ast.name, [], null, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitKeyedAccess(ast:KeyedAccess) {
|
|
||||||
var obj = ast.obj.visit(this);
|
|
||||||
var key = ast.key.visit(this);
|
|
||||||
return this._addRecord(RECORD_TYPE_KEYED_ACCESS, "keyedAccess",
|
|
||||||
ChangeDetectionUtil.keyedAccess, [key], null, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
_visitAll(asts:List) {
|
|
||||||
var res = ListWrapper.createFixedSize(asts.length);
|
|
||||||
for (var i = 0; i < asts.length; ++i) {
|
|
||||||
res[i] = asts[i].visit(this);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
|
|
||||||
var selfIndex = ++ this.contextIndex;
|
|
||||||
if (context instanceof DirectiveIndex) {
|
|
||||||
ListWrapper.push(this.protoRecords,
|
|
||||||
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context, selfIndex,
|
|
||||||
this.bindingRecord, this.expressionAsString, false, false));
|
|
||||||
} else {
|
|
||||||
ListWrapper.push(this.protoRecords,
|
|
||||||
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null, selfIndex,
|
|
||||||
this.bindingRecord, this.expressionAsString, false, false));
|
|
||||||
}
|
|
||||||
return selfIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function _arrayFn(length:number):Function {
|
|
||||||
switch (length) {
|
|
||||||
case 0: return ChangeDetectionUtil.arrayFn0;
|
|
||||||
case 1: return ChangeDetectionUtil.arrayFn1;
|
|
||||||
case 2: return ChangeDetectionUtil.arrayFn2;
|
|
||||||
case 3: return ChangeDetectionUtil.arrayFn3;
|
|
||||||
case 4: return ChangeDetectionUtil.arrayFn4;
|
|
||||||
case 5: return ChangeDetectionUtil.arrayFn5;
|
|
||||||
case 6: return ChangeDetectionUtil.arrayFn6;
|
|
||||||
case 7: return ChangeDetectionUtil.arrayFn7;
|
|
||||||
case 8: return ChangeDetectionUtil.arrayFn8;
|
|
||||||
case 9: return ChangeDetectionUtil.arrayFn9;
|
|
||||||
default: throw new BaseException(`Does not support literal maps with more than 9 elements`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _mapPrimitiveName(keys:List) {
|
|
||||||
var stringifiedKeys = ListWrapper.join(
|
|
||||||
ListWrapper.map(keys, (k) => isString(k) ? `"${k}"` : `${k}`),
|
|
||||||
", ");
|
|
||||||
return `mapFn([${stringifiedKeys}])`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _operationToPrimitiveName(operation:string):string {
|
|
||||||
switch(operation) {
|
|
||||||
case '+' : return "operation_add";
|
|
||||||
case '-' : return "operation_subtract";
|
|
||||||
case '*' : return "operation_multiply";
|
|
||||||
case '/' : return "operation_divide";
|
|
||||||
case '%' : return "operation_remainder";
|
|
||||||
case '==' : return "operation_equals";
|
|
||||||
case '!=' : return "operation_not_equals";
|
|
||||||
case '<' : return "operation_less_then";
|
|
||||||
case '>' : return "operation_greater_then";
|
|
||||||
case '<=' : return "operation_less_or_equals_then";
|
|
||||||
case '>=' : return "operation_greater_or_equals_then";
|
|
||||||
case '&&' : return "operation_logical_and";
|
|
||||||
case '||' : return "operation_logical_or";
|
|
||||||
default: throw new BaseException(`Unsupported operation ${operation}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _operationToFunction(operation:string):Function {
|
|
||||||
switch(operation) {
|
|
||||||
case '+' : return ChangeDetectionUtil.operation_add;
|
|
||||||
case '-' : return ChangeDetectionUtil.operation_subtract;
|
|
||||||
case '*' : return ChangeDetectionUtil.operation_multiply;
|
|
||||||
case '/' : return ChangeDetectionUtil.operation_divide;
|
|
||||||
case '%' : return ChangeDetectionUtil.operation_remainder;
|
|
||||||
case '==' : return ChangeDetectionUtil.operation_equals;
|
|
||||||
case '!=' : return ChangeDetectionUtil.operation_not_equals;
|
|
||||||
case '<' : return ChangeDetectionUtil.operation_less_then;
|
|
||||||
case '>' : return ChangeDetectionUtil.operation_greater_then;
|
|
||||||
case '<=' : return ChangeDetectionUtil.operation_less_or_equals_then;
|
|
||||||
case '>=' : return ChangeDetectionUtil.operation_greater_or_equals_then;
|
|
||||||
case '&&' : return ChangeDetectionUtil.operation_logical_and;
|
|
||||||
case '||' : return ChangeDetectionUtil.operation_logical_or;
|
|
||||||
default: throw new BaseException(`Unsupported operation ${operation}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function s(v) {
|
|
||||||
return isPresent(v) ? `${v}` : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function _interpolationFn(strings:List) {
|
|
||||||
var length = strings.length;
|
|
||||||
var c0 = length > 0 ? strings[0] : null;
|
|
||||||
var c1 = length > 1 ? strings[1] : null;
|
|
||||||
var c2 = length > 2 ? strings[2] : null;
|
|
||||||
var c3 = length > 3 ? strings[3] : null;
|
|
||||||
var c4 = length > 4 ? strings[4] : null;
|
|
||||||
var c5 = length > 5 ? strings[5] : null;
|
|
||||||
var c6 = length > 6 ? strings[6] : null;
|
|
||||||
var c7 = length > 7 ? strings[7] : null;
|
|
||||||
var c8 = length > 8 ? strings[8] : null;
|
|
||||||
var c9 = length > 9 ? strings[9] : null;
|
|
||||||
switch (length - 1) {
|
|
||||||
case 1: return (a1) => c0 + s(a1) + c1;
|
|
||||||
case 2: return (a1, a2) => c0 + s(a1) + c1 + s(a2) + c2;
|
|
||||||
case 3: return (a1, a2, a3) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3;
|
|
||||||
case 4: return (a1, a2, a3, a4) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4;
|
|
||||||
case 5: return (a1, a2, a3, a4, a5) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5;
|
|
||||||
case 6: return (a1, a2, a3, a4, a5, a6) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6;
|
|
||||||
case 7: return (a1, a2, a3, a4, a5, a6, a7) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7;
|
|
||||||
case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7 + s(a8) + c8;
|
|
||||||
case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7 + s(a8) + c8 + s(a9) + c9;
|
|
||||||
default: throw new BaseException(`Does not support more than 9 expressions`);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,406 @@
|
||||||
|
import {isPresent, isBlank, BaseException, Type, isString} from 'angular2/src/facade/lang';
|
||||||
|
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AccessMember,
|
||||||
|
Assignment,
|
||||||
|
AST,
|
||||||
|
ASTWithSource,
|
||||||
|
AstVisitor,
|
||||||
|
Binary,
|
||||||
|
Chain,
|
||||||
|
Conditional,
|
||||||
|
Pipe,
|
||||||
|
FunctionCall,
|
||||||
|
ImplicitReceiver,
|
||||||
|
Interpolation,
|
||||||
|
KeyedAccess,
|
||||||
|
LiteralArray,
|
||||||
|
LiteralMap,
|
||||||
|
LiteralPrimitive,
|
||||||
|
MethodCall,
|
||||||
|
PrefixNot
|
||||||
|
} from './parser/ast';
|
||||||
|
|
||||||
|
import {ChangeDispatcher, ChangeDetector, ProtoChangeDetector} from './interfaces';
|
||||||
|
import {ChangeDetectionUtil} from './change_detection_util';
|
||||||
|
import {DynamicChangeDetector} from './dynamic_change_detector';
|
||||||
|
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
||||||
|
import {PipeRegistry} from './pipes/pipe_registry';
|
||||||
|
import {BindingRecord} from './binding_record';
|
||||||
|
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
||||||
|
|
||||||
|
import {coalesce} from './coalesce';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ProtoRecord,
|
||||||
|
RECORD_TYPE_SELF,
|
||||||
|
RECORD_TYPE_PROPERTY,
|
||||||
|
RECORD_TYPE_LOCAL,
|
||||||
|
RECORD_TYPE_INVOKE_METHOD,
|
||||||
|
RECORD_TYPE_CONST,
|
||||||
|
RECORD_TYPE_INVOKE_CLOSURE,
|
||||||
|
RECORD_TYPE_PRIMITIVE_OP,
|
||||||
|
RECORD_TYPE_KEYED_ACCESS,
|
||||||
|
RECORD_TYPE_PIPE,
|
||||||
|
RECORD_TYPE_BINDING_PIPE,
|
||||||
|
RECORD_TYPE_INTERPOLATE
|
||||||
|
} from './proto_record';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
|
||||||
|
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
||||||
|
_records: List<ProtoRecord>;
|
||||||
|
|
||||||
|
constructor(private _pipeRegistry: PipeRegistry, private _bindingRecords: List<any>,
|
||||||
|
private _variableBindings: List<any>, private _directiveRecords: List<any>,
|
||||||
|
private _changeControlStrategy: string) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
instantiate(dispatcher: any) {
|
||||||
|
this._createRecordsIfNecessary();
|
||||||
|
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher, this._pipeRegistry,
|
||||||
|
this._records, this._directiveRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createRecordsIfNecessary() {
|
||||||
|
if (isBlank(this._records)) {
|
||||||
|
var recordBuilder = new ProtoRecordBuilder();
|
||||||
|
ListWrapper.forEach(this._bindingRecords,
|
||||||
|
(b) => { recordBuilder.addAst(b, this._variableBindings); });
|
||||||
|
this._records = coalesce(recordBuilder.records);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _jitProtoChangeDetectorClassCounter: number = 0;
|
||||||
|
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||||
|
_factory: Function;
|
||||||
|
|
||||||
|
constructor(private _pipeRegistry, private _bindingRecords: List<any>,
|
||||||
|
private _variableBindings: List<any>, private _directiveRecords: List<any>,
|
||||||
|
private _changeControlStrategy: string) {
|
||||||
|
super();
|
||||||
|
this._factory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
instantiate(dispatcher: any) {
|
||||||
|
this._createFactoryIfNecessary();
|
||||||
|
return this._factory(dispatcher, this._pipeRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createFactoryIfNecessary() {
|
||||||
|
if (isBlank(this._factory)) {
|
||||||
|
var recordBuilder = new ProtoRecordBuilder();
|
||||||
|
ListWrapper.forEach(this._bindingRecords,
|
||||||
|
(b) => { recordBuilder.addAst(b, this._variableBindings); });
|
||||||
|
var c = _jitProtoChangeDetectorClassCounter++;
|
||||||
|
var records = coalesce(recordBuilder.records);
|
||||||
|
var typeName = `ChangeDetector${c}`;
|
||||||
|
this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records,
|
||||||
|
this._directiveRecords)
|
||||||
|
.generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProtoRecordBuilder {
|
||||||
|
records: List<ProtoRecord>;
|
||||||
|
|
||||||
|
constructor() { this.records = []; }
|
||||||
|
|
||||||
|
addAst(b: BindingRecord, variableBindings: List < any >= null) {
|
||||||
|
var last = ListWrapper.last(this.records);
|
||||||
|
if (isPresent(last) && last.bindingRecord.directiveRecord == b.directiveRecord) {
|
||||||
|
last.lastInDirective = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pr = _ConvertAstIntoProtoRecords.convert(b, this.records.length, variableBindings);
|
||||||
|
if (!ListWrapper.isEmpty(pr)) {
|
||||||
|
var last = ListWrapper.last(pr);
|
||||||
|
last.lastInBinding = true;
|
||||||
|
last.lastInDirective = true;
|
||||||
|
|
||||||
|
this.records = ListWrapper.concat(this.records, pr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConvertAstIntoProtoRecords {
|
||||||
|
protoRecords: List<any>;
|
||||||
|
|
||||||
|
constructor(private bindingRecord: BindingRecord, private contextIndex: number,
|
||||||
|
private expressionAsString: string, private variableBindings: List<any>) {
|
||||||
|
this.protoRecords = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
static convert(b: BindingRecord, contextIndex: number, variableBindings: List<any>) {
|
||||||
|
var c = new _ConvertAstIntoProtoRecords(b, contextIndex, b.ast.toString(), variableBindings);
|
||||||
|
b.ast.visit(c);
|
||||||
|
return c.protoRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitImplicitReceiver(ast: ImplicitReceiver) { return this.bindingRecord.implicitReceiver; }
|
||||||
|
|
||||||
|
visitInterpolation(ast: Interpolation) {
|
||||||
|
var args = this._visitAll(ast.expressions);
|
||||||
|
return this._addRecord(RECORD_TYPE_INTERPOLATE, "interpolate", _interpolationFn(ast.strings),
|
||||||
|
args, ast.strings, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitLiteralPrimitive(ast: LiteralPrimitive) {
|
||||||
|
return this._addRecord(RECORD_TYPE_CONST, "literal", ast.value, [], null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAccessMember(ast: AccessMember) {
|
||||||
|
var receiver = ast.receiver.visit(this);
|
||||||
|
if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name) &&
|
||||||
|
ast.receiver instanceof
|
||||||
|
ImplicitReceiver) {
|
||||||
|
return this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
|
||||||
|
} else {
|
||||||
|
return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitMethodCall(ast: MethodCall) {
|
||||||
|
;
|
||||||
|
var receiver = ast.receiver.visit(this);
|
||||||
|
var args = this._visitAll(ast.args);
|
||||||
|
if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name)) {
|
||||||
|
var target = this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
|
||||||
|
return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
|
||||||
|
} else {
|
||||||
|
return this._addRecord(RECORD_TYPE_INVOKE_METHOD, ast.name, ast.fn, args, null, receiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitFunctionCall(ast: FunctionCall) {
|
||||||
|
var target = ast.target.visit(this);
|
||||||
|
var args = this._visitAll(ast.args);
|
||||||
|
return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitLiteralArray(ast: LiteralArray) {
|
||||||
|
var primitiveName = `arrayFn${ast.expressions.length}`;
|
||||||
|
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, primitiveName,
|
||||||
|
_arrayFn(ast.expressions.length), this._visitAll(ast.expressions), null,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitLiteralMap(ast: LiteralMap) {
|
||||||
|
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _mapPrimitiveName(ast.keys),
|
||||||
|
ChangeDetectionUtil.mapFn(ast.keys), this._visitAll(ast.values), null,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitBinary(ast: Binary) {
|
||||||
|
var left = ast.left.visit(this);
|
||||||
|
var right = ast.right.visit(this);
|
||||||
|
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _operationToPrimitiveName(ast.operation),
|
||||||
|
_operationToFunction(ast.operation), [left, right], null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPrefixNot(ast: PrefixNot) {
|
||||||
|
var exp = ast.expression.visit(this);
|
||||||
|
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "operation_negate",
|
||||||
|
ChangeDetectionUtil.operation_negate, [exp], null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitConditional(ast: Conditional) {
|
||||||
|
var c = ast.condition.visit(this);
|
||||||
|
var t = ast.trueExp.visit(this);
|
||||||
|
var f = ast.falseExp.visit(this);
|
||||||
|
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "cond", ChangeDetectionUtil.cond, [c, t, f],
|
||||||
|
null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPipe(ast: Pipe) {
|
||||||
|
var value = ast.exp.visit(this);
|
||||||
|
var type = ast.inBinding ? RECORD_TYPE_BINDING_PIPE : RECORD_TYPE_PIPE;
|
||||||
|
return this._addRecord(type, ast.name, ast.name, [], null, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitKeyedAccess(ast: KeyedAccess) {
|
||||||
|
var obj = ast.obj.visit(this);
|
||||||
|
var key = ast.key.visit(this);
|
||||||
|
return this._addRecord(RECORD_TYPE_KEYED_ACCESS, "keyedAccess", ChangeDetectionUtil.keyedAccess,
|
||||||
|
[key], null, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
_visitAll(asts: List<any>) {
|
||||||
|
var res = ListWrapper.createFixedSize(asts.length);
|
||||||
|
for (var i = 0; i < asts.length; ++i) {
|
||||||
|
res[i] = asts[i].visit(this);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
|
||||||
|
var selfIndex = ++this.contextIndex;
|
||||||
|
if (context instanceof DirectiveIndex) {
|
||||||
|
ListWrapper.push(
|
||||||
|
this.protoRecords,
|
||||||
|
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context, selfIndex,
|
||||||
|
this.bindingRecord, this.expressionAsString, false, false));
|
||||||
|
} else {
|
||||||
|
ListWrapper.push(
|
||||||
|
this.protoRecords,
|
||||||
|
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null, selfIndex,
|
||||||
|
this.bindingRecord, this.expressionAsString, false, false));
|
||||||
|
}
|
||||||
|
return selfIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _arrayFn(length: number): Function {
|
||||||
|
switch (length) {
|
||||||
|
case 0:
|
||||||
|
return ChangeDetectionUtil.arrayFn0;
|
||||||
|
case 1:
|
||||||
|
return ChangeDetectionUtil.arrayFn1;
|
||||||
|
case 2:
|
||||||
|
return ChangeDetectionUtil.arrayFn2;
|
||||||
|
case 3:
|
||||||
|
return ChangeDetectionUtil.arrayFn3;
|
||||||
|
case 4:
|
||||||
|
return ChangeDetectionUtil.arrayFn4;
|
||||||
|
case 5:
|
||||||
|
return ChangeDetectionUtil.arrayFn5;
|
||||||
|
case 6:
|
||||||
|
return ChangeDetectionUtil.arrayFn6;
|
||||||
|
case 7:
|
||||||
|
return ChangeDetectionUtil.arrayFn7;
|
||||||
|
case 8:
|
||||||
|
return ChangeDetectionUtil.arrayFn8;
|
||||||
|
case 9:
|
||||||
|
return ChangeDetectionUtil.arrayFn9;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Does not support literal maps with more than 9 elements`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _mapPrimitiveName(keys: List<any>) {
|
||||||
|
var stringifiedKeys =
|
||||||
|
ListWrapper.join(ListWrapper.map(keys, (k) => isString(k) ? `"${k}"` : `${k}`), ", ");
|
||||||
|
return `mapFn([${stringifiedKeys}])`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _operationToPrimitiveName(operation: string): string {
|
||||||
|
switch (operation) {
|
||||||
|
case '+':
|
||||||
|
return "operation_add";
|
||||||
|
case '-':
|
||||||
|
return "operation_subtract";
|
||||||
|
case '*':
|
||||||
|
return "operation_multiply";
|
||||||
|
case '/':
|
||||||
|
return "operation_divide";
|
||||||
|
case '%':
|
||||||
|
return "operation_remainder";
|
||||||
|
case '==':
|
||||||
|
return "operation_equals";
|
||||||
|
case '!=':
|
||||||
|
return "operation_not_equals";
|
||||||
|
case '<':
|
||||||
|
return "operation_less_then";
|
||||||
|
case '>':
|
||||||
|
return "operation_greater_then";
|
||||||
|
case '<=':
|
||||||
|
return "operation_less_or_equals_then";
|
||||||
|
case '>=':
|
||||||
|
return "operation_greater_or_equals_then";
|
||||||
|
case '&&':
|
||||||
|
return "operation_logical_and";
|
||||||
|
case '||':
|
||||||
|
return "operation_logical_or";
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unsupported operation ${operation}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _operationToFunction(operation: string): Function {
|
||||||
|
switch (operation) {
|
||||||
|
case '+':
|
||||||
|
return ChangeDetectionUtil.operation_add;
|
||||||
|
case '-':
|
||||||
|
return ChangeDetectionUtil.operation_subtract;
|
||||||
|
case '*':
|
||||||
|
return ChangeDetectionUtil.operation_multiply;
|
||||||
|
case '/':
|
||||||
|
return ChangeDetectionUtil.operation_divide;
|
||||||
|
case '%':
|
||||||
|
return ChangeDetectionUtil.operation_remainder;
|
||||||
|
case '==':
|
||||||
|
return ChangeDetectionUtil.operation_equals;
|
||||||
|
case '!=':
|
||||||
|
return ChangeDetectionUtil.operation_not_equals;
|
||||||
|
case '<':
|
||||||
|
return ChangeDetectionUtil.operation_less_then;
|
||||||
|
case '>':
|
||||||
|
return ChangeDetectionUtil.operation_greater_then;
|
||||||
|
case '<=':
|
||||||
|
return ChangeDetectionUtil.operation_less_or_equals_then;
|
||||||
|
case '>=':
|
||||||
|
return ChangeDetectionUtil.operation_greater_or_equals_then;
|
||||||
|
case '&&':
|
||||||
|
return ChangeDetectionUtil.operation_logical_and;
|
||||||
|
case '||':
|
||||||
|
return ChangeDetectionUtil.operation_logical_or;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unsupported operation ${operation}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function s(v) {
|
||||||
|
return isPresent(v) ? `${v}` : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function _interpolationFn(strings: List<any>) {
|
||||||
|
var length = strings.length;
|
||||||
|
var c0 = length > 0 ? strings[0] : null;
|
||||||
|
var c1 = length > 1 ? strings[1] : null;
|
||||||
|
var c2 = length > 2 ? strings[2] : null;
|
||||||
|
var c3 = length > 3 ? strings[3] : null;
|
||||||
|
var c4 = length > 4 ? strings[4] : null;
|
||||||
|
var c5 = length > 5 ? strings[5] : null;
|
||||||
|
var c6 = length > 6 ? strings[6] : null;
|
||||||
|
var c7 = length > 7 ? strings[7] : null;
|
||||||
|
var c8 = length > 8 ? strings[8] : null;
|
||||||
|
var c9 = length > 9 ? strings[9] : null;
|
||||||
|
switch (length - 1) {
|
||||||
|
case 1:
|
||||||
|
return (a1) => c0 + s(a1) + c1;
|
||||||
|
case 2:
|
||||||
|
return (a1, a2) => c0 + s(a1) + c1 + s(a2) + c2;
|
||||||
|
case 3:
|
||||||
|
return (a1, a2, a3) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3;
|
||||||
|
case 4:
|
||||||
|
return (a1, a2, a3, a4) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4;
|
||||||
|
case 5:
|
||||||
|
return (a1, a2, a3, a4, a5) =>
|
||||||
|
c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5;
|
||||||
|
case 6:
|
||||||
|
return (a1, a2, a3, a4, a5, a6) =>
|
||||||
|
c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6;
|
||||||
|
case 7:
|
||||||
|
return (a1, a2, a3, a4, a5, a6, a7) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) +
|
||||||
|
c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7;
|
||||||
|
case 8:
|
||||||
|
return (a1, a2, a3, a4, a5, a6, a7, a8) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) +
|
||||||
|
c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7 + s(a8) +
|
||||||
|
c8;
|
||||||
|
case 9:
|
||||||
|
return (a1, a2, a3, a4, a5, a6, a7, a8, a9) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 +
|
||||||
|
s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) +
|
||||||
|
c7 + s(a8) + c8 + s(a9) + c9;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Does not support more than 9 expressions`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,66 +0,0 @@
|
||||||
import {List} from 'angular2/src/facade/collection';
|
|
||||||
import {BindingRecord} from './binding_record';
|
|
||||||
import {DirectiveIndex} from './directive_record';
|
|
||||||
|
|
||||||
export const RECORD_TYPE_SELF = 0;
|
|
||||||
export const RECORD_TYPE_CONST = 1;
|
|
||||||
export const RECORD_TYPE_PRIMITIVE_OP = 2;
|
|
||||||
export const RECORD_TYPE_PROPERTY = 3;
|
|
||||||
export const RECORD_TYPE_LOCAL = 4;
|
|
||||||
export const RECORD_TYPE_INVOKE_METHOD = 5;
|
|
||||||
export const RECORD_TYPE_INVOKE_CLOSURE = 6;
|
|
||||||
export const RECORD_TYPE_KEYED_ACCESS = 7;
|
|
||||||
export const RECORD_TYPE_PIPE = 8;
|
|
||||||
export const RECORD_TYPE_BINDING_PIPE = 9;
|
|
||||||
export const RECORD_TYPE_INTERPOLATE = 10;
|
|
||||||
|
|
||||||
export class ProtoRecord {
|
|
||||||
mode:number;
|
|
||||||
name:string;
|
|
||||||
funcOrValue:any;
|
|
||||||
args:List;
|
|
||||||
fixedArgs:List;
|
|
||||||
|
|
||||||
contextIndex:number;
|
|
||||||
directiveIndex:DirectiveIndex;
|
|
||||||
|
|
||||||
selfIndex:number;
|
|
||||||
bindingRecord:BindingRecord;
|
|
||||||
lastInBinding:boolean;
|
|
||||||
lastInDirective:boolean;
|
|
||||||
expressionAsString:string;
|
|
||||||
|
|
||||||
constructor(mode:number,
|
|
||||||
name:string,
|
|
||||||
funcOrValue,
|
|
||||||
args:List,
|
|
||||||
fixedArgs:List,
|
|
||||||
contextIndex:number,
|
|
||||||
directiveIndex:DirectiveIndex,
|
|
||||||
selfIndex:number,
|
|
||||||
bindingRecord:BindingRecord,
|
|
||||||
expressionAsString:string,
|
|
||||||
lastInBinding:boolean,
|
|
||||||
lastInDirective:boolean) {
|
|
||||||
|
|
||||||
this.mode = mode;
|
|
||||||
this.name = name;
|
|
||||||
this.funcOrValue = funcOrValue;
|
|
||||||
this.args = args;
|
|
||||||
this.fixedArgs = fixedArgs;
|
|
||||||
|
|
||||||
this.contextIndex = contextIndex;
|
|
||||||
this.directiveIndex = directiveIndex;
|
|
||||||
|
|
||||||
this.selfIndex = selfIndex;
|
|
||||||
this.bindingRecord = bindingRecord;
|
|
||||||
this.lastInBinding = lastInBinding;
|
|
||||||
this.lastInDirective = lastInDirective;
|
|
||||||
this.expressionAsString = expressionAsString;
|
|
||||||
}
|
|
||||||
|
|
||||||
isPureFunction():boolean {
|
|
||||||
return this.mode === RECORD_TYPE_INTERPOLATE ||
|
|
||||||
this.mode === RECORD_TYPE_PRIMITIVE_OP;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import {List} from 'angular2/src/facade/collection';
|
||||||
|
import {BindingRecord} from './binding_record';
|
||||||
|
import {DirectiveIndex} from './directive_record';
|
||||||
|
|
||||||
|
// HACK: workaround for Traceur behavior.
|
||||||
|
// It expects all transpiled modules to contain this marker.
|
||||||
|
// TODO: remove this when we no longer use traceur
|
||||||
|
export var __esModule = true;
|
||||||
|
|
||||||
|
export const RECORD_TYPE_SELF = 0;
|
||||||
|
export const RECORD_TYPE_CONST = 1;
|
||||||
|
export const RECORD_TYPE_PRIMITIVE_OP = 2;
|
||||||
|
export const RECORD_TYPE_PROPERTY = 3;
|
||||||
|
export const RECORD_TYPE_LOCAL = 4;
|
||||||
|
export const RECORD_TYPE_INVOKE_METHOD = 5;
|
||||||
|
export const RECORD_TYPE_INVOKE_CLOSURE = 6;
|
||||||
|
export const RECORD_TYPE_KEYED_ACCESS = 7;
|
||||||
|
export const RECORD_TYPE_PIPE = 8;
|
||||||
|
export const RECORD_TYPE_BINDING_PIPE = 9;
|
||||||
|
export const RECORD_TYPE_INTERPOLATE = 10;
|
||||||
|
|
||||||
|
export class ProtoRecord {
|
||||||
|
constructor(public mode: number, public name: string, public funcOrValue, public args: List<any>,
|
||||||
|
public fixedArgs: List<any>, public contextIndex: number,
|
||||||
|
public directiveIndex: DirectiveIndex, public selfIndex: number,
|
||||||
|
public bindingRecord: BindingRecord, public expressionAsString: string,
|
||||||
|
public lastInBinding: boolean, public lastInDirective: boolean) {}
|
||||||
|
|
||||||
|
isPureFunction(): boolean {
|
||||||
|
return this.mode === RECORD_TYPE_INTERPOLATE || this.mode === RECORD_TYPE_PRIMITIVE_OP;
|
||||||
|
}
|
||||||
|
}
|
|
@ -449,7 +449,9 @@ function _constructDependencies(factoryFunction: Function, dependencies: List<an
|
||||||
function _dependenciesFor(typeOrFunc): List<any> {
|
function _dependenciesFor(typeOrFunc): List<any> {
|
||||||
var params = reflector.parameters(typeOrFunc);
|
var params = reflector.parameters(typeOrFunc);
|
||||||
if (isBlank(params)) return [];
|
if (isBlank(params)) return [];
|
||||||
if (ListWrapper.any(params, (p) => isBlank(p))) throw new NoAnnotationError(typeOrFunc);
|
if (ListWrapper.any(params, (p) => isBlank(p))) {
|
||||||
|
throw new NoAnnotationError(typeOrFunc);
|
||||||
|
}
|
||||||
return ListWrapper.map(params, (p) => _extractToken(typeOrFunc, p));
|
return ListWrapper.map(params, (p) => _extractToken(typeOrFunc, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
library angular2.di.decorators;
|
library angular2.di.decorators;
|
||||||
|
|
||||||
/* This file is empty because, Dart does not have decorators. */
|
/* This file is empty because, Dart does not have decorators. */
|
||||||
|
|
||||||
|
export 'annotations.dart';
|
|
@ -66,12 +66,12 @@ function _isWaiting(obj): boolean {
|
||||||
* @exportedAs angular2/di
|
* @exportedAs angular2/di
|
||||||
*/
|
*/
|
||||||
export class Injector {
|
export class Injector {
|
||||||
_bindings: List<any>;
|
private _bindings: List<any>;
|
||||||
_instances: List<any>;
|
private _instances: List<any>;
|
||||||
_parent: Injector;
|
private _parent: Injector;
|
||||||
_defaultBindings: boolean;
|
private _defaultBindings: boolean;
|
||||||
_asyncStrategy: _AsyncInjectorStrategy;
|
private _asyncStrategy: _AsyncInjectorStrategy;
|
||||||
_syncStrategy: _SyncInjectorStrategy;
|
private _syncStrategy: _SyncInjectorStrategy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns a list of binding definitions into an internal resolved list of resolved bindings.
|
* Turns a list of binding definitions into an internal resolved list of resolved bindings.
|
||||||
|
|
|
@ -52,7 +52,7 @@ export class PromiseWrapper {
|
||||||
|
|
||||||
|
|
||||||
export class ObservableWrapper {
|
export class ObservableWrapper {
|
||||||
static subscribe(emitter: EventEmitter, onNext, onThrow = null, onReturn = null) {
|
static subscribe(emitter: Observable, onNext, onThrow = null, onReturn = null): Object {
|
||||||
return emitter.observer({next: onNext, throw: onThrow, return: onReturn});
|
return emitter.observer({next: onNext, throw: onThrow, return: onReturn});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ export class ObservableWrapper {
|
||||||
|
|
||||||
// TODO: vsavkin change to interface
|
// TODO: vsavkin change to interface
|
||||||
export class Observable {
|
export class Observable {
|
||||||
observer(generator: any) {}
|
observer(generator: any): Object { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -117,9 +117,9 @@ export class ListWrapper {
|
||||||
static indexOf(array: List<any>, value, startIndex = -1) {
|
static indexOf(array: List<any>, value, startIndex = -1) {
|
||||||
return array.indexOf(value, startIndex);
|
return array.indexOf(value, startIndex);
|
||||||
}
|
}
|
||||||
static reduce<T>(list: List<T>,
|
static reduce<T, E>(list: List<T>,
|
||||||
fn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T,
|
fn: (accumValue: E, currentValue: T, currentIndex: number, array: T[]) => E,
|
||||||
init: T) {
|
init: E) {
|
||||||
return list.reduce(fn, init);
|
return list.reduce(fn, init);
|
||||||
}
|
}
|
||||||
static filter(array, pred: Function) { return array.filter(pred); }
|
static filter(array, pred: Function) { return array.filter(pred); }
|
||||||
|
|
|
@ -5,6 +5,9 @@ import 'types.dart';
|
||||||
import 'dart:mirrors';
|
import 'dart:mirrors';
|
||||||
|
|
||||||
class ReflectionCapabilities {
|
class ReflectionCapabilities {
|
||||||
|
ReflectionCapabilities([metadataReader]) {
|
||||||
|
}
|
||||||
|
|
||||||
Function factory(Type type) {
|
Function factory(Type type) {
|
||||||
ClassMirror classMirror = reflectType(type);
|
ClassMirror classMirror = reflectType(type);
|
||||||
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];
|
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];
|
||||||
|
|
|
@ -8,37 +8,39 @@ import {GetterFn, SetterFn, MethodFn} from './types';
|
||||||
export var __esModule = true;
|
export var __esModule = true;
|
||||||
|
|
||||||
export class ReflectionCapabilities {
|
export class ReflectionCapabilities {
|
||||||
factory(type: Type): Function {
|
private _reflect: any;
|
||||||
switch (type.length) {
|
|
||||||
|
constructor(reflect?: any) { this._reflect = isPresent(reflect) ? reflect : global.Reflect; }
|
||||||
|
|
||||||
|
factory(t: Type): Function {
|
||||||
|
switch (t.length) {
|
||||||
case 0:
|
case 0:
|
||||||
return function() { return new type(); };
|
return function() { return new t(); };
|
||||||
case 1:
|
case 1:
|
||||||
return function(a1) { return new type(a1); };
|
return function(a1) { return new t(a1); };
|
||||||
case 2:
|
case 2:
|
||||||
return function(a1, a2) { return new type(a1, a2); };
|
return function(a1, a2) { return new t(a1, a2); };
|
||||||
case 3:
|
case 3:
|
||||||
return function(a1, a2, a3) { return new type(a1, a2, a3); };
|
return function(a1, a2, a3) { return new t(a1, a2, a3); };
|
||||||
case 4:
|
case 4:
|
||||||
return function(a1, a2, a3, a4) { return new type(a1, a2, a3, a4); };
|
return function(a1, a2, a3, a4) { return new t(a1, a2, a3, a4); };
|
||||||
case 5:
|
case 5:
|
||||||
return function(a1, a2, a3, a4, a5) { return new type(a1, a2, a3, a4, a5); };
|
return function(a1, a2, a3, a4, a5) { return new t(a1, a2, a3, a4, a5); };
|
||||||
case 6:
|
case 6:
|
||||||
return function(a1, a2, a3, a4, a5, a6) { return new type(a1, a2, a3, a4, a5, a6); };
|
return function(a1, a2, a3, a4, a5, a6) { return new t(a1, a2, a3, a4, a5, a6); };
|
||||||
case 7:
|
case 7:
|
||||||
return function(a1, a2, a3, a4, a5, a6, a7) {
|
return function(a1, a2, a3, a4, a5, a6, a7) { return new t(a1, a2, a3, a4, a5, a6, a7); };
|
||||||
return new type(a1, a2, a3, a4, a5, a6, a7);
|
|
||||||
};
|
|
||||||
case 8:
|
case 8:
|
||||||
return function(a1, a2, a3, a4, a5, a6, a7, a8) {
|
return function(a1, a2, a3, a4, a5, a6, a7, a8) {
|
||||||
return new type(a1, a2, a3, a4, a5, a6, a7, a8);
|
return new t(a1, a2, a3, a4, a5, a6, a7, a8);
|
||||||
};
|
};
|
||||||
case 9:
|
case 9:
|
||||||
return function(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
return function(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
||||||
return new type(a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
return new t(a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
||||||
};
|
};
|
||||||
case 10:
|
case 10:
|
||||||
return function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
|
return function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
|
||||||
return new type(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
|
return new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,9 +70,9 @@ export class ReflectionCapabilities {
|
||||||
if (isPresent(typeOfFunc.parameters)) {
|
if (isPresent(typeOfFunc.parameters)) {
|
||||||
return typeOfFunc.parameters;
|
return typeOfFunc.parameters;
|
||||||
}
|
}
|
||||||
if (isPresent(global.Reflect) && isPresent(global.Reflect.getMetadata)) {
|
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
||||||
var paramAnnotations = global.Reflect.getMetadata('parameters', typeOfFunc);
|
var paramAnnotations = this._reflect.getMetadata('parameters', typeOfFunc);
|
||||||
var paramTypes = global.Reflect.getMetadata('design:paramtypes', typeOfFunc);
|
var paramTypes = this._reflect.getMetadata('design:paramtypes', typeOfFunc);
|
||||||
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
|
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
|
||||||
return this._zipTypesAndAnnotaions(paramTypes, paramAnnotations);
|
return this._zipTypesAndAnnotaions(paramTypes, paramAnnotations);
|
||||||
}
|
}
|
||||||
|
@ -83,8 +85,8 @@ export class ReflectionCapabilities {
|
||||||
if (isPresent(typeOfFunc.annotations)) {
|
if (isPresent(typeOfFunc.annotations)) {
|
||||||
return typeOfFunc.annotations;
|
return typeOfFunc.annotations;
|
||||||
}
|
}
|
||||||
if (isPresent(global.Reflect) && isPresent(global.Reflect.getMetadata)) {
|
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
||||||
var annotations = global.Reflect.getMetadata('annotations', typeOfFunc);
|
var annotations = this._reflect.getMetadata('annotations', typeOfFunc);
|
||||||
if (isPresent(annotations)) return annotations;
|
if (isPresent(annotations)) return annotations;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -8,17 +8,6 @@ export function main() {
|
||||||
rc = new ReflectionCapabilities();
|
rc = new ReflectionCapabilities();
|
||||||
});
|
});
|
||||||
|
|
||||||
function mockReflect(mockData, cls) {
|
|
||||||
// This only makes sense for JS, but Dart passes trivially too.
|
|
||||||
if (!IS_DARTIUM) {
|
|
||||||
global.Reflect = {
|
|
||||||
'getMetadata': (key, targetCls) => {
|
|
||||||
return (targetCls == cls) ? mockData[key] : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertTestClassAnnotations(annotations) {
|
function assertTestClassAnnotations(annotations) {
|
||||||
expect(annotations[0]).toBeAnInstanceOf(ClassDec1);
|
expect(annotations[0]).toBeAnInstanceOf(ClassDec1);
|
||||||
expect(annotations[1]).toBeAnInstanceOf(ClassDec2);
|
expect(annotations[1]).toBeAnInstanceOf(ClassDec2);
|
||||||
|
@ -54,23 +43,34 @@ export function main() {
|
||||||
expect(rc.parameters(TestClassTypesOnly)).toEqual([[P1], [P2]]);
|
expect(rc.parameters(TestClassTypesOnly)).toEqual([[P1], [P2]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (!IS_DARTIUM) {
|
if (!IS_DARTIUM) {
|
||||||
// Mocking in the tests below is needed because the test runs through Traceur.
|
// Mocking in the tests below is needed because the test runs through Traceur.
|
||||||
// After the switch to TS the setup will have to change, where the direct key
|
// After the switch to TS the setup will have to change, where the direct key
|
||||||
// access will be mocked, and the tests below will be direct.
|
// access will be mocked, and the tests below will be direct.
|
||||||
it('can read out class annotations though Reflect APIs', () => {
|
it('can read out class annotations though Reflect APIs', () => {
|
||||||
mockReflect(mockDataForTestClassDec, TestClassDec);
|
var rc = new ReflectionCapabilities({
|
||||||
|
'getMetadata': (key, targetCls) => {
|
||||||
|
return (targetCls == TestClassDec) ? mockDataForTestClassDec[key] : null;
|
||||||
|
}
|
||||||
|
});
|
||||||
assertTestClassAnnotations(rc.annotations(TestClassDec));
|
assertTestClassAnnotations(rc.annotations(TestClassDec));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can read out parameter annotations though Reflect APIs', () => {
|
it('can read out parameter annotations though Reflect APIs', () => {
|
||||||
mockReflect(mockDataForTestClassDec, TestClassDec);
|
var rc = new ReflectionCapabilities({
|
||||||
|
'getMetadata': (key, targetCls) => {
|
||||||
|
return (targetCls == TestClassDec) ? mockDataForTestClassDec[key] : null;
|
||||||
|
}
|
||||||
|
});
|
||||||
assertTestClassParameters(rc.parameters(TestClassDec));
|
assertTestClassParameters(rc.parameters(TestClassDec));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can read out parameter annotations though Reflect APIs for types only class', () => {
|
it('can read out parameter annotations though Reflect APIs for types only class', () => {
|
||||||
mockReflect(mockDataForTestClassTypesOnly, TestClassTypesOnlyDec);
|
var rc = new ReflectionCapabilities({
|
||||||
|
'getMetadata': (key, targetCls) => {
|
||||||
|
return (targetCls == TestClassTypesOnlyDec) ? mockDataForTestClassTypesOnly[key] : null;
|
||||||
|
}
|
||||||
|
});
|
||||||
expect(rc.parameters(TestClassTypesOnlyDec)).toEqual([[P1], [P2]]);
|
expect(rc.parameters(TestClassTypesOnlyDec)).toEqual([[P1], [P2]]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@ module.exports = function makeNodeTree(destinationPath) {
|
||||||
|
|
||||||
var typescriptTree = compileWithTypescript(modulesTree, {
|
var typescriptTree = compileWithTypescript(modulesTree, {
|
||||||
allowNonTsExtensions: false,
|
allowNonTsExtensions: false,
|
||||||
|
emitDecoratorMetadata: true,
|
||||||
declaration: true,
|
declaration: true,
|
||||||
mapRoot: '', /* force sourcemaps to use relative path */
|
mapRoot: '', /* force sourcemaps to use relative path */
|
||||||
module: 'commonjs',
|
module: 'commonjs',
|
||||||
|
|
|
@ -5,6 +5,7 @@ var minijasminenode2 = require('minijasminenode2');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
// Require traceur to exposes $traceurRuntime on global context so that CJS files can run
|
// Require traceur to exposes $traceurRuntime on global context so that CJS files can run
|
||||||
require('traceur/bin/traceur-runtime.js');
|
require('traceur/bin/traceur-runtime.js');
|
||||||
|
require('reflect-metadata/Reflect');
|
||||||
|
|
||||||
glob(process.argv[2], function (error, specFiles) {
|
glob(process.argv[2], function (error, specFiles) {
|
||||||
minijasminenode2.executeSpecs({
|
minijasminenode2.executeSpecs({
|
||||||
|
|
Loading…
Reference in New Issue