build(typescript): Migrated change detection to typescript
This commit is contained in:
parent
f0ef72d6cc
commit
fa28b28d0a
@ -6,17 +6,40 @@
|
||||
*/
|
||||
|
||||
export {
|
||||
ASTWithSource, AST, AstTransformer, AccessMember, LiteralArray, ImplicitReceiver
|
||||
ASTWithSource,
|
||||
AST,
|
||||
AstTransformer,
|
||||
AccessMember,
|
||||
LiteralArray,
|
||||
ImplicitReceiver
|
||||
} from './src/change_detection/parser/ast';
|
||||
|
||||
export {Lexer} from './src/change_detection/parser/lexer';
|
||||
export {Parser} from './src/change_detection/parser/parser';
|
||||
export {Locals} from './src/change_detection/parser/locals';
|
||||
|
||||
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} 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 {
|
||||
ExpressionChangedAfterItHasBeenChecked,
|
||||
ChangeDetectionError
|
||||
} 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 {DirectiveIndex, DirectiveRecord} from './src/change_detection/directive_record';
|
||||
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 {WrappedValue, Pipe} from './src/change_detection/pipes/pipe';
|
||||
export {
|
||||
defaultPipes, DynamicChangeDetection, JitChangeDetection, defaultPipeRegistry
|
||||
defaultPipes,
|
||||
DynamicChangeDetection,
|
||||
JitChangeDetection,
|
||||
defaultPipeRegistry
|
||||
} 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 {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 {
|
||||
lightDomChildren:List;
|
||||
shadowDomChildren:List;
|
||||
parent:ChangeDetector;
|
||||
mode:string;
|
||||
ref:ChangeDetectorRef;
|
||||
lightDomChildren: List<any>;
|
||||
shadowDomChildren: List<any>;
|
||||
parent: ChangeDetector;
|
||||
mode: string;
|
||||
ref: ChangeDetectorRef;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -19,37 +24,27 @@ export class AbstractChangeDetector extends ChangeDetector {
|
||||
this.mode = null;
|
||||
}
|
||||
|
||||
addChild(cd:ChangeDetector) {
|
||||
addChild(cd: ChangeDetector) {
|
||||
ListWrapper.push(this.lightDomChildren, cd);
|
||||
cd.parent = this;
|
||||
}
|
||||
|
||||
removeChild(cd:ChangeDetector) {
|
||||
ListWrapper.remove(this.lightDomChildren, cd);
|
||||
}
|
||||
removeChild(cd: ChangeDetector) { ListWrapper.remove(this.lightDomChildren, cd); }
|
||||
|
||||
addShadowDomChild(cd:ChangeDetector) {
|
||||
addShadowDomChild(cd: ChangeDetector) {
|
||||
ListWrapper.push(this.shadowDomChildren, cd);
|
||||
cd.parent = this;
|
||||
}
|
||||
|
||||
removeShadowDomChild(cd:ChangeDetector) {
|
||||
ListWrapper.remove(this.shadowDomChildren, cd);
|
||||
}
|
||||
removeShadowDomChild(cd: ChangeDetector) { ListWrapper.remove(this.shadowDomChildren, cd); }
|
||||
|
||||
remove() {
|
||||
this.parent.removeChild(this);
|
||||
}
|
||||
remove() { this.parent.removeChild(this); }
|
||||
|
||||
detectChanges() {
|
||||
this._detectChanges(false);
|
||||
}
|
||||
detectChanges() { this._detectChanges(false); }
|
||||
|
||||
checkNoChanges() {
|
||||
this._detectChanges(true);
|
||||
}
|
||||
checkNoChanges() { this._detectChanges(true); }
|
||||
|
||||
_detectChanges(throwOnChange:boolean) {
|
||||
_detectChanges(throwOnChange: boolean) {
|
||||
if (this.mode === DETACHED || this.mode === CHECKED) return;
|
||||
|
||||
this.detectChangesInRecords(throwOnChange);
|
||||
@ -63,30 +58,28 @@ export class AbstractChangeDetector extends ChangeDetector {
|
||||
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
|
||||
}
|
||||
|
||||
detectChangesInRecords(throwOnChange:boolean){}
|
||||
callOnAllChangesDone(){}
|
||||
detectChangesInRecords(throwOnChange: boolean) {}
|
||||
callOnAllChangesDone() {}
|
||||
|
||||
_detectChangesInLightDomChildren(throwOnChange:boolean) {
|
||||
_detectChangesInLightDomChildren(throwOnChange: boolean) {
|
||||
var c = this.lightDomChildren;
|
||||
for(var i = 0; i < c.length; ++i) {
|
||||
for (var i = 0; i < c.length; ++i) {
|
||||
c[i]._detectChanges(throwOnChange);
|
||||
}
|
||||
}
|
||||
|
||||
_detectChangesInShadowDomChildren(throwOnChange:boolean) {
|
||||
_detectChangesInShadowDomChildren(throwOnChange: boolean) {
|
||||
var c = this.shadowDomChildren;
|
||||
for(var i = 0; i < c.length; ++i) {
|
||||
for (var i = 0; i < c.length; ++i) {
|
||||
c[i]._detectChanges(throwOnChange);
|
||||
}
|
||||
}
|
||||
|
||||
markAsCheckOnce() {
|
||||
this.mode = CHECK_ONCE;
|
||||
}
|
||||
markAsCheckOnce() { this.mode = CHECK_ONCE; }
|
||||
|
||||
markPathToRootAsCheckOnce() {
|
||||
var c = this;
|
||||
while(isPresent(c) && c.mode != DETACHED) {
|
||||
var c: ChangeDetector = this;
|
||||
while (isPresent(c) && c.mode != DETACHED) {
|
||||
if (c.mode === CHECKED) c.mode = CHECK_ONCE;
|
||||
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);
|
||||
}
|
||||
}
|
49
modules/angular2/src/change_detection/binding_record.ts
Normal file
49
modules/angular2/src/change_detection/binding_record.ts
Normal file
@ -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);
|
88
modules/angular2/src/change_detection/change_detection.ts
Normal file
88
modules/angular2/src/change_detection/change_detection.ts
Normal file
@ -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_BINDING_PIPE,
|
||||
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
|
||||
@ -43,8 +49,8 @@ var MODE_ACCESSOR = "this.mode";
|
||||
var TEMP_LOCAL = "temp";
|
||||
var CURRENT_PROTO = "currentProto";
|
||||
|
||||
function typeTemplate(type:string, cons:string, detectChanges:string,
|
||||
notifyOnAllChangesDone:string, setContext:string):string {
|
||||
function typeTemplate(type: string, cons: string, detectChanges: string,
|
||||
notifyOnAllChangesDone: string, setContext: string): string {
|
||||
return `
|
||||
${cons}
|
||||
${detectChanges}
|
||||
@ -57,7 +63,7 @@ return function(dispatcher, pipeRegistry) {
|
||||
`;
|
||||
}
|
||||
|
||||
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
||||
function constructorTemplate(type: string, fieldsDefinitions: string): string {
|
||||
return `
|
||||
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveRecords) {
|
||||
${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");
|
||||
}
|
||||
|
||||
function hydrateTemplate(type:string, mode:string, fieldDefinitions:string, pipeOnDestroy:string,
|
||||
directiveFieldNames:List<String>, detectorFieldNames:List<String>):string {
|
||||
function hydrateTemplate(type: string, mode: string, fieldDefinitions: string,
|
||||
pipeOnDestroy: string, directiveFieldNames: List<String>,
|
||||
detectorFieldNames: List<String>): string {
|
||||
var directiveInit = "";
|
||||
for(var i = 0; i < directiveFieldNames.length; ++i) {
|
||||
directiveInit += `${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||
for (var i = 0; i < directiveFieldNames.length; ++i) {
|
||||
directiveInit +=
|
||||
`${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||
}
|
||||
|
||||
var detectorInit = "";
|
||||
for(var i = 0; i < detectorFieldNames.length; ++i) {
|
||||
detectorInit += `${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||
for (var i = 0; i < detectorFieldNames.length; ++i) {
|
||||
detectorInit +=
|
||||
`${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||
}
|
||||
|
||||
return `
|
||||
@ -108,7 +117,7 @@ ${type}.prototype.hydrated = function() {
|
||||
`;
|
||||
}
|
||||
|
||||
function detectChangesTemplate(type:string, body:string):string {
|
||||
function detectChangesTemplate(type: string, body: string): string {
|
||||
return `
|
||||
${type}.prototype.detectChangesInRecords = function(throwOnChange) {
|
||||
${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 `
|
||||
${type}.prototype.callOnAllChangesDone = function() {
|
||||
${body}
|
||||
@ -124,12 +133,13 @@ ${type}.prototype.callOnAllChangesDone = function() {
|
||||
`;
|
||||
}
|
||||
|
||||
function onAllChangesDoneTemplate(directive:string):string {
|
||||
function onAllChangesDoneTemplate(directive: string): string {
|
||||
return `${directive}.onAllChangesDone();`;
|
||||
}
|
||||
|
||||
|
||||
function detectChangesBodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
|
||||
function detectChangesBodyTemplate(localDefinitions: string, changeDefinitions: string,
|
||||
records: string): string {
|
||||
return `
|
||||
${localDefinitions}
|
||||
${changeDefinitions}
|
||||
@ -143,9 +153,10 @@ ${records}
|
||||
`;
|
||||
}
|
||||
|
||||
function pipeCheckTemplate(protoIndex:number, context:string, bindingPropagationConfig:string, pipe:string, pipeType:string,
|
||||
oldValue:string, newValue:string, change:string, update:string,
|
||||
addToChanges, lastInDirective:string):string{
|
||||
function pipeCheckTemplate(protoIndex: number, context: string, bindingPropagationConfig: string,
|
||||
pipe: string, pipeType: string, oldValue: string, newValue: string,
|
||||
change: string, update: string, addToChanges,
|
||||
lastInDirective: string): string {
|
||||
return `
|
||||
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
|
||||
if (${pipe} === ${UTIL}.unitialized()) {
|
||||
@ -167,8 +178,9 @@ ${lastInDirective}
|
||||
`;
|
||||
}
|
||||
|
||||
function referenceCheckTemplate(protoIndex:number, assignment:string, oldValue:string, newValue:string, change:string,
|
||||
update:string, addToChanges:string, lastInDirective:string):string {
|
||||
function referenceCheckTemplate(protoIndex: number, assignment: string, oldValue: string,
|
||||
newValue: string, change: string, update: string,
|
||||
addToChanges: string, lastInDirective: string): string {
|
||||
return `
|
||||
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
|
||||
${assignment}
|
||||
@ -182,23 +194,23 @@ ${lastInDirective}
|
||||
`;
|
||||
}
|
||||
|
||||
function assignmentTemplate(field:string, value:string) {
|
||||
function assignmentTemplate(field: string, value: string) {
|
||||
return `${field} = ${value};`;
|
||||
}
|
||||
|
||||
function localDefinitionsTemplate(names:List):string {
|
||||
function localDefinitionsTemplate(names: List<any>): string {
|
||||
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");
|
||||
}
|
||||
|
||||
function fieldDefinitionsTemplate(names:List):string {
|
||||
function fieldDefinitionsTemplate(names: List<any>): string {
|
||||
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(" || ");
|
||||
return `
|
||||
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}));`;
|
||||
}
|
||||
|
||||
function updateDirectiveTemplate(oldValue:string, newValue:string, directiveProperty:string):string {
|
||||
function updateDirectiveTemplate(oldValue: string, newValue: string,
|
||||
directiveProperty: string): string {
|
||||
return `
|
||||
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${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 `
|
||||
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
|
||||
${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
|
||||
`;
|
||||
}
|
||||
|
||||
function notifyOnChangesTemplate(directive:string):string{
|
||||
return `
|
||||
function notifyOnChangesTemplate(directive: string): string {
|
||||
return `
|
||||
if(${CHANGES_LOCAL}) {
|
||||
${directive}.onChange(${CHANGES_LOCAL});
|
||||
${CHANGES_LOCAL} = null;
|
||||
@ -235,16 +248,16 @@ if(${CHANGES_LOCAL}) {
|
||||
`;
|
||||
}
|
||||
|
||||
function notifyOnPushDetectorsTemplate(detector:string):string{
|
||||
return `
|
||||
function notifyOnPushDetectorsTemplate(detector: string): string {
|
||||
return `
|
||||
if(${IS_CHANGED_LOCAL}) {
|
||||
${detector}.markAsCheckOnce();
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function lastInDirectiveTemplate(notifyOnChanges:string, notifyOnPush:string):string{
|
||||
return `
|
||||
function lastInDirectiveTemplate(notifyOnChanges: string, notifyOnPush: string): string {
|
||||
return `
|
||||
${notifyOnChanges}
|
||||
${notifyOnPush}
|
||||
${IS_CHANGED_LOCAL} = false;
|
||||
@ -253,28 +266,20 @@ ${IS_CHANGED_LOCAL} = false;
|
||||
|
||||
|
||||
export class ChangeDetectorJITGenerator {
|
||||
typeName:string;
|
||||
records:List<ProtoRecord>;
|
||||
directiveRecords:List;
|
||||
localNames: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;
|
||||
localNames: List<string>;
|
||||
changeNames: List<string>;
|
||||
fieldNames: List<string>;
|
||||
pipeNames: List<string>;
|
||||
|
||||
constructor(public typeName: string, public changeDetectionStrategy: string,
|
||||
public records: List<ProtoRecord>, public directiveRecords: List<any>) {
|
||||
this.localNames = this.getLocalNames(records);
|
||||
this.changeNames = this.getChangeNames(this.localNames);
|
||||
this.fieldNames = this.getFieldNames(this.localNames);
|
||||
this.pipeNames = this.getPipeNames(this.localNames);
|
||||
}
|
||||
|
||||
getLocalNames(records:List<ProtoRecord>):List<string> {
|
||||
getLocalNames(records: List<ProtoRecord>): List<string> {
|
||||
var index = 0;
|
||||
var names = records.map((r) => {
|
||||
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
|
||||
@ -283,51 +288,49 @@ export class ChangeDetectorJITGenerator {
|
||||
return ["context"].concat(names);
|
||||
}
|
||||
|
||||
getChangeNames(localNames:List<string>):List<string> {
|
||||
getChangeNames(localNames: List<string>): List<string> {
|
||||
return localNames.map((n) => `change_${n}`);
|
||||
}
|
||||
|
||||
getFieldNames(localNames:List<string>):List<string> {
|
||||
getFieldNames(localNames: List<string>): List<string> {
|
||||
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`);
|
||||
}
|
||||
|
||||
generate():Function {
|
||||
generate(): Function {
|
||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
|
||||
this.genCallOnAllChangesDone(), this.genHydrate());
|
||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveRecords', text)
|
||||
(AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
|
||||
this.genCallOnAllChangesDone(), this.genHydrate());
|
||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos',
|
||||
'directiveRecords', text)(AbstractChangeDetector, ChangeDetectionUtil,
|
||||
this.records, this.directiveRecords);
|
||||
}
|
||||
|
||||
genConstructor():string {
|
||||
genConstructor(): string {
|
||||
return constructorTemplate(this.typeName, this.genFieldDefinitions());
|
||||
}
|
||||
|
||||
genHydrate():string {
|
||||
genHydrate(): string {
|
||||
var mode = ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy);
|
||||
return hydrateTemplate(this.typeName, mode, this.genFieldDefinitions(),
|
||||
pipeOnDestroyTemplate(this.getNonNullPipeNames()),
|
||||
this.getDirectiveFieldNames(), this.getDetectorFieldNames());
|
||||
pipeOnDestroyTemplate(this.getNonNullPipeNames()),
|
||||
this.getDirectiveFieldNames(), this.getDetectorFieldNames());
|
||||
}
|
||||
|
||||
getDirectiveFieldNames():List<string> {
|
||||
getDirectiveFieldNames(): List<string> {
|
||||
return this.directiveRecords.map((d) => this.getDirective(d.directiveIndex));
|
||||
}
|
||||
|
||||
getDetectorFieldNames():List<string> {
|
||||
return this.directiveRecords.filter(r => r.isOnPushChangeDetection()).map((d) => this.getDetector(d.directiveIndex));
|
||||
getDetectorFieldNames(): List<string> {
|
||||
return this.directiveRecords.filter(r => r.isOnPushChangeDetection())
|
||||
.map((d) => this.getDetector(d.directiveIndex));
|
||||
}
|
||||
|
||||
getDirective(d:DirectiveIndex) {
|
||||
return `this.directive_${d.name}`;
|
||||
}
|
||||
getDirective(d: DirectiveIndex) { return `this.directive_${d.name}`; }
|
||||
|
||||
getDetector(d:DirectiveIndex) {
|
||||
return `this.detector_${d.name}`;
|
||||
}
|
||||
getDetector(d: DirectiveIndex) { return `this.detector_${d.name}`; }
|
||||
|
||||
genFieldDefinitions() {
|
||||
var fields = [];
|
||||
@ -338,7 +341,7 @@ export class ChangeDetectorJITGenerator {
|
||||
return fieldDefinitionsTemplate(fields);
|
||||
}
|
||||
|
||||
getNonNullPipeNames():List<string> {
|
||||
getNonNullPipeNames(): List<string> {
|
||||
var pipes = [];
|
||||
this.records.forEach((r) => {
|
||||
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||
@ -348,12 +351,12 @@ export class ChangeDetectorJITGenerator {
|
||||
return pipes;
|
||||
}
|
||||
|
||||
genDetectChanges():string {
|
||||
genDetectChanges(): string {
|
||||
var body = this.genDetectChangesBody();
|
||||
return detectChangesTemplate(this.typeName, body);
|
||||
}
|
||||
|
||||
genCallOnAllChangesDone():string {
|
||||
genCallOnAllChangesDone(): string {
|
||||
var notifications = [];
|
||||
var dirs = this.directiveRecords;
|
||||
|
||||
@ -368,28 +371,24 @@ export class ChangeDetectorJITGenerator {
|
||||
return callOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
|
||||
}
|
||||
|
||||
genDetectChangesBody():string {
|
||||
genDetectChangesBody(): string {
|
||||
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
|
||||
return detectChangesBodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
|
||||
}
|
||||
|
||||
genLocalDefinitions():string {
|
||||
return localDefinitionsTemplate(this.localNames);
|
||||
}
|
||||
genLocalDefinitions(): string { return localDefinitionsTemplate(this.localNames); }
|
||||
|
||||
genChangeDefinitions():string {
|
||||
return changeDefinitionsTemplate(this.changeNames);
|
||||
}
|
||||
genChangeDefinitions(): string { return changeDefinitionsTemplate(this.changeNames); }
|
||||
|
||||
genRecord(r:ProtoRecord):string {
|
||||
genRecord(r: ProtoRecord): string {
|
||||
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||
return this.genPipeCheck (r);
|
||||
return this.genPipeCheck(r);
|
||||
} else {
|
||||
return this.genReferenceCheck(r);
|
||||
}
|
||||
}
|
||||
|
||||
genPipeCheck(r:ProtoRecord):string {
|
||||
genPipeCheck(r: ProtoRecord): string {
|
||||
var context = this.localNames[r.contextIndex];
|
||||
var oldValue = this.fieldNames[r.selfIndex];
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
@ -402,11 +401,11 @@ export class ChangeDetectorJITGenerator {
|
||||
var addToChanges = this.genAddToChanges(r);
|
||||
var lastInDirective = this.genLastInDirective(r);
|
||||
|
||||
return pipeCheckTemplate(r.selfIndex - 1, context, cdRef, pipe, r.name, oldValue, newValue, change,
|
||||
update, addToChanges, lastInDirective);
|
||||
return pipeCheckTemplate(r.selfIndex - 1, context, cdRef, pipe, r.name, oldValue, newValue,
|
||||
change, update, addToChanges, lastInDirective);
|
||||
}
|
||||
|
||||
genReferenceCheck(r:ProtoRecord):string {
|
||||
genReferenceCheck(r: ProtoRecord): string {
|
||||
var oldValue = this.fieldNames[r.selfIndex];
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var change = this.changeNames[r.selfIndex];
|
||||
@ -417,7 +416,7 @@ export class ChangeDetectorJITGenerator {
|
||||
var lastInDirective = this.genLastInDirective(r);
|
||||
|
||||
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
|
||||
update, addToChanges, lastInDirective);
|
||||
update, addToChanges, lastInDirective);
|
||||
if (r.isPureFunction()) {
|
||||
return this.ifChangedGuard(r, check);
|
||||
} else {
|
||||
@ -425,7 +424,7 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
genUpdateCurrentValue(r:ProtoRecord):string {
|
||||
genUpdateCurrentValue(r: ProtoRecord): string {
|
||||
var context = this.getContext(r);
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var args = this.genArgs(r);
|
||||
@ -464,7 +463,7 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
getContext(r:ProtoRecord):string {
|
||||
getContext(r: ProtoRecord): string {
|
||||
if (r.contextIndex == -1) {
|
||||
return this.getDirective(r.directiveIndex);
|
||||
} 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);
|
||||
}
|
||||
|
||||
genInterpolation(r:ProtoRecord):string{
|
||||
genInterpolation(r: ProtoRecord): string {
|
||||
var res = "";
|
||||
for (var i = 0; i < r.args.length; ++i) {
|
||||
res += this.genLiteral(r.fixedArgs[i]);
|
||||
@ -488,38 +487,37 @@ export class ChangeDetectorJITGenerator {
|
||||
return res;
|
||||
}
|
||||
|
||||
genLiteral(value):string {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
genLiteral(value): string { return JSON.stringify(value); }
|
||||
|
||||
genUpdateDirectiveOrElement(r:ProtoRecord):string {
|
||||
if (! r.lastInBinding) return "";
|
||||
genUpdateDirectiveOrElement(r: ProtoRecord): string {
|
||||
if (!r.lastInBinding) return "";
|
||||
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var oldValue = this.fieldNames[r.selfIndex];
|
||||
|
||||
var br = r.bindingRecord;
|
||||
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);
|
||||
} else {
|
||||
return updateElementTemplate(oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
genAddToChanges(r:ProtoRecord):string {
|
||||
genAddToChanges(r: ProtoRecord): string {
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var oldValue = this.fieldNames[r.selfIndex];
|
||||
return r.bindingRecord.callOnChange() ? addToChangesTemplate(oldValue, newValue) : "";
|
||||
}
|
||||
|
||||
genLastInDirective(r:ProtoRecord):string{
|
||||
genLastInDirective(r: ProtoRecord): string {
|
||||
var onChanges = this.genNotifyOnChanges(r);
|
||||
var onPush = this.genNotifyOnPushDetectors(r);
|
||||
return lastInDirectiveTemplate(onChanges, onPush);
|
||||
}
|
||||
|
||||
genNotifyOnChanges(r:ProtoRecord):string{
|
||||
genNotifyOnChanges(r: ProtoRecord): string {
|
||||
var br = r.bindingRecord;
|
||||
if (r.lastInDirective && br.callOnChange()) {
|
||||
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;
|
||||
if (r.lastInDirective && br.isOnPushChangeDetection()) {
|
||||
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord.directiveIndex));
|
||||
@ -537,11 +535,5 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
genArgs(r:ProtoRecord):string {
|
||||
return r.args.map((arg) => this.localNames[arg]).join(", ");
|
||||
}
|
||||
genArgs(r: ProtoRecord): string { 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;
|
||||
}
|
||||
}
|
148
modules/angular2/src/change_detection/change_detection_util.ts
Normal file
148
modules/angular2/src/change_detection/change_detection_util.ts
Normal file
@ -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 {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.
|
||||
*
|
||||
* {@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.
|
||||
*
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
export class ChangeDetectorRef {
|
||||
_cd:ChangeDetector;
|
||||
|
||||
constructor(cd:ChangeDetector) {
|
||||
this._cd = cd;
|
||||
}
|
||||
constructor(private _cd: ChangeDetector) {}
|
||||
|
||||
/**
|
||||
* Request to check all ON_PUSH ancestors.
|
||||
*/
|
||||
requestCheck() {
|
||||
this._cd.markPathToRootAsCheckOnce();
|
||||
}
|
||||
requestCheck() { this._cd.markPathToRootAsCheckOnce(); }
|
||||
|
||||
/**
|
||||
* Detaches the change detector from the change detector tree.
|
||||
*
|
||||
* The detached change detector will not be checked until it is reattached.
|
||||
*/
|
||||
detach() {
|
||||
this._cd.mode = DETACHED;
|
||||
}
|
||||
detach() { this._cd.mode = DETACHED; }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
reattach() {
|
@ -2,6 +2,11 @@ import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
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
|
||||
* 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
|
||||
* 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 indexMap = MapWrapper.create();
|
||||
|
||||
@ -37,52 +42,27 @@ export function coalesce(records:List<ProtoRecord>):List<ProtoRecord> {
|
||||
return res;
|
||||
}
|
||||
|
||||
function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):ProtoRecord {
|
||||
return new ProtoRecord(
|
||||
RECORD_TYPE_SELF,
|
||||
"self",
|
||||
null,
|
||||
[],
|
||||
r.fixedArgs,
|
||||
contextIndex,
|
||||
r.directiveIndex,
|
||||
selfIndex,
|
||||
r.bindingRecord,
|
||||
r.expressionAsString,
|
||||
r.lastInBinding,
|
||||
r.lastInDirective
|
||||
);
|
||||
function _selfRecord(r: ProtoRecord, contextIndex: number, selfIndex: number): ProtoRecord {
|
||||
return new ProtoRecord(RECORD_TYPE_SELF, "self", null, [], r.fixedArgs, contextIndex,
|
||||
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString,
|
||||
r.lastInBinding, r.lastInDirective);
|
||||
}
|
||||
|
||||
function _findMatching(r:ProtoRecord, rs:List<ProtoRecord>){
|
||||
return ListWrapper.find(rs, (rr) =>
|
||||
rr.mode === r.mode &&
|
||||
rr.funcOrValue === r.funcOrValue &&
|
||||
rr.contextIndex === r.contextIndex &&
|
||||
ListWrapper.equals(rr.args, r.args)
|
||||
);
|
||||
function _findMatching(r: ProtoRecord, rs: List<ProtoRecord>) {
|
||||
return ListWrapper.find(rs, (rr) => rr.mode === r.mode && rr.funcOrValue === r.funcOrValue &&
|
||||
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 contextIndex = _map(indexMap, r.contextIndex);
|
||||
return new ProtoRecord(
|
||||
r.mode,
|
||||
r.name,
|
||||
r.funcOrValue,
|
||||
args,
|
||||
r.fixedArgs,
|
||||
contextIndex,
|
||||
r.directiveIndex,
|
||||
selfIndex,
|
||||
r.bindingRecord,
|
||||
r.expressionAsString,
|
||||
r.lastInBinding,
|
||||
r.lastInDirective
|
||||
);
|
||||
return new ProtoRecord(r.mode, r.name, r.funcOrValue, args, r.fixedArgs, contextIndex,
|
||||
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString,
|
||||
r.lastInBinding, r.lastInDirective);
|
||||
}
|
||||
|
||||
function _map(indexMap:Map, value:number) {
|
||||
var r = MapWrapper.get(indexMap, value)
|
||||
function _map(indexMap: Map<any, any>, value: number) {
|
||||
var r = MapWrapper.get(indexMap, 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
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* 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.
|
@ -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);
|
||||
}
|
||||
}
|
20
modules/angular2/src/change_detection/directive_record.ts
Normal file
20
modules/angular2/src/change_detection/directive_record.ts
Normal file
@ -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 {BindingRecord} from './binding_record';
|
||||
import {PipeRegistry} from './pipes/pipe_registry';
|
||||
import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
|
||||
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
|
||||
|
||||
|
||||
import {
|
||||
@ -20,35 +20,31 @@ import {
|
||||
RECORD_TYPE_PIPE,
|
||||
RECORD_TYPE_BINDING_PIPE,
|
||||
RECORD_TYPE_INTERPOLATE
|
||||
} from './proto_record';
|
||||
} from './proto_record';
|
||||
|
||||
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 {
|
||||
dispatcher:any;
|
||||
pipeRegistry;
|
||||
locals: any;
|
||||
values: List<any>;
|
||||
changes: List<any>;
|
||||
pipes: List<any>;
|
||||
prevContexts: List<any>;
|
||||
directives: any;
|
||||
|
||||
locals:any;
|
||||
values:List;
|
||||
changes:List;
|
||||
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) {
|
||||
constructor(private changeControlStrategy: string, private dispatcher: any,
|
||||
private pipeRegistry: PipeRegistry, private protos: List<ProtoRecord>,
|
||||
private directiveRecords: List<any>) {
|
||||
super();
|
||||
this.dispatcher = dispatcher;
|
||||
this.pipeRegistry = pipeRegistry;
|
||||
|
||||
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||
this.pipes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||
this.prevContexts = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||
this.values = ListWrapper.createFixedSize(protos.length + 1);
|
||||
this.pipes = ListWrapper.createFixedSize(protos.length + 1);
|
||||
this.prevContexts = ListWrapper.createFixedSize(protos.length + 1);
|
||||
this.changes = ListWrapper.createFixedSize(protos.length + 1);
|
||||
|
||||
ListWrapper.fill(this.values, uninitialized);
|
||||
ListWrapper.fill(this.pipes, null);
|
||||
@ -56,13 +52,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
ListWrapper.fill(this.changes, false);
|
||||
this.locals = 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.values[0] = context;
|
||||
this.locals = locals;
|
||||
@ -79,24 +71,22 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
}
|
||||
|
||||
_destroyPipes() {
|
||||
for(var i = 0; i < this.pipes.length; ++i) {
|
||||
for (var i = 0; i < this.pipes.length; ++i) {
|
||||
if (isPresent(this.pipes[i])) {
|
||||
this.pipes[i].onDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hydrated():boolean {
|
||||
return this.values[0] !== uninitialized;
|
||||
}
|
||||
hydrated(): boolean { return this.values[0] !== uninitialized; }
|
||||
|
||||
detectChangesInRecords(throwOnChange:boolean) {
|
||||
var protos:List<ProtoRecord> = this.protos;
|
||||
detectChangesInRecords(throwOnChange: boolean) {
|
||||
var protos: List < ProtoRecord >= this.protos;
|
||||
|
||||
var changes = null;
|
||||
var isChanged = false;
|
||||
for (var i = 0; i < protos.length; ++i) {
|
||||
var proto:ProtoRecord = protos[i];
|
||||
var proto: ProtoRecord = protos[i];
|
||||
var bindingRecord = proto.bindingRecord;
|
||||
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()) {
|
||||
return ChangeDetectionUtil.addChange(changes, bindingRecord.propertyName, change);
|
||||
} else {
|
||||
@ -150,15 +140,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
}
|
||||
}
|
||||
|
||||
_getDirectiveFor(directiveIndex) {
|
||||
return this.directives.getDirectiveFor(directiveIndex);
|
||||
}
|
||||
_getDirectiveFor(directiveIndex) { return this.directives.getDirectiveFor(directiveIndex); }
|
||||
|
||||
_getDetectorFor(directiveIndex) {
|
||||
return this.directives.getDetectorFor(directiveIndex);
|
||||
}
|
||||
_getDetectorFor(directiveIndex) { return this.directives.getDetectorFor(directiveIndex); }
|
||||
|
||||
_check(proto:ProtoRecord) {
|
||||
_check(proto: ProtoRecord): SimpleChange {
|
||||
try {
|
||||
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||
return this._pipeCheck(proto);
|
||||
@ -170,7 +156,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
}
|
||||
}
|
||||
|
||||
_referenceCheck(proto:ProtoRecord) {
|
||||
_referenceCheck(proto: ProtoRecord) {
|
||||
if (this._pureFuncAndArgsDidNotChange(proto)) {
|
||||
this._setChanged(proto, false);
|
||||
return null;
|
||||
@ -194,7 +180,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
}
|
||||
}
|
||||
|
||||
_calculateCurrValue(proto:ProtoRecord) {
|
||||
_calculateCurrValue(proto: ProtoRecord) {
|
||||
switch (proto.mode) {
|
||||
case RECORD_TYPE_SELF:
|
||||
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 pipe = this._pipeFor(proto, context);
|
||||
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);
|
||||
if (isPresent(storedPipe) && storedPipe.supports(context)) {
|
||||
return storedPipe;
|
||||
@ -274,7 +260,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
return pipe;
|
||||
}
|
||||
|
||||
_readContext(proto:ProtoRecord) {
|
||||
_readContext(proto: ProtoRecord) {
|
||||
if (proto.contextIndex == -1) {
|
||||
return this._getDirectiveFor(proto.directiveIndex);
|
||||
} else {
|
||||
@ -284,33 +270,23 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
return this.values[proto.contextIndex];
|
||||
}
|
||||
|
||||
_readSelf(proto:ProtoRecord) {
|
||||
return this.values[proto.selfIndex];
|
||||
}
|
||||
_readSelf(proto: ProtoRecord) { return this.values[proto.selfIndex]; }
|
||||
|
||||
_writeSelf(proto:ProtoRecord, value) {
|
||||
this.values[proto.selfIndex] = value;
|
||||
}
|
||||
_writeSelf(proto: ProtoRecord, value) { this.values[proto.selfIndex] = value; }
|
||||
|
||||
_readPipe(proto:ProtoRecord) {
|
||||
return this.pipes[proto.selfIndex];
|
||||
}
|
||||
_readPipe(proto: ProtoRecord) { return this.pipes[proto.selfIndex]; }
|
||||
|
||||
_writePipe(proto:ProtoRecord, value) {
|
||||
this.pipes[proto.selfIndex] = value;
|
||||
}
|
||||
_writePipe(proto: ProtoRecord, value) { this.pipes[proto.selfIndex] = value; }
|
||||
|
||||
_setChanged(proto:ProtoRecord, value:boolean) {
|
||||
this.changes[proto.selfIndex] = value;
|
||||
}
|
||||
_setChanged(proto: ProtoRecord, value: boolean) { this.changes[proto.selfIndex] = value; }
|
||||
|
||||
_pureFuncAndArgsDidNotChange(proto:ProtoRecord):boolean {
|
||||
_pureFuncAndArgsDidNotChange(proto: ProtoRecord): boolean {
|
||||
return proto.isPureFunction() && !this._argsChanged(proto);
|
||||
}
|
||||
|
||||
_argsChanged(proto:ProtoRecord):boolean {
|
||||
_argsChanged(proto: ProtoRecord): boolean {
|
||||
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]]) {
|
||||
return true;
|
||||
}
|
||||
@ -318,7 +294,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
return false;
|
||||
}
|
||||
|
||||
_readArgs(proto:ProtoRecord) {
|
||||
_readArgs(proto: ProtoRecord) {
|
||||
var res = ListWrapper.createFixedSize(proto.args.length);
|
||||
var args = proto.args;
|
||||
for (var i = 0; i < args.length; ++i) {
|
||||
@ -334,6 +310,3 @@ function isSame(a, b) {
|
||||
if ((a !== a) && (b !== b)) return true;
|
||||
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;
|
||||
}
|
||||
}
|
36
modules/angular2/src/change_detection/exceptions.ts
Normal file
36
modules/angular2/src/change_detection/exceptions.ts
Normal file
@ -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 {BindingRecord} from './binding_record';
|
||||
|
||||
export class ProtoChangeDetector {
|
||||
instantiate(dispatcher:any):ChangeDetector{
|
||||
return null;
|
||||
}
|
||||
// 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 ProtoChangeDetector {
|
||||
instantiate(dispatcher: any): ChangeDetector { return null; }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -17,10 +20,12 @@ export class ProtoChangeDetector {
|
||||
* - {@link DynamicChangeDetection}: slower, but does not require `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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
@ -33,26 +38,27 @@ export class ProtoChangeDetector {
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
export class ChangeDetection {
|
||||
createProtoChangeDetector(name:string, bindingRecords:List, variableBindings:List, directiveRecords:List,
|
||||
changeControlStrategy:string=DEFAULT):ProtoChangeDetector{
|
||||
createProtoChangeDetector(name: string, bindingRecords: List<any>, variableBindings: List<any>,
|
||||
directiveRecords: List<any>,
|
||||
changeControlStrategy: string = DEFAULT): ProtoChangeDetector {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class ChangeDispatcher {
|
||||
notifyOnBinding(bindingRecord:BindingRecord, value:any) {}
|
||||
notifyOnBinding(bindingRecord: BindingRecord, value: any) {}
|
||||
}
|
||||
|
||||
export class ChangeDetector {
|
||||
parent:ChangeDetector;
|
||||
mode:string;
|
||||
parent: ChangeDetector;
|
||||
mode: string;
|
||||
|
||||
addChild(cd:ChangeDetector) {}
|
||||
addShadowDomChild(cd:ChangeDetector) {}
|
||||
removeChild(cd:ChangeDetector) {}
|
||||
removeShadowDomChild(cd:ChangeDetector) {}
|
||||
addChild(cd: ChangeDetector) {}
|
||||
addShadowDomChild(cd: ChangeDetector) {}
|
||||
removeChild(cd: ChangeDetector) {}
|
||||
removeShadowDomChild(cd: ChangeDetector) {}
|
||||
remove() {}
|
||||
hydrate(context:any, locals:Locals, directives:any) {}
|
||||
hydrate(context: any, locals: Locals, directives: any) {}
|
||||
dehydrate() {}
|
||||
markPathToRootAsCheckOnce() {}
|
||||
|
532
modules/angular2/src/change_detection/parser/ast.js
vendored
532
modules/angular2/src/change_detection/parser/ast.js
vendored
@ -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;
|
||||
}
|
388
modules/angular2/src/change_detection/parser/ast.ts
Normal file
388
modules/angular2/src/change_detection/parser/ast.ts
Normal file
@ -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'
|
||||
]);
|
452
modules/angular2/src/change_detection/parser/lexer.ts
Normal file
452
modules/angular2/src/change_detection/parser/lexer.ts
Normal file
@ -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 {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 {
|
||||
parent:Locals;
|
||||
current:Map;
|
||||
constructor(public parent: Locals, public current: Map<any, any>) {}
|
||||
|
||||
constructor(parent:Locals, current:Map) {
|
||||
this.parent = parent;
|
||||
this.current = current;
|
||||
}
|
||||
|
||||
contains(name:string):boolean {
|
||||
contains(name: string): boolean {
|
||||
if (MapWrapper.contains(this.current, name)) {
|
||||
return true;
|
||||
}
|
||||
@ -22,7 +21,7 @@ export class Locals {
|
||||
return false;
|
||||
}
|
||||
|
||||
get(name:string) {
|
||||
get(name: string) {
|
||||
if (MapWrapper.contains(this.current, name)) {
|
||||
return MapWrapper.get(this.current, name);
|
||||
}
|
||||
@ -34,7 +33,7 @@ export class Locals {
|
||||
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
|
||||
// exposed to the public API.
|
||||
// TODO: vsavkin maybe it should check only the local map
|
||||
@ -45,7 +44,5 @@ export class Locals {
|
||||
}
|
||||
}
|
||||
|
||||
clearValues():void {
|
||||
MapWrapper.clearValues(this.current);
|
||||
}
|
||||
clearValues(): void { MapWrapper.clearValues(this.current); }
|
||||
}
|
@ -1,8 +1,28 @@
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {int, isBlank, isPresent, BaseException, StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
|
||||
import {Injectable} from 'angular2/src/di/decorators';
|
||||
import {
|
||||
int,
|
||||
isBlank,
|
||||
isPresent,
|
||||
BaseException,
|
||||
StringWrapper,
|
||||
RegExpWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {Lexer, EOF, Token, $PERIOD, $COLON, $SEMICOLON, $LBRACKET, $RBRACKET,
|
||||
$COMMA, $LBRACE, $RBRACE, $LPAREN, $RPAREN} from './lexer';
|
||||
import {
|
||||
Lexer,
|
||||
EOF,
|
||||
Token,
|
||||
$PERIOD,
|
||||
$COLON,
|
||||
$SEMICOLON,
|
||||
$LBRACKET,
|
||||
$RBRACKET,
|
||||
$COMMA,
|
||||
$LBRACE,
|
||||
$RBRACE,
|
||||
$LPAREN,
|
||||
$RPAREN
|
||||
} from './lexer';
|
||||
import {reflector, Reflector} from 'angular2/src/reflection/reflection';
|
||||
import {
|
||||
AST,
|
||||
@ -10,7 +30,6 @@ import {
|
||||
ImplicitReceiver,
|
||||
AccessMember,
|
||||
LiteralPrimitive,
|
||||
Expression,
|
||||
Binary,
|
||||
PrefixNot,
|
||||
Conditional,
|
||||
@ -23,10 +42,15 @@ import {
|
||||
Interpolation,
|
||||
MethodCall,
|
||||
FunctionCall,
|
||||
TemplateBindings,
|
||||
TemplateBinding,
|
||||
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();
|
||||
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
|
||||
@ -34,40 +58,40 @@ var INTERPOLATION_REGEXP = RegExpWrapper.create('\\{\\{(.*?)\\}\\}');
|
||||
|
||||
@Injectable()
|
||||
export class Parser {
|
||||
_lexer:Lexer;
|
||||
_reflector:Reflector;
|
||||
constructor(lexer:Lexer, providedReflector:Reflector = null){
|
||||
_lexer: Lexer;
|
||||
_reflector: Reflector;
|
||||
constructor(lexer: Lexer, providedReflector: Reflector = null) {
|
||||
this._lexer = lexer;
|
||||
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 ast = new _ParseAST(input, location, tokens, this._reflector, true).parseChain();
|
||||
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 ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
|
||||
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;
|
||||
|
||||
var res = ListWrapper.reduce(pipes,
|
||||
(result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
|
||||
bindingAst.ast);
|
||||
var res: AST = ListWrapper.reduce(
|
||||
pipes, (result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
|
||||
bindingAst.ast);
|
||||
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);
|
||||
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);
|
||||
if (parts.length <= 1) {
|
||||
return null;
|
||||
@ -75,9 +99,9 @@ export class Parser {
|
||||
var strings = [];
|
||||
var expressions = [];
|
||||
|
||||
for (var i=0; i<parts.length; i++) {
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var part = parts[i];
|
||||
if (i%2 === 0) {
|
||||
if (i % 2 === 0) {
|
||||
// fixed string
|
||||
ListWrapper.push(strings, part);
|
||||
} else {
|
||||
@ -89,46 +113,32 @@ export class Parser {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _ParseAST {
|
||||
input:string;
|
||||
location:any;
|
||||
tokens:List<Token>;
|
||||
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;
|
||||
index: int;
|
||||
constructor(public input: string, public location: any, public tokens: List<any>,
|
||||
public reflector: Reflector, public parseAction: boolean) {
|
||||
this.index = 0;
|
||||
this.reflector = reflector;
|
||||
this.parseAction = parseAction;
|
||||
}
|
||||
|
||||
peek(offset:int):Token {
|
||||
peek(offset: int): Token {
|
||||
var i = this.index + offset;
|
||||
return i < this.tokens.length ? this.tokens[i] : EOF;
|
||||
}
|
||||
|
||||
get next():Token {
|
||||
return this.peek(0);
|
||||
}
|
||||
get next(): Token { return this.peek(0); }
|
||||
|
||||
get inputIndex():int {
|
||||
get inputIndex(): int {
|
||||
return (this.index < this.tokens.length) ? this.next.index : this.input.length;
|
||||
}
|
||||
|
||||
advance() {
|
||||
this.index ++;
|
||||
}
|
||||
advance() { this.index++; }
|
||||
|
||||
optionalCharacter(code:int):boolean {
|
||||
optionalCharacter(code: int): boolean {
|
||||
if (this.next.isCharacter(code)) {
|
||||
this.advance();
|
||||
return true;
|
||||
@ -137,7 +147,7 @@ class _ParseAST {
|
||||
}
|
||||
}
|
||||
|
||||
optionalKeywordVar():boolean {
|
||||
optionalKeywordVar(): boolean {
|
||||
if (this.peekKeywordVar()) {
|
||||
this.advance();
|
||||
return true;
|
||||
@ -146,17 +156,15 @@ class _ParseAST {
|
||||
}
|
||||
}
|
||||
|
||||
peekKeywordVar():boolean {
|
||||
return this.next.isKeywordVar() || this.next.isOperator('#');
|
||||
}
|
||||
peekKeywordVar(): boolean { return this.next.isKeywordVar() || this.next.isOperator('#'); }
|
||||
|
||||
expectCharacter(code:int) {
|
||||
expectCharacter(code: int) {
|
||||
if (this.optionalCharacter(code)) return;
|
||||
this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
|
||||
}
|
||||
|
||||
|
||||
optionalOperator(op:string):boolean {
|
||||
optionalOperator(op: string): boolean {
|
||||
if (this.next.isOperator(op)) {
|
||||
this.advance();
|
||||
return true;
|
||||
@ -165,12 +173,12 @@ class _ParseAST {
|
||||
}
|
||||
}
|
||||
|
||||
expectOperator(operator:string) {
|
||||
expectOperator(operator: string) {
|
||||
if (this.optionalOperator(operator)) return;
|
||||
this.error(`Missing expected operator ${operator}`);
|
||||
}
|
||||
|
||||
expectIdentifierOrKeyword():string {
|
||||
expectIdentifierOrKeyword(): string {
|
||||
var n = this.next;
|
||||
if (!n.isIdentifier() && !n.isKeyword()) {
|
||||
this.error(`Unexpected token ${n}, expected identifier or keyword`)
|
||||
@ -179,7 +187,7 @@ class _ParseAST {
|
||||
return n.toString();
|
||||
}
|
||||
|
||||
expectIdentifierOrKeywordOrString():string {
|
||||
expectIdentifierOrKeywordOrString(): string {
|
||||
var n = this.next;
|
||||
if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
|
||||
this.error(`Unexpected token ${n}, expected identifier, keyword, or string`)
|
||||
@ -188,17 +196,18 @@ class _ParseAST {
|
||||
return n.toString();
|
||||
}
|
||||
|
||||
parseChain():AST {
|
||||
parseChain(): AST {
|
||||
var exprs = [];
|
||||
while (this.index < this.tokens.length) {
|
||||
var expr = this.parsePipe();
|
||||
ListWrapper.push(exprs, expr);
|
||||
|
||||
if (this.optionalCharacter($SEMICOLON)) {
|
||||
if (! this.parseAction) {
|
||||
if (!this.parseAction) {
|
||||
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) {
|
||||
this.error(`Unexpected token '${this.next}'`);
|
||||
}
|
||||
@ -353,7 +362,7 @@ class _ParseAST {
|
||||
}
|
||||
}
|
||||
|
||||
parseCallChain():AST {
|
||||
parseCallChain(): AST {
|
||||
var result = this.parsePrimary();
|
||||
while (true) {
|
||||
if (this.optionalCharacter($PERIOD)) {
|
||||
@ -410,9 +419,9 @@ class _ParseAST {
|
||||
return new LiteralPrimitive(value);
|
||||
|
||||
} else if (this.next.isString()) {
|
||||
var value = this.next.toString();
|
||||
var literalValue = this.next.toString();
|
||||
this.advance();
|
||||
return new LiteralPrimitive(value);
|
||||
return new LiteralPrimitive(literalValue);
|
||||
|
||||
} else if (this.index >= this.tokens.length) {
|
||||
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 = [];
|
||||
if (!this.next.isCharacter(terminator)) {
|
||||
do {
|
||||
@ -448,7 +457,7 @@ class _ParseAST {
|
||||
return new LiteralMap(keys, values);
|
||||
}
|
||||
|
||||
parseAccessMemberOrMethodCall(receiver):AST {
|
||||
parseAccessMemberOrMethodCall(receiver): AST {
|
||||
var id = this.expectIdentifierOrKeyword();
|
||||
|
||||
if (this.optionalCharacter($LPAREN)) {
|
||||
@ -471,7 +480,7 @@ class _ParseAST {
|
||||
}
|
||||
|
||||
parseInlinedPipe(result) {
|
||||
do {
|
||||
do {
|
||||
if (this.parseAction) {
|
||||
this.error("Cannot have a pipe in an action expression");
|
||||
}
|
||||
@ -481,7 +490,7 @@ class _ParseAST {
|
||||
ListWrapper.push(args, this.parseExpression());
|
||||
}
|
||||
result = new Pipe(result, name, args, true);
|
||||
} while(this.optionalOperator("|"));
|
||||
} while (this.optionalOperator("|"));
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -515,7 +524,7 @@ class _ParseAST {
|
||||
parseTemplateBindings() {
|
||||
var bindings = [];
|
||||
while (this.index < this.tokens.length) {
|
||||
var keyIsVar:boolean = this.optionalKeywordVar();
|
||||
var keyIsVar: boolean = this.optionalKeywordVar();
|
||||
var key = this.expectTemplateBindingKey();
|
||||
this.optionalCharacter($COLON);
|
||||
var name = null;
|
||||
@ -542,13 +551,13 @@ class _ParseAST {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
error(message:string, index:int = null) {
|
||||
error(message: string, index: int = null) {
|
||||
if (isBlank(index)) index = this.index;
|
||||
|
||||
var location = (index < this.tokens.length)
|
||||
? `at column ${this.tokens[index].index + 1} in`
|
||||
: `at the end of the expression`;
|
||||
var location = (index < this.tokens.length) ? `at column ${this.tokens[index].index + 1} in` :
|
||||
`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 {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.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* In this example we bind the description observable to the DOM. The async pipe will convert an observable to the
|
||||
* latest value it emitted. It will also request a change detection check when a new value is emitted.
|
||||
* In this example we bind the description observable to the DOM. The async pipe will convert an
|
||||
*observable to the
|
||||
* latest value it emitted. It will also request a change detection check when a new value is
|
||||
*emitted.
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
@ -28,15 +36,15 @@ import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class AsyncPipe extends Pipe {
|
||||
_ref:ChangeDetectorRef;
|
||||
_ref: ChangeDetectorRef;
|
||||
|
||||
_latestValue:Object;
|
||||
_latestReturnedValue:Object;
|
||||
_latestValue: Object;
|
||||
_latestReturnedValue: Object;
|
||||
|
||||
_subscription:Object;
|
||||
_observable:Observable;
|
||||
_subscription: Object;
|
||||
_observable: Observable;
|
||||
|
||||
constructor(ref:ChangeDetectorRef) {
|
||||
constructor(ref: ChangeDetectorRef) {
|
||||
super();
|
||||
this._ref = ref;
|
||||
this._latestValue = null;
|
||||
@ -45,17 +53,15 @@ export class AsyncPipe extends Pipe {
|
||||
this._observable = null;
|
||||
}
|
||||
|
||||
supports(obs):boolean {
|
||||
return ObservableWrapper.isObservable(obs);
|
||||
}
|
||||
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
|
||||
|
||||
onDestroy():void {
|
||||
onDestroy(): void {
|
||||
if (isPresent(this._subscription)) {
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
|
||||
transform(obs:Observable):any {
|
||||
transform(obs: Observable): any {
|
||||
if (isBlank(this._subscription)) {
|
||||
this._subscribe(obs);
|
||||
return null;
|
||||
@ -74,15 +80,13 @@ export class AsyncPipe extends Pipe {
|
||||
}
|
||||
}
|
||||
|
||||
_subscribe(obs:Observable):void {
|
||||
_subscribe(obs: Observable): void {
|
||||
this._observable = obs;
|
||||
this._subscription = ObservableWrapper.subscribe(obs,
|
||||
value => this._updateLatestValue(value),
|
||||
e => {throw e;}
|
||||
);
|
||||
this._subscription = ObservableWrapper.subscribe(obs, value => {this._updateLatestValue(value)},
|
||||
e => { throw e; });
|
||||
}
|
||||
|
||||
_dispose():void {
|
||||
_dispose(): void {
|
||||
ObservableWrapper.dispose(this._subscription);
|
||||
this._latestValue = null;
|
||||
this._latestReturnedValue = null;
|
||||
@ -90,7 +94,7 @@ export class AsyncPipe extends Pipe {
|
||||
this._observable = null;
|
||||
}
|
||||
|
||||
_updateLatestValue(value:Object) {
|
||||
_updateLatestValue(value: Object) {
|
||||
this._latestValue = value;
|
||||
this._ref.requestCheck();
|
||||
}
|
||||
@ -101,17 +105,11 @@ export class AsyncPipe extends Pipe {
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
@CONST()
|
||||
export class AsyncPipeFactory extends PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
constructor() { super(); }
|
||||
|
||||
supports(obs):boolean {
|
||||
return ObservableWrapper.isObservable(obs);
|
||||
}
|
||||
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
|
||||
|
||||
create(cdRef):Pipe {
|
||||
return new AsyncPipe(cdRef);
|
||||
}
|
||||
create(cdRef): Pipe { return new AsyncPipe(cdRef); }
|
||||
}
|
@ -17,38 +17,37 @@ import {
|
||||
|
||||
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 {
|
||||
@CONST()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
constructor() { super(); }
|
||||
|
||||
supports(obj):boolean {
|
||||
return IterableChanges.supportsObj(obj);
|
||||
}
|
||||
supports(obj): boolean { return IterableChanges.supportsObj(obj); }
|
||||
|
||||
create(cdRef):Pipe {
|
||||
return new IterableChanges();
|
||||
}
|
||||
create(cdRef): Pipe { return new IterableChanges(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class IterableChanges extends Pipe {
|
||||
_collection;
|
||||
_length:int;
|
||||
_linkedRecords:_DuplicateMap;
|
||||
_unlinkedRecords:_DuplicateMap;
|
||||
_previousItHead:CollectionChangeRecord;
|
||||
_itHead:CollectionChangeRecord;
|
||||
_itTail:CollectionChangeRecord;
|
||||
_additionsHead:CollectionChangeRecord;
|
||||
_additionsTail:CollectionChangeRecord;
|
||||
_movesHead:CollectionChangeRecord;
|
||||
_movesTail:CollectionChangeRecord;
|
||||
_removalsHead:CollectionChangeRecord;
|
||||
_removalsTail:CollectionChangeRecord;
|
||||
private _collection;
|
||||
private _length: int;
|
||||
private _linkedRecords: _DuplicateMap;
|
||||
private _unlinkedRecords: _DuplicateMap;
|
||||
private _previousItHead: CollectionChangeRecord;
|
||||
private _itHead: CollectionChangeRecord;
|
||||
private _itTail: CollectionChangeRecord;
|
||||
private _additionsHead: CollectionChangeRecord;
|
||||
private _additionsTail: CollectionChangeRecord;
|
||||
private _movesHead: CollectionChangeRecord;
|
||||
private _movesTail: CollectionChangeRecord;
|
||||
private _removalsHead: CollectionChangeRecord;
|
||||
private _removalsTail: CollectionChangeRecord;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -70,58 +69,50 @@ export class IterableChanges extends Pipe {
|
||||
this._removalsTail = null;
|
||||
}
|
||||
|
||||
static supportsObj(obj):boolean {
|
||||
return isListLikeIterable(obj);
|
||||
}
|
||||
static supportsObj(obj): boolean { return isListLikeIterable(obj); }
|
||||
|
||||
supports(obj):boolean {
|
||||
return IterableChanges.supportsObj(obj);
|
||||
}
|
||||
supports(obj): boolean { return IterableChanges.supportsObj(obj); }
|
||||
|
||||
get collection() {
|
||||
return this._collection;
|
||||
}
|
||||
get collection() { return this._collection; }
|
||||
|
||||
get length():int {
|
||||
return this._length;
|
||||
}
|
||||
get length(): int { return this._length; }
|
||||
|
||||
forEachItem(fn:Function) {
|
||||
var record:CollectionChangeRecord;
|
||||
forEachItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._itHead; record !== null; record = record._next) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachPreviousItem(fn:Function) {
|
||||
var record:CollectionChangeRecord;
|
||||
forEachPreviousItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachAddedItem(fn:Function){
|
||||
var record:CollectionChangeRecord;
|
||||
forEachAddedItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachMovedItem(fn:Function) {
|
||||
var record:CollectionChangeRecord;
|
||||
forEachMovedItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachRemovedItem(fn:Function){
|
||||
var record:CollectionChangeRecord;
|
||||
forEachRemovedItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
transform(collection){
|
||||
transform(collection): any {
|
||||
if (this.check(collection)) {
|
||||
return WrappedValue.wrap(this);
|
||||
} else {
|
||||
@ -130,12 +121,12 @@ export class IterableChanges extends Pipe {
|
||||
}
|
||||
|
||||
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
||||
check(collection):boolean {
|
||||
check(collection): boolean {
|
||||
this._reset();
|
||||
|
||||
var record:CollectionChangeRecord = this._itHead;
|
||||
var mayBeDirty:boolean = false;
|
||||
var index:int;
|
||||
var record: CollectionChangeRecord = this._itHead;
|
||||
var mayBeDirty: boolean = false;
|
||||
var index: int;
|
||||
var item;
|
||||
|
||||
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.
|
||||
get isDirty():boolean {
|
||||
return this._additionsHead !== null ||
|
||||
this._movesHead !== null ||
|
||||
this._removalsHead !== null;
|
||||
get isDirty(): boolean {
|
||||
return this._additionsHead !== null || this._movesHead !== null || this._removalsHead !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,8 +178,8 @@ export class IterableChanges extends Pipe {
|
||||
*/
|
||||
_reset() {
|
||||
if (this.isDirty) {
|
||||
var record:CollectionChangeRecord;
|
||||
var nextRecord:CollectionChangeRecord;
|
||||
var record: CollectionChangeRecord;
|
||||
var nextRecord: CollectionChangeRecord;
|
||||
|
||||
for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
|
||||
record._nextPrevious = record._next;
|
||||
@ -221,9 +210,9 @@ export class IterableChanges extends Pipe {
|
||||
* - `item` is the current 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.
|
||||
var previousRecord:CollectionChangeRecord;
|
||||
var previousRecord: CollectionChangeRecord;
|
||||
|
||||
if (record === null) {
|
||||
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'
|
||||
* at the end.
|
||||
*/
|
||||
_verifyReinsertion(record:CollectionChangeRecord, item, index:int):CollectionChangeRecord {
|
||||
var reinsertRecord:CollectionChangeRecord = this._unlinkedRecords === null ?
|
||||
null : this._unlinkedRecords.get(item);
|
||||
_verifyReinsertion(record: CollectionChangeRecord, item, index: int): CollectionChangeRecord {
|
||||
var reinsertRecord: CollectionChangeRecord =
|
||||
this._unlinkedRecords === null ? null : this._unlinkedRecords.get(item);
|
||||
if (reinsertRecord !== null) {
|
||||
record = this._reinsertAfter(reinsertRecord, record._prev, index);
|
||||
} else if (record.currentIndex != index) {
|
||||
@ -294,10 +283,10 @@ export class IterableChanges extends Pipe {
|
||||
*
|
||||
* - `record` The first excess {@link CollectionChangeRecord}.
|
||||
*/
|
||||
_truncate(record:CollectionChangeRecord) {
|
||||
_truncate(record: CollectionChangeRecord) {
|
||||
// Anything after that needs to be removed;
|
||||
while (record !== null) {
|
||||
var nextRecord:CollectionChangeRecord = record._next;
|
||||
var nextRecord: CollectionChangeRecord = record._next;
|
||||
this._addToRemovals(this._unlink(record));
|
||||
record = nextRecord;
|
||||
}
|
||||
@ -319,8 +308,8 @@ export class IterableChanges extends Pipe {
|
||||
}
|
||||
}
|
||||
|
||||
_reinsertAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
|
||||
index:int):CollectionChangeRecord {
|
||||
_reinsertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||
index: int): CollectionChangeRecord {
|
||||
if (this._unlinkedRecords !== null) {
|
||||
this._unlinkedRecords.remove(record);
|
||||
}
|
||||
@ -343,42 +332,42 @@ export class IterableChanges extends Pipe {
|
||||
return record;
|
||||
}
|
||||
|
||||
_moveAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
|
||||
index:int):CollectionChangeRecord {
|
||||
_moveAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||
index: int): CollectionChangeRecord {
|
||||
this._unlink(record);
|
||||
this._insertAfter(record, prevRecord, index);
|
||||
this._addToMoves(record, index);
|
||||
return record;
|
||||
}
|
||||
|
||||
_addAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
|
||||
index:int):CollectionChangeRecord {
|
||||
_addAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||
index: int): CollectionChangeRecord {
|
||||
this._insertAfter(record, prevRecord, index);
|
||||
|
||||
if (this._additionsTail === null) {
|
||||
// todo(vicb)
|
||||
//assert(this._additionsHead === null);
|
||||
// assert(this._additionsHead === null);
|
||||
this._additionsTail = this._additionsHead = record;
|
||||
} else {
|
||||
// todo(vicb)
|
||||
//assert(_additionsTail._nextAdded === null);
|
||||
//assert(record._nextAdded === null);
|
||||
// assert(_additionsTail._nextAdded === null);
|
||||
// assert(record._nextAdded === null);
|
||||
this._additionsTail = this._additionsTail._nextAdded = record;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
_insertAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
|
||||
index:int):CollectionChangeRecord {
|
||||
_insertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||
index: int): CollectionChangeRecord {
|
||||
// todo(vicb)
|
||||
//assert(record != prevRecord);
|
||||
//assert(record._next === null);
|
||||
//assert(record._prev === null);
|
||||
// assert(record != prevRecord);
|
||||
// assert(record._next === 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)
|
||||
//assert(next != record);
|
||||
//assert(prevRecord != record);
|
||||
// assert(next != record);
|
||||
// assert(prevRecord != record);
|
||||
record._next = next;
|
||||
record._prev = prevRecord;
|
||||
if (next === null) {
|
||||
@ -401,11 +390,11 @@ export class IterableChanges extends Pipe {
|
||||
return record;
|
||||
}
|
||||
|
||||
_remove(record:CollectionChangeRecord):CollectionChangeRecord {
|
||||
_remove(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||
return this._addToRemovals(this._unlink(record));
|
||||
}
|
||||
|
||||
_unlink(record:CollectionChangeRecord):CollectionChangeRecord {
|
||||
_unlink(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||
if (this._linkedRecords !== null) {
|
||||
this._linkedRecords.remove(record);
|
||||
}
|
||||
@ -414,8 +403,8 @@ export class IterableChanges extends Pipe {
|
||||
var next = record._next;
|
||||
|
||||
// todo(vicb)
|
||||
//assert((record._prev = null) === null);
|
||||
//assert((record._next = null) === null);
|
||||
// assert((record._prev = null) === null);
|
||||
// assert((record._next = null) === null);
|
||||
|
||||
if (prev === null) {
|
||||
this._itHead = next;
|
||||
@ -431,9 +420,9 @@ export class IterableChanges extends Pipe {
|
||||
return record;
|
||||
}
|
||||
|
||||
_addToMoves(record:CollectionChangeRecord, toIndex:int):CollectionChangeRecord {
|
||||
_addToMoves(record: CollectionChangeRecord, toIndex: int): CollectionChangeRecord {
|
||||
// todo(vicb)
|
||||
//assert(record._nextMoved === null);
|
||||
// assert(record._nextMoved === null);
|
||||
|
||||
if (record.previousIndex === toIndex) {
|
||||
return record;
|
||||
@ -441,18 +430,18 @@ export class IterableChanges extends Pipe {
|
||||
|
||||
if (this._movesTail === null) {
|
||||
// todo(vicb)
|
||||
//assert(_movesHead === null);
|
||||
// assert(_movesHead === null);
|
||||
this._movesTail = this._movesHead = record;
|
||||
} else {
|
||||
// todo(vicb)
|
||||
//assert(_movesTail._nextMoved === null);
|
||||
// assert(_movesTail._nextMoved === null);
|
||||
this._movesTail = this._movesTail._nextMoved = record;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
_addToRemovals(record:CollectionChangeRecord):CollectionChangeRecord {
|
||||
_addToRemovals(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||
if (this._unlinkedRecords === null) {
|
||||
this._unlinkedRecords = new _DuplicateMap();
|
||||
}
|
||||
@ -462,21 +451,21 @@ export class IterableChanges extends Pipe {
|
||||
|
||||
if (this._removalsTail === null) {
|
||||
// todo(vicb)
|
||||
//assert(_removalsHead === null);
|
||||
// assert(_removalsHead === null);
|
||||
this._removalsTail = this._removalsHead = record;
|
||||
record._prevRemoved = null;
|
||||
} else {
|
||||
// todo(vicb)
|
||||
//assert(_removalsTail._nextRemoved === null);
|
||||
//assert(record._nextRemoved === null);
|
||||
// assert(_removalsTail._nextRemoved === null);
|
||||
// assert(record._nextRemoved === null);
|
||||
record._prevRemoved = this._removalsTail;
|
||||
this._removalsTail = this._removalsTail._nextRemoved = record;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
toString():string {
|
||||
var record:CollectionChangeRecord;
|
||||
toString(): string {
|
||||
var record: CollectionChangeRecord;
|
||||
|
||||
var list = [];
|
||||
for (record = this._itHead; record !== null; record = record._next) {
|
||||
@ -502,10 +491,8 @@ export class IterableChanges extends Pipe {
|
||||
ListWrapper.push(removals, record);
|
||||
}
|
||||
|
||||
return "collection: " + list.join(', ') + "\n" +
|
||||
"previous: " + previous.join(', ') + "\n" +
|
||||
"additions: " + additions.join(', ') + "\n" +
|
||||
"moves: " + moves.join(', ') + "\n" +
|
||||
return "collection: " + list.join(', ') + "\n" + "previous: " + previous.join(', ') + "\n" +
|
||||
"additions: " + additions.join(', ') + "\n" + "moves: " + moves.join(', ') + "\n" +
|
||||
"removals: " + removals.join(', ') + "\n";
|
||||
}
|
||||
}
|
||||
@ -513,17 +500,20 @@ export class IterableChanges extends Pipe {
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class CollectionChangeRecord {
|
||||
currentIndex:int;
|
||||
previousIndex:int;
|
||||
export class CollectionChangeRecord {
|
||||
currentIndex: int;
|
||||
previousIndex: int;
|
||||
item;
|
||||
|
||||
_nextPrevious:CollectionChangeRecord;
|
||||
_prev:CollectionChangeRecord; _next:CollectionChangeRecord;
|
||||
_prevDup:CollectionChangeRecord; _nextDup:CollectionChangeRecord;
|
||||
_prevRemoved:CollectionChangeRecord; _nextRemoved:CollectionChangeRecord;
|
||||
_nextAdded:CollectionChangeRecord;
|
||||
_nextMoved:CollectionChangeRecord;
|
||||
_nextPrevious: CollectionChangeRecord;
|
||||
_prev: CollectionChangeRecord;
|
||||
_next: CollectionChangeRecord;
|
||||
_prevDup: CollectionChangeRecord;
|
||||
_nextDup: CollectionChangeRecord;
|
||||
_prevRemoved: CollectionChangeRecord;
|
||||
_nextRemoved: CollectionChangeRecord;
|
||||
_nextAdded: CollectionChangeRecord;
|
||||
_nextMoved: CollectionChangeRecord;
|
||||
|
||||
constructor(item) {
|
||||
this.currentIndex = null;
|
||||
@ -541,18 +531,18 @@ export class CollectionChangeRecord {
|
||||
this._nextMoved = null;
|
||||
}
|
||||
|
||||
toString():string {
|
||||
toString(): string {
|
||||
return this.previousIndex === this.currentIndex ?
|
||||
stringify(this.item) :
|
||||
stringify(this.item) + '[' + stringify(this.previousIndex) + '->' +
|
||||
stringify(this.currentIndex) + ']';
|
||||
stringify(this.item) :
|
||||
stringify(this.item) + '[' + stringify(this.previousIndex) + '->' +
|
||||
stringify(this.currentIndex) + ']';
|
||||
}
|
||||
}
|
||||
|
||||
// A linked list of CollectionChangeRecords with the same CollectionChangeRecord.item
|
||||
class _DuplicateItemRecordList {
|
||||
_head:CollectionChangeRecord;
|
||||
_tail:CollectionChangeRecord;
|
||||
_head: CollectionChangeRecord;
|
||||
_tail: CollectionChangeRecord;
|
||||
|
||||
constructor() {
|
||||
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.
|
||||
*/
|
||||
add(record:CollectionChangeRecord) {
|
||||
add(record: CollectionChangeRecord) {
|
||||
if (this._head === null) {
|
||||
this._head = this._tail = record;
|
||||
record._nextDup = null;
|
||||
record._prevDup = null;
|
||||
} else {
|
||||
// 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);
|
||||
this._tail._nextDup = record;
|
||||
record._prevDup = this._tail;
|
||||
@ -582,14 +572,14 @@ class _DuplicateItemRecordList {
|
||||
|
||||
// Returns a CollectionChangeRecord having CollectionChangeRecord.item == item and
|
||||
// CollectionChangeRecord.currentIndex >= afterIndex
|
||||
get(item, afterIndex:int):CollectionChangeRecord {
|
||||
var record:CollectionChangeRecord;
|
||||
get(item, afterIndex: int): CollectionChangeRecord {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._head; record !== null; record = record._nextDup) {
|
||||
if ((afterIndex === null || afterIndex < record.currentIndex) &&
|
||||
looseIdentical(record.item, item)) {
|
||||
return record;
|
||||
}
|
||||
looseIdentical(record.item, item)) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -598,9 +588,9 @@ class _DuplicateItemRecordList {
|
||||
*
|
||||
* Returns whether the list of duplicates is empty.
|
||||
*/
|
||||
remove(record:CollectionChangeRecord):boolean {
|
||||
remove(record: CollectionChangeRecord): boolean {
|
||||
// todo(vicb)
|
||||
//assert(() {
|
||||
// assert(() {
|
||||
// // verify that the record being removed is in the list.
|
||||
// for (CollectionChangeRecord cursor = _head; cursor != null; cursor = cursor._nextDup) {
|
||||
// if (identical(cursor, record)) return true;
|
||||
@ -608,8 +598,8 @@ class _DuplicateItemRecordList {
|
||||
// return false;
|
||||
//});
|
||||
|
||||
var prev:CollectionChangeRecord = record._prevDup;
|
||||
var next:CollectionChangeRecord = record._nextDup;
|
||||
var prev: CollectionChangeRecord = record._prevDup;
|
||||
var next: CollectionChangeRecord = record._nextDup;
|
||||
if (prev === null) {
|
||||
this._head = next;
|
||||
} else {
|
||||
@ -625,12 +615,10 @@ class _DuplicateItemRecordList {
|
||||
}
|
||||
|
||||
class _DuplicateMap {
|
||||
map:Map;
|
||||
constructor() {
|
||||
this.map = MapWrapper.create();
|
||||
}
|
||||
map: Map<any, any>;
|
||||
constructor() { this.map = MapWrapper.create(); }
|
||||
|
||||
put(record:CollectionChangeRecord) {
|
||||
put(record: CollectionChangeRecord) {
|
||||
// todo(vicb) handle corner cases
|
||||
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
|
||||
* 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 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.
|
||||
*/
|
||||
remove(record:CollectionChangeRecord):CollectionChangeRecord {
|
||||
remove(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||
var key = getMapKey(record.item);
|
||||
// todo(vicb)
|
||||
//assert(this.map.containsKey(key));
|
||||
var recordList:_DuplicateItemRecordList = MapWrapper.get(this.map, key);
|
||||
// assert(this.map.containsKey(key));
|
||||
var recordList: _DuplicateItemRecordList = MapWrapper.get(this.map, key);
|
||||
// Remove the list of duplicates when it gets empty
|
||||
if (recordList.remove(record)) {
|
||||
MapWrapper.delete(this.map, key);
|
||||
@ -673,15 +661,9 @@ class _DuplicateMap {
|
||||
return record;
|
||||
}
|
||||
|
||||
get isEmpty():boolean {
|
||||
return MapWrapper.size(this.map) === 0;
|
||||
}
|
||||
get isEmpty(): boolean { return MapWrapper.size(this.map) === 0; }
|
||||
|
||||
clear() {
|
||||
MapWrapper.clear(this.map);
|
||||
}
|
||||
clear() { MapWrapper.clear(this.map); }
|
||||
|
||||
toString():string {
|
||||
return '_DuplicateMap(' + stringify(this.map) + ')';
|
||||
}
|
||||
toString(): string { return '_DuplicateMap(' + stringify(this.map) + ')'; }
|
||||
}
|
@ -3,38 +3,36 @@ import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/
|
||||
|
||||
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
|
||||
*/
|
||||
@CONST()
|
||||
export class KeyValueChangesFactory extends PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
constructor() { super(); }
|
||||
|
||||
supports(obj):boolean {
|
||||
return KeyValueChanges.supportsObj(obj);
|
||||
}
|
||||
supports(obj): boolean { return KeyValueChanges.supportsObj(obj); }
|
||||
|
||||
create(cdRef):Pipe {
|
||||
return new KeyValueChanges();
|
||||
}
|
||||
create(cdRef): Pipe { return new KeyValueChanges(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class KeyValueChanges extends Pipe {
|
||||
_records:Map;
|
||||
|
||||
_mapHead:KVChangeRecord;
|
||||
_previousMapHead:KVChangeRecord;
|
||||
_changesHead:KVChangeRecord;
|
||||
_changesTail:KVChangeRecord;
|
||||
_additionsHead:KVChangeRecord;
|
||||
_additionsTail:KVChangeRecord;
|
||||
_removalsHead:KVChangeRecord;
|
||||
_removalsTail:KVChangeRecord;
|
||||
private _records: Map<any, any>;
|
||||
private _mapHead: KVChangeRecord;
|
||||
private _previousMapHead: KVChangeRecord;
|
||||
private _changesHead: KVChangeRecord;
|
||||
private _changesTail: KVChangeRecord;
|
||||
private _additionsHead: KVChangeRecord;
|
||||
private _additionsTail: KVChangeRecord;
|
||||
private _removalsHead: KVChangeRecord;
|
||||
private _removalsTail: KVChangeRecord;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -49,15 +47,11 @@ export class KeyValueChanges extends Pipe {
|
||||
this._removalsTail = null;
|
||||
}
|
||||
|
||||
static supportsObj(obj):boolean {
|
||||
return obj instanceof Map || isJsObject(obj);
|
||||
}
|
||||
static supportsObj(obj): boolean { return obj instanceof Map || isJsObject(obj); }
|
||||
|
||||
supports(obj):boolean {
|
||||
return KeyValueChanges.supportsObj(obj);
|
||||
}
|
||||
supports(obj): boolean { return KeyValueChanges.supportsObj(obj); }
|
||||
|
||||
transform(map){
|
||||
transform(map): any {
|
||||
if (this.check(map)) {
|
||||
return WrappedValue.wrap(this);
|
||||
} else {
|
||||
@ -65,54 +59,53 @@ export class KeyValueChanges extends Pipe {
|
||||
}
|
||||
}
|
||||
|
||||
get isDirty():boolean {
|
||||
return this._additionsHead !== null ||
|
||||
this._changesHead !== null ||
|
||||
get isDirty(): boolean {
|
||||
return this._additionsHead !== null || this._changesHead !== null ||
|
||||
this._removalsHead !== null;
|
||||
}
|
||||
|
||||
forEachItem(fn:Function) {
|
||||
var record:KVChangeRecord;
|
||||
forEachItem(fn: Function) {
|
||||
var record: KVChangeRecord;
|
||||
for (record = this._mapHead; record !== null; record = record._next) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachPreviousItem(fn:Function) {
|
||||
var record:KVChangeRecord;
|
||||
forEachPreviousItem(fn: Function) {
|
||||
var record: KVChangeRecord;
|
||||
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachChangedItem(fn:Function) {
|
||||
var record:KVChangeRecord;
|
||||
forEachChangedItem(fn: Function) {
|
||||
var record: KVChangeRecord;
|
||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachAddedItem(fn:Function){
|
||||
var record:KVChangeRecord;
|
||||
forEachAddedItem(fn: Function) {
|
||||
var record: KVChangeRecord;
|
||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachRemovedItem(fn:Function){
|
||||
var record:KVChangeRecord;
|
||||
forEachRemovedItem(fn: Function) {
|
||||
var record: KVChangeRecord;
|
||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
check(map):boolean {
|
||||
check(map): boolean {
|
||||
this._reset();
|
||||
var records = this._records;
|
||||
var oldSeqRecord:KVChangeRecord = this._mapHead;
|
||||
var lastOldSeqRecord:KVChangeRecord = null;
|
||||
var lastNewSeqRecord:KVChangeRecord = null;
|
||||
var seqChanged:boolean = false;
|
||||
var oldSeqRecord: KVChangeRecord = this._mapHead;
|
||||
var lastOldSeqRecord: KVChangeRecord = null;
|
||||
var lastNewSeqRecord: KVChangeRecord = null;
|
||||
var seqChanged: boolean = false;
|
||||
|
||||
this._forEach(map, (value, key) => {
|
||||
var newSeqRecord;
|
||||
@ -160,11 +153,9 @@ export class KeyValueChanges extends Pipe {
|
||||
|
||||
_reset() {
|
||||
if (this.isDirty) {
|
||||
var record:KVChangeRecord;
|
||||
var record: KVChangeRecord;
|
||||
// Record the state of the mapping
|
||||
for (record = this._previousMapHead = this._mapHead;
|
||||
record !== null;
|
||||
record = record._next) {
|
||||
for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) {
|
||||
record._nextPrevious = record._next;
|
||||
}
|
||||
|
||||
@ -177,7 +168,7 @@ export class KeyValueChanges extends Pipe {
|
||||
}
|
||||
|
||||
// todo(vicb) once assert is supported
|
||||
//assert(() {
|
||||
// assert(() {
|
||||
// var r = _changesHead;
|
||||
// while (r != null) {
|
||||
// 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) {
|
||||
if (lastRecord === null) {
|
||||
this._mapHead = null;
|
||||
@ -216,7 +207,7 @@ export class KeyValueChanges extends Pipe {
|
||||
}
|
||||
var nextRecord = record._next;
|
||||
// todo(vicb) assert
|
||||
//assert((() {
|
||||
// assert((() {
|
||||
// record._next = null;
|
||||
// return true;
|
||||
//}));
|
||||
@ -225,26 +216,25 @@ export class KeyValueChanges extends Pipe {
|
||||
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.currentValue = null;
|
||||
MapWrapper.delete(this._records, rec.key);
|
||||
}
|
||||
}
|
||||
|
||||
_isInRemovals(record:KVChangeRecord) {
|
||||
return record === this._removalsHead ||
|
||||
record._nextRemoved !== null ||
|
||||
_isInRemovals(record: KVChangeRecord) {
|
||||
return record === this._removalsHead || record._nextRemoved !== null ||
|
||||
record._prevRemoved !== null;
|
||||
}
|
||||
|
||||
_addToRemovals(record:KVChangeRecord) {
|
||||
_addToRemovals(record: KVChangeRecord) {
|
||||
// todo(vicb) assert
|
||||
//assert(record._next == null);
|
||||
//assert(record._nextAdded == null);
|
||||
//assert(record._nextChanged == null);
|
||||
//assert(record._nextRemoved == null);
|
||||
//assert(record._prevRemoved == null);
|
||||
// assert(record._next == null);
|
||||
// assert(record._nextAdded == null);
|
||||
// assert(record._nextChanged == null);
|
||||
// assert(record._nextRemoved == null);
|
||||
// assert(record._prevRemoved == null);
|
||||
if (this._removalsHead === null) {
|
||||
this._removalsHead = this._removalsTail = record;
|
||||
} else {
|
||||
@ -254,7 +244,7 @@ export class KeyValueChanges extends Pipe {
|
||||
}
|
||||
}
|
||||
|
||||
_removeFromSeq(prev:KVChangeRecord, record:KVChangeRecord) {
|
||||
_removeFromSeq(prev: KVChangeRecord, record: KVChangeRecord) {
|
||||
var next = record._next;
|
||||
if (prev === null) {
|
||||
this._mapHead = next;
|
||||
@ -262,17 +252,17 @@ export class KeyValueChanges extends Pipe {
|
||||
prev._next = next;
|
||||
}
|
||||
// todo(vicb) assert
|
||||
//assert((() {
|
||||
// assert((() {
|
||||
// record._next = null;
|
||||
// return true;
|
||||
//})());
|
||||
}
|
||||
|
||||
_removeFromRemovals(record:KVChangeRecord) {
|
||||
_removeFromRemovals(record: KVChangeRecord) {
|
||||
// todo(vicb) assert
|
||||
//assert(record._next == null);
|
||||
//assert(record._nextAdded == null);
|
||||
//assert(record._nextChanged == null);
|
||||
// assert(record._next == null);
|
||||
// assert(record._nextAdded == null);
|
||||
// assert(record._nextChanged == null);
|
||||
|
||||
var prev = record._prevRemoved;
|
||||
var next = record._nextRemoved;
|
||||
@ -289,13 +279,13 @@ export class KeyValueChanges extends Pipe {
|
||||
record._prevRemoved = record._nextRemoved = null;
|
||||
}
|
||||
|
||||
_addToAdditions(record:KVChangeRecord) {
|
||||
_addToAdditions(record: KVChangeRecord) {
|
||||
// todo(vicb): assert
|
||||
//assert(record._next == null);
|
||||
//assert(record._nextAdded == null);
|
||||
//assert(record._nextChanged == null);
|
||||
//assert(record._nextRemoved == null);
|
||||
//assert(record._prevRemoved == null);
|
||||
// assert(record._next == null);
|
||||
// assert(record._nextAdded == null);
|
||||
// assert(record._nextChanged == null);
|
||||
// assert(record._nextRemoved == null);
|
||||
// assert(record._prevRemoved == null);
|
||||
if (this._additionsHead === null) {
|
||||
this._additionsHead = this._additionsTail = record;
|
||||
} else {
|
||||
@ -304,12 +294,12 @@ export class KeyValueChanges extends Pipe {
|
||||
}
|
||||
}
|
||||
|
||||
_addToChanges(record:KVChangeRecord) {
|
||||
_addToChanges(record: KVChangeRecord) {
|
||||
// todo(vicb) assert
|
||||
//assert(record._nextAdded == null);
|
||||
//assert(record._nextChanged == null);
|
||||
//assert(record._nextRemoved == null);
|
||||
//assert(record._prevRemoved == null);
|
||||
// assert(record._nextAdded == null);
|
||||
// assert(record._nextChanged == null);
|
||||
// assert(record._nextRemoved == null);
|
||||
// assert(record._prevRemoved == null);
|
||||
if (this._changesHead === null) {
|
||||
this._changesHead = this._changesTail = record;
|
||||
} else {
|
||||
@ -318,13 +308,13 @@ export class KeyValueChanges extends Pipe {
|
||||
}
|
||||
}
|
||||
|
||||
toString():string {
|
||||
toString(): string {
|
||||
var items = [];
|
||||
var previous = [];
|
||||
var changes = [];
|
||||
var additions = [];
|
||||
var removals = [];
|
||||
var record:KVChangeRecord;
|
||||
var record: KVChangeRecord;
|
||||
|
||||
for (record = this._mapHead; record !== null; record = record._next) {
|
||||
ListWrapper.push(items, stringify(record));
|
||||
@ -342,14 +332,12 @@ export class KeyValueChanges extends Pipe {
|
||||
ListWrapper.push(removals, stringify(record));
|
||||
}
|
||||
|
||||
return "map: " + items.join(', ') + "\n" +
|
||||
"previous: " + previous.join(', ') + "\n" +
|
||||
"additions: " + additions.join(', ') + "\n" +
|
||||
"changes: " + changes.join(', ') + "\n" +
|
||||
return "map: " + items.join(', ') + "\n" + "previous: " + previous.join(', ') + "\n" +
|
||||
"additions: " + additions.join(', ') + "\n" + "changes: " + changes.join(', ') + "\n" +
|
||||
"removals: " + removals.join(', ') + "\n";
|
||||
}
|
||||
|
||||
_forEach(obj, fn:Function) {
|
||||
_forEach(obj, fn: Function) {
|
||||
if (obj instanceof Map) {
|
||||
MapWrapper.forEach(obj, fn);
|
||||
} else {
|
||||
@ -368,12 +356,12 @@ export class KVChangeRecord {
|
||||
previousValue;
|
||||
currentValue;
|
||||
|
||||
_nextPrevious:KVChangeRecord;
|
||||
_next:KVChangeRecord;
|
||||
_nextAdded:KVChangeRecord;
|
||||
_nextRemoved:KVChangeRecord;
|
||||
_prevRemoved:KVChangeRecord;
|
||||
_nextChanged:KVChangeRecord;
|
||||
_nextPrevious: KVChangeRecord;
|
||||
_next: KVChangeRecord;
|
||||
_nextAdded: KVChangeRecord;
|
||||
_nextRemoved: KVChangeRecord;
|
||||
_prevRemoved: KVChangeRecord;
|
||||
_nextChanged: KVChangeRecord;
|
||||
|
||||
constructor(key) {
|
||||
this.key = key;
|
||||
@ -388,10 +376,10 @@ export class KVChangeRecord {
|
||||
this._nextChanged = null;
|
||||
}
|
||||
|
||||
toString():string {
|
||||
toString(): string {
|
||||
return looseIdentical(this.previousValue, this.currentValue) ?
|
||||
stringify(this.key) :
|
||||
(stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
|
||||
stringify(this.currentValue) + ']');
|
||||
stringify(this.key) :
|
||||
(stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
43
modules/angular2/src/change_detection/pipes/null_pipe.ts
Normal file
43
modules/angular2/src/change_detection/pipes/null_pipe.ts
Normal file
@ -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';
|
||||
|
||||
// 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.
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class WrappedValue {
|
||||
wrapped:any;
|
||||
constructor(public wrapped: any) {}
|
||||
|
||||
constructor(wrapped:any) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
static wrap(value:any):WrappedValue {
|
||||
static wrap(value: any): WrappedValue {
|
||||
var w = _wrappedValues[_wrappedIndex++ % 5];
|
||||
w.wrapped = value;
|
||||
return w;
|
||||
@ -53,26 +55,25 @@ var _wrappedIndex = 0;
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class Pipe {
|
||||
supports(obj):boolean {return false;}
|
||||
supports(obj): boolean { return false; }
|
||||
onDestroy() {}
|
||||
transform(value:any):any {return null;}
|
||||
transform(value: any): any { return null; }
|
||||
}
|
||||
|
||||
@ABSTRACT()
|
||||
// TODO: vsavkin: make it an interface
|
||||
@CONST()
|
||||
export class PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
supports(obs): boolean {
|
||||
_abstract();
|
||||
return false;
|
||||
}
|
||||
|
||||
supports(obs):boolean {
|
||||
return _abstract();
|
||||
}
|
||||
|
||||
create(cdRef):Pipe {
|
||||
return _abstract();
|
||||
create(cdRef): Pipe {
|
||||
_abstract();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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 {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||
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';
|
||||
|
||||
// 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()
|
||||
export class PipeRegistry {
|
||||
config;
|
||||
constructor(public config) {}
|
||||
|
||||
constructor(config){
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
get(type:string, obj, cdRef:ChangeDetectorRef):Pipe {
|
||||
get(type: string, obj, cdRef: ChangeDetectorRef): Pipe {
|
||||
var listOfConfigs = this.config[type];
|
||||
if (isBlank(listOfConfigs)) {
|
||||
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
||||
}
|
||||
|
||||
var matchingConfig = ListWrapper.find(listOfConfigs,
|
||||
(pipeConfig) => pipeConfig.supports(obj));
|
||||
var matchingConfig = ListWrapper.find(listOfConfigs, (pipeConfig) => pipeConfig.supports(obj));
|
||||
|
||||
if (isBlank(matchingConfig)) {
|
||||
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`);
|
||||
}
|
||||
}
|
406
modules/angular2/src/change_detection/proto_change_detector.ts
Normal file
406
modules/angular2/src/change_detection/proto_change_detector.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
32
modules/angular2/src/change_detection/proto_record.ts
Normal file
32
modules/angular2/src/change_detection/proto_record.ts
Normal file
@ -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> {
|
||||
var params = reflector.parameters(typeOrFunc);
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
library angular2.di.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
|
||||
*/
|
||||
export class Injector {
|
||||
_bindings: List<any>;
|
||||
_instances: List<any>;
|
||||
_parent: Injector;
|
||||
_defaultBindings: boolean;
|
||||
_asyncStrategy: _AsyncInjectorStrategy;
|
||||
_syncStrategy: _SyncInjectorStrategy;
|
||||
private _bindings: List<any>;
|
||||
private _instances: List<any>;
|
||||
private _parent: Injector;
|
||||
private _defaultBindings: boolean;
|
||||
private _asyncStrategy: _AsyncInjectorStrategy;
|
||||
private _syncStrategy: _SyncInjectorStrategy;
|
||||
|
||||
/**
|
||||
* Turns a list of binding definitions into an internal resolved list of resolved bindings.
|
||||
|
@ -52,7 +52,7 @@ export class PromiseWrapper {
|
||||
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ export class ObservableWrapper {
|
||||
|
||||
// TODO: vsavkin change to interface
|
||||
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) {
|
||||
return array.indexOf(value, startIndex);
|
||||
}
|
||||
static reduce<T>(list: List<T>,
|
||||
fn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T,
|
||||
init: T) {
|
||||
static reduce<T, E>(list: List<T>,
|
||||
fn: (accumValue: E, currentValue: T, currentIndex: number, array: T[]) => E,
|
||||
init: E) {
|
||||
return list.reduce(fn, init);
|
||||
}
|
||||
static filter(array, pred: Function) { return array.filter(pred); }
|
||||
|
@ -5,6 +5,9 @@ import 'types.dart';
|
||||
import 'dart:mirrors';
|
||||
|
||||
class ReflectionCapabilities {
|
||||
ReflectionCapabilities([metadataReader]) {
|
||||
}
|
||||
|
||||
Function factory(Type type) {
|
||||
ClassMirror classMirror = reflectType(type);
|
||||
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];
|
||||
|
@ -8,37 +8,39 @@ import {GetterFn, SetterFn, MethodFn} from './types';
|
||||
export var __esModule = true;
|
||||
|
||||
export class ReflectionCapabilities {
|
||||
factory(type: Type): Function {
|
||||
switch (type.length) {
|
||||
private _reflect: any;
|
||||
|
||||
constructor(reflect?: any) { this._reflect = isPresent(reflect) ? reflect : global.Reflect; }
|
||||
|
||||
factory(t: Type): Function {
|
||||
switch (t.length) {
|
||||
case 0:
|
||||
return function() { return new type(); };
|
||||
return function() { return new t(); };
|
||||
case 1:
|
||||
return function(a1) { return new type(a1); };
|
||||
return function(a1) { return new t(a1); };
|
||||
case 2:
|
||||
return function(a1, a2) { return new type(a1, a2); };
|
||||
return function(a1, a2) { return new t(a1, a2); };
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
return function(a1, a2, a3, a4, a5, a6, a7) {
|
||||
return new type(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); };
|
||||
case 8:
|
||||
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:
|
||||
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:
|
||||
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)) {
|
||||
return typeOfFunc.parameters;
|
||||
}
|
||||
if (isPresent(global.Reflect) && isPresent(global.Reflect.getMetadata)) {
|
||||
var paramAnnotations = global.Reflect.getMetadata('parameters', typeOfFunc);
|
||||
var paramTypes = global.Reflect.getMetadata('design:paramtypes', typeOfFunc);
|
||||
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
||||
var paramAnnotations = this._reflect.getMetadata('parameters', typeOfFunc);
|
||||
var paramTypes = this._reflect.getMetadata('design:paramtypes', typeOfFunc);
|
||||
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
|
||||
return this._zipTypesAndAnnotaions(paramTypes, paramAnnotations);
|
||||
}
|
||||
@ -83,8 +85,8 @@ export class ReflectionCapabilities {
|
||||
if (isPresent(typeOfFunc.annotations)) {
|
||||
return typeOfFunc.annotations;
|
||||
}
|
||||
if (isPresent(global.Reflect) && isPresent(global.Reflect.getMetadata)) {
|
||||
var annotations = global.Reflect.getMetadata('annotations', typeOfFunc);
|
||||
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
||||
var annotations = this._reflect.getMetadata('annotations', typeOfFunc);
|
||||
if (isPresent(annotations)) return annotations;
|
||||
}
|
||||
return [];
|
||||
|
@ -8,17 +8,6 @@ export function main() {
|
||||
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) {
|
||||
expect(annotations[0]).toBeAnInstanceOf(ClassDec1);
|
||||
expect(annotations[1]).toBeAnInstanceOf(ClassDec2);
|
||||
@ -54,23 +43,34 @@ export function main() {
|
||||
expect(rc.parameters(TestClassTypesOnly)).toEqual([[P1], [P2]]);
|
||||
});
|
||||
|
||||
|
||||
if (!IS_DARTIUM) {
|
||||
// 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
|
||||
// access will be mocked, and the tests below will be direct.
|
||||
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));
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
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]]);
|
||||
});
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ module.exports = function makeNodeTree(destinationPath) {
|
||||
|
||||
var typescriptTree = compileWithTypescript(modulesTree, {
|
||||
allowNonTsExtensions: false,
|
||||
emitDecoratorMetadata: true,
|
||||
declaration: true,
|
||||
mapRoot: '', /* force sourcemaps to use relative path */
|
||||
module: 'commonjs',
|
||||
|
@ -5,6 +5,7 @@ var minijasminenode2 = require('minijasminenode2');
|
||||
var path = require('path');
|
||||
// Require traceur to exposes $traceurRuntime on global context so that CJS files can run
|
||||
require('traceur/bin/traceur-runtime.js');
|
||||
require('reflect-metadata/Reflect');
|
||||
|
||||
glob(process.argv[2], function (error, specFiles) {
|
||||
minijasminenode2.executeSpecs({
|
||||
|
Loading…
x
Reference in New Issue
Block a user