refactor(compiler): remove unused code

BREAKING CHANGE:
- Removes `ChangeDetection`, use a binding for `ChangeDetectorGenConfig` instead
  to configure change detection.
- `RenderElementRef.renderBoundElementIndex` was renamed to `RenderElementRef.boundElementIndex`.
- Removes `ViewLoader`, use `XHRImpl` instead.
This commit is contained in:
Tobias Bosch
2015-10-01 20:47:49 -07:00
parent b154f1a44f
commit d21c7bdf90
81 changed files with 140 additions and 7365 deletions

View File

@ -3,11 +3,9 @@ import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {
ChangeDetection,
DirectiveIndex,
BindingRecord,
DirectiveRecord,
ProtoChangeDetector,
ChangeDetectionStrategy,
ChangeDetectorDefinition,
ChangeDetectorGenConfig,

View File

@ -12,8 +12,6 @@ import {
import {BrowserDomAdapter} from 'angular2/src/core/dom/browser_adapter';
import {BrowserGetTestability} from 'angular2/src/core/testability/browser_testability';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {ViewLoader} from 'angular2/src/core/render/dom/compiler/view_loader';
import {StyleInliner} from 'angular2/src/core/render/dom/compiler/style_inliner';
import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/core/facade/async';
import {XHR} from 'angular2/src/core/render/xhr';
import {XHRImpl} from 'angular2/src/core/render/xhr_impl';
@ -31,15 +29,8 @@ import {
DynamicComponentLoader
} from 'angular2/src/core/compiler/dynamic_component_loader';
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
import {Renderer, RenderCompiler} from 'angular2/src/core/render/api';
import {
DomRenderer,
DOCUMENT,
DefaultDomCompiler,
APP_ID_RANDOM_BINDING,
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
TemplateCloner
} from 'angular2/src/core/render/render';
import {Renderer} from 'angular2/src/core/render/api';
import {DomRenderer, DOCUMENT, APP_ID_RANDOM_BINDING} from 'angular2/src/core/render/render';
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
import {
DomElementSchemaRegistry
@ -72,17 +63,11 @@ export function applicationDomBindings(): Array<Type | Binding | any[]> {
DomRenderer,
bind(Renderer).toAlias(DomRenderer),
APP_ID_RANDOM_BINDING,
TemplateCloner,
bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(20),
DefaultDomCompiler,
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
bind(RenderCompiler).toAlias(DefaultDomCompiler),
DomSharedStylesHost,
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
ViewLoader,
EXCEPTION_BINDING,
bind(XHR).toValue(new XHRImpl()),
StyleInliner,
Testability,
AnchorBasedAppRootUrl,
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),

View File

@ -21,10 +21,6 @@ import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
import {
Parser,
Lexer,
ChangeDetection,
DynamicChangeDetection,
JitChangeDetection,
PreGeneratedChangeDetection,
IterableDiffers,
defaultIterableDiffers,
KeyValueDiffers,
@ -39,9 +35,7 @@ import {DEFAULT_PIPES} from 'angular2/src/core/pipes';
import {ViewResolver} from './compiler/view_resolver';
import {DirectiveResolver} from './compiler/directive_resolver';
import {PipeResolver} from './compiler/pipe_resolver';
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {
APP_ID_RANDOM_BINDING,
} from 'angular2/src/core/render/render';
@ -90,12 +84,6 @@ function _componentBindings(appComponentType: Type): Array<Type | Binding | any[
* application, regardless of whether it runs on the UI thread or in a web worker.
*/
export function applicationCommonBindings(): Array<Type | Binding | any[]> {
var bestChangeDetection = new DynamicChangeDetection();
if (PreGeneratedChangeDetection.isSupported()) {
bestChangeDetection = new PreGeneratedChangeDetection();
} else if (JitChangeDetection.isSupported()) {
bestChangeDetection = new JitChangeDetection();
}
return [
Compiler,
APP_ID_RANDOM_BINDING,
@ -109,12 +97,9 @@ export function applicationCommonBindings(): Array<Type | Binding | any[]> {
DEFAULT_PIPES,
bind(IterableDiffers).toValue(defaultIterableDiffers),
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
bind(ChangeDetection).toValue(bestChangeDetection),
DirectiveResolver,
UrlResolver,
StyleUrlResolver,
PipeResolver,
ComponentUrlMapper,
Parser,
Lexer,
DynamicComponentLoader,

View File

@ -1,19 +1,8 @@
import {JitProtoChangeDetector} from './jit_proto_change_detector';
import {PregenProtoChangeDetector} from './pregen_proto_change_detector';
import {DynamicProtoChangeDetector} from './proto_change_detector';
import {IterableDiffers, IterableDifferFactory} from './differs/iterable_differs';
import {DefaultIterableDifferFactory} from './differs/default_iterable_differ';
import {KeyValueDiffers, KeyValueDifferFactory} from './differs/keyvalue_differs';
import {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ';
import {
ChangeDetection,
ProtoChangeDetector,
ChangeDetectorDefinition,
ChangeDetectorGenConfig
} from './interfaces';
import {Injector, Inject, Injectable, OpaqueToken, Optional, Binding} from 'angular2/src/core/di';
import {StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {CONST, CONST_EXPR, isPresent, assertionsEnabled} from 'angular2/src/core/facade/lang';
import {CONST, CONST_EXPR, isPresent} from 'angular2/src/core/facade/lang';
export {
ASTWithSource,
@ -37,13 +26,13 @@ export {
ProtoChangeDetector,
ChangeDetector,
ChangeDispatcher,
ChangeDetection,
ChangeDetectorDefinition,
DebugContext,
ChangeDetectorGenConfig
} from './interfaces';
export {ChangeDetectionStrategy, CHANGE_DECTION_STRATEGY_VALUES} from './constants';
export {DynamicProtoChangeDetector} from './proto_change_detector';
export {JitProtoChangeDetector} from './jit_proto_change_detector';
export {BindingRecord, BindingTarget} from './binding_record';
export {DirectiveIndex, DirectiveRecord} from './directive_record';
export {DynamicChangeDetector} from './dynamic_change_detector';
@ -68,98 +57,3 @@ export const iterableDiff: IterableDifferFactory[] =
export const defaultIterableDiffers = CONST_EXPR(new IterableDiffers(iterableDiff));
export const defaultKeyValueDiffers = CONST_EXPR(new KeyValueDiffers(keyValDiff));
/**
* Map from {@link ChangeDetectorDefinition#id} to a factory method which takes a
* {@link Pipes} and a {@link ChangeDetectorDefinition} and generates a
* {@link ProtoChangeDetector} associated with the definition.
*/
// TODO(kegluneq): Use PregenProtoChangeDetectorFactory rather than Function once possible in
// dart2js. See https://github.com/dart-lang/sdk/issues/23630 for details.
export var preGeneratedProtoDetectors: StringMap<string, Function> = {};
/**
* Implements change detection using a map of pregenerated proto detectors.
*/
@Injectable()
export class PreGeneratedChangeDetection extends ChangeDetection {
_dynamicChangeDetection: ChangeDetection;
_protoChangeDetectorFactories: StringMap<string, Function>;
_genConfig: ChangeDetectorGenConfig;
constructor(config?: ChangeDetectorGenConfig,
protoChangeDetectorsForTest?: StringMap<string, Function>) {
super();
this._dynamicChangeDetection = new DynamicChangeDetection();
this._protoChangeDetectorFactories = isPresent(protoChangeDetectorsForTest) ?
protoChangeDetectorsForTest :
preGeneratedProtoDetectors;
this._genConfig =
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), false, false);
}
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
if (StringMapWrapper.contains(this._protoChangeDetectorFactories, id)) {
return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(definition);
}
return this._dynamicChangeDetection.getProtoChangeDetector(id, definition);
}
get genConfig(): ChangeDetectorGenConfig { return this._genConfig; }
get generateDetectors(): boolean { return true; }
}
/**
* Implements change detection that does not require `eval()`.
*
* This is slower than {@link JitChangeDetection}.
*/
@Injectable()
export class DynamicChangeDetection extends ChangeDetection {
_genConfig: ChangeDetectorGenConfig;
constructor(config?: ChangeDetectorGenConfig) {
super();
this._genConfig =
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), false, false);
}
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
return new DynamicProtoChangeDetector(definition);
}
get genConfig(): ChangeDetectorGenConfig { return this._genConfig; }
get generateDetectors(): boolean { return true; }
}
/**
* Implements faster change detection by generating source code.
*
* This requires `eval()`. For change detection that does not require `eval()`, see
* {@link DynamicChangeDetection} and {@link PreGeneratedChangeDetection}.
*/
@Injectable()
export class JitChangeDetection extends ChangeDetection {
_genConfig: ChangeDetectorGenConfig;
constructor(config?: ChangeDetectorGenConfig) {
super();
this._genConfig =
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), false, true);
}
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
return new JitProtoChangeDetector(definition);
}
get genConfig(): ChangeDetectorGenConfig { return this._genConfig; }
get generateDetectors(): boolean { return true; }
}

View File

@ -1,46 +1,9 @@
import {CONST} from 'angular2/src/core/facade/lang';
import {Locals} from './parser/locals';
import {BindingTarget, BindingRecord} from './binding_record';
import {DirectiveIndex, DirectiveRecord} from './directive_record';
import {ChangeDetectionStrategy} from './constants';
import {ChangeDetectorRef} from './change_detector_ref';
/**
* Interface used by Angular to control the change detection strategy for an application.
*
* Angular implements the following change detection strategies by default:
*
* - {@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
* [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
* `JitChangeDetection` strategy at compile time.
*
*
* See: {@link DynamicChangeDetection}, {@link JitChangeDetection},
* {@link PreGeneratedChangeDetection}
*
* # Example
* ```javascript
* bootstrap(MyApp, [bind(ChangeDetection).toValue(new DynamicChangeDetection())]);
* ```
*/
@CONST()
export class ChangeDetection {
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
return null;
}
get generateDetectors(): boolean { return null; }
get genConfig(): ChangeDetectorGenConfig { return null; }
}
export class DebugContext {
constructor(public element: any, public componentElement: any, public directive: any,
public context: any, public locals: any, public injector: any) {}

View File

@ -9,7 +9,6 @@ export {
OnInit,
DoCheck
} from './compiler/interfaces';
export {ComponentUrlMapper} from './compiler/component_url_mapper';
export {DirectiveResolver} from './compiler/directive_resolver';
export {Compiler} from './compiler/compiler';
export {AppViewManager} from './compiler/view_manager';

View File

@ -48,7 +48,6 @@ import {
import {QueryList} from './query_list';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {SetterFn} from 'angular2/src/core/reflection/types';
import {RenderDirectiveMetadata} from 'angular2/src/core/render/api';
import {EventConfig} from 'angular2/src/core/render/event_config';
import {PipeBinding} from '../pipes/pipe_binding';
@ -128,17 +127,17 @@ export class DirectiveDependency extends Dependency {
}
export class DirectiveBinding extends ResolvedBinding {
constructor(key: Key, factory: Function, deps: Dependency[],
public metadata: RenderDirectiveMetadata,
public callOnDestroy: boolean;
constructor(key: Key, factory: Function, deps: Dependency[], public metadata: DirectiveMetadata,
public bindings: Array<Type | Binding | any[]>,
public viewBindings: Array<Type | Binding | any[]>) {
super(key, [new ResolvedFactory(factory, deps)], false);
this.callOnDestroy = hasLifecycleHook(LifecycleHooks.OnDestroy, key.token);
}
get displayName(): string { return this.key.displayName; }
get callOnDestroy(): boolean { return this.metadata.callOnDestroy; }
get queries(): QueryMetadataWithSetter[] {
if (isBlank(this.metadata.queries)) return [];
@ -163,47 +162,11 @@ export class DirectiveBinding extends ResolvedBinding {
var rb = resolveBinding(binding);
var rf = rb.resolvedFactories[0];
var deps = rf.dependencies.map(DirectiveDependency.createFrom);
var token = binding.token;
var metadata = RenderDirectiveMetadata.create({
id: stringify(binding.token),
type: meta instanceof ComponentMetadata ? RenderDirectiveMetadata.COMPONENT_TYPE :
RenderDirectiveMetadata.DIRECTIVE_TYPE,
selector: meta.selector,
compileChildren: true,
outputs: meta.outputs,
host: isPresent(meta.host) ? MapWrapper.createFromStringMap(meta.host) : null,
inputs: meta.inputs,
readAttributes: DirectiveBinding._readAttributes(<any>deps),
queries: meta.queries,
callOnDestroy: hasLifecycleHook(LifecycleHooks.OnDestroy, token),
callOnChanges: hasLifecycleHook(LifecycleHooks.OnChanges, token),
callDoCheck: hasLifecycleHook(LifecycleHooks.DoCheck, token),
callOnInit: hasLifecycleHook(LifecycleHooks.OnInit, token),
callAfterContentInit: hasLifecycleHook(LifecycleHooks.AfterContentInit, token),
callAfterContentChecked: hasLifecycleHook(LifecycleHooks.AfterContentChecked, token),
callAfterViewInit: hasLifecycleHook(LifecycleHooks.AfterViewInit, token),
callAfterViewChecked: hasLifecycleHook(LifecycleHooks.AfterViewChecked, token),
changeDetection: meta instanceof ComponentMetadata ? meta.changeDetection : null,
exportAs: meta.exportAs
});
var bindings = isPresent(meta.bindings) ? meta.bindings : [];
var viewBindigs =
meta instanceof ComponentMetadata && isPresent(meta.viewBindings) ? meta.viewBindings : [];
return new DirectiveBinding(rb.key, rf.factory, deps, metadata, bindings, viewBindigs);
}
static _readAttributes(deps: DirectiveDependency[]): string[] {
var readAttributes = [];
deps.forEach(dep => {
if (isPresent(dep.attributeName)) {
readAttributes.push(dep.attributeName);
}
});
return readAttributes;
return new DirectiveBinding(rb.key, rf.factory, deps, meta, bindings, viewBindigs);
}
static createFromType(type: Type, annotation: DirectiveMetadata): DirectiveBinding {
@ -502,7 +465,7 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) {
// We provide the component's view change detector to components and
// the surrounding component's change detector to directives.
if (dirBin.metadata.type === RenderDirectiveMetadata.COMPONENT_TYPE) {
if (dirBin.metadata instanceof ComponentMetadata) {
var componentView = this._preBuiltObjects.view.getNestedView(
this._preBuiltObjects.elementRef.boundElementIndex);
return componentView.changeDetector.ref;

View File

@ -29,24 +29,12 @@ export class ElementRef implements RenderElementRef {
* This is used internally by the Angular framework to locate elements.
*/
boundElementIndex: number;
/**
* @private
*
* TODO(tbosch): remove this when the new compiler lands
* Index of the element inside the `RenderViewRef`.
*
* This is used internally by the Angular framework to locate elements.
*/
renderBoundElementIndex: number;
/**
* @private
*/
constructor(parentView: ViewRef, boundElementIndex: number, private _renderer: Renderer) {
this.parentView = parentView;
this.boundElementIndex = boundElementIndex;
this.renderBoundElementIndex = boundElementIndex;
}
/**

View File

@ -1,28 +1,7 @@
import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
import {ListWrapper} from 'angular2/src/core/facade/collection';
import {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/core/facade/lang';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {
ChangeDetection,
DirectiveIndex,
BindingRecord,
DirectiveRecord,
ProtoChangeDetector,
ChangeDetectionStrategy,
ChangeDetectorDefinition,
ChangeDetectorGenConfig,
ASTWithSource
} from 'angular2/src/core/change_detection/change_detection';
import {
RenderDirectiveMetadata,
RenderElementBinder,
PropertyBindingType,
DirectiveBinder,
ProtoViewDto,
ViewType,
RenderProtoViewRef
} from 'angular2/src/core/render/api';
import {ViewType, RenderProtoViewRef} from 'angular2/src/core/render/api';
import {Injectable, Binding, resolveForwardRef, Inject} from 'angular2/src/core/di';
@ -344,272 +323,3 @@ function _flattenList(tree: any[], out: Array<Type | Binding | any[]>): void {
}
}
}
export class BindingRecordsCreator {
_directiveRecordsMap: Map<number, DirectiveRecord> = new Map<number, DirectiveRecord>();
getEventBindingRecords(elementBinders: RenderElementBinder[],
allDirectiveMetadatas: RenderDirectiveMetadata[]): BindingRecord[] {
var res = [];
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length;
boundElementIndex++) {
var renderElementBinder = elementBinders[boundElementIndex];
this._createTemplateEventRecords(res, renderElementBinder, boundElementIndex);
this._createHostEventRecords(res, renderElementBinder, allDirectiveMetadatas,
boundElementIndex);
}
return res;
}
private _createTemplateEventRecords(res: BindingRecord[],
renderElementBinder: RenderElementBinder,
boundElementIndex: number): void {
renderElementBinder.eventBindings.forEach(eb => {
res.push(BindingRecord.createForEvent(eb.source, eb.fullName, boundElementIndex));
});
}
private _createHostEventRecords(res: BindingRecord[], renderElementBinder: RenderElementBinder,
allDirectiveMetadatas: RenderDirectiveMetadata[],
boundElementIndex: number): void {
for (var i = 0; i < renderElementBinder.directives.length; ++i) {
var dir = renderElementBinder.directives[i];
var directiveMetadata = allDirectiveMetadatas[dir.directiveIndex];
var dirRecord = this._getDirectiveRecord(boundElementIndex, i, directiveMetadata);
dir.eventBindings.forEach(heb => {
res.push(BindingRecord.createForHostEvent(heb.source, heb.fullName, dirRecord));
});
}
}
getPropertyBindingRecords(textBindings: ASTWithSource[], elementBinders: RenderElementBinder[],
allDirectiveMetadatas: RenderDirectiveMetadata[]): BindingRecord[] {
var bindings = [];
this._createTextNodeRecords(bindings, textBindings);
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length;
boundElementIndex++) {
var renderElementBinder = elementBinders[boundElementIndex];
this._createElementPropertyRecords(bindings, boundElementIndex, renderElementBinder);
this._createDirectiveRecords(bindings, boundElementIndex, renderElementBinder.directives,
allDirectiveMetadatas);
}
return bindings;
}
getDirectiveRecords(elementBinders: RenderElementBinder[],
allDirectiveMetadatas: RenderDirectiveMetadata[]): DirectiveRecord[] {
var directiveRecords = [];
for (var elementIndex = 0; elementIndex < elementBinders.length; ++elementIndex) {
var dirs = elementBinders[elementIndex].directives;
for (var dirIndex = 0; dirIndex < dirs.length; ++dirIndex) {
directiveRecords.push(this._getDirectiveRecord(
elementIndex, dirIndex, allDirectiveMetadatas[dirs[dirIndex].directiveIndex]));
}
}
return directiveRecords;
}
_createTextNodeRecords(bindings: BindingRecord[], textBindings: ASTWithSource[]) {
for (var i = 0; i < textBindings.length; i++) {
bindings.push(BindingRecord.createForTextNode(textBindings[i], i));
}
}
_createElementPropertyRecords(bindings: BindingRecord[], boundElementIndex: number,
renderElementBinder: RenderElementBinder) {
ListWrapper.forEach(renderElementBinder.propertyBindings, (binding) => {
if (binding.type === PropertyBindingType.PROPERTY) {
bindings.push(BindingRecord.createForElementProperty(binding.astWithSource,
boundElementIndex, binding.property));
} else if (binding.type === PropertyBindingType.ATTRIBUTE) {
bindings.push(BindingRecord.createForElementAttribute(binding.astWithSource,
boundElementIndex, binding.property));
} else if (binding.type === PropertyBindingType.CLASS) {
bindings.push(BindingRecord.createForElementClass(binding.astWithSource, boundElementIndex,
binding.property));
} else if (binding.type === PropertyBindingType.STYLE) {
bindings.push(BindingRecord.createForElementStyle(binding.astWithSource, boundElementIndex,
binding.property, binding.unit));
}
});
}
_createDirectiveRecords(bindings: BindingRecord[], boundElementIndex: number,
directiveBinders: DirectiveBinder[],
allDirectiveMetadatas: RenderDirectiveMetadata[]) {
for (var i = 0; i < directiveBinders.length; i++) {
var directiveBinder = directiveBinders[i];
var directiveMetadata = allDirectiveMetadatas[directiveBinder.directiveIndex];
var directiveRecord = this._getDirectiveRecord(boundElementIndex, i, directiveMetadata);
// directive properties
MapWrapper.forEach(directiveBinder.propertyBindings, (astWithSource, propertyName) => {
// TODO: these setters should eventually be created by change detection, to make
// it monomorphic!
var setter = reflector.setter(propertyName);
bindings.push(
BindingRecord.createForDirective(astWithSource, propertyName, setter, directiveRecord));
});
if (directiveRecord.callOnChanges) {
bindings.push(BindingRecord.createDirectiveOnChanges(directiveRecord));
}
if (directiveRecord.callOnInit) {
bindings.push(BindingRecord.createDirectiveOnInit(directiveRecord));
}
if (directiveRecord.callDoCheck) {
bindings.push(BindingRecord.createDirectiveDoCheck(directiveRecord));
}
}
for (var i = 0; i < directiveBinders.length; i++) {
var directiveBinder = directiveBinders[i];
// host properties
ListWrapper.forEach(directiveBinder.hostPropertyBindings, (binding) => {
var dirIndex = new DirectiveIndex(boundElementIndex, i);
if (binding.type === PropertyBindingType.PROPERTY) {
bindings.push(BindingRecord.createForHostProperty(dirIndex, binding.astWithSource,
binding.property));
} else if (binding.type === PropertyBindingType.ATTRIBUTE) {
bindings.push(BindingRecord.createForHostAttribute(dirIndex, binding.astWithSource,
binding.property));
} else if (binding.type === PropertyBindingType.CLASS) {
bindings.push(
BindingRecord.createForHostClass(dirIndex, binding.astWithSource, binding.property));
} else if (binding.type === PropertyBindingType.STYLE) {
bindings.push(BindingRecord.createForHostStyle(dirIndex, binding.astWithSource,
binding.property, binding.unit));
}
});
}
}
_getDirectiveRecord(boundElementIndex: number, directiveIndex: number,
directiveMetadata: RenderDirectiveMetadata): DirectiveRecord {
var id = boundElementIndex * 100 + directiveIndex;
if (!this._directiveRecordsMap.has(id)) {
this._directiveRecordsMap.set(
id, new DirectiveRecord({
directiveIndex: new DirectiveIndex(boundElementIndex, directiveIndex),
callAfterContentInit: directiveMetadata.callAfterContentInit,
callAfterContentChecked: directiveMetadata.callAfterContentChecked,
callAfterViewInit: directiveMetadata.callAfterViewInit,
callAfterViewChecked: directiveMetadata.callAfterViewChecked,
callOnChanges: directiveMetadata.callOnChanges,
callDoCheck: directiveMetadata.callDoCheck,
callOnInit: directiveMetadata.callOnInit,
changeDetection: directiveMetadata.changeDetection
}));
}
return this._directiveRecordsMap.get(id);
}
}
/**
* Returns the data needed to create ChangeDetectors
* for the given ProtoView and all nested ProtoViews.
*/
export function getChangeDetectorDefinitions(
hostComponentMetadata: RenderDirectiveMetadata, rootRenderProtoView: ProtoViewDto,
allRenderDirectiveMetadata: RenderDirectiveMetadata[], genConfig: ChangeDetectorGenConfig):
ChangeDetectorDefinition[] {
var nestedPvsWithIndex = _collectNestedProtoViews(rootRenderProtoView);
var nestedPvVariableNames = _collectNestedProtoViewsVariableNames(nestedPvsWithIndex);
return _getChangeDetectorDefinitions(hostComponentMetadata, nestedPvsWithIndex,
nestedPvVariableNames, allRenderDirectiveMetadata,
genConfig);
}
function _collectNestedProtoViews(
renderProtoView: ProtoViewDto, parentIndex: number = null, boundElementIndex = null,
result: RenderProtoViewWithIndex[] = null): RenderProtoViewWithIndex[] {
if (isBlank(result)) {
result = [];
}
// reserve the place in the array
result.push(
new RenderProtoViewWithIndex(renderProtoView, result.length, parentIndex, boundElementIndex));
var currentIndex = result.length - 1;
var childBoundElementIndex = 0;
ListWrapper.forEach(renderProtoView.elementBinders, (elementBinder) => {
if (isPresent(elementBinder.nestedProtoView)) {
_collectNestedProtoViews(elementBinder.nestedProtoView, currentIndex, childBoundElementIndex,
result);
}
childBoundElementIndex++;
});
return result;
}
function _getChangeDetectorDefinitions(
hostComponentMetadata: RenderDirectiveMetadata, nestedPvsWithIndex: RenderProtoViewWithIndex[],
nestedPvVariableNames: string[][], allRenderDirectiveMetadata: RenderDirectiveMetadata[],
genConfig: ChangeDetectorGenConfig): ChangeDetectorDefinition[] {
return ListWrapper.map(nestedPvsWithIndex, (pvWithIndex) => {
var elementBinders = pvWithIndex.renderProtoView.elementBinders;
var bindingRecordsCreator = new BindingRecordsCreator();
var propBindingRecords = bindingRecordsCreator.getPropertyBindingRecords(
pvWithIndex.renderProtoView.textBindings, elementBinders, allRenderDirectiveMetadata);
var eventBindingRecords =
bindingRecordsCreator.getEventBindingRecords(elementBinders, allRenderDirectiveMetadata);
var directiveRecords =
bindingRecordsCreator.getDirectiveRecords(elementBinders, allRenderDirectiveMetadata);
var strategyName = ChangeDetectionStrategy.Default;
if (pvWithIndex.renderProtoView.type === ViewType.COMPONENT) {
strategyName = hostComponentMetadata.changeDetection;
}
var id = _protoViewId(hostComponentMetadata, pvWithIndex);
var variableNames = nestedPvVariableNames[pvWithIndex.index];
return new ChangeDetectorDefinition(id, strategyName, variableNames, propBindingRecords,
eventBindingRecords, directiveRecords, genConfig);
});
}
function _protoViewId(hostComponentMetadata: RenderDirectiveMetadata,
pvWithIndex: RenderProtoViewWithIndex): string {
var typeString;
if (pvWithIndex.renderProtoView.type === ViewType.COMPONENT) {
typeString = 'comp';
} else if (pvWithIndex.renderProtoView.type === ViewType.HOST) {
typeString = 'host';
} else {
typeString = 'embedded';
}
return `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`;
}
function _collectNestedProtoViewsVariableNames(nestedPvsWithIndex: RenderProtoViewWithIndex[]):
string[][] {
var nestedPvVariableNames = ListWrapper.createFixedSize(nestedPvsWithIndex.length);
ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex) => {
var parentVariableNames =
isPresent(pvWithIndex.parentIndex) ? nestedPvVariableNames[pvWithIndex.parentIndex] : null;
nestedPvVariableNames[pvWithIndex.index] =
_createVariableNames(parentVariableNames, pvWithIndex.renderProtoView);
});
return nestedPvVariableNames;
}
function _createVariableNames(parentVariableNames: string[], renderProtoView): string[] {
var res = isBlank(parentVariableNames) ? <string[]>[] : ListWrapper.clone(parentVariableNames);
MapWrapper.forEach(renderProtoView.variableBindings,
(mappedName, varName) => { res.push(mappedName); });
ListWrapper.forEach(renderProtoView.elementBinders, binder => {
MapWrapper.forEach(binder.variableBindings,
(mappedName: string, varName: string) => { res.push(mappedName); });
});
return res;
}
class RenderProtoViewWithIndex {
constructor(public renderProtoView: ProtoViewDto, public index: number,
public parentIndex: number, public boundElementIndex: number) {}
}

View File

@ -1,6 +1,5 @@
// Public API for render
export {
RenderDirectiveMetadata,
RenderEventDispatcher,
Renderer,
RenderElementRef,
@ -8,10 +7,8 @@ export {
RenderProtoViewRef,
RenderFragmentRef,
RenderViewWithFragments,
ViewDefinition,
DOCUMENT,
APP_ID,
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
RenderTemplateCmd,
RenderCommandVisitor,
RenderTextCmd,

View File

@ -1,103 +1,4 @@
import {isPresent, isBlank, RegExpWrapper} from 'angular2/src/core/facade/lang';
import {Promise} from 'angular2/src/core/facade/async';
import {Map, MapWrapper, StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {
ASTWithSource,
ChangeDetectionStrategy
} from 'angular2/src/core/change_detection/change_detection';
/**
* General notes:
*
* The methods for creating / destroying views in this API are used in the AppViewHydrator
* and RenderViewHydrator as well.
*
* We are already parsing expressions on the render side:
* - this makes the ElementBinders more compact
* (e.g. no need to distinguish interpolations from regular expressions from literals)
* - allows to retrieve which properties should be accessed from the event
* by looking at the expression
* - we need the parse at least for the `template` attribute to match
* directives in it
* - render compiler is not on the critical path as
* its output will be stored in precompiled templates.
*/
export class EventBinding {
constructor(public fullName: string, public source: ASTWithSource) {}
}
export enum PropertyBindingType {
PROPERTY,
ATTRIBUTE,
CLASS,
STYLE
}
export class ElementPropertyBinding {
constructor(public type: PropertyBindingType, public astWithSource: ASTWithSource,
public property: string, public unit: string = null) {}
}
export class RenderElementBinder {
index: number;
parentIndex: number;
distanceToParent: number;
directives: DirectiveBinder[];
nestedProtoView: ProtoViewDto;
propertyBindings: ElementPropertyBinding[];
variableBindings: Map<string, string>;
// Note: this contains a preprocessed AST
// that replaced the values that should be extracted from the element
// with a local name
eventBindings: EventBinding[];
readAttributes: Map<string, string>;
constructor({index, parentIndex, distanceToParent, directives, nestedProtoView, propertyBindings,
variableBindings, eventBindings, readAttributes}: {
index?: number,
parentIndex?: number,
distanceToParent?: number,
directives?: DirectiveBinder[],
nestedProtoView?: ProtoViewDto,
propertyBindings?: ElementPropertyBinding[],
variableBindings?: Map<string, string>,
eventBindings?: EventBinding[],
readAttributes?: Map<string, string>
} = {}) {
this.index = index;
this.parentIndex = parentIndex;
this.distanceToParent = distanceToParent;
this.directives = directives;
this.nestedProtoView = nestedProtoView;
this.propertyBindings = propertyBindings;
this.variableBindings = variableBindings;
this.eventBindings = eventBindings;
this.readAttributes = readAttributes;
}
}
export class DirectiveBinder {
// Index into the array of directives in the View instance
directiveIndex: number;
propertyBindings: Map<string, ASTWithSource>;
// Note: this contains a preprocessed AST
// that replaced the values that should be extracted from the element
// with a local name
eventBindings: EventBinding[];
hostPropertyBindings: ElementPropertyBinding[];
constructor({directiveIndex, propertyBindings, eventBindings, hostPropertyBindings}: {
directiveIndex?: number,
propertyBindings?: Map<string, ASTWithSource>,
eventBindings?: EventBinding[],
hostPropertyBindings?: ElementPropertyBinding[]
}) {
this.directiveIndex = directiveIndex;
this.propertyBindings = propertyBindings;
this.eventBindings = eventBindings;
this.hostPropertyBindings = hostPropertyBindings;
}
}
import {Map} from 'angular2/src/core/facade/collection';
export enum ViewType {
// A view that contains the host element with bound component directive.
@ -111,176 +12,6 @@ export enum ViewType {
EMBEDDED
}
export class ProtoViewDto {
render: RenderProtoViewRef;
elementBinders: RenderElementBinder[];
variableBindings: Map<string, string>;
type: ViewType;
textBindings: ASTWithSource[];
transitiveNgContentCount: number;
constructor({render, elementBinders, variableBindings, type, textBindings,
transitiveNgContentCount}: {
render?: RenderProtoViewRef,
elementBinders?: RenderElementBinder[],
variableBindings?: Map<string, string>,
type?: ViewType,
textBindings?: ASTWithSource[],
transitiveNgContentCount?: number
}) {
this.render = render;
this.elementBinders = elementBinders;
this.variableBindings = variableBindings;
this.type = type;
this.textBindings = textBindings;
this.transitiveNgContentCount = transitiveNgContentCount;
}
}
export class RenderDirectiveMetadata {
static get DIRECTIVE_TYPE() { return 0; }
static get COMPONENT_TYPE() { return 1; }
id: any;
selector: string;
compileChildren: boolean;
outputs: string[];
inputs: string[];
readAttributes: string[];
type: number;
callOnDestroy: boolean;
callOnChanges: boolean;
callDoCheck: boolean;
callOnInit: boolean;
callAfterContentInit: boolean;
callAfterContentChecked: boolean;
callAfterViewInit: boolean;
callAfterViewChecked: boolean;
changeDetection: ChangeDetectionStrategy;
exportAs: string;
hostListeners: Map<string, string>;
hostProperties: Map<string, string>;
hostAttributes: Map<string, string>;
queries: StringMap<string, any>;
// group 1: "property" from "[property]"
// group 2: "event" from "(event)"
private static _hostRegExp = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
constructor({id, selector, compileChildren, outputs, hostListeners, hostProperties,
hostAttributes, inputs, readAttributes, type, callOnDestroy, callOnChanges,
callDoCheck, callOnInit, callAfterContentInit, callAfterContentChecked,
callAfterViewInit, callAfterViewChecked, changeDetection, exportAs, queries}: {
id?: string,
selector?: string,
compileChildren?: boolean,
outputs?: string[],
hostListeners?: Map<string, string>,
hostProperties?: Map<string, string>,
hostAttributes?: Map<string, string>,
inputs?: string[],
readAttributes?: string[],
type?: number,
callOnDestroy?: boolean,
callOnChanges?: boolean,
callDoCheck?: boolean,
callOnInit?: boolean,
callAfterContentInit?: boolean,
callAfterContentChecked?: boolean,
callAfterViewInit?: boolean,
callAfterViewChecked?: boolean,
changeDetection?: ChangeDetectionStrategy,
exportAs?: string,
queries?: StringMap<string, any>
}) {
this.id = id;
this.selector = selector;
this.compileChildren = isPresent(compileChildren) ? compileChildren : true;
this.outputs = outputs;
this.hostListeners = hostListeners;
this.hostAttributes = hostAttributes;
this.hostProperties = hostProperties;
this.inputs = inputs;
this.readAttributes = readAttributes;
this.type = type;
this.callOnDestroy = callOnDestroy;
this.callOnChanges = callOnChanges;
this.callDoCheck = callDoCheck;
this.callOnInit = callOnInit;
this.callAfterContentInit = callAfterContentInit;
this.callAfterContentChecked = callAfterContentChecked;
this.callAfterViewInit = callAfterViewInit;
this.callAfterViewChecked = callAfterViewChecked;
this.changeDetection = changeDetection;
this.exportAs = exportAs;
this.queries = queries;
}
static create({id, selector, compileChildren, outputs, host, inputs, readAttributes, type,
callOnDestroy, callOnChanges, callDoCheck, callOnInit, callAfterContentInit,
callAfterContentChecked, callAfterViewInit, callAfterViewChecked, changeDetection,
exportAs, queries}: {
id?: string,
selector?: string,
compileChildren?: boolean,
outputs?: string[],
host?: Map<string, string>,
inputs?: string[],
readAttributes?: string[],
type?: number,
callOnDestroy?: boolean,
callOnChanges?: boolean,
callDoCheck?: boolean,
callOnInit?: boolean,
callAfterContentInit?: boolean,
callAfterContentChecked?: boolean,
callAfterViewInit?: boolean,
callAfterViewChecked?: boolean,
changeDetection?: ChangeDetectionStrategy,
exportAs?: string,
queries?: StringMap<string, any>
}): RenderDirectiveMetadata {
let hostListeners = new Map<string, string>();
let hostProperties = new Map<string, string>();
let hostAttributes = new Map<string, string>();
if (isPresent(host)) {
MapWrapper.forEach(host, (value: string, key: string) => {
var matches = RegExpWrapper.firstMatch(RenderDirectiveMetadata._hostRegExp, key);
if (isBlank(matches)) {
hostAttributes.set(key, value);
} else if (isPresent(matches[1])) {
hostProperties.set(matches[1], value);
} else if (isPresent(matches[2])) {
hostListeners.set(matches[2], value);
}
});
}
return new RenderDirectiveMetadata({
id: id,
selector: selector,
compileChildren: compileChildren,
outputs: outputs,
hostListeners: hostListeners,
hostProperties: hostProperties,
hostAttributes: hostAttributes,
inputs: inputs,
readAttributes: readAttributes,
type: type,
callOnDestroy: callOnDestroy,
callOnChanges: callOnChanges,
callDoCheck: callDoCheck,
callOnInit: callOnInit,
callAfterContentInit: callAfterContentInit,
callAfterContentChecked: callAfterContentChecked,
callAfterViewInit: callAfterViewInit,
callAfterViewChecked: callAfterViewChecked,
changeDetection: changeDetection,
exportAs: exportAs,
queries: queries
});
}
}
// An opaque reference to a render proto view
export class RenderProtoViewRef {}
@ -312,86 +43,6 @@ export enum ViewEncapsulation {
export var VIEW_ENCAPSULATION_VALUES =
[ViewEncapsulation.Emulated, ViewEncapsulation.Native, ViewEncapsulation.None];
export class ViewDefinition {
componentId: string;
templateAbsUrl: string;
template: string;
directives: RenderDirectiveMetadata[];
styleAbsUrls: string[];
styles: string[];
encapsulation: ViewEncapsulation;
constructor({componentId, templateAbsUrl, template, styleAbsUrls, styles, directives,
encapsulation}: {
componentId?: string,
templateAbsUrl?: string,
template?: string,
styleAbsUrls?: string[],
styles?: string[],
directives?: RenderDirectiveMetadata[],
encapsulation?: ViewEncapsulation
} = {}) {
this.componentId = componentId;
this.templateAbsUrl = templateAbsUrl;
this.template = template;
this.styleAbsUrls = styleAbsUrls;
this.styles = styles;
this.directives = directives;
this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.Emulated;
}
}
export class RenderProtoViewMergeMapping {
constructor(public mergedProtoViewRef: RenderProtoViewRef,
// Number of fragments in the merged ProtoView.
// Fragments are stored in depth first order of nested ProtoViews.
public fragmentCount: number,
// Mapping from app element index to render element index.
// Mappings of nested ProtoViews are in depth first order, with all
// indices for one ProtoView in a consecutive block.
public mappedElementIndices: number[],
// Number of bound render element.
// Note: This could be more than the original ones
// as we might have bound a new element for projecting bound text nodes.
public mappedElementCount: number,
// Mapping from app text index to render text index.
// Mappings of nested ProtoViews are in depth first order, with all
// indices for one ProtoView in a consecutive block.
public mappedTextIndices: number[],
// Mapping from view index to app element index
public hostElementIndicesByViewIndex: number[],
// Number of contained views by view index
public nestedViewCountByViewIndex: number[]) {}
}
export class RenderCompiler {
/**
* Creates a ProtoViewDto that contains a single nested component with the given componentId.
*/
compileHost(directiveMetadata: RenderDirectiveMetadata): Promise<ProtoViewDto> { return null; }
/**
* Compiles a single DomProtoView. Non recursive so that
* we don't need to serialize all possible components over the wire,
* but only the needed ones based on previous calls.
*/
compile(view: ViewDefinition): Promise<ProtoViewDto> { return null; }
/**
* Merges ProtoViews.
* The first entry of the array is the protoview into which all the other entries of the array
* should be merged.
* If the array contains other arrays, they will be merged before processing the parent array.
* The array must contain an entry for every component and embedded ProtoView of the first entry.
* @param protoViewRefs Array of ProtoViewRefs or nested
* @return the merge result
*/
mergeProtoViewsRecursively(
protoViewRefs: Array<RenderProtoViewRef | any[]>): Promise<RenderProtoViewMergeMapping> {
return null;
}
}
export interface RenderTemplateCmd { visit(visitor: RenderCommandVisitor, context: any): any; }
export interface RenderBeginCmd extends RenderTemplateCmd {
@ -449,7 +100,7 @@ export interface RenderElementRef {
*
* This is used internally by the Angular framework to locate elements.
*/
renderBoundElementIndex: number;
boundElementIndex: number;
}
export class Renderer {

View File

@ -1,66 +0,0 @@
import {isBlank} from 'angular2/src/core/facade/lang';
import {CompileElement} from './compile_element';
import {CompileStep} from './compile_step';
/**
* Controls the processing order of elements.
* Right now it only allows to add a parent element.
*/
export class CompileControl {
_currentStepIndex: number = 0;
_parent: CompileElement = null;
_results: any[] = null;
_additionalChildren: CompileElement[] = null;
_ignoreCurrentElement: boolean;
constructor(public _steps: CompileStep[]) {}
// only public so that it can be used by compile_pipeline
internalProcess(results: any[], startStepIndex: number, parent: CompileElement,
current: CompileElement): CompileElement[] {
this._results = results;
var previousStepIndex = this._currentStepIndex;
var previousParent = this._parent;
this._ignoreCurrentElement = false;
for (var i = startStepIndex; i < this._steps.length && !this._ignoreCurrentElement; i++) {
var step = this._steps[i];
this._parent = parent;
this._currentStepIndex = i;
step.processElement(parent, current, this);
parent = this._parent;
}
if (!this._ignoreCurrentElement) {
results.push(current);
}
this._currentStepIndex = previousStepIndex;
this._parent = previousParent;
var localAdditionalChildren = this._additionalChildren;
this._additionalChildren = null;
return localAdditionalChildren;
}
addParent(newElement: CompileElement) {
this.internalProcess(this._results, this._currentStepIndex + 1, this._parent, newElement);
this._parent = newElement;
}
addChild(element: CompileElement) {
if (isBlank(this._additionalChildren)) {
this._additionalChildren = [];
}
this._additionalChildren.push(element);
}
/**
* Ignores the current element.
*
* When a step calls `ignoreCurrentElement`, no further steps are executed on the current
* element and no `CompileElement` is added to the result list.
*/
ignoreCurrentElement() { this._ignoreCurrentElement = true; }
}

View File

@ -1,109 +0,0 @@
import {Map, ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {
isBlank,
isPresent,
Type,
StringJoiner,
assertionsEnabled
} from 'angular2/src/core/facade/lang';
import {ProtoViewBuilder, ElementBinderBuilder} from '../view/proto_view_builder';
/**
* Collects all data that is needed to process an element
* in the compile process. Fields are filled
* by the CompileSteps starting out with the pure HTMLElement.
*/
export class CompileElement {
_attrs: Map<string, string> = null;
_classList: string[] = null;
isViewRoot: boolean = false;
// inherited down to children if they don't have an own protoView
inheritedProtoView: ProtoViewBuilder = null;
distanceToInheritedBinder: number = 0;
// inherited down to children if they don't have an own elementBinder
inheritedElementBinder: ElementBinderBuilder = null;
compileChildren: boolean = true;
elementDescription: string; // e.g. '<div [title]="foo">' : used to provide context in case of
// error
constructor(public element, compilationUnit: string = '') {
// description is calculated here as compilation steps may change the element
var tplDesc = assertionsEnabled() ? getElementDescription(element) : null;
if (compilationUnit !== '') {
this.elementDescription = compilationUnit;
if (isPresent(tplDesc)) this.elementDescription += ": " + tplDesc;
} else {
this.elementDescription = tplDesc;
}
}
isBound(): boolean {
return isPresent(this.inheritedElementBinder) && this.distanceToInheritedBinder === 0;
}
bindElement(): ElementBinderBuilder {
if (!this.isBound()) {
var parentBinder = this.inheritedElementBinder;
this.inheritedElementBinder =
this.inheritedProtoView.bindElement(this.element, this.elementDescription);
if (isPresent(parentBinder)) {
this.inheritedElementBinder.setParent(parentBinder, this.distanceToInheritedBinder);
}
this.distanceToInheritedBinder = 0;
}
return this.inheritedElementBinder;
}
attrs(): Map<string, string> {
if (isBlank(this._attrs)) {
this._attrs = DOM.attributeMap(this.element);
}
return this._attrs;
}
classList(): string[] {
if (isBlank(this._classList)) {
this._classList = [];
var elClassList = DOM.classList(this.element);
for (var i = 0; i < elClassList.length; i++) {
this._classList.push(elClassList[i]);
}
}
return this._classList;
}
}
// return an HTML representation of an element start tag - without its content
// this is used to give contextual information in case of errors
function getElementDescription(domElement): string {
var buf = new StringJoiner();
var atts = DOM.attributeMap(domElement);
buf.add("<");
buf.add(DOM.tagName(domElement).toLowerCase());
// show id and class first to ease element identification
addDescriptionAttribute(buf, "id", atts.get("id"));
addDescriptionAttribute(buf, "class", atts.get("class"));
MapWrapper.forEach(atts, (attValue, attName) => {
if (attName !== "id" && attName !== "class") {
addDescriptionAttribute(buf, attName, attValue);
}
});
buf.add(">");
return buf.toString();
}
function addDescriptionAttribute(buffer: StringJoiner, attName: string, attValue) {
if (isPresent(attValue)) {
if (attValue.length === 0) {
buffer.add(' ' + attName);
} else {
buffer.add(' ' + attName + '="' + attValue + '"');
}
}
}

View File

@ -1,63 +0,0 @@
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {CompileStep} from './compile_step';
import {ProtoViewBuilder} from '../view/proto_view_builder';
import {ProtoViewDto, ViewType, ViewDefinition} from '../../api';
/**
* CompilePipeline for executing CompileSteps recursively for
* all elements in a template.
*/
export class CompilePipeline {
_control: CompileControl;
constructor(public steps: CompileStep[]) { this._control = new CompileControl(steps); }
processStyles(styles: string[]): string[] {
return styles.map(style => {
this.steps.forEach(step => { style = step.processStyle(style); });
return style;
});
}
processElements(rootElement: Element, protoViewType: ViewType,
viewDef: ViewDefinition): CompileElement[] {
var results: CompileElement[] = [];
var compilationCtxtDescription = viewDef.componentId;
var rootCompileElement = new CompileElement(rootElement, compilationCtxtDescription);
rootCompileElement.inheritedProtoView =
new ProtoViewBuilder(rootElement, protoViewType, viewDef.encapsulation);
rootCompileElement.isViewRoot = true;
this._processElement(results, null, rootCompileElement, compilationCtxtDescription);
return results;
}
_processElement(results: CompileElement[], parent: CompileElement, current: CompileElement,
compilationCtxtDescription: string = '') {
var additionalChildren = this._control.internalProcess(results, 0, parent, current);
if (current.compileChildren) {
var node = DOM.firstChild(DOM.templateAwareRoot(current.element));
while (isPresent(node)) {
// compilation can potentially move the node, so we need to store the
// next sibling before recursing.
var nextNode = DOM.nextSibling(node);
if (DOM.isElementNode(node)) {
var childCompileElement = new CompileElement(node, compilationCtxtDescription);
childCompileElement.inheritedProtoView = current.inheritedProtoView;
childCompileElement.inheritedElementBinder = current.inheritedElementBinder;
childCompileElement.distanceToInheritedBinder = current.distanceToInheritedBinder + 1;
this._processElement(results, current, childCompileElement);
}
node = nextNode;
}
}
if (isPresent(additionalChildren)) {
for (var i = 0; i < additionalChildren.length; i++) {
this._processElement(results, current, additionalChildren[i]);
}
}
}
}

View File

@ -1,13 +0,0 @@
import {CompileElement} from './compile_element';
import * as compileControlModule from './compile_control';
/**
* One part of the compile process.
* Is guaranteed to be called in depth first order
*/
export interface CompileStep {
processElement(parent: CompileElement, current: CompileElement,
control: compileControlModule.CompileControl): void;
processStyle(style: string): string;
}

View File

@ -1,27 +0,0 @@
import {Parser} from 'angular2/src/core/change_detection/change_detection';
import {ViewDefinition} from '../../api';
import {CompileStep} from './compile_step';
import {PropertyBindingParser} from './property_binding_parser';
import {TextInterpolationParser} from './text_interpolation_parser';
import {DirectiveParser} from './directive_parser';
import {ViewSplitter} from './view_splitter';
import {StyleEncapsulator} from './style_encapsulator';
export class CompileStepFactory {
createSteps(view: ViewDefinition): CompileStep[] { return null; }
}
export class DefaultStepFactory extends CompileStepFactory {
private _componentUIDsCache = new Map<string, string>();
constructor(private _parser: Parser, private _appId: string) { super(); }
createSteps(view: ViewDefinition): CompileStep[] {
return [
new ViewSplitter(this._parser),
new PropertyBindingParser(this._parser),
new DirectiveParser(this._parser, view.directives),
new TextInterpolationParser(this._parser),
new StyleEncapsulator(this._appId, view, this._componentUIDsCache)
];
}
}

View File

@ -1,133 +0,0 @@
import {Injectable, Inject} from 'angular2/src/core/di';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {
ViewDefinition,
ProtoViewDto,
ViewType,
RenderDirectiveMetadata,
RenderCompiler,
RenderProtoViewRef,
RenderProtoViewMergeMapping,
ViewEncapsulation
} from '../../api';
import {CompilePipeline} from './compile_pipeline';
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
import {ViewLoader, TemplateAndStyles} from 'angular2/src/core/render/dom/compiler/view_loader';
import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {Parser} from 'angular2/src/core/change_detection/change_detection';
import * as pvm from '../view/proto_view_merger';
import {CssSelector} from './selector';
import {DOCUMENT, APP_ID} from '../dom_tokens';
import {SharedStylesHost} from '../view/shared_styles_host';
import {prependAll} from '../util';
import {TemplateCloner} from '../template_cloner';
/**
* The compiler loads and translates the html templates of components into
* nested ProtoViews. To decompose its functionality it uses
* the CompilePipeline and the CompileSteps.
*/
export class DomCompiler extends RenderCompiler {
constructor(private _schemaRegistry: ElementSchemaRegistry,
private _templateCloner: TemplateCloner, private _stepFactory: CompileStepFactory,
private _viewLoader: ViewLoader, private _sharedStylesHost: SharedStylesHost) {
super();
}
compile(view: ViewDefinition): Promise<ProtoViewDto> {
var tplPromise = this._viewLoader.load(view);
return PromiseWrapper.then(
tplPromise, (tplAndStyles: TemplateAndStyles) =>
this._compileView(view, tplAndStyles, ViewType.COMPONENT),
(e) => {
throw new BaseException(`Failed to load the template for "${view.componentId}" : ${e}`);
return null;
});
}
compileHost(directiveMetadata: RenderDirectiveMetadata): Promise<ProtoViewDto> {
let hostViewDef = new ViewDefinition({
componentId: directiveMetadata.id,
templateAbsUrl: null, template: null,
styles: null,
styleAbsUrls: null,
directives: [directiveMetadata],
encapsulation: ViewEncapsulation.None
});
let selector = CssSelector.parse(directiveMetadata.selector)[0];
let hostTemplate = selector.getMatchingElementTemplate();
let templateAndStyles = new TemplateAndStyles(hostTemplate, []);
return this._compileView(hostViewDef, templateAndStyles, ViewType.HOST);
}
mergeProtoViewsRecursively(
protoViewRefs: Array<RenderProtoViewRef | any[]>): Promise<RenderProtoViewMergeMapping> {
return PromiseWrapper.resolve(
pvm.mergeProtoViewsRecursively(this._templateCloner, protoViewRefs));
}
_compileView(viewDef: ViewDefinition, templateAndStyles: TemplateAndStyles,
protoViewType: ViewType): Promise<ProtoViewDto> {
if (viewDef.encapsulation === ViewEncapsulation.Emulated &&
templateAndStyles.styles.length === 0) {
viewDef = this._normalizeViewEncapsulationIfThereAreNoStyles(viewDef);
}
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef));
var compiledStyles = pipeline.processStyles(templateAndStyles.styles);
var compileElements = pipeline.processElements(
this._createTemplateElm(templateAndStyles.template), protoViewType, viewDef);
if (viewDef.encapsulation === ViewEncapsulation.Native) {
prependAll(DOM.content(compileElements[0].element),
compiledStyles.map(style => DOM.createStyleElement(style)));
} else {
this._sharedStylesHost.addStyles(compiledStyles);
}
return PromiseWrapper.resolve(
compileElements[0].inheritedProtoView.build(this._schemaRegistry, this._templateCloner));
}
_createTemplateElm(template: string) {
var templateElm = DOM.createTemplate(template);
var scriptTags = DOM.querySelectorAll(DOM.templateAwareRoot(templateElm), 'script');
for (var i = 0; i < scriptTags.length; i++) {
DOM.remove(scriptTags[i]);
}
return templateElm;
}
_normalizeViewEncapsulationIfThereAreNoStyles(viewDef: ViewDefinition): ViewDefinition {
if (viewDef.encapsulation === ViewEncapsulation.Emulated) {
return new ViewDefinition({
componentId: viewDef.componentId,
templateAbsUrl: viewDef.templateAbsUrl, template: viewDef.template,
styleAbsUrls: viewDef.styleAbsUrls,
styles: viewDef.styles,
directives: viewDef.directives,
encapsulation: ViewEncapsulation.None
});
} else {
return viewDef;
}
}
}
@Injectable()
export class DefaultDomCompiler extends DomCompiler {
constructor(schemaRegistry: ElementSchemaRegistry, templateCloner: TemplateCloner, parser: Parser,
viewLoader: ViewLoader, sharedStylesHost: SharedStylesHost,
@Inject(APP_ID) appId: any) {
super(schemaRegistry, templateCloner, new DefaultStepFactory(parser, appId), viewLoader,
sharedStylesHost);
}
}

View File

@ -1,174 +0,0 @@
import {isPresent, isBlank, StringWrapper} from 'angular2/src/core/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {Parser} from 'angular2/src/core/change_detection/change_detection';
import {SelectorMatcher, CssSelector} from 'angular2/src/core/render/dom/compiler/selector';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {RenderDirectiveMetadata} from '../../api';
import {dashCaseToCamelCase, camelCaseToDashCase} from '../util';
import {EventConfig} from '../../event_config';
import {DirectiveBuilder, ElementBinderBuilder} from '../view/proto_view_builder';
/**
* Parses the directives on a single element. Assumes ViewSplitter has already created
* <template> elements for template directives.
*/
export class DirectiveParser implements CompileStep {
_selectorMatcher: SelectorMatcher = new SelectorMatcher();
constructor(public _parser: Parser, public _directives: RenderDirectiveMetadata[]) {
for (var i = 0; i < _directives.length; i++) {
var directive = _directives[i];
var selector = CssSelector.parse(directive.selector);
this._selectorMatcher.addSelectables(selector, i);
}
}
processStyle(style: string): string { return style; }
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
var attrs = current.attrs();
var classList = current.classList();
var cssSelector = new CssSelector();
var foundDirectiveIndices = [];
var elementBinder: ElementBinderBuilder = null;
cssSelector.setElement(DOM.nodeName(current.element));
for (var i = 0; i < classList.length; i++) {
cssSelector.addClassName(classList[i]);
}
MapWrapper.forEach(attrs,
(attrValue, attrName) => { cssSelector.addAttribute(attrName, attrValue); });
this._selectorMatcher.match(cssSelector, (selector, directiveIndex) => {
var directive = this._directives[directiveIndex];
elementBinder = current.bindElement();
if (directive.type === RenderDirectiveMetadata.COMPONENT_TYPE) {
this._ensureHasOnlyOneComponent(elementBinder, current.elementDescription);
// components need to go first, so it is easier to locate them in the result.
ListWrapper.insert(foundDirectiveIndices, 0, directiveIndex);
elementBinder.setComponentId(directive.id);
} else {
foundDirectiveIndices.push(directiveIndex);
}
});
ListWrapper.forEach(foundDirectiveIndices, (directiveIndex) => {
var dirMetadata = this._directives[directiveIndex];
var directiveBinderBuilder = elementBinder.bindDirective(directiveIndex);
current.compileChildren = current.compileChildren && dirMetadata.compileChildren;
if (isPresent(dirMetadata.inputs)) {
ListWrapper.forEach(dirMetadata.inputs, (bindConfig) => {
this._bindDirectiveProperty(bindConfig, current, directiveBinderBuilder);
});
}
if (isPresent(dirMetadata.hostListeners)) {
this._sortedKeysForEach(dirMetadata.hostListeners, (action, eventName) => {
this._bindDirectiveEvent(eventName, action, current, directiveBinderBuilder);
});
}
if (isPresent(dirMetadata.hostProperties)) {
this._sortedKeysForEach(dirMetadata.hostProperties, (expression, hostPropertyName) => {
this._bindHostProperty(hostPropertyName, expression, current, directiveBinderBuilder);
});
}
if (isPresent(dirMetadata.hostAttributes)) {
this._sortedKeysForEach(dirMetadata.hostAttributes, (hostAttrValue, hostAttrName) => {
this._addHostAttribute(hostAttrName, hostAttrValue, current);
});
}
if (isPresent(dirMetadata.readAttributes)) {
ListWrapper.forEach(dirMetadata.readAttributes,
(attrName) => { elementBinder.readAttribute(attrName); });
}
});
}
_sortedKeysForEach(map: Map<string, string>, fn: (value: string, key: string) => void): void {
var keys = MapWrapper.keys(map);
ListWrapper.sort(keys, (a, b) => {
// Ensure a stable sort.
var compareVal = StringWrapper.compare(a, b);
return compareVal == 0 ? -1 : compareVal;
});
ListWrapper.forEach(keys, (key) => { fn(MapWrapper.get(map, key), key); });
}
_ensureHasOnlyOneComponent(elementBinder: ElementBinderBuilder, elDescription: string): void {
if (isPresent(elementBinder.componentId)) {
throw new BaseException(
`Only one component directive is allowed per element - check ${elDescription}`);
}
}
_bindDirectiveProperty(bindConfig: string, compileElement: CompileElement,
directiveBinderBuilder: DirectiveBuilder) {
// Name of the property on the directive
let dirProperty: string;
// Name of the property on the element
let elProp: string;
let pipes: string[];
let assignIndex: number = bindConfig.indexOf(':');
if (assignIndex > -1) {
// canonical syntax: `dirProp: elProp | pipe0 | ... | pipeN`
dirProperty = StringWrapper.substring(bindConfig, 0, assignIndex).trim();
pipes = this._splitBindConfig(StringWrapper.substring(bindConfig, assignIndex + 1));
elProp = ListWrapper.removeAt(pipes, 0);
} else {
// shorthand syntax when the name of the property on the directive and on the element is the
// same, ie `property`
dirProperty = bindConfig;
elProp = bindConfig;
pipes = [];
}
elProp = dashCaseToCamelCase(elProp);
var bindingAst = compileElement.bindElement().propertyBindings.get(elProp);
if (isBlank(bindingAst)) {
var attributeValue = compileElement.attrs().get(camelCaseToDashCase(elProp));
if (isPresent(attributeValue)) {
bindingAst =
this._parser.wrapLiteralPrimitive(attributeValue, compileElement.elementDescription);
}
}
// Bindings are optional, so this binding only needs to be set up if an expression is given.
if (isPresent(bindingAst)) {
directiveBinderBuilder.bindProperty(dirProperty, bindingAst, elProp);
}
}
_bindDirectiveEvent(eventName, action, compileElement, directiveBinderBuilder) {
var ast = this._parser.parseAction(action, compileElement.elementDescription);
var parsedEvent = EventConfig.parse(eventName);
var targetName = parsedEvent.isLongForm ? parsedEvent.fieldName : null;
directiveBinderBuilder.bindEvent(parsedEvent.eventName, ast, targetName);
}
_bindHostProperty(hostPropertyName, expression, compileElement, directiveBinderBuilder) {
var ast = this._parser.parseSimpleBinding(
expression, `hostProperties of ${compileElement.elementDescription}`);
directiveBinderBuilder.bindHostProperty(hostPropertyName, ast);
}
_addHostAttribute(attrName, attrValue, compileElement) {
if (StringWrapper.equals(attrName, 'class')) {
ListWrapper.forEach(attrValue.split(' '),
(className) => { DOM.addClass(compileElement.element, className); });
} else if (!DOM.hasAttribute(compileElement.element, attrName)) {
DOM.setAttribute(compileElement.element, attrName, attrValue);
}
}
_splitBindConfig(bindConfig: string) {
return ListWrapper.map(bindConfig.split('|'), (s) => s.trim());
}
}

View File

@ -1,109 +0,0 @@
import {isPresent, RegExpWrapper, StringWrapper} from 'angular2/src/core/facade/lang';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {Parser} from 'angular2/src/core/change_detection/change_detection';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {dashCaseToCamelCase} from '../util';
// Group 1 = "bind-"
// Group 2 = "var-" or "#"
// Group 3 = "on-"
// Group 4 = "bindon-"
// Group 5 = the identifier after "bind-", "var-/#", or "on-"
// Group 6 = identifier inside [()]
// Group 7 = identifier inside []
// Group 8 = identifier inside ()
var BIND_NAME_REGEXP =
/^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
/**
* Parses the property bindings on a single element.
*/
export class PropertyBindingParser implements CompileStep {
constructor(private _parser: Parser) {}
processStyle(style: string): string { return style; }
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
var attrs = current.attrs();
var newAttrs = new Map();
MapWrapper.forEach(attrs, (attrValue, attrName) => {
attrName = this._normalizeAttributeName(attrName);
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
if (isPresent(bindParts)) {
if (isPresent(bindParts[1])) { // match: bind-prop
this._bindProperty(bindParts[5], attrValue, current, newAttrs);
} else if (isPresent(
bindParts[2])) { // match: var-name / var-name="iden" / #name / #name="iden"
var identifier = bindParts[5];
var value = attrValue == '' ? '\$implicit' : attrValue;
this._bindVariable(identifier, value, current, newAttrs);
} else if (isPresent(bindParts[3])) { // match: on-event
this._bindEvent(bindParts[5], attrValue, current, newAttrs);
} else if (isPresent(bindParts[4])) { // match: bindon-prop
this._bindProperty(bindParts[5], attrValue, current, newAttrs);
this._bindAssignmentEvent(bindParts[5], attrValue, current, newAttrs);
} else if (isPresent(bindParts[6])) { // match: [(expr)]
this._bindProperty(bindParts[6], attrValue, current, newAttrs);
this._bindAssignmentEvent(bindParts[6], attrValue, current, newAttrs);
} else if (isPresent(bindParts[7])) { // match: [expr]
this._bindProperty(bindParts[7], attrValue, current, newAttrs);
} else if (isPresent(bindParts[8])) { // match: (event)
this._bindEvent(bindParts[8], attrValue, current, newAttrs);
}
} else {
var expr = this._parser.parseInterpolation(attrValue, current.elementDescription);
if (isPresent(expr)) {
this._bindPropertyAst(attrName, expr, current, newAttrs);
}
}
});
MapWrapper.forEach(newAttrs, (attrValue, attrName) => { attrs.set(attrName, attrValue); });
}
_normalizeAttributeName(attrName: string): string {
return StringWrapper.startsWith(attrName, 'data-') ? StringWrapper.substring(attrName, 5) :
attrName;
}
_bindVariable(identifier, value, current: CompileElement, newAttrs: Map<any, any>) {
current.bindElement().bindVariable(dashCaseToCamelCase(identifier), value);
newAttrs.set(identifier, value);
}
_bindProperty(name, expression, current: CompileElement, newAttrs) {
this._bindPropertyAst(name, this._parser.parseBinding(expression, current.elementDescription),
current, newAttrs);
}
_bindPropertyAst(name, ast, current: CompileElement, newAttrs: Map<any, any>) {
var binder = current.bindElement();
binder.bindProperty(dashCaseToCamelCase(name), ast);
newAttrs.set(name, ast.source);
}
_bindAssignmentEvent(name, expression, current: CompileElement, newAttrs) {
this._bindEvent(name, `${expression}=$event`, current, newAttrs);
}
_bindEvent(name, expression, current: CompileElement, newAttrs) {
current.bindElement().bindEvent(
dashCaseToCamelCase(name),
this._parser.parseAction(expression, current.elementDescription));
// Don't detect directives for event names for now,
// so don't add the event name to the CompileElement.attrs
}
}

View File

@ -1,73 +0,0 @@
import {CompileStep} from '../compiler/compile_step';
import {CompileElement} from '../compiler/compile_element';
import {CompileControl} from '../compiler/compile_control';
import {ViewDefinition, ViewEncapsulation, ViewType} from '../../api';
import {NG_CONTENT_ELEMENT_NAME, isElementWithTag} from '../util';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
import {ShadowCss} from './shadow_css';
export class StyleEncapsulator implements CompileStep {
constructor(private _appId: string, private _view: ViewDefinition,
private _componentUIDsCache: Map<string, string>) {}
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
if (isElementWithTag(current.element, NG_CONTENT_ELEMENT_NAME)) {
current.inheritedProtoView.bindNgContent();
} else {
if (this._view.encapsulation === ViewEncapsulation.Emulated) {
this._processEmulatedScopedElement(current, parent);
}
}
}
processStyle(style: string): string {
var encapsulation = this._view.encapsulation;
if (encapsulation === ViewEncapsulation.Emulated) {
return this._shimCssForComponent(style, this._view.componentId);
} else {
return style;
}
}
_processEmulatedScopedElement(current: CompileElement, parent: CompileElement): void {
var element = current.element;
var hostComponentId = this._view.componentId;
var viewType = current.inheritedProtoView.type;
// Shim the element as a child of the compiled component
if (viewType !== ViewType.HOST && isPresent(hostComponentId)) {
var contentAttribute = getContentAttribute(this._getComponentId(hostComponentId));
DOM.setAttribute(element, contentAttribute, '');
// also shim the host
if (isBlank(parent) && viewType == ViewType.COMPONENT) {
var hostAttribute = getHostAttribute(this._getComponentId(hostComponentId));
current.inheritedProtoView.setHostAttribute(hostAttribute, '');
}
}
}
_shimCssForComponent(cssText: string, componentId: string): string {
var id = this._getComponentId(componentId);
var shadowCss = new ShadowCss();
return shadowCss.shimCssText(cssText, getContentAttribute(id), getHostAttribute(id));
}
_getComponentId(componentStringId: string): string {
var id = this._componentUIDsCache.get(componentStringId);
if (isBlank(id)) {
id = `${this._appId}-${this._componentUIDsCache.size}`;
this._componentUIDsCache.set(componentStringId, id);
}
return id;
}
}
// Return the attribute to be added to the component
function getHostAttribute(compId: string): string {
return `_nghost-${compId}`;
}
// Returns the attribute to be added on every single element nodes in the component
function getContentAttribute(compId: string): string {
return `_ngcontent-${compId}`;
}

View File

@ -1,137 +0,0 @@
import {Injectable} from 'angular2/src/core/di';
import {XHR} from 'angular2/src/core/render/xhr';
import {ListWrapper} from 'angular2/src/core/facade/collection';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {StyleUrlResolver} from './style_url_resolver';
import {
isBlank,
isPresent,
RegExp,
RegExpWrapper,
StringWrapper,
normalizeBlank,
isPromise
} from 'angular2/src/core/facade/lang';
import {
Promise,
PromiseWrapper,
} from 'angular2/src/core/facade/async';
/**
* Inline @import rules in the given CSS.
*
* When an @import rules is inlined, it's url are rewritten.
*/
@Injectable()
export class StyleInliner {
constructor(public _xhr: XHR, public _styleUrlResolver: StyleUrlResolver,
public _urlResolver: UrlResolver) {}
/**
* Inline the @imports rules in the given CSS text.
*
* The baseUrl is required to rewrite URLs in the inlined content.
*
* @param {string} cssText
* @param {string} baseUrl
* @returns {*} a Promise<string> when @import rules are present, a string otherwise
*/
inlineImports(cssText: string, baseUrl: string): Promise<string>| string {
return this._inlineImports(cssText, baseUrl, []);
}
_inlineImports(cssText: string, baseUrl: string, inlinedUrls: string[]): Promise<string>| string {
var partIndex = 0;
var parts = StringWrapper.split(cssText, _importRe);
if (parts.length === 1) {
// no @import rule found, return the original css
return cssText;
}
var promises = [];
while (partIndex < parts.length - 1) {
// prefix is the content before the @import rule
var prefix = parts[partIndex];
// rule is the parameter of the @import rule
var rule = parts[partIndex + 1];
var url = _extractUrl(rule);
if (isPresent(url)) {
url = this._urlResolver.resolve(baseUrl, url);
}
var mediaQuery = _extractMediaQuery(rule);
var promise;
if (isBlank(url)) {
promise = PromiseWrapper.resolve(`/* Invalid import rule: "@import ${rule};" */`);
} else if (ListWrapper.contains(inlinedUrls, url)) {
// The current import rule has already been inlined, return the prefix only
// Importing again might cause a circular dependency
promise = PromiseWrapper.resolve(prefix);
} else {
inlinedUrls.push(url);
promise = PromiseWrapper.then(this._xhr.get(url), (rawCss) => {
// resolve nested @import rules
var inlinedCss = this._inlineImports(rawCss, url, inlinedUrls);
if (isPromise(inlinedCss)) {
// wait until nested @import are inlined
return (<Promise<string>>inlinedCss)
.then((css) => prefix + this._transformImportedCss(css, mediaQuery, url) + '\n');
} else {
// there are no nested @import, return the css
return prefix + this._transformImportedCss(<string>inlinedCss, mediaQuery, url) + '\n';
}
}, (error) => `/* failed to import ${url} */\n`);
}
promises.push(promise);
partIndex += 2;
}
return PromiseWrapper.all(promises).then(function(cssParts) {
var cssText = (<any[]>cssParts).join('');
if (partIndex < parts.length) {
// append then content located after the last @import rule
cssText += parts[partIndex];
}
return cssText;
});
}
_transformImportedCss(css: string, mediaQuery: string, url: string): string {
css = this._styleUrlResolver.resolveUrls(css, url);
return _wrapInMediaRule(css, mediaQuery);
}
}
// Extracts the url from an import rule, supported formats:
// - 'url' / "url",
// - url(url) / url('url') / url("url")
function _extractUrl(importRule: string): string {
var match = RegExpWrapper.firstMatch(_urlRe, importRule);
if (isBlank(match)) return null;
return isPresent(match[1]) ? match[1] : match[2];
}
// Extracts the media query from an import rule.
// Returns null when there is no media query.
function _extractMediaQuery(importRule: string): string {
var match = RegExpWrapper.firstMatch(_mediaQueryRe, importRule);
if (isBlank(match)) return null;
var mediaQuery = match[1].trim();
return (mediaQuery.length > 0) ? mediaQuery : null;
}
// Wraps the css in a media rule when the media query is not null
function _wrapInMediaRule(css: string, query: string): string {
return (isBlank(query)) ? css : `@media ${query} {\n${css}\n}`;
}
var _importRe = /@import\s+([^;]+);/g;
var _urlRe = RegExpWrapper.create(
'url\\(\\s*?[\'"]?([^\'")]+)[\'"]?|' + // url(url) or url('url') or url("url")
'[\'"]([^\'")]+)[\'"]' // "url" or 'url'
);
var _mediaQueryRe = /['"][^'"]+['"]\s*\)?\s*(.*)/g;

View File

@ -1,42 +0,0 @@
// Some of the code comes from WebComponents.JS
// https://github.com/webcomponents/webcomponentsjs/blob/master/src/HTMLImports/path.js
import {Injectable} from 'angular2/src/core/di';
import {RegExp, RegExpWrapper, StringWrapper} from 'angular2/src/core/facade/lang';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
/**
* Rewrites URLs by resolving '@import' and 'url()' URLs from the given base URL.
*/
@Injectable()
export class StyleUrlResolver {
constructor(public _resolver: UrlResolver) {}
resolveUrls(cssText: string, baseUrl: string): string {
cssText = this._replaceUrls(cssText, _cssUrlRe, baseUrl);
cssText = this._replaceUrls(cssText, _cssImportRe, baseUrl);
return cssText;
}
_replaceUrls(cssText: string, re: RegExp, baseUrl: string) {
return StringWrapper.replaceAllMapped(cssText, re, (m) => {
var pre = m[1];
var originalUrl = m[2];
if (RegExpWrapper.test(_dataUrlRe, originalUrl)) {
// Do not attempt to resolve data: URLs
return m[0];
}
var url = StringWrapper.replaceAll(originalUrl, _quoteRe, '');
var post = m[3];
var resolvedUrl = this._resolver.resolve(baseUrl, url);
return pre + "'" + resolvedUrl + "'" + post;
});
}
}
var _cssUrlRe = /(url\()([^)]*)(\))/g;
var _cssImportRe = /(@import[\s]+(?!url\())['"]([^'"]*)['"](.*;)/g;
var _quoteRe = /['"]/g;
var _dataUrlRe = /^['"]?data:/g;

View File

@ -1,41 +0,0 @@
import {RegExpWrapper, StringWrapper, isPresent} from 'angular2/src/core/facade/lang';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {Parser} from 'angular2/src/core/change_detection/change_detection';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
/**
* Parses interpolations in direct text child nodes of the current element.
*/
export class TextInterpolationParser implements CompileStep {
constructor(public _parser: Parser) {}
processStyle(style: string): string { return style; }
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
if (!current.compileChildren) {
return;
}
var element = current.element;
var childNodes = DOM.childNodes(DOM.templateAwareRoot(element));
for (var i = 0; i < childNodes.length; i++) {
var node = childNodes[i];
if (DOM.isTextNode(node)) {
var textNode = <Text>node;
var text = DOM.nodeValue(textNode);
var expr = this._parser.parseInterpolation(text, current.elementDescription);
if (isPresent(expr)) {
DOM.setText(textNode, ' ');
if (current.element === current.inheritedProtoView.rootElement) {
current.inheritedProtoView.bindRootText(textNode, expr);
} else {
current.bindElement().bindText(textNode, expr);
}
}
}
}
}
}

View File

@ -1,168 +0,0 @@
import {Injectable} from 'angular2/src/core/di';
import {
isBlank,
isPresent,
stringify,
isPromise,
StringWrapper
} from 'angular2/src/core/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
import {Map, MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {ViewDefinition} from '../../api';
import {XHR} from 'angular2/src/core/render/xhr';
import {StyleInliner} from './style_inliner';
import {StyleUrlResolver} from './style_url_resolver';
import {wtfStartTimeRange, wtfEndTimeRange} from '../../../profile/profile';
export class TemplateAndStyles {
constructor(public template: string, public styles: string[]) {}
}
/**
* Strategy to load component views.
* TODO: Make public API once we are more confident in this approach.
*/
@Injectable()
export class ViewLoader {
_cache = new Map<string, Promise<string>>();
constructor(private _xhr: XHR, private _styleInliner: StyleInliner,
private _styleUrlResolver: StyleUrlResolver) {}
load(viewDef: ViewDefinition): Promise<TemplateAndStyles> {
var r = wtfStartTimeRange('ViewLoader#load()', stringify(viewDef.componentId));
let tplAndStyles: Array<Promise<TemplateAndStyles>| Promise<string>| string> =
[this._loadHtml(viewDef.template, viewDef.templateAbsUrl, viewDef.componentId)];
if (isPresent(viewDef.styles)) {
viewDef.styles.forEach((cssText: string) => {
let textOrPromise = this._resolveAndInlineCssText(cssText, viewDef.templateAbsUrl);
tplAndStyles.push(textOrPromise);
});
}
if (isPresent(viewDef.styleAbsUrls)) {
viewDef.styleAbsUrls.forEach(url => {
let promise = this._loadText(url).then(
cssText => this._resolveAndInlineCssText(cssText, viewDef.templateAbsUrl));
tplAndStyles.push(promise);
});
}
// Inline the styles from the @View annotation
return PromiseWrapper.all(tplAndStyles)
.then((res: Array<TemplateAndStyles | string>) => {
let loadedTplAndStyles = <TemplateAndStyles>res[0];
let styles = <string[]>ListWrapper.slice(res, 1);
var templateAndStyles = new TemplateAndStyles(loadedTplAndStyles.template,
loadedTplAndStyles.styles.concat(styles));
wtfEndTimeRange(r);
return templateAndStyles;
});
}
private _loadText(url: string): Promise<string> {
var response = this._cache.get(url);
if (isBlank(response)) {
// TODO(vicb): change error when TS gets fixed
// https://github.com/angular/angular/issues/2280
// throw new BaseException(`Failed to fetch url "${url}"`);
response = PromiseWrapper.catchError(
this._xhr.get(url),
_ => PromiseWrapper.reject(new BaseException(`Failed to fetch url "${url}"`), null));
this._cache.set(url, response);
}
return response;
}
// Load the html and inline any style tags
private _loadHtml(template: string, templateAbsUrl: string,
componentId: string): Promise<TemplateAndStyles> {
let html;
// Load the HTML
if (isPresent(template)) {
html = PromiseWrapper.resolve(template);
} else if (isPresent(templateAbsUrl)) {
html = this._loadText(templateAbsUrl);
} else {
throw new BaseException(
`View should have either the templateUrl or template property set but none was found for the '${componentId}' component`);
}
return html.then(html => {
var tplEl = DOM.createTemplate(html);
// Replace $baseUrl with the base url for the template
if (isPresent(templateAbsUrl) && templateAbsUrl.indexOf("/") >= 0) {
let baseUrl = templateAbsUrl.substring(0, templateAbsUrl.lastIndexOf("/"));
this._substituteBaseUrl(DOM.content(tplEl), baseUrl);
}
let styleEls = DOM.querySelectorAll(DOM.content(tplEl), 'STYLE');
let unresolvedStyles: string[] = [];
for (let i = 0; i < styleEls.length; i++) {
var styleEl = styleEls[i];
unresolvedStyles.push(DOM.getText(styleEl));
DOM.remove(styleEl);
}
let syncStyles: string[] = [];
let asyncStyles: Promise<string>[] = [];
// Inline the style tags from the html
for (let i = 0; i < styleEls.length; i++) {
let styleEl = styleEls[i];
let resolvedStyled = this._resolveAndInlineCssText(DOM.getText(styleEl), templateAbsUrl);
if (isPromise(resolvedStyled)) {
asyncStyles.push(<Promise<string>>resolvedStyled);
} else {
syncStyles.push(<string>resolvedStyled);
}
}
if (asyncStyles.length === 0) {
return PromiseWrapper.resolve(new TemplateAndStyles(DOM.getInnerHTML(tplEl), syncStyles));
} else {
return PromiseWrapper.all(asyncStyles)
.then(loadedStyles => new TemplateAndStyles(DOM.getInnerHTML(tplEl),
syncStyles.concat(<string[]>loadedStyles)));
}
});
}
/**
* Replace all occurrences of $baseUrl in the attributes of an element and its
* children with the base URL of the template.
*
* @param element The element to process
* @param baseUrl The base URL of the template.
* @private
*/
private _substituteBaseUrl(element, baseUrl: string): void {
if (DOM.isElementNode(element)) {
var attrs = DOM.attributeMap(element);
MapWrapper.forEach(attrs, (v, k) => {
if (isPresent(v) && v.indexOf('$baseUrl') >= 0) {
DOM.setAttribute(element, k, StringWrapper.replaceAll(v, /\$baseUrl/g, baseUrl));
}
});
}
let children = DOM.childNodes(element);
for (let i = 0; i < children.length; i++) {
if (DOM.isElementNode(children[i])) {
this._substituteBaseUrl(children[i], baseUrl);
}
}
}
private _resolveAndInlineCssText(cssText: string, baseUrl: string): string | Promise<string> {
cssText = this._styleUrlResolver.resolveUrls(cssText, baseUrl);
return this._styleInliner.inlineImports(cssText, baseUrl);
}
}

View File

@ -1,125 +0,0 @@
import {isBlank, isPresent, StringWrapper} from 'angular2/src/core/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {Parser} from 'angular2/src/core/change_detection/change_detection';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {dashCaseToCamelCase} from '../util';
/**
* Splits views at `<template>` elements or elements with `template` attribute:
* For `<template>` elements:
* - moves the content into a new and disconnected `<template>` element
* that is marked as view root.
*
* For elements with a `template` attribute:
* - replaces the element with an empty `<template>` element,
* parses the content of the `template` attribute and adds the information to that
* `<template>` element. Marks the elements as view root.
*
* Note: In both cases the root of the nested view is disconnected from its parent element.
* This is needed for browsers that don't support the `<template>` element
* as we want to do locate elements with bindings using `getElementsByClassName` later on,
* which should not descend into the nested view.
*/
export class ViewSplitter implements CompileStep {
constructor(public _parser: Parser) {}
processStyle(style: string): string { return style; }
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
var attrs = current.attrs();
var templateBindings = attrs.get('template');
var hasTemplateBinding = isPresent(templateBindings);
// look for template shortcuts such as *ng-if="condition" and treat them as template="if
// condition"
MapWrapper.forEach(attrs, (attrValue, attrName) => {
if (StringWrapper.startsWith(attrName, '*')) {
var key = StringWrapper.substring(attrName, 1); // remove the star
if (hasTemplateBinding) {
// 2nd template binding detected
throw new BaseException(`Only one template directive per element is allowed: ` +
`${templateBindings} and ${key} cannot be used simultaneously ` +
`in ${current.elementDescription}`);
} else {
templateBindings = (attrValue.length == 0) ? key : key + ' ' + attrValue;
hasTemplateBinding = true;
}
}
});
if (isPresent(parent)) {
if (DOM.isTemplateElement(current.element)) {
if (!current.isViewRoot) {
var viewRoot = new CompileElement(DOM.createTemplate(''));
viewRoot.inheritedProtoView = current.bindElement().bindNestedProtoView(viewRoot.element);
// viewRoot doesn't appear in the original template, so we associate
// the current element description to get a more meaningful message in case of error
viewRoot.elementDescription = current.elementDescription;
viewRoot.isViewRoot = true;
this._moveChildNodes(DOM.content(current.element), DOM.content(viewRoot.element));
control.addChild(viewRoot);
}
}
if (hasTemplateBinding) {
var anchor = new CompileElement(DOM.createTemplate(''));
anchor.inheritedProtoView = current.inheritedProtoView;
anchor.inheritedElementBinder = current.inheritedElementBinder;
anchor.distanceToInheritedBinder = current.distanceToInheritedBinder;
// newParent doesn't appear in the original template, so we associate
// the current element description to get a more meaningful message in case of error
anchor.elementDescription = current.elementDescription;
var viewRoot = new CompileElement(DOM.createTemplate(''));
viewRoot.inheritedProtoView = anchor.bindElement().bindNestedProtoView(viewRoot.element);
// viewRoot doesn't appear in the original template, so we associate
// the current element description to get a more meaningful message in case of error
viewRoot.elementDescription = current.elementDescription;
viewRoot.isViewRoot = true;
current.inheritedProtoView = viewRoot.inheritedProtoView;
current.inheritedElementBinder = null;
current.distanceToInheritedBinder = 0;
this._parseTemplateBindings(templateBindings, anchor);
DOM.insertBefore(current.element, anchor.element);
control.addParent(anchor);
DOM.appendChild(DOM.content(viewRoot.element), current.element);
control.addParent(viewRoot);
}
}
}
_moveChildNodes(source, target) {
var next = DOM.firstChild(source);
while (isPresent(next)) {
DOM.appendChild(target, next);
next = DOM.firstChild(source);
}
}
_parseTemplateBindings(templateBindings: string, compileElement: CompileElement) {
var bindings =
this._parser.parseTemplateBindings(templateBindings, compileElement.elementDescription);
for (var i = 0; i < bindings.length; i++) {
var binding = bindings[i];
if (binding.keyIsVar) {
compileElement.bindElement().bindVariable(dashCaseToCamelCase(binding.key), binding.name);
compileElement.attrs().set(binding.key, binding.name);
} else if (isPresent(binding.expression)) {
compileElement.bindElement().bindProperty(dashCaseToCamelCase(binding.key),
binding.expression);
compileElement.attrs().set(binding.key, binding.expression.source);
} else {
DOM.setAttribute(compileElement.element, binding.key, '');
}
}
}
}

View File

@ -96,8 +96,7 @@ export class DomRenderer implements Renderer, NodeFactory<Node> {
}
getNativeElementSync(location: RenderElementRef): any {
return resolveInternalDomView(location.renderView)
.boundElements[location.renderBoundElementIndex];
return resolveInternalDomView(location.renderView).boundElements[location.boundElementIndex];
}
getRootNodes(fragment: RenderFragmentRef): Node[] { return resolveInternalDomFragment(fragment); }
@ -157,7 +156,7 @@ export class DomRenderer implements Renderer, NodeFactory<Node> {
attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
var parentView = resolveInternalDomView(elementRef.renderView);
var element = parentView.boundElements[elementRef.renderBoundElementIndex];
var element = parentView.boundElements[elementRef.boundElementIndex];
var nodes = resolveInternalDomFragment(fragmentRef);
moveNodesAfterSibling(element, nodes);
this.animateNodesEnter(nodes);
@ -208,14 +207,14 @@ export class DomRenderer implements Renderer, NodeFactory<Node> {
setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void {
var view = resolveInternalDomView(location.renderView);
DOM.setProperty(<Element>view.boundElements[location.renderBoundElementIndex], propertyName,
DOM.setProperty(<Element>view.boundElements[location.boundElementIndex], propertyName,
propertyValue);
}
setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string):
void {
var view = resolveInternalDomView(location.renderView);
var element = view.boundElements[location.renderBoundElementIndex];
var element = view.boundElements[location.boundElementIndex];
var dashCasedAttributeName = camelCaseToDashCase(attributeName);
if (isPresent(attributeValue)) {
DOM.setAttribute(element, dashCasedAttributeName, stringify(attributeValue));
@ -226,7 +225,7 @@ export class DomRenderer implements Renderer, NodeFactory<Node> {
setElementClass(location: RenderElementRef, className: string, isAdd: boolean): void {
var view = resolveInternalDomView(location.renderView);
var element = view.boundElements[location.renderBoundElementIndex];
var element = view.boundElements[location.boundElementIndex];
if (isAdd) {
DOM.addClass(element, className);
} else {
@ -236,7 +235,7 @@ export class DomRenderer implements Renderer, NodeFactory<Node> {
setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void {
var view = resolveInternalDomView(location.renderView);
var element = view.boundElements[location.renderBoundElementIndex];
var element = view.boundElements[location.boundElementIndex];
var dashCasedStyleName = camelCaseToDashCase(styleName);
if (isPresent(styleValue)) {
DOM.setStyle(element, dashCasedStyleName, stringify(styleValue));
@ -247,7 +246,7 @@ export class DomRenderer implements Renderer, NodeFactory<Node> {
invokeElementMethod(location: RenderElementRef, methodName: string, args: any[]): void {
var view = resolveInternalDomView(location.renderView);
var element = <Element>view.boundElements[location.renderBoundElementIndex];
var element = <Element>view.boundElements[location.boundElementIndex];
DOM.invoke(element, methodName, args);
}

View File

@ -18,13 +18,6 @@ function _appIdRandomBindingFactory() {
export const APP_ID_RANDOM_BINDING: Binding =
CONST_EXPR(new Binding(APP_ID, {toFactory: _appIdRandomBindingFactory, deps: []}));
/**
* Defines when a compiled template should be stored as a string
* rather than keeping its Nodes to preserve memory.
*/
export const MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE: OpaqueToken =
CONST_EXPR(new OpaqueToken('MaxInMemoryElementsPerTemplate'));
function _randomChar(): string {
return StringWrapper.fromCharCode(97 + Math.floor(Math.random() * 25));
}

View File

@ -1,47 +0,0 @@
import {isString} from 'angular2/src/core/facade/lang';
import {Injectable, Inject} from 'angular2/src/core/di';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE} from './dom_tokens';
@Injectable()
export class TemplateCloner {
maxInMemoryElementsPerTemplate: number;
constructor(@Inject(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE) maxInMemoryElementsPerTemplate) {
this.maxInMemoryElementsPerTemplate = maxInMemoryElementsPerTemplate;
}
prepareForClone(templateRoot: Element): Element | string {
var elementCount = DOM.querySelectorAll(DOM.content(templateRoot), '*').length;
if (this.maxInMemoryElementsPerTemplate >= 0 &&
elementCount >= this.maxInMemoryElementsPerTemplate) {
return DOM.getInnerHTML(templateRoot);
} else {
return templateRoot;
}
}
cloneContent(preparedTemplateRoot: Element | string, importNode: boolean): Node {
var templateContent;
if (isString(preparedTemplateRoot)) {
templateContent = DOM.content(DOM.createTemplate(preparedTemplateRoot));
if (importNode) {
// Attention: We can't use document.adoptNode here
// as this does NOT wake up custom elements in Chrome 43
// TODO: Use div.innerHTML instead of template.innerHTML when we
// have code to support the various special cases and
// don't use importNode additionally (e.g. for <tr>, svg elements, ...)
// see https://github.com/angular/angular/issues/3364
templateContent = DOM.importIntoDoc(templateContent);
}
} else {
templateContent = DOM.content(<Element>preparedTemplateRoot);
if (importNode) {
templateContent = DOM.importIntoDoc(templateContent);
} else {
templateContent = DOM.clone(templateContent);
}
}
return templateContent;
}
}

View File

@ -1,17 +1,4 @@
import {StringWrapper, isPresent, isBlank} from 'angular2/src/core/facade/lang';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {ListWrapper} from 'angular2/src/core/facade/collection';
import {DomProtoView} from './view/proto_view';
import {DomElementBinder} from './view/element_binder';
import {TemplateCloner} from './template_cloner';
export const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
export const NG_BINDING_CLASS = 'ng-binding';
export const NG_CONTENT_ELEMENT_NAME = 'ng-content';
export const NG_SHADOW_ROOT_ELEMENT_NAME = 'shadow-root';
const MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE = 20;
import {StringWrapper} from 'angular2/src/core/facade/lang';
var CAMEL_CASE_REGEXP = /([A-Z])/g;
var DASH_CASE_REGEXP = /-([a-z])/g;
@ -26,121 +13,3 @@ export function dashCaseToCamelCase(input: string): string {
return StringWrapper.replaceAllMapped(input, DASH_CASE_REGEXP,
(m) => { return m[1].toUpperCase(); });
}
// Attention: This is on the hot path, so don't use closures or default values!
export function queryBoundElements(templateContent: Node, isSingleElementChild: boolean):
Element[] {
var result;
var dynamicElementList;
var elementIdx = 0;
if (isSingleElementChild) {
var rootElement = DOM.firstChild(templateContent);
var rootHasBinding = DOM.hasClass(rootElement, NG_BINDING_CLASS);
dynamicElementList = DOM.getElementsByClassName(rootElement, NG_BINDING_CLASS);
result = ListWrapper.createFixedSize(dynamicElementList.length + (rootHasBinding ? 1 : 0));
if (rootHasBinding) {
result[elementIdx++] = rootElement;
}
} else {
dynamicElementList = DOM.querySelectorAll(templateContent, NG_BINDING_CLASS_SELECTOR);
result = ListWrapper.createFixedSize(dynamicElementList.length);
}
for (var i = 0; i < dynamicElementList.length; i++) {
result[elementIdx++] = dynamicElementList[i];
}
return result;
}
export class ClonedProtoView {
constructor(public original: DomProtoView, public fragments: Node[][],
public boundElements: Element[], public boundTextNodes: Node[]) {}
}
export function cloneAndQueryProtoView(templateCloner: TemplateCloner, pv: DomProtoView,
importIntoDocument: boolean): ClonedProtoView {
var templateContent = templateCloner.cloneContent(pv.cloneableTemplate, importIntoDocument);
var boundElements = queryBoundElements(templateContent, pv.isSingleElementFragment);
var boundTextNodes = queryBoundTextNodes(templateContent, pv.rootTextNodeIndices, boundElements,
pv.elementBinders, pv.boundTextNodeCount);
var fragments = queryFragments(templateContent, pv.fragmentsRootNodeCount);
return new ClonedProtoView(pv, fragments, boundElements, boundTextNodes);
}
function queryFragments(templateContent: Node, fragmentsRootNodeCount: number[]): Node[][] {
var fragments = ListWrapper.createGrowableSize(fragmentsRootNodeCount.length);
// Note: An explicit loop is the fastest way to convert a DOM array into a JS array!
var childNode = DOM.firstChild(templateContent);
for (var fragmentIndex = 0; fragmentIndex < fragments.length; fragmentIndex++) {
var fragment = ListWrapper.createFixedSize(fragmentsRootNodeCount[fragmentIndex]);
fragments[fragmentIndex] = fragment;
// Note: the 2nd, 3rd, ... fragments are separated by each other via a '|'
if (fragmentIndex >= 1) {
childNode = DOM.nextSibling(childNode);
}
for (var i = 0; i < fragment.length; i++) {
fragment[i] = childNode;
childNode = DOM.nextSibling(childNode);
}
}
return fragments;
}
function queryBoundTextNodes(templateContent: Node, rootTextNodeIndices: number[],
boundElements: Element[], elementBinders: DomElementBinder[],
boundTextNodeCount: number): Node[] {
var boundTextNodes = ListWrapper.createFixedSize(boundTextNodeCount);
var textNodeIndex = 0;
if (rootTextNodeIndices.length > 0) {
var rootChildNodes = DOM.childNodes(templateContent);
for (var i = 0; i < rootTextNodeIndices.length; i++) {
boundTextNodes[textNodeIndex++] = rootChildNodes[rootTextNodeIndices[i]];
}
}
for (var i = 0; i < elementBinders.length; i++) {
var binder = elementBinders[i];
var element: Node = boundElements[i];
if (binder.textNodeIndices.length > 0) {
var childNodes = DOM.childNodes(element);
for (var j = 0; j < binder.textNodeIndices.length; j++) {
boundTextNodes[textNodeIndex++] = childNodes[binder.textNodeIndices[j]];
}
}
}
return boundTextNodes;
}
export function isElementWithTag(node: Node, elementName: string): boolean {
return DOM.isElementNode(node) && DOM.tagName(node).toLowerCase() == elementName.toLowerCase();
}
export function queryBoundTextNodeIndices(parentNode: Node, boundTextNodes: Map<Node, any>,
resultCallback: Function) {
var childNodes = DOM.childNodes(parentNode);
for (var j = 0; j < childNodes.length; j++) {
var node = childNodes[j];
if (boundTextNodes.has(node)) {
resultCallback(node, j, boundTextNodes.get(node));
}
}
}
export function prependAll(parentNode: Node, nodes: Node[]) {
var lastInsertedNode = null;
nodes.forEach(node => {
if (isBlank(lastInsertedNode)) {
var firstChild = DOM.firstChild(parentNode);
if (isPresent(firstChild)) {
DOM.insertBefore(firstChild, node);
} else {
DOM.appendChild(parentNode, node);
}
} else {
DOM.insertAfter(lastInsertedNode, node);
}
lastInsertedNode = node;
});
}

View File

@ -1,36 +0,0 @@
import {AST} from 'angular2/src/core/change_detection/change_detection';
import {isPresent} from 'angular2/src/core/facade/lang';
export class DomElementBinder {
textNodeIndices: number[];
hasNestedProtoView: boolean;
eventLocals: AST;
localEvents: Event[];
globalEvents: Event[];
hasNativeShadowRoot: boolean;
constructor({textNodeIndices, hasNestedProtoView, eventLocals, localEvents, globalEvents,
hasNativeShadowRoot}: {
textNodeIndices?: number[],
hasNestedProtoView?: boolean,
eventLocals?: AST,
localEvents?: Event[],
globalEvents?: Event[],
hasNativeShadowRoot?: boolean
} = {}) {
this.textNodeIndices = textNodeIndices;
this.hasNestedProtoView = hasNestedProtoView;
this.eventLocals = eventLocals;
this.localEvents = localEvents;
this.globalEvents = globalEvents;
this.hasNativeShadowRoot = isPresent(hasNativeShadowRoot) ? hasNativeShadowRoot : false;
}
}
export class Event {
constructor(public name: string, public target: string, public fullName: string) {}
}
export class HostAction {
constructor(public actionName: string, public actionExpression: string, public expression: AST) {}
}

View File

@ -1,9 +0,0 @@
import {RenderFragmentRef} from '../../api';
export function resolveInternalDomFragment(fragmentRef: RenderFragmentRef): Node[] {
return (<DomFragmentRef>fragmentRef)._nodes;
}
export class DomFragmentRef extends RenderFragmentRef {
constructor(public _nodes: Node[]) { super(); }
}

View File

@ -1,40 +0,0 @@
import {ListWrapper} from 'angular2/src/core/facade/collection';
import {DomElementBinder} from './element_binder';
import {RenderProtoViewRef, ViewType, ViewEncapsulation} from '../../api';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {TemplateCloner} from '../template_cloner';
export function resolveInternalDomProtoView(protoViewRef: RenderProtoViewRef): DomProtoView {
return (<DomProtoViewRef>protoViewRef)._protoView;
}
export class DomProtoViewRef extends RenderProtoViewRef {
constructor(public _protoView: DomProtoView) { super(); }
}
export class DomProtoView {
static create(templateCloner: TemplateCloner, type: ViewType, rootElement: Element,
viewEncapsulation: ViewEncapsulation, fragmentsRootNodeCount: number[],
rootTextNodeIndices: number[], elementBinders: DomElementBinder[],
hostAttributes: Map<string, string>): DomProtoView {
var boundTextNodeCount = rootTextNodeIndices.length;
for (var i = 0; i < elementBinders.length; i++) {
boundTextNodeCount += elementBinders[i].textNodeIndices.length;
}
var isSingleElementFragment = fragmentsRootNodeCount.length === 1 &&
fragmentsRootNodeCount[0] === 1 &&
DOM.isElementNode(DOM.firstChild(DOM.content(rootElement)));
return new DomProtoView(type, templateCloner.prepareForClone(rootElement), viewEncapsulation,
elementBinders, hostAttributes, rootTextNodeIndices, boundTextNodeCount,
fragmentsRootNodeCount, isSingleElementFragment);
}
// Note: fragments are separated by a comment node that is not counted in fragmentsRootNodeCount!
constructor(public type: ViewType, public cloneableTemplate: Element | string,
public encapsulation: ViewEncapsulation, public elementBinders: DomElementBinder[],
public hostAttributes: Map<string, string>, public rootTextNodeIndices: number[],
public boundTextNodeCount: number, public fragmentsRootNodeCount: number[],
public isSingleElementFragment: boolean) {}
}

View File

@ -1,397 +0,0 @@
import {isPresent, isBlank, StringWrapper} from 'angular2/src/core/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
import {
ListWrapper,
MapWrapper,
Set,
SetWrapper,
StringMapWrapper
} from 'angular2/src/core/facade/collection';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {
ASTWithSource,
AST,
AstTransformer,
PropertyRead,
LiteralArray,
ImplicitReceiver
} from 'angular2/src/core/change_detection/change_detection';
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './proto_view';
import {DomElementBinder, Event, HostAction} from './element_binder';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {TemplateCloner} from '../template_cloner';
import {
ViewType,
ViewEncapsulation,
ProtoViewDto,
DirectiveBinder,
RenderElementBinder,
EventBinding,
ElementPropertyBinding,
PropertyBindingType
} from '../../api';
import {NG_BINDING_CLASS, queryBoundTextNodeIndices, camelCaseToDashCase} from '../util';
import {EVENT_TARGET_SEPARATOR} from "../../event_config";
export class ProtoViewBuilder {
variableBindings = new Map<string, string>();
elements: ElementBinderBuilder[] = [];
rootTextBindings = new Map<Node, ASTWithSource>();
ngContentCount: number = 0;
hostAttributes = new Map<string, string>();
constructor(public rootElement, public type: ViewType,
public viewEncapsulation: ViewEncapsulation) {}
bindElement(element: HTMLElement, description: string = null): ElementBinderBuilder {
var builder = new ElementBinderBuilder(this.elements.length, element, description);
this.elements.push(builder);
DOM.addClass(element, NG_BINDING_CLASS);
return builder;
}
bindVariable(name: string, value: string) {
// Store the variable map from value to variable, reflecting how it will be used later by
// DomView. When a local is set to the view, a lookup for the variable name will take place
// keyed
// by the "value", or exported identifier. For example, ng-for sets a view local of "index".
// When this occurs, a lookup keyed by "index" must occur to find if there is a var referencing
// it.
this.variableBindings.set(value, name);
}
// Note: We don't store the node index until the compilation is complete,
// as the compiler might change the order of elements.
bindRootText(textNode: Text, expression: ASTWithSource) {
this.rootTextBindings.set(textNode, expression);
}
bindNgContent() { this.ngContentCount++; }
setHostAttribute(name: string, value: string) { this.hostAttributes.set(name, value); }
build(schemaRegistry: ElementSchemaRegistry, templateCloner: TemplateCloner): ProtoViewDto {
var domElementBinders = [];
var apiElementBinders = [];
var textNodeExpressions = [];
var rootTextNodeIndices = [];
var transitiveNgContentCount = this.ngContentCount;
queryBoundTextNodeIndices(DOM.content(this.rootElement), this.rootTextBindings,
(node, nodeIndex, expression) => {
textNodeExpressions.push(expression);
rootTextNodeIndices.push(nodeIndex);
});
ListWrapper.forEach(this.elements, (ebb: ElementBinderBuilder) => {
var directiveTemplatePropertyNames = new Set<string>();
var apiDirectiveBinders = ListWrapper.map(ebb.directives, (dbb: DirectiveBuilder) => {
ebb.eventBuilder.merge(dbb.eventBuilder);
ListWrapper.forEach(dbb.templatePropertyNames,
(name) => directiveTemplatePropertyNames.add(name));
return new DirectiveBinder({
directiveIndex: dbb.directiveIndex,
propertyBindings: dbb.propertyBindings,
eventBindings: dbb.eventBindings,
hostPropertyBindings: buildElementPropertyBindings(schemaRegistry, ebb.element, true,
dbb.hostPropertyBindings, null)
});
});
var nestedProtoView = isPresent(ebb.nestedProtoView) ?
ebb.nestedProtoView.build(schemaRegistry, templateCloner) :
null;
if (isPresent(nestedProtoView)) {
transitiveNgContentCount += nestedProtoView.transitiveNgContentCount;
}
var parentIndex = isPresent(ebb.parent) ? ebb.parent.index : -1;
var textNodeIndices = [];
queryBoundTextNodeIndices(ebb.element, ebb.textBindings, (node, nodeIndex, expression) => {
textNodeExpressions.push(expression);
textNodeIndices.push(nodeIndex);
});
apiElementBinders.push(new RenderElementBinder({
index: ebb.index,
parentIndex: parentIndex,
distanceToParent: ebb.distanceToParent,
directives: apiDirectiveBinders,
nestedProtoView: nestedProtoView,
propertyBindings:
buildElementPropertyBindings(schemaRegistry, ebb.element, isPresent(ebb.componentId),
ebb.propertyBindings, directiveTemplatePropertyNames),
variableBindings: ebb.variableBindings,
eventBindings: ebb.eventBindings,
readAttributes: ebb.readAttributes
}));
domElementBinders.push(new DomElementBinder({
textNodeIndices: textNodeIndices,
hasNestedProtoView: isPresent(nestedProtoView) || isPresent(ebb.componentId),
hasNativeShadowRoot: false,
eventLocals: new LiteralArray(ebb.eventBuilder.buildEventLocals()),
localEvents: ebb.eventBuilder.buildLocalEvents(),
globalEvents: ebb.eventBuilder.buildGlobalEvents()
}));
});
var rootNodeCount = DOM.childNodes(DOM.content(this.rootElement)).length;
return new ProtoViewDto({
render: new DomProtoViewRef(DomProtoView.create(
templateCloner, this.type, this.rootElement, this.viewEncapsulation, [rootNodeCount],
rootTextNodeIndices, domElementBinders, this.hostAttributes)),
type: this.type,
elementBinders: apiElementBinders,
variableBindings: this.variableBindings,
textBindings: textNodeExpressions,
transitiveNgContentCount: transitiveNgContentCount
});
}
}
export class ElementBinderBuilder {
parent: ElementBinderBuilder = null;
distanceToParent: number = 0;
directives: DirectiveBuilder[] = [];
nestedProtoView: ProtoViewBuilder = null;
propertyBindings = new Map<string, ASTWithSource>();
variableBindings = new Map<string, string>();
eventBindings: EventBinding[] = [];
eventBuilder: EventBuilder = new EventBuilder();
textBindings = new Map<Node, ASTWithSource>();
readAttributes = new Map<string, string>();
componentId: string = null;
constructor(public index: number, public element, description: string) {}
setParent(parent: ElementBinderBuilder, distanceToParent: number): ElementBinderBuilder {
this.parent = parent;
if (isPresent(parent)) {
this.distanceToParent = distanceToParent;
}
return this;
}
readAttribute(attrName: string) {
if (isBlank(this.readAttributes.get(attrName))) {
this.readAttributes.set(attrName, DOM.getAttribute(this.element, attrName));
}
}
bindDirective(directiveIndex: number): DirectiveBuilder {
var directive = new DirectiveBuilder(directiveIndex);
this.directives.push(directive);
return directive;
}
bindNestedProtoView(rootElement: HTMLElement): ProtoViewBuilder {
if (isPresent(this.nestedProtoView)) {
throw new BaseException('Only one nested view per element is allowed');
}
this.nestedProtoView =
new ProtoViewBuilder(rootElement, ViewType.EMBEDDED, ViewEncapsulation.None);
return this.nestedProtoView;
}
bindProperty(name: string, expression: ASTWithSource) {
this.propertyBindings.set(name, expression);
}
bindVariable(name: string, value: string) {
// When current is a view root, the variable bindings are set to the *nested* proto view.
// The root view conceptually signifies a new "block scope" (the nested view), to which
// the variables are bound.
if (isPresent(this.nestedProtoView)) {
this.nestedProtoView.bindVariable(name, value);
} else {
// Store the variable map from value to variable, reflecting how it will be used later by
// DomView. When a local is set to the view, a lookup for the variable name will take place
// keyed
// by the "value", or exported identifier. For example, ng-for sets a view local of "index".
// When this occurs, a lookup keyed by "index" must occur to find if there is a var
// referencing
// it.
this.variableBindings.set(value, name);
}
}
bindEvent(name: string, expression: ASTWithSource, target: string = null) {
this.eventBindings.push(this.eventBuilder.add(name, expression, target));
}
// Note: We don't store the node index until the compilation is complete,
// as the compiler might change the order of elements.
bindText(textNode: Text, expression: ASTWithSource) {
this.textBindings.set(textNode, expression);
}
setComponentId(componentId: string) { this.componentId = componentId; }
}
export class DirectiveBuilder {
// mapping from directive property name to AST for that directive
propertyBindings = new Map<string, ASTWithSource>();
// property names used in the template
templatePropertyNames: string[] = [];
hostPropertyBindings = new Map<string, ASTWithSource>();
eventBindings: EventBinding[] = [];
eventBuilder: EventBuilder = new EventBuilder();
constructor(public directiveIndex: number) {}
bindProperty(name: string, expression: ASTWithSource, elProp: string) {
this.propertyBindings.set(name, expression);
if (isPresent(elProp)) {
// we are filling in a set of property names that are bound to a property
// of at least one directive. This allows us to report "dangling" bindings.
this.templatePropertyNames.push(elProp);
}
}
bindHostProperty(name: string, expression: ASTWithSource) {
this.hostPropertyBindings.set(name, expression);
}
bindEvent(name: string, expression: ASTWithSource, target: string = null) {
this.eventBindings.push(this.eventBuilder.add(name, expression, target));
}
}
class EventBuilder extends AstTransformer {
locals: AST[] = [];
localEvents: Event[] = [];
globalEvents: Event[] = [];
_implicitReceiver: AST = new ImplicitReceiver();
constructor() { super(); }
add(name: string, source: ASTWithSource, target: string): EventBinding {
// TODO(tbosch): reenable this when we are parsing element properties
// out of action expressions
// var adjustedAst = astWithSource.ast.visit(this);
var adjustedAst = source.ast;
var fullName = isPresent(target) ? target + EVENT_TARGET_SEPARATOR + name : name;
var result =
new EventBinding(fullName, new ASTWithSource(adjustedAst, source.source, source.location));
var event = new Event(name, target, fullName);
if (isBlank(target)) {
this.localEvents.push(event);
} else {
this.globalEvents.push(event);
}
return result;
}
visitPropertyRead(ast: PropertyRead): PropertyRead {
var isEventAccess = false;
var current: AST = ast;
while (!isEventAccess && (current instanceof PropertyRead)) {
var am = <PropertyRead>current;
if (am.name == '$event') {
isEventAccess = true;
}
current = am.receiver;
}
if (isEventAccess) {
this.locals.push(ast);
var index = this.locals.length - 1;
return new PropertyRead(this._implicitReceiver, `${index}`, (arr) => arr[index]);
} else {
return ast;
}
}
buildEventLocals(): AST[] { return this.locals; }
buildLocalEvents(): Event[] { return this.localEvents; }
buildGlobalEvents(): Event[] { return this.globalEvents; }
merge(eventBuilder: EventBuilder) {
this._merge(this.localEvents, eventBuilder.localEvents);
this._merge(this.globalEvents, eventBuilder.globalEvents);
this.locals.concat(eventBuilder.locals);
}
_merge(host: Event[], tobeAdded: Event[]) {
var names = [];
for (var i = 0; i < host.length; i++) {
names.push(host[i].fullName);
}
for (var j = 0; j < tobeAdded.length; j++) {
if (!ListWrapper.contains(names, tobeAdded[j].fullName)) {
host.push(tobeAdded[j]);
}
}
}
}
const ATTRIBUTE_PREFIX = 'attr';
const CLASS_PREFIX = 'class';
const STYLE_PREFIX = 'style';
function buildElementPropertyBindings(
schemaRegistry: ElementSchemaRegistry, protoElement: /*element*/ any, isNgComponent: boolean,
bindingsInTemplate: Map<string, ASTWithSource>, directiveTemplatePropertyNames: Set<string>):
ElementPropertyBinding[] {
var propertyBindings = [];
MapWrapper.forEach(bindingsInTemplate, (ast, propertyNameInTemplate) => {
var propertyBinding = createElementPropertyBinding(schemaRegistry, ast, propertyNameInTemplate);
if (isPresent(directiveTemplatePropertyNames) &&
SetWrapper.has(directiveTemplatePropertyNames, propertyNameInTemplate)) {
// We do nothing because directives shadow native elements properties.
} else if (isValidElementPropertyBinding(schemaRegistry, protoElement, isNgComponent,
propertyBinding)) {
propertyBindings.push(propertyBinding);
} else {
var exMsg =
`Can't bind to '${propertyNameInTemplate}' since it isn't a known property of the '<${DOM.tagName(protoElement).toLowerCase()}>' element`;
// directiveTemplatePropertyNames is null for host property bindings
if (isPresent(directiveTemplatePropertyNames)) {
exMsg += ' and there are no matching directives with a corresponding property';
}
throw new BaseException(exMsg);
}
});
return propertyBindings;
}
function isValidElementPropertyBinding(schemaRegistry: ElementSchemaRegistry,
protoElement: /*element*/ any, isNgComponent: boolean,
binding: ElementPropertyBinding): boolean {
if (binding.type === PropertyBindingType.PROPERTY) {
if (!isNgComponent) {
return schemaRegistry.hasProperty(DOM.tagName(protoElement), binding.property);
} else {
// TODO(pk): change this logic as soon as we can properly detect custom elements
return DOM.hasProperty(protoElement, binding.property);
}
}
return true;
}
function createElementPropertyBinding(schemaRegistry: ElementSchemaRegistry, ast: ASTWithSource,
propertyNameInTemplate: string): ElementPropertyBinding {
var parts = propertyNameInTemplate.split('.');
if (parts.length === 1) {
var propName = schemaRegistry.getMappedPropName(parts[0]);
return new ElementPropertyBinding(PropertyBindingType.PROPERTY, ast, propName);
} else if (parts[0] == ATTRIBUTE_PREFIX) {
return new ElementPropertyBinding(PropertyBindingType.ATTRIBUTE, ast, parts[1]);
} else if (parts[0] == CLASS_PREFIX) {
return new ElementPropertyBinding(PropertyBindingType.CLASS, ast,
camelCaseToDashCase(parts[1]));
} else if (parts[0] == STYLE_PREFIX) {
var unit = parts.length > 2 ? parts[2] : null;
return new ElementPropertyBinding(PropertyBindingType.STYLE, ast, parts[1], unit);
} else {
throw new BaseException(`Invalid property name ${propertyNameInTemplate}`);
}
}

View File

@ -1,450 +0,0 @@
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {isPresent, isBlank, isArray} from 'angular2/src/core/facade/lang';
import {ListWrapper, SetWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './proto_view';
import {DomElementBinder} from './element_binder';
import {
RenderProtoViewMergeMapping,
RenderProtoViewRef,
ViewType,
ViewEncapsulation
} from '../../api';
import {
NG_BINDING_CLASS,
NG_CONTENT_ELEMENT_NAME,
ClonedProtoView,
cloneAndQueryProtoView,
queryBoundElements,
queryBoundTextNodeIndices,
NG_SHADOW_ROOT_ELEMENT_NAME,
isElementWithTag,
prependAll
} from '../util';
import {TemplateCloner} from '../template_cloner';
export function mergeProtoViewsRecursively(templateCloner: TemplateCloner,
protoViewRefs: Array<RenderProtoViewRef | any[]>):
RenderProtoViewMergeMapping {
// clone
var clonedProtoViews = [];
var hostViewAndBinderIndices: number[][] = [];
cloneProtoViews(templateCloner, protoViewRefs, clonedProtoViews, hostViewAndBinderIndices);
var mainProtoView: ClonedProtoView = clonedProtoViews[0];
// modify the DOM
mergeEmbeddedPvsIntoComponentOrRootPv(clonedProtoViews, hostViewAndBinderIndices);
var fragments = [];
var elementsWithNativeShadowRoot = new Set<Element>();
mergeComponents(clonedProtoViews, hostViewAndBinderIndices, fragments,
elementsWithNativeShadowRoot);
// Note: Need to remark parent elements of bound text nodes
// so that we can find them later via queryBoundElements!
markBoundTextNodeParentsAsBoundElements(clonedProtoViews);
// create a new root element with the changed fragments and elements
var fragmentsRootNodeCount = fragments.map(fragment => fragment.length);
var rootElement = createRootElementFromFragments(fragments);
var rootNode = DOM.content(rootElement);
// read out the new element / text node / ElementBinder order
var mergedBoundElements = queryBoundElements(rootNode, false);
var mergedBoundTextIndices = new Map<Node, number>();
var boundTextNodeMap: Map<Node, any> = indexBoundTextNodes(clonedProtoViews);
var rootTextNodeIndices =
calcRootTextNodeIndices(rootNode, boundTextNodeMap, mergedBoundTextIndices);
var mergedElementBinders =
calcElementBinders(clonedProtoViews, mergedBoundElements, elementsWithNativeShadowRoot,
boundTextNodeMap, mergedBoundTextIndices);
// create element / text index mappings
var mappedElementIndices = calcMappedElementIndices(clonedProtoViews, mergedBoundElements);
var mappedTextIndices = calcMappedTextIndices(clonedProtoViews, mergedBoundTextIndices);
// create result
var hostElementIndicesByViewIndex =
calcHostElementIndicesByViewIndex(clonedProtoViews, hostViewAndBinderIndices);
var nestedViewCounts = calcNestedViewCounts(hostViewAndBinderIndices);
var mergedProtoView =
DomProtoView.create(templateCloner, mainProtoView.original.type, rootElement,
mainProtoView.original.encapsulation, fragmentsRootNodeCount,
rootTextNodeIndices, mergedElementBinders, new Map<string, string>());
return new RenderProtoViewMergeMapping(new DomProtoViewRef(mergedProtoView),
fragmentsRootNodeCount.length, mappedElementIndices,
mergedBoundElements.length, mappedTextIndices,
hostElementIndicesByViewIndex, nestedViewCounts);
}
function cloneProtoViews(
templateCloner: TemplateCloner, protoViewRefs: Array<RenderProtoViewRef | any[]>,
targetClonedProtoViews: ClonedProtoView[], targetHostViewAndBinderIndices: number[][]) {
var hostProtoView = resolveInternalDomProtoView(protoViewRefs[0]);
var hostPvIdx = targetClonedProtoViews.length;
targetClonedProtoViews.push(cloneAndQueryProtoView(templateCloner, hostProtoView, false));
if (targetHostViewAndBinderIndices.length === 0) {
targetHostViewAndBinderIndices.push([null, null]);
}
var protoViewIdx = 1;
for (var i = 0; i < hostProtoView.elementBinders.length; i++) {
var binder = hostProtoView.elementBinders[i];
if (binder.hasNestedProtoView) {
var nestedEntry = protoViewRefs[protoViewIdx++];
if (isPresent(nestedEntry)) {
targetHostViewAndBinderIndices.push([hostPvIdx, i]);
if (isArray(nestedEntry)) {
cloneProtoViews(templateCloner, <any[]>nestedEntry, targetClonedProtoViews,
targetHostViewAndBinderIndices);
} else {
targetClonedProtoViews.push(cloneAndQueryProtoView(
templateCloner, resolveInternalDomProtoView(nestedEntry), false));
}
}
}
}
}
function markBoundTextNodeParentsAsBoundElements(mergableProtoViews: ClonedProtoView[]) {
mergableProtoViews.forEach((mergableProtoView) => {
mergableProtoView.boundTextNodes.forEach((textNode) => {
var parentNode = textNode.parentNode;
if (isPresent(parentNode) && DOM.isElementNode(parentNode)) {
DOM.addClass(parentNode, NG_BINDING_CLASS);
}
});
});
}
function indexBoundTextNodes(mergableProtoViews: ClonedProtoView[]): Map<Node, any> {
var boundTextNodeMap = new Map<Node, any>();
for (var pvIndex = 0; pvIndex < mergableProtoViews.length; pvIndex++) {
var mergableProtoView = mergableProtoViews[pvIndex];
mergableProtoView.boundTextNodes.forEach(
(textNode) => { boundTextNodeMap.set(textNode, null); });
}
return boundTextNodeMap;
}
function mergeEmbeddedPvsIntoComponentOrRootPv(clonedProtoViews: ClonedProtoView[],
hostViewAndBinderIndices: number[][]) {
var nearestHostComponentOrRootPvIndices =
calcNearestHostComponentOrRootPvIndices(clonedProtoViews, hostViewAndBinderIndices);
for (var viewIdx = 1; viewIdx < clonedProtoViews.length; viewIdx++) {
var clonedProtoView = clonedProtoViews[viewIdx];
if (clonedProtoView.original.type === ViewType.EMBEDDED) {
var hostComponentIdx = nearestHostComponentOrRootPvIndices[viewIdx];
var hostPv = clonedProtoViews[hostComponentIdx];
clonedProtoView.fragments.forEach((fragment) => hostPv.fragments.push(fragment));
}
}
}
function calcNearestHostComponentOrRootPvIndices(clonedProtoViews: ClonedProtoView[],
hostViewAndBinderIndices: number[][]): number[] {
var nearestHostComponentOrRootPvIndices = ListWrapper.createFixedSize(clonedProtoViews.length);
nearestHostComponentOrRootPvIndices[0] = null;
for (var viewIdx = 1; viewIdx < hostViewAndBinderIndices.length; viewIdx++) {
var hostViewIdx = hostViewAndBinderIndices[viewIdx][0];
var hostProtoView = clonedProtoViews[hostViewIdx];
if (hostViewIdx === 0 || hostProtoView.original.type === ViewType.COMPONENT) {
nearestHostComponentOrRootPvIndices[viewIdx] = hostViewIdx;
} else {
nearestHostComponentOrRootPvIndices[viewIdx] =
nearestHostComponentOrRootPvIndices[hostViewIdx];
}
}
return nearestHostComponentOrRootPvIndices;
}
function mergeComponents(clonedProtoViews: ClonedProtoView[], hostViewAndBinderIndices: number[][],
targetFragments: Node[][],
targetElementsWithNativeShadowRoot: Set<Element>) {
var hostProtoView = clonedProtoViews[0];
hostProtoView.fragments.forEach((fragment) => targetFragments.push(fragment));
for (var viewIdx = 1; viewIdx < clonedProtoViews.length; viewIdx++) {
var hostViewIdx = hostViewAndBinderIndices[viewIdx][0];
var hostBinderIdx = hostViewAndBinderIndices[viewIdx][1];
var hostProtoView = clonedProtoViews[hostViewIdx];
var clonedProtoView = clonedProtoViews[viewIdx];
if (clonedProtoView.original.type === ViewType.COMPONENT) {
mergeComponent(hostProtoView, hostBinderIdx, clonedProtoView, targetFragments,
targetElementsWithNativeShadowRoot);
}
}
}
function mergeComponent(hostProtoView: ClonedProtoView, binderIdx: number,
nestedProtoView: ClonedProtoView, targetFragments: Node[][],
targetElementsWithNativeShadowRoot: Set<Element>) {
var hostElement = hostProtoView.boundElements[binderIdx];
// We wrap the fragments into elements so that we can expand <ng-content>
// even for root nodes in the fragment without special casing them.
var fragmentElements = mapFragmentsIntoElements(nestedProtoView.fragments);
var contentElements = findContentElements(fragmentElements);
var projectableNodes = DOM.childNodesAsList(hostElement);
for (var i = 0; i < contentElements.length; i++) {
var contentElement = contentElements[i];
var select = DOM.getAttribute(contentElement, 'select');
projectableNodes = projectMatchingNodes(select, contentElement, projectableNodes);
}
// unwrap the fragment elements into arrays of nodes after projecting
var fragments = extractFragmentNodesFromElements(fragmentElements);
var useNativeShadowRoot = nestedProtoView.original.encapsulation === ViewEncapsulation.Native;
if (useNativeShadowRoot) {
targetElementsWithNativeShadowRoot.add(hostElement);
}
MapWrapper.forEach(nestedProtoView.original.hostAttributes, (attrValue, attrName) => {
DOM.setAttribute(hostElement, attrName, attrValue);
});
appendComponentNodesToHost(hostProtoView, binderIdx, fragments[0], useNativeShadowRoot);
for (var i = 1; i < fragments.length; i++) {
targetFragments.push(fragments[i]);
}
}
function mapFragmentsIntoElements(fragments: Node[][]): Element[] {
return fragments.map(fragment => {
var fragmentElement = DOM.createTemplate('');
fragment.forEach(node => DOM.appendChild(DOM.content(fragmentElement), node));
return fragmentElement;
});
}
function extractFragmentNodesFromElements(fragmentElements: Element[]): Node[][] {
return fragmentElements.map(
(fragmentElement) => { return DOM.childNodesAsList(DOM.content(fragmentElement)); });
}
function findContentElements(fragmentElements: Element[]): Element[] {
var contentElements = [];
fragmentElements.forEach((fragmentElement: Element) => {
var fragmentContentElements =
DOM.querySelectorAll(DOM.content(fragmentElement), NG_CONTENT_ELEMENT_NAME);
for (var i = 0; i < fragmentContentElements.length; i++) {
contentElements.push(fragmentContentElements[i]);
}
});
return sortContentElements(contentElements);
}
function appendComponentNodesToHost(hostProtoView: ClonedProtoView, binderIdx: number,
componentRootNodes: Node[], useNativeShadowRoot: boolean) {
var hostElement = hostProtoView.boundElements[binderIdx];
if (useNativeShadowRoot) {
var shadowRootWrapper = DOM.createElement(NG_SHADOW_ROOT_ELEMENT_NAME);
for (var i = 0; i < componentRootNodes.length; i++) {
DOM.appendChild(shadowRootWrapper, componentRootNodes[i]);
}
var firstChild = DOM.firstChild(hostElement);
if (isPresent(firstChild)) {
DOM.insertBefore(firstChild, shadowRootWrapper);
} else {
DOM.appendChild(hostElement, shadowRootWrapper);
}
} else {
DOM.clearNodes(hostElement);
for (var i = 0; i < componentRootNodes.length; i++) {
DOM.appendChild(hostElement, componentRootNodes[i]);
}
}
}
function projectMatchingNodes(selector: string, contentElement: Element, nodes: Node[]): Node[] {
var remaining = [];
DOM.insertBefore(contentElement, DOM.createComment('['));
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
var matches = false;
if (isWildcard(selector)) {
matches = true;
} else if (DOM.isElementNode(node) && DOM.elementMatches(node, selector)) {
matches = true;
}
if (matches) {
DOM.insertBefore(contentElement, node);
} else {
remaining.push(node);
}
}
DOM.insertBefore(contentElement, DOM.createComment(']'));
DOM.remove(contentElement);
return remaining;
}
function isWildcard(selector): boolean {
return isBlank(selector) || selector.length === 0 || selector == '*';
}
// we need to sort content elements as they can originate from
// different sub views
function sortContentElements(contentElements: Element[]): Element[] {
// for now, only move the wildcard selector to the end.
// TODO(tbosch): think about sorting by selector specificity...
var firstWildcard = null;
var sorted = [];
contentElements.forEach((contentElement) => {
var select = DOM.getAttribute(contentElement, 'select');
if (isWildcard(select)) {
if (isBlank(firstWildcard)) {
firstWildcard = contentElement;
}
} else {
sorted.push(contentElement);
}
});
if (isPresent(firstWildcard)) {
sorted.push(firstWildcard);
}
return sorted;
}
function createRootElementFromFragments(fragments: Node[][]): Element {
var rootElement = DOM.createTemplate('');
var rootNode = DOM.content(rootElement);
for (var i = 0; i < fragments.length; i++) {
var fragment = fragments[i];
if (i >= 1) {
// Note: We need to separate fragments by a comment so that sibling
// text nodes don't get merged when we serialize the DomProtoView into a string
// and parse it back again.
DOM.appendChild(rootNode, DOM.createComment('|'));
}
fragment.forEach((node) => { DOM.appendChild(rootNode, node); });
}
return rootElement;
}
function calcRootTextNodeIndices(rootNode: Node, boundTextNodes: Map<Node, any>,
targetBoundTextIndices: Map<Node, number>): number[] {
var rootTextNodeIndices = [];
queryBoundTextNodeIndices(rootNode, boundTextNodes, (textNode, nodeIndex, _) => {
rootTextNodeIndices.push(nodeIndex);
targetBoundTextIndices.set(textNode, targetBoundTextIndices.size);
});
return rootTextNodeIndices;
}
function calcElementBinders(clonedProtoViews: ClonedProtoView[], mergedBoundElements: Element[],
elementsWithNativeShadowRoot: Set<Element>,
boundTextNodes: Map<Node, any>,
targetBoundTextIndices: Map<Node, number>): DomElementBinder[] {
var elementBinderByElement: Map<Element, DomElementBinder> =
indexElementBindersByElement(clonedProtoViews);
var mergedElementBinders = [];
for (var i = 0; i < mergedBoundElements.length; i++) {
var element = mergedBoundElements[i];
var textNodeIndices = [];
queryBoundTextNodeIndices(element, boundTextNodes, (textNode, nodeIndex, _) => {
textNodeIndices.push(nodeIndex);
targetBoundTextIndices.set(textNode, targetBoundTextIndices.size);
});
mergedElementBinders.push(
updateElementBinders(elementBinderByElement.get(element), textNodeIndices,
SetWrapper.has(elementsWithNativeShadowRoot, element)));
}
return mergedElementBinders;
}
function indexElementBindersByElement(mergableProtoViews: ClonedProtoView[]):
Map<Element, DomElementBinder> {
var elementBinderByElement = new Map<Element, DomElementBinder>();
mergableProtoViews.forEach((mergableProtoView) => {
for (var i = 0; i < mergableProtoView.boundElements.length; i++) {
var el = mergableProtoView.boundElements[i];
if (isPresent(el)) {
elementBinderByElement.set(el, mergableProtoView.original.elementBinders[i]);
}
}
});
return elementBinderByElement;
}
function updateElementBinders(elementBinder: DomElementBinder, textNodeIndices: number[],
hasNativeShadowRoot: boolean): DomElementBinder {
var result;
if (isBlank(elementBinder)) {
result = new DomElementBinder({
textNodeIndices: textNodeIndices,
hasNestedProtoView: false,
eventLocals: null,
localEvents: [],
globalEvents: [],
hasNativeShadowRoot: false
});
} else {
result = new DomElementBinder({
textNodeIndices: textNodeIndices,
hasNestedProtoView: false,
eventLocals: elementBinder.eventLocals,
localEvents: elementBinder.localEvents,
globalEvents: elementBinder.globalEvents,
hasNativeShadowRoot: hasNativeShadowRoot
});
}
return result;
}
function calcMappedElementIndices(clonedProtoViews: ClonedProtoView[],
mergedBoundElements: Element[]): number[] {
var mergedBoundElementIndices: Map<Element, number> = indexArray(mergedBoundElements);
var mappedElementIndices = [];
clonedProtoViews.forEach((clonedProtoView) => {
clonedProtoView.boundElements.forEach((boundElement) => {
var mappedElementIndex = mergedBoundElementIndices.get(boundElement);
mappedElementIndices.push(mappedElementIndex);
});
});
return mappedElementIndices;
}
function calcMappedTextIndices(clonedProtoViews: ClonedProtoView[],
mergedBoundTextIndices: Map<Node, number>): number[] {
var mappedTextIndices = [];
clonedProtoViews.forEach((clonedProtoView) => {
clonedProtoView.boundTextNodes.forEach((textNode) => {
var mappedTextIndex = mergedBoundTextIndices.get(textNode);
mappedTextIndices.push(mappedTextIndex);
});
});
return mappedTextIndices;
}
function calcHostElementIndicesByViewIndex(clonedProtoViews: ClonedProtoView[],
hostViewAndBinderIndices: number[][]): number[] {
var hostElementIndices = [null];
var viewElementOffsets = [0];
var elementIndex = clonedProtoViews[0].original.elementBinders.length;
for (var viewIdx = 1; viewIdx < hostViewAndBinderIndices.length; viewIdx++) {
viewElementOffsets.push(elementIndex);
elementIndex += clonedProtoViews[viewIdx].original.elementBinders.length;
var hostViewIdx = hostViewAndBinderIndices[viewIdx][0];
var hostBinderIdx = hostViewAndBinderIndices[viewIdx][1];
hostElementIndices.push(viewElementOffsets[hostViewIdx] + hostBinderIdx);
}
return hostElementIndices;
}
function calcNestedViewCounts(hostViewAndBinderIndices: number[][]): number[] {
var nestedViewCounts = ListWrapper.createFixedSize(hostViewAndBinderIndices.length);
ListWrapper.fill(nestedViewCounts, 0);
for (var viewIdx = hostViewAndBinderIndices.length - 1; viewIdx >= 1; viewIdx--) {
var hostViewAndElementIdx = hostViewAndBinderIndices[viewIdx];
if (isPresent(hostViewAndElementIdx)) {
nestedViewCounts[hostViewAndElementIdx[0]] += nestedViewCounts[viewIdx] + 1;
}
}
return nestedViewCounts;
}
function indexArray(arr: any[]): Map<any, number> {
var map = new Map<any, number>();
for (var i = 0; i < arr.length; i++) {
map.set(arr[i], i);
}
return map;
}

View File

@ -1,87 +0,0 @@
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {isPresent, isBlank, stringify} from 'angular2/src/core/facade/lang';
import {DomProtoView} from './proto_view';
import {RenderViewRef, RenderEventDispatcher} from '../../api';
import {camelCaseToDashCase} from '../util';
export function resolveInternalDomView(viewRef: RenderViewRef): DomView {
return (<DomViewRef>viewRef)._view;
}
export class DomViewRef extends RenderViewRef {
constructor(public _view: DomView) { super(); }
}
/**
* Const of making objects: http://jsperf.com/instantiate-size-of-object
*/
export class DomView {
hydrated: boolean = false;
eventDispatcher: RenderEventDispatcher = null;
eventHandlerRemovers: Function[] = [];
constructor(public proto: DomProtoView, public boundTextNodes: Node[],
public boundElements: Element[]) {}
setElementProperty(elementIndex: number, propertyName: string, value: any) {
DOM.setProperty(this.boundElements[elementIndex], propertyName, value);
}
setElementAttribute(elementIndex: number, attributeName: string, value: string) {
var element = this.boundElements[elementIndex];
var dashCasedAttributeName = camelCaseToDashCase(attributeName);
if (isPresent(value)) {
DOM.setAttribute(element, dashCasedAttributeName, stringify(value));
} else {
DOM.removeAttribute(element, dashCasedAttributeName);
}
}
setElementClass(elementIndex: number, className: string, isAdd: boolean) {
var element = this.boundElements[elementIndex];
if (isAdd) {
DOM.addClass(element, className);
} else {
DOM.removeClass(element, className);
}
}
setElementStyle(elementIndex: number, styleName: string, value: string) {
var element = this.boundElements[elementIndex];
var dashCasedStyleName = camelCaseToDashCase(styleName);
if (isPresent(value)) {
DOM.setStyle(element, dashCasedStyleName, stringify(value));
} else {
DOM.removeStyle(element, dashCasedStyleName);
}
}
invokeElementMethod(elementIndex: number, methodName: string, args: any[]) {
var element = this.boundElements[elementIndex];
DOM.invoke(element, methodName, args);
}
setText(textIndex: number, value: string) { DOM.setText(this.boundTextNodes[textIndex], value); }
dispatchEvent(elementIndex: number, eventName: string, event: Event): boolean {
var allowDefaultBehavior = true;
if (isPresent(this.eventDispatcher)) {
var evalLocals = new Map<string, any>();
evalLocals.set('$event', event);
// TODO(tbosch): reenable this when we are parsing element properties
// out of action expressions
// var localValues = this.proto.elementBinders[elementIndex].eventLocals.eval(null, new
// Locals(null, evalLocals));
// this.eventDispatcher.dispatchEvent(elementIndex, eventName, localValues);
allowDefaultBehavior =
this.eventDispatcher.dispatchRenderEvent(elementIndex, eventName, evalLocals);
if (!allowDefaultBehavior) {
DOM.preventDefault(event);
}
}
return allowDefaultBehavior;
}
}

View File

@ -1,8 +0,0 @@
import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
import * as viewModule from './view';
export class DomViewContainer {
// The order in this list matches the DOM order.
views: Array<viewModule.DomView> = [];
}

View File

@ -4,10 +4,7 @@
* This module provides advanced support for extending dom strategy.
*/
export * from './dom/compiler/view_loader';
export * from './dom/view/shared_styles_host';
export * from './dom/compiler/compiler';
export * from './dom/dom_renderer';
export * from './dom/dom_tokens';
export * from './dom/template_cloner';
export * from './api';

View File

@ -8,8 +8,6 @@ import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
import {
Parser,
Lexer,
ChangeDetection,
DynamicChangeDetection,
IterableDiffers,
defaultIterableDiffers,
KeyValueDiffers,
@ -17,18 +15,14 @@ import {
ChangeDetectorGenConfig
} from 'angular2/src/core/change_detection/change_detection';
import {ExceptionHandler} from 'angular2/src/core/facade/exceptions';
import {ViewLoader} from 'angular2/src/core/render/dom/compiler/view_loader';
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver';
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
import {XHR} from 'angular2/src/core/render/xhr';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
import {AnchorBasedAppRootUrl} from 'angular2/src/core/services/anchor_based_app_root_url';
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
import {StyleInliner} from 'angular2/src/core/render/dom/compiler/style_inliner';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
@ -57,16 +51,13 @@ import {FunctionWrapper, Type} from 'angular2/src/core/facade/lang';
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
import {RenderCompiler, Renderer} from 'angular2/src/core/render/api';
import {Renderer} from 'angular2/src/core/render/api';
import {
DomRenderer,
DOCUMENT,
DefaultDomCompiler,
APP_ID,
SharedStylesHost,
DomSharedStylesHost,
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
TemplateCloner
DomSharedStylesHost
} from 'angular2/src/core/render/render';
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
import {
@ -114,10 +105,6 @@ function _getAppBindings() {
DomRenderer,
bind(Renderer).toAlias(DomRenderer),
bind(APP_ID).toValue('a'),
TemplateCloner,
bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(-1),
DefaultDomCompiler,
bind(RenderCompiler).toAlias(DefaultDomCompiler),
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
DomSharedStylesHost,
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
@ -133,9 +120,7 @@ function _getAppBindings() {
DEFAULT_PIPES,
bind(IterableDiffers).toValue(defaultIterableDiffers),
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
bind(ChangeDetection).toValue(new DynamicChangeDetection()),
Log,
ViewLoader,
DynamicComponentLoader,
PipeResolver,
Parser,
@ -143,12 +128,9 @@ function _getAppBindings() {
bind(ExceptionHandler).toValue(new ExceptionHandler(DOM)),
bind(LocationStrategy).toClass(MockLocationStrategy),
bind(XHR).toClass(MockXHR),
ComponentUrlMapper,
UrlResolver,
AnchorBasedAppRootUrl,
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
StyleUrlResolver,
StyleInliner,
TestComponentBuilder,
bind(NgZone).toClass(MockNgZone),
bind(AnimationBuilder).toClass(MockAnimationBuilder),

View File

@ -15,7 +15,7 @@ import {
export const ON_WEB_WORKER = CONST_EXPR(new OpaqueToken('WebWorker.onWebWorker'));
export class WebWorkerElementRef implements RenderElementRef {
constructor(public renderView: RenderViewRef, public renderBoundElementIndex: number) {}
constructor(public renderView: RenderViewRef, public boundElementIndex: number) {}
}
export class WebWorkerTemplateCmd implements RenderTemplateCmd {

View File

@ -15,21 +15,12 @@ import {
MapWrapper
} from "angular2/src/core/facade/collection";
import {
ProtoViewDto,
RenderDirectiveMetadata,
RenderElementBinder,
DirectiveBinder,
ElementPropertyBinding,
EventBinding,
ViewDefinition,
RenderProtoViewRef,
RenderProtoViewMergeMapping,
RenderViewRef,
RenderFragmentRef,
RenderElementRef,
ViewType,
ViewEncapsulation,
PropertyBindingType,
RenderTemplateCmd,
RenderCommandVisitor,
RenderTextCmd,
@ -49,8 +40,6 @@ import {
WebWorkerEndComponentCmd,
WebWorkerEmbeddedTemplateCmd
} from 'angular2/src/web_workers/shared/api';
import {AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
import {Parser} from "angular2/src/core/change_detection/parser/parser";
import {Injectable} from "angular2/src/core/di";
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
import {
@ -64,7 +53,7 @@ export const PRIMITIVE: Type = String;
@Injectable()
export class Serializer {
private _enumRegistry: Map<any, Map<number, any>>;
constructor(private _parser: Parser, private _protoViewStore: RenderProtoViewRefStore,
constructor(private _protoViewStore: RenderProtoViewRefStore,
private _renderViewStore: RenderViewWithFragmentsStore) {
this._enumRegistry = new Map<any, Map<number, any>>();
@ -79,13 +68,6 @@ export class Serializer {
viewEncapsulationMap[1] = ViewEncapsulation.Native;
viewEncapsulationMap[2] = ViewEncapsulation.None;
this._enumRegistry.set(ViewEncapsulation, viewEncapsulationMap);
var propertyBindingTypeMap = new Map<number, any>();
propertyBindingTypeMap[0] = PropertyBindingType.PROPERTY;
propertyBindingTypeMap[1] = PropertyBindingType.ATTRIBUTE;
propertyBindingTypeMap[2] = PropertyBindingType.CLASS;
propertyBindingTypeMap[3] = PropertyBindingType.STYLE;
this._enumRegistry.set(PropertyBindingType, propertyBindingTypeMap);
}
serialize(obj: any, type: Type): Object {
@ -100,32 +82,14 @@ export class Serializer {
if (type == PRIMITIVE) {
return obj;
}
if (type == ViewDefinition) {
return this._serializeViewDefinition(obj);
} else if (type == DirectiveBinder) {
return this._serializeDirectiveBinder(obj);
} else if (type == ProtoViewDto) {
return this._serializeProtoViewDto(obj);
} else if (type == RenderElementBinder) {
return this._serializeElementBinder(obj);
} else if (type == RenderDirectiveMetadata) {
return this._serializeDirectiveMetadata(obj);
} else if (type == ASTWithSource) {
return this._serializeASTWithSource(obj);
} else if (type == RenderProtoViewRef) {
if (type == RenderProtoViewRef) {
return this._protoViewStore.serialize(obj);
} else if (type == RenderProtoViewMergeMapping) {
return this._serializeRenderProtoViewMergeMapping(obj);
} else if (type == RenderViewRef) {
return this._renderViewStore.serializeRenderViewRef(obj);
} else if (type == RenderFragmentRef) {
return this._renderViewStore.serializeRenderFragmentRef(obj);
} else if (type == WebWorkerElementRef) {
return this._serializeWorkerElementRef(obj);
} else if (type == ElementPropertyBinding) {
return this._serializeElementPropertyBinding(obj);
} else if (type == EventBinding) {
return this._serializeEventBinding(obj);
} else if (type == WebWorkerTemplateCmd) {
return serializeTemplateCmd(obj);
} else {
@ -146,32 +110,14 @@ export class Serializer {
return map;
}
if (type == ViewDefinition) {
return this._deserializeViewDefinition(map);
} else if (type == DirectiveBinder) {
return this._deserializeDirectiveBinder(map);
} else if (type == ProtoViewDto) {
return this._deserializeProtoViewDto(map);
} else if (type == RenderDirectiveMetadata) {
return this._deserializeDirectiveMetadata(map);
} else if (type == RenderElementBinder) {
return this._deserializeElementBinder(map);
} else if (type == ASTWithSource) {
return this._deserializeASTWithSource(map, data);
} else if (type == RenderProtoViewRef) {
if (type == RenderProtoViewRef) {
return this._protoViewStore.deserialize(map);
} else if (type == RenderProtoViewMergeMapping) {
return this._deserializeRenderProtoViewMergeMapping(map);
} else if (type == RenderViewRef) {
return this._renderViewStore.deserializeRenderViewRef(map);
} else if (type == RenderFragmentRef) {
return this._renderViewStore.deserializeRenderFragmentRef(map);
} else if (type == WebWorkerElementRef) {
return this._deserializeWorkerElementRef(map);
} else if (type == EventBinding) {
return this._deserializeEventBinding(map);
} else if (type == ElementPropertyBinding) {
return this._deserializeElementPropertyBinding(map);
} else if (type == WebWorkerTemplateCmd) {
return deserializeTemplateCmd(map);
} else {
@ -211,220 +157,16 @@ export class Serializer {
allocateRenderViews(fragmentCount: number) { this._renderViewStore.allocate(fragmentCount); }
private _serializeElementPropertyBinding(binding:
ElementPropertyBinding): StringMap<string, any> {
return {
'type': serializeEnum(binding.type),
'astWithSource': this.serialize(binding.astWithSource, ASTWithSource),
'property': binding.property,
'unit': binding.unit
};
}
private _deserializeElementPropertyBinding(map: StringMap<string, any>): ElementPropertyBinding {
var type = deserializeEnum(map['type'], this._enumRegistry.get(PropertyBindingType));
var ast = this.deserialize(map['astWithSource'], ASTWithSource, "binding");
return new ElementPropertyBinding(type, ast, map['property'], map['unit']);
}
private _serializeEventBinding(binding: EventBinding): StringMap<string, any> {
return {'fullName': binding.fullName, 'source': this.serialize(binding.source, ASTWithSource)};
}
private _deserializeEventBinding(map: StringMap<string, any>): EventBinding {
return new EventBinding(map['fullName'],
this.deserialize(map['source'], ASTWithSource, "action"));
}
private _serializeWorkerElementRef(elementRef: RenderElementRef): StringMap<string, any> {
return {
'renderView': this.serialize(elementRef.renderView, RenderViewRef),
'renderBoundElementIndex': elementRef.renderBoundElementIndex
'boundElementIndex': elementRef.boundElementIndex
};
}
private _deserializeWorkerElementRef(map: StringMap<string, any>): RenderElementRef {
return new WebWorkerElementRef(this.deserialize(map['renderView'], RenderViewRef),
map['renderBoundElementIndex']);
}
private _serializeRenderProtoViewMergeMapping(mapping: RenderProtoViewMergeMapping): Object {
return {
'mergedProtoViewRef': this._protoViewStore.serialize(mapping.mergedProtoViewRef),
'fragmentCount': mapping.fragmentCount,
'mappedElementIndices': mapping.mappedElementIndices,
'mappedElementCount': mapping.mappedElementCount,
'mappedTextIndices': mapping.mappedTextIndices,
'hostElementIndicesByViewIndex': mapping.hostElementIndicesByViewIndex,
'nestedViewCountByViewIndex': mapping.nestedViewCountByViewIndex
};
}
private _deserializeRenderProtoViewMergeMapping(obj: StringMap<string, any>):
RenderProtoViewMergeMapping {
return new RenderProtoViewMergeMapping(
this._protoViewStore.deserialize(obj['mergedProtoViewRef']), obj['fragmentCount'],
obj['mappedElementIndices'], obj['mappedElementCount'], obj['mappedTextIndices'],
obj['hostElementIndicesByViewIndex'], obj['nestedViewCountByViewIndex']);
}
private _serializeASTWithSource(tree: ASTWithSource): Object {
return {'input': tree.source, 'location': tree.location};
}
private _deserializeASTWithSource(obj: StringMap<string, any>, data: string): AST {
// TODO: make ASTs serializable
var ast: AST;
switch (data) {
case "action":
ast = this._parser.parseAction(obj['input'], obj['location']);
break;
case "binding":
ast = this._parser.parseBinding(obj['input'], obj['location']);
break;
case "interpolation":
ast = this._parser.parseInterpolation(obj['input'], obj['location']);
break;
default:
throw "No AST deserializer for " + data;
}
return ast;
}
private _serializeViewDefinition(view: ViewDefinition): Object {
return {
'componentId': view.componentId,
'templateAbsUrl': view.templateAbsUrl,
'template': view.template,
'directives': this.serialize(view.directives, RenderDirectiveMetadata),
'styleAbsUrls': view.styleAbsUrls,
'styles': view.styles,
'encapsulation': serializeEnum(view.encapsulation)
};
}
private _deserializeViewDefinition(obj: StringMap<string, any>): ViewDefinition {
return new ViewDefinition({
componentId: obj['componentId'],
templateAbsUrl: obj['templateAbsUrl'], template: obj['template'],
directives: this.deserialize(obj['directives'], RenderDirectiveMetadata),
styleAbsUrls: obj['styleAbsUrls'],
styles: obj['styles'],
encapsulation:
deserializeEnum(obj['encapsulation'], this._enumRegistry.get(ViewEncapsulation))
});
}
private _serializeDirectiveBinder(binder: DirectiveBinder): Object {
return {
'directiveIndex': binder.directiveIndex,
'propertyBindings': this.mapToObject(binder.propertyBindings, ASTWithSource),
'eventBindings': this.serialize(binder.eventBindings, EventBinding),
'hostPropertyBindings': this.serialize(binder.hostPropertyBindings, ElementPropertyBinding)
};
}
private _deserializeDirectiveBinder(obj: StringMap<string, any>): DirectiveBinder {
return new DirectiveBinder({
directiveIndex: obj['directiveIndex'],
propertyBindings: this.objectToMap(obj['propertyBindings'], ASTWithSource, "binding"),
eventBindings: this.deserialize(obj['eventBindings'], EventBinding),
hostPropertyBindings: this.deserialize(obj['hostPropertyBindings'], ElementPropertyBinding)
});
}
private _serializeElementBinder(binder: RenderElementBinder): Object {
return {
'index': binder.index,
'parentIndex': binder.parentIndex,
'distanceToParent': binder.distanceToParent,
'directives': this.serialize(binder.directives, DirectiveBinder),
'nestedProtoView': this.serialize(binder.nestedProtoView, ProtoViewDto),
'propertyBindings': this.serialize(binder.propertyBindings, ElementPropertyBinding),
'variableBindings': this.mapToObject(binder.variableBindings),
'eventBindings': this.serialize(binder.eventBindings, EventBinding),
'readAttributes': this.mapToObject(binder.readAttributes)
};
}
private _deserializeElementBinder(obj: StringMap<string, any>): RenderElementBinder {
return new RenderElementBinder({
index: obj['index'],
parentIndex: obj['parentIndex'],
distanceToParent: obj['distanceToParent'],
directives: this.deserialize(obj['directives'], DirectiveBinder),
nestedProtoView: this.deserialize(obj['nestedProtoView'], ProtoViewDto),
propertyBindings: this.deserialize(obj['propertyBindings'], ElementPropertyBinding),
variableBindings: this.objectToMap(obj['variableBindings']),
eventBindings: this.deserialize(obj['eventBindings'], EventBinding),
readAttributes: this.objectToMap(obj['readAttributes'])
});
}
private _serializeProtoViewDto(view: ProtoViewDto): Object {
return {
'render': this._protoViewStore.serialize(view.render),
'elementBinders': this.serialize(view.elementBinders, RenderElementBinder),
'variableBindings': this.mapToObject(view.variableBindings),
'type': serializeEnum(view.type),
'textBindings': this.serialize(view.textBindings, ASTWithSource),
'transitiveNgContentCount': view.transitiveNgContentCount
};
}
private _deserializeProtoViewDto(obj: StringMap<string, any>): ProtoViewDto {
return new ProtoViewDto({
render: this._protoViewStore.deserialize(obj["render"]),
elementBinders: this.deserialize(obj['elementBinders'], RenderElementBinder),
variableBindings: this.objectToMap(obj['variableBindings']),
textBindings: this.deserialize(obj['textBindings'], ASTWithSource, "interpolation"),
type: deserializeEnum(obj['type'], this._enumRegistry.get(ViewType)),
transitiveNgContentCount: obj['transitiveNgContentCount']
});
}
private _serializeDirectiveMetadata(meta: RenderDirectiveMetadata): Object {
var obj = {
'id': meta.id,
'selector': meta.selector,
'compileChildren': meta.compileChildren,
'events': meta.outputs,
'inputs': meta.inputs,
'readAttributes': meta.readAttributes,
'type': meta.type,
'callOnDestroy': meta.callOnDestroy,
'callOnChanges': meta.callOnChanges,
'callDoCheck': meta.callDoCheck,
'callOnInit': meta.callOnInit,
'callAfterContentChecked': meta.callAfterContentChecked,
'changeDetection': meta.changeDetection,
'exportAs': meta.exportAs,
'hostProperties': this.mapToObject(meta.hostProperties),
'hostListeners': this.mapToObject(meta.hostListeners),
'hostAttributes': this.mapToObject(meta.hostAttributes)
};
return obj;
}
private _deserializeDirectiveMetadata(obj: StringMap<string, any>): RenderDirectiveMetadata {
return new RenderDirectiveMetadata({
id: obj['id'],
selector: obj['selector'],
compileChildren: obj['compileChildren'],
hostProperties: this.objectToMap(obj['hostProperties']),
hostListeners: this.objectToMap(obj['hostListeners']),
hostAttributes: this.objectToMap(obj['hostAttributes']),
inputs: obj['inputs'],
readAttributes: obj['readAttributes'],
type: obj['type'],
exportAs: obj['exportAs'],
callOnDestroy: obj['callOnDestroy'],
callOnChanges: obj['callOnChanges'],
callDoCheck: obj['callDoCheck'],
callOnInit: obj['callOnInit'],
callAfterContentChecked: obj['callAfterContentChecked'],
changeDetection: obj['changeDetection'],
outputs: obj['events']
});
map['boundElementIndex']);
}
}

View File

@ -5,14 +5,7 @@ import {DEFAULT_PIPES} from 'angular2/src/core/pipes';
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
import {BrowserDetails} from 'angular2/src/animate/browser_details';
import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
import {
Parser,
Lexer,
ChangeDetection,
DynamicChangeDetection,
JitChangeDetection,
PreGeneratedChangeDetection
} from 'angular2/src/core/change_detection/change_detection';
import {Parser, Lexer} from 'angular2/src/core/change_detection/change_detection';
import {
EventManager,
DomEventsPlugin,
@ -23,16 +16,9 @@ import {BrowserDomAdapter} from 'angular2/src/core/dom/browser_adapter';
import {KeyEventsPlugin} from 'angular2/src/core/render/dom/events/key_events';
import {HammerGesturesPlugin} from 'angular2/src/core/render/dom/events/hammer_gestures';
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
import {Renderer, RenderCompiler} from 'angular2/src/core/render/api';
import {Renderer} from 'angular2/src/core/render/api';
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
import {
DomRenderer,
DOCUMENT,
DefaultDomCompiler,
APP_ID_RANDOM_BINDING,
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
TemplateCloner
} from 'angular2/src/core/render/render';
import {DomRenderer, DOCUMENT, APP_ID_RANDOM_BINDING} from 'angular2/src/core/render/render';
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
import {
DomElementSchemaRegistry
@ -47,13 +33,9 @@ import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
import {ViewLoader} from 'angular2/src/core/render/dom/compiler/view_loader';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {ExceptionHandler} from 'angular2/src/core/facade/exceptions';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {StyleInliner} from 'angular2/src/core/render/dom/compiler/style_inliner';
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {Testability} from 'angular2/src/core/testability/testability';
import {XHR} from 'angular2/src/core/render/xhr';
@ -81,13 +63,6 @@ var _rootBindings = [bind(Reflector).toValue(reflector)];
// TODO: This code is nearly identical to core/application. There should be a way to only write it
// once
function _injectorBindings(): any[] {
var bestChangeDetection = new DynamicChangeDetection();
if (PreGeneratedChangeDetection.isSupported()) {
bestChangeDetection = new PreGeneratedChangeDetection();
} else if (JitChangeDetection.isSupported()) {
bestChangeDetection = new JitChangeDetection();
}
return [
bind(DOCUMENT)
.toValue(DOM.defaultDoc()),
@ -98,10 +73,6 @@ function _injectorBindings(): any[] {
DomRenderer,
bind(Renderer).toAlias(DomRenderer),
APP_ID_RANDOM_BINDING,
TemplateCloner,
bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(20),
DefaultDomCompiler,
bind(RenderCompiler).toAlias(DefaultDomCompiler),
DomSharedStylesHost,
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
Serializer,
@ -117,17 +88,12 @@ function _injectorBindings(): any[] {
ProtoViewFactory,
ViewResolver,
DEFAULT_PIPES,
bind(ChangeDetection).toValue(bestChangeDetection),
ViewLoader,
DirectiveResolver,
Parser,
Lexer,
bind(ExceptionHandler).toFactory(() => new ExceptionHandler(DOM), []),
bind(XHR).toValue(new XHRImpl()),
ComponentUrlMapper,
UrlResolver,
StyleUrlResolver,
StyleInliner,
DynamicComponentLoader,
Testability,
AnchorBasedAppRootUrl,

View File

@ -15,7 +15,7 @@ import {XHR} from 'angular2/src/core/render/xhr';
import {WebWorkerXHRImpl} from 'angular2/src/web_workers/worker/xhr_impl';
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
import {WebWorkerRenderer} from './renderer';
import {Renderer, RenderCompiler} from 'angular2/src/core/render/api';
import {Renderer} from 'angular2/src/core/render/api';
import {ClientMessageBrokerFactory} from 'angular2/src/web_workers/shared/client_message_broker';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {

View File

@ -1,14 +1,9 @@
import {
Renderer,
RenderCompiler,
RenderDirectiveMetadata,
ProtoViewDto,
ViewDefinition,
RenderProtoViewRef,
RenderViewRef,
RenderElementRef,
RenderEventDispatcher,
RenderProtoViewMergeMapping,
RenderViewWithFragments,
RenderFragmentRef,
RenderTemplateCmd