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:
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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; }
|
||||
}
|
||||
@@ -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 + '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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}`;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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(); }
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user