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:
@ -3,11 +3,9 @@ import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
|||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChangeDetection,
|
|
||||||
DirectiveIndex,
|
DirectiveIndex,
|
||||||
BindingRecord,
|
BindingRecord,
|
||||||
DirectiveRecord,
|
DirectiveRecord,
|
||||||
ProtoChangeDetector,
|
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorDefinition,
|
ChangeDetectorDefinition,
|
||||||
ChangeDetectorGenConfig,
|
ChangeDetectorGenConfig,
|
||||||
|
@ -12,8 +12,6 @@ import {
|
|||||||
import {BrowserDomAdapter} from 'angular2/src/core/dom/browser_adapter';
|
import {BrowserDomAdapter} from 'angular2/src/core/dom/browser_adapter';
|
||||||
import {BrowserGetTestability} from 'angular2/src/core/testability/browser_testability';
|
import {BrowserGetTestability} from 'angular2/src/core/testability/browser_testability';
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
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 {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/core/facade/async';
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
import {XHR} from 'angular2/src/core/render/xhr';
|
||||||
import {XHRImpl} from 'angular2/src/core/render/xhr_impl';
|
import {XHRImpl} from 'angular2/src/core/render/xhr_impl';
|
||||||
@ -31,15 +29,8 @@ import {
|
|||||||
DynamicComponentLoader
|
DynamicComponentLoader
|
||||||
} from 'angular2/src/core/compiler/dynamic_component_loader';
|
} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
|
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
|
||||||
import {Renderer, RenderCompiler} from 'angular2/src/core/render/api';
|
import {Renderer} from 'angular2/src/core/render/api';
|
||||||
import {
|
import {DomRenderer, DOCUMENT, APP_ID_RANDOM_BINDING} from 'angular2/src/core/render/render';
|
||||||
DomRenderer,
|
|
||||||
DOCUMENT,
|
|
||||||
DefaultDomCompiler,
|
|
||||||
APP_ID_RANDOM_BINDING,
|
|
||||||
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
|
|
||||||
TemplateCloner
|
|
||||||
} from 'angular2/src/core/render/render';
|
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
||||||
import {
|
import {
|
||||||
DomElementSchemaRegistry
|
DomElementSchemaRegistry
|
||||||
@ -72,17 +63,11 @@ export function applicationDomBindings(): Array<Type | Binding | any[]> {
|
|||||||
DomRenderer,
|
DomRenderer,
|
||||||
bind(Renderer).toAlias(DomRenderer),
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
APP_ID_RANDOM_BINDING,
|
APP_ID_RANDOM_BINDING,
|
||||||
TemplateCloner,
|
|
||||||
bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(20),
|
|
||||||
DefaultDomCompiler,
|
|
||||||
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
|
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
|
||||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
|
||||||
DomSharedStylesHost,
|
DomSharedStylesHost,
|
||||||
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
||||||
ViewLoader,
|
|
||||||
EXCEPTION_BINDING,
|
EXCEPTION_BINDING,
|
||||||
bind(XHR).toValue(new XHRImpl()),
|
bind(XHR).toValue(new XHRImpl()),
|
||||||
StyleInliner,
|
|
||||||
Testability,
|
Testability,
|
||||||
AnchorBasedAppRootUrl,
|
AnchorBasedAppRootUrl,
|
||||||
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
|
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
|
||||||
|
@ -21,10 +21,6 @@ import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
|||||||
import {
|
import {
|
||||||
Parser,
|
Parser,
|
||||||
Lexer,
|
Lexer,
|
||||||
ChangeDetection,
|
|
||||||
DynamicChangeDetection,
|
|
||||||
JitChangeDetection,
|
|
||||||
PreGeneratedChangeDetection,
|
|
||||||
IterableDiffers,
|
IterableDiffers,
|
||||||
defaultIterableDiffers,
|
defaultIterableDiffers,
|
||||||
KeyValueDiffers,
|
KeyValueDiffers,
|
||||||
@ -39,9 +35,7 @@ import {DEFAULT_PIPES} from 'angular2/src/core/pipes';
|
|||||||
import {ViewResolver} from './compiler/view_resolver';
|
import {ViewResolver} from './compiler/view_resolver';
|
||||||
import {DirectiveResolver} from './compiler/directive_resolver';
|
import {DirectiveResolver} from './compiler/directive_resolver';
|
||||||
import {PipeResolver} from './compiler/pipe_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 {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
|
||||||
import {
|
import {
|
||||||
APP_ID_RANDOM_BINDING,
|
APP_ID_RANDOM_BINDING,
|
||||||
} from 'angular2/src/core/render/render';
|
} 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.
|
* application, regardless of whether it runs on the UI thread or in a web worker.
|
||||||
*/
|
*/
|
||||||
export function applicationCommonBindings(): Array<Type | Binding | any[]> {
|
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 [
|
return [
|
||||||
Compiler,
|
Compiler,
|
||||||
APP_ID_RANDOM_BINDING,
|
APP_ID_RANDOM_BINDING,
|
||||||
@ -109,12 +97,9 @@ export function applicationCommonBindings(): Array<Type | Binding | any[]> {
|
|||||||
DEFAULT_PIPES,
|
DEFAULT_PIPES,
|
||||||
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
||||||
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
||||||
bind(ChangeDetection).toValue(bestChangeDetection),
|
|
||||||
DirectiveResolver,
|
DirectiveResolver,
|
||||||
UrlResolver,
|
UrlResolver,
|
||||||
StyleUrlResolver,
|
|
||||||
PipeResolver,
|
PipeResolver,
|
||||||
ComponentUrlMapper,
|
|
||||||
Parser,
|
Parser,
|
||||||
Lexer,
|
Lexer,
|
||||||
DynamicComponentLoader,
|
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 {IterableDiffers, IterableDifferFactory} from './differs/iterable_differs';
|
||||||
import {DefaultIterableDifferFactory} from './differs/default_iterable_differ';
|
import {DefaultIterableDifferFactory} from './differs/default_iterable_differ';
|
||||||
import {KeyValueDiffers, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
import {KeyValueDiffers, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
||||||
import {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ';
|
import {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ';
|
||||||
import {
|
import {CONST, CONST_EXPR, isPresent} from 'angular2/src/core/facade/lang';
|
||||||
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';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ASTWithSource,
|
ASTWithSource,
|
||||||
@ -37,13 +26,13 @@ export {
|
|||||||
ProtoChangeDetector,
|
ProtoChangeDetector,
|
||||||
ChangeDetector,
|
ChangeDetector,
|
||||||
ChangeDispatcher,
|
ChangeDispatcher,
|
||||||
ChangeDetection,
|
|
||||||
ChangeDetectorDefinition,
|
ChangeDetectorDefinition,
|
||||||
DebugContext,
|
DebugContext,
|
||||||
ChangeDetectorGenConfig
|
ChangeDetectorGenConfig
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
export {ChangeDetectionStrategy, CHANGE_DECTION_STRATEGY_VALUES} from './constants';
|
export {ChangeDetectionStrategy, CHANGE_DECTION_STRATEGY_VALUES} from './constants';
|
||||||
export {DynamicProtoChangeDetector} from './proto_change_detector';
|
export {DynamicProtoChangeDetector} from './proto_change_detector';
|
||||||
|
export {JitProtoChangeDetector} from './jit_proto_change_detector';
|
||||||
export {BindingRecord, BindingTarget} from './binding_record';
|
export {BindingRecord, BindingTarget} from './binding_record';
|
||||||
export {DirectiveIndex, DirectiveRecord} from './directive_record';
|
export {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||||
export {DynamicChangeDetector} from './dynamic_change_detector';
|
export {DynamicChangeDetector} from './dynamic_change_detector';
|
||||||
@ -68,98 +57,3 @@ export const iterableDiff: IterableDifferFactory[] =
|
|||||||
export const defaultIterableDiffers = CONST_EXPR(new IterableDiffers(iterableDiff));
|
export const defaultIterableDiffers = CONST_EXPR(new IterableDiffers(iterableDiff));
|
||||||
|
|
||||||
export const defaultKeyValueDiffers = CONST_EXPR(new KeyValueDiffers(keyValDiff));
|
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 {Locals} from './parser/locals';
|
||||||
import {BindingTarget, BindingRecord} from './binding_record';
|
import {BindingTarget, BindingRecord} from './binding_record';
|
||||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||||
import {ChangeDetectionStrategy} from './constants';
|
import {ChangeDetectionStrategy} from './constants';
|
||||||
import {ChangeDetectorRef} from './change_detector_ref';
|
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 {
|
export class DebugContext {
|
||||||
constructor(public element: any, public componentElement: any, public directive: any,
|
constructor(public element: any, public componentElement: any, public directive: any,
|
||||||
public context: any, public locals: any, public injector: any) {}
|
public context: any, public locals: any, public injector: any) {}
|
||||||
|
@ -9,7 +9,6 @@ export {
|
|||||||
OnInit,
|
OnInit,
|
||||||
DoCheck
|
DoCheck
|
||||||
} from './compiler/interfaces';
|
} from './compiler/interfaces';
|
||||||
export {ComponentUrlMapper} from './compiler/component_url_mapper';
|
|
||||||
export {DirectiveResolver} from './compiler/directive_resolver';
|
export {DirectiveResolver} from './compiler/directive_resolver';
|
||||||
export {Compiler} from './compiler/compiler';
|
export {Compiler} from './compiler/compiler';
|
||||||
export {AppViewManager} from './compiler/view_manager';
|
export {AppViewManager} from './compiler/view_manager';
|
||||||
|
@ -48,7 +48,6 @@ import {
|
|||||||
import {QueryList} from './query_list';
|
import {QueryList} from './query_list';
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
import {SetterFn} from 'angular2/src/core/reflection/types';
|
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 {EventConfig} from 'angular2/src/core/render/event_config';
|
||||||
import {PipeBinding} from '../pipes/pipe_binding';
|
import {PipeBinding} from '../pipes/pipe_binding';
|
||||||
|
|
||||||
@ -128,17 +127,17 @@ export class DirectiveDependency extends Dependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DirectiveBinding extends ResolvedBinding {
|
export class DirectiveBinding extends ResolvedBinding {
|
||||||
constructor(key: Key, factory: Function, deps: Dependency[],
|
public callOnDestroy: boolean;
|
||||||
public metadata: RenderDirectiveMetadata,
|
|
||||||
|
constructor(key: Key, factory: Function, deps: Dependency[], public metadata: DirectiveMetadata,
|
||||||
public bindings: Array<Type | Binding | any[]>,
|
public bindings: Array<Type | Binding | any[]>,
|
||||||
public viewBindings: Array<Type | Binding | any[]>) {
|
public viewBindings: Array<Type | Binding | any[]>) {
|
||||||
super(key, [new ResolvedFactory(factory, deps)], false);
|
super(key, [new ResolvedFactory(factory, deps)], false);
|
||||||
|
this.callOnDestroy = hasLifecycleHook(LifecycleHooks.OnDestroy, key.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
get displayName(): string { return this.key.displayName; }
|
get displayName(): string { return this.key.displayName; }
|
||||||
|
|
||||||
get callOnDestroy(): boolean { return this.metadata.callOnDestroy; }
|
|
||||||
|
|
||||||
get queries(): QueryMetadataWithSetter[] {
|
get queries(): QueryMetadataWithSetter[] {
|
||||||
if (isBlank(this.metadata.queries)) return [];
|
if (isBlank(this.metadata.queries)) return [];
|
||||||
|
|
||||||
@ -163,47 +162,11 @@ export class DirectiveBinding extends ResolvedBinding {
|
|||||||
var rb = resolveBinding(binding);
|
var rb = resolveBinding(binding);
|
||||||
var rf = rb.resolvedFactories[0];
|
var rf = rb.resolvedFactories[0];
|
||||||
var deps = rf.dependencies.map(DirectiveDependency.createFrom);
|
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 bindings = isPresent(meta.bindings) ? meta.bindings : [];
|
||||||
var viewBindigs =
|
var viewBindigs =
|
||||||
meta instanceof ComponentMetadata && isPresent(meta.viewBindings) ? meta.viewBindings : [];
|
meta instanceof ComponentMetadata && isPresent(meta.viewBindings) ? meta.viewBindings : [];
|
||||||
return new DirectiveBinding(rb.key, rf.factory, deps, metadata, bindings, viewBindigs);
|
return new DirectiveBinding(rb.key, rf.factory, deps, meta, bindings, viewBindigs);
|
||||||
}
|
|
||||||
|
|
||||||
static _readAttributes(deps: DirectiveDependency[]): string[] {
|
|
||||||
var readAttributes = [];
|
|
||||||
deps.forEach(dep => {
|
|
||||||
if (isPresent(dep.attributeName)) {
|
|
||||||
readAttributes.push(dep.attributeName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return readAttributes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static createFromType(type: Type, annotation: DirectiveMetadata): DirectiveBinding {
|
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) {
|
if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) {
|
||||||
// We provide the component's view change detector to components and
|
// We provide the component's view change detector to components and
|
||||||
// the surrounding component's change detector to directives.
|
// 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(
|
var componentView = this._preBuiltObjects.view.getNestedView(
|
||||||
this._preBuiltObjects.elementRef.boundElementIndex);
|
this._preBuiltObjects.elementRef.boundElementIndex);
|
||||||
return componentView.changeDetector.ref;
|
return componentView.changeDetector.ref;
|
||||||
|
@ -29,24 +29,12 @@ export class ElementRef implements RenderElementRef {
|
|||||||
* This is used internally by the Angular framework to locate elements.
|
* This is used internally by the Angular framework to locate elements.
|
||||||
*/
|
*/
|
||||||
boundElementIndex: number;
|
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
|
* @private
|
||||||
*/
|
*/
|
||||||
constructor(parentView: ViewRef, boundElementIndex: number, private _renderer: Renderer) {
|
constructor(parentView: ViewRef, boundElementIndex: number, private _renderer: Renderer) {
|
||||||
this.parentView = parentView;
|
this.parentView = parentView;
|
||||||
this.boundElementIndex = boundElementIndex;
|
this.boundElementIndex = boundElementIndex;
|
||||||
this.renderBoundElementIndex = boundElementIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/core/facade/lang';
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
|
||||||
|
|
||||||
import {
|
import {ViewType, RenderProtoViewRef} from 'angular2/src/core/render/api';
|
||||||
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 {Injectable, Binding, resolveForwardRef, Inject} from 'angular2/src/core/di';
|
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) {}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// Public API for render
|
// Public API for render
|
||||||
export {
|
export {
|
||||||
RenderDirectiveMetadata,
|
|
||||||
RenderEventDispatcher,
|
RenderEventDispatcher,
|
||||||
Renderer,
|
Renderer,
|
||||||
RenderElementRef,
|
RenderElementRef,
|
||||||
@ -8,10 +7,8 @@ export {
|
|||||||
RenderProtoViewRef,
|
RenderProtoViewRef,
|
||||||
RenderFragmentRef,
|
RenderFragmentRef,
|
||||||
RenderViewWithFragments,
|
RenderViewWithFragments,
|
||||||
ViewDefinition,
|
|
||||||
DOCUMENT,
|
DOCUMENT,
|
||||||
APP_ID,
|
APP_ID,
|
||||||
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
|
|
||||||
RenderTemplateCmd,
|
RenderTemplateCmd,
|
||||||
RenderCommandVisitor,
|
RenderCommandVisitor,
|
||||||
RenderTextCmd,
|
RenderTextCmd,
|
||||||
|
@ -1,103 +1,4 @@
|
|||||||
import {isPresent, isBlank, RegExpWrapper} from 'angular2/src/core/facade/lang';
|
import {Map} from 'angular2/src/core/facade/collection';
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ViewType {
|
export enum ViewType {
|
||||||
// A view that contains the host element with bound component directive.
|
// A view that contains the host element with bound component directive.
|
||||||
@ -111,176 +12,6 @@ export enum ViewType {
|
|||||||
EMBEDDED
|
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
|
// An opaque reference to a render proto view
|
||||||
export class RenderProtoViewRef {}
|
export class RenderProtoViewRef {}
|
||||||
|
|
||||||
@ -312,86 +43,6 @@ export enum ViewEncapsulation {
|
|||||||
export var VIEW_ENCAPSULATION_VALUES =
|
export var VIEW_ENCAPSULATION_VALUES =
|
||||||
[ViewEncapsulation.Emulated, ViewEncapsulation.Native, ViewEncapsulation.None];
|
[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 RenderTemplateCmd { visit(visitor: RenderCommandVisitor, context: any): any; }
|
||||||
|
|
||||||
export interface RenderBeginCmd extends RenderTemplateCmd {
|
export interface RenderBeginCmd extends RenderTemplateCmd {
|
||||||
@ -449,7 +100,7 @@ export interface RenderElementRef {
|
|||||||
*
|
*
|
||||||
* This is used internally by the Angular framework to locate elements.
|
* This is used internally by the Angular framework to locate elements.
|
||||||
*/
|
*/
|
||||||
renderBoundElementIndex: number;
|
boundElementIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Renderer {
|
export class Renderer {
|
||||||
|
@ -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 {
|
getNativeElementSync(location: RenderElementRef): any {
|
||||||
return resolveInternalDomView(location.renderView)
|
return resolveInternalDomView(location.renderView).boundElements[location.boundElementIndex];
|
||||||
.boundElements[location.renderBoundElementIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getRootNodes(fragment: RenderFragmentRef): Node[] { return resolveInternalDomFragment(fragment); }
|
getRootNodes(fragment: RenderFragmentRef): Node[] { return resolveInternalDomFragment(fragment); }
|
||||||
@ -157,7 +156,7 @@ export class DomRenderer implements Renderer, NodeFactory<Node> {
|
|||||||
|
|
||||||
attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
|
attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
|
||||||
var parentView = resolveInternalDomView(elementRef.renderView);
|
var parentView = resolveInternalDomView(elementRef.renderView);
|
||||||
var element = parentView.boundElements[elementRef.renderBoundElementIndex];
|
var element = parentView.boundElements[elementRef.boundElementIndex];
|
||||||
var nodes = resolveInternalDomFragment(fragmentRef);
|
var nodes = resolveInternalDomFragment(fragmentRef);
|
||||||
moveNodesAfterSibling(element, nodes);
|
moveNodesAfterSibling(element, nodes);
|
||||||
this.animateNodesEnter(nodes);
|
this.animateNodesEnter(nodes);
|
||||||
@ -208,14 +207,14 @@ export class DomRenderer implements Renderer, NodeFactory<Node> {
|
|||||||
|
|
||||||
setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void {
|
setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void {
|
||||||
var view = resolveInternalDomView(location.renderView);
|
var view = resolveInternalDomView(location.renderView);
|
||||||
DOM.setProperty(<Element>view.boundElements[location.renderBoundElementIndex], propertyName,
|
DOM.setProperty(<Element>view.boundElements[location.boundElementIndex], propertyName,
|
||||||
propertyValue);
|
propertyValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string):
|
setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string):
|
||||||
void {
|
void {
|
||||||
var view = resolveInternalDomView(location.renderView);
|
var view = resolveInternalDomView(location.renderView);
|
||||||
var element = view.boundElements[location.renderBoundElementIndex];
|
var element = view.boundElements[location.boundElementIndex];
|
||||||
var dashCasedAttributeName = camelCaseToDashCase(attributeName);
|
var dashCasedAttributeName = camelCaseToDashCase(attributeName);
|
||||||
if (isPresent(attributeValue)) {
|
if (isPresent(attributeValue)) {
|
||||||
DOM.setAttribute(element, dashCasedAttributeName, stringify(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 {
|
setElementClass(location: RenderElementRef, className: string, isAdd: boolean): void {
|
||||||
var view = resolveInternalDomView(location.renderView);
|
var view = resolveInternalDomView(location.renderView);
|
||||||
var element = view.boundElements[location.renderBoundElementIndex];
|
var element = view.boundElements[location.boundElementIndex];
|
||||||
if (isAdd) {
|
if (isAdd) {
|
||||||
DOM.addClass(element, className);
|
DOM.addClass(element, className);
|
||||||
} else {
|
} else {
|
||||||
@ -236,7 +235,7 @@ export class DomRenderer implements Renderer, NodeFactory<Node> {
|
|||||||
|
|
||||||
setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void {
|
setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void {
|
||||||
var view = resolveInternalDomView(location.renderView);
|
var view = resolveInternalDomView(location.renderView);
|
||||||
var element = view.boundElements[location.renderBoundElementIndex];
|
var element = view.boundElements[location.boundElementIndex];
|
||||||
var dashCasedStyleName = camelCaseToDashCase(styleName);
|
var dashCasedStyleName = camelCaseToDashCase(styleName);
|
||||||
if (isPresent(styleValue)) {
|
if (isPresent(styleValue)) {
|
||||||
DOM.setStyle(element, dashCasedStyleName, stringify(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 {
|
invokeElementMethod(location: RenderElementRef, methodName: string, args: any[]): void {
|
||||||
var view = resolveInternalDomView(location.renderView);
|
var view = resolveInternalDomView(location.renderView);
|
||||||
var element = <Element>view.boundElements[location.renderBoundElementIndex];
|
var element = <Element>view.boundElements[location.boundElementIndex];
|
||||||
DOM.invoke(element, methodName, args);
|
DOM.invoke(element, methodName, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,13 +18,6 @@ function _appIdRandomBindingFactory() {
|
|||||||
export const APP_ID_RANDOM_BINDING: Binding =
|
export const APP_ID_RANDOM_BINDING: Binding =
|
||||||
CONST_EXPR(new Binding(APP_ID, {toFactory: _appIdRandomBindingFactory, deps: []}));
|
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 {
|
function _randomChar(): string {
|
||||||
return StringWrapper.fromCharCode(97 + Math.floor(Math.random() * 25));
|
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 {StringWrapper} 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;
|
|
||||||
|
|
||||||
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||||
var DASH_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,
|
return StringWrapper.replaceAllMapped(input, DASH_CASE_REGEXP,
|
||||||
(m) => { return m[1].toUpperCase(); });
|
(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(); }
|
|
||||||
}
|
|
@ -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) {}
|
|
||||||
}
|
|
@ -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}`);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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> = [];
|
|
||||||
}
|
|
@ -4,10 +4,7 @@
|
|||||||
* This module provides advanced support for extending dom strategy.
|
* 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/view/shared_styles_host';
|
||||||
export * from './dom/compiler/compiler';
|
|
||||||
export * from './dom/dom_renderer';
|
export * from './dom/dom_renderer';
|
||||||
export * from './dom/dom_tokens';
|
export * from './dom/dom_tokens';
|
||||||
export * from './dom/template_cloner';
|
|
||||||
export * from './api';
|
export * from './api';
|
||||||
|
@ -8,8 +8,6 @@ import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
|
|||||||
import {
|
import {
|
||||||
Parser,
|
Parser,
|
||||||
Lexer,
|
Lexer,
|
||||||
ChangeDetection,
|
|
||||||
DynamicChangeDetection,
|
|
||||||
IterableDiffers,
|
IterableDiffers,
|
||||||
defaultIterableDiffers,
|
defaultIterableDiffers,
|
||||||
KeyValueDiffers,
|
KeyValueDiffers,
|
||||||
@ -17,18 +15,14 @@ import {
|
|||||||
ChangeDetectorGenConfig
|
ChangeDetectorGenConfig
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
import {ExceptionHandler} from 'angular2/src/core/facade/exceptions';
|
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 {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver';
|
import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver';
|
||||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
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 {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||||
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
|
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
|
||||||
import {AnchorBasedAppRootUrl} from 'angular2/src/core/services/anchor_based_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 {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
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 {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
|
||||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
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 {
|
import {
|
||||||
DomRenderer,
|
DomRenderer,
|
||||||
DOCUMENT,
|
DOCUMENT,
|
||||||
DefaultDomCompiler,
|
|
||||||
APP_ID,
|
APP_ID,
|
||||||
SharedStylesHost,
|
SharedStylesHost,
|
||||||
DomSharedStylesHost,
|
DomSharedStylesHost
|
||||||
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
|
|
||||||
TemplateCloner
|
|
||||||
} from 'angular2/src/core/render/render';
|
} from 'angular2/src/core/render/render';
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
||||||
import {
|
import {
|
||||||
@ -114,10 +105,6 @@ function _getAppBindings() {
|
|||||||
DomRenderer,
|
DomRenderer,
|
||||||
bind(Renderer).toAlias(DomRenderer),
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
bind(APP_ID).toValue('a'),
|
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()),
|
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
|
||||||
DomSharedStylesHost,
|
DomSharedStylesHost,
|
||||||
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
||||||
@ -133,9 +120,7 @@ function _getAppBindings() {
|
|||||||
DEFAULT_PIPES,
|
DEFAULT_PIPES,
|
||||||
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
||||||
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
||||||
bind(ChangeDetection).toValue(new DynamicChangeDetection()),
|
|
||||||
Log,
|
Log,
|
||||||
ViewLoader,
|
|
||||||
DynamicComponentLoader,
|
DynamicComponentLoader,
|
||||||
PipeResolver,
|
PipeResolver,
|
||||||
Parser,
|
Parser,
|
||||||
@ -143,12 +128,9 @@ function _getAppBindings() {
|
|||||||
bind(ExceptionHandler).toValue(new ExceptionHandler(DOM)),
|
bind(ExceptionHandler).toValue(new ExceptionHandler(DOM)),
|
||||||
bind(LocationStrategy).toClass(MockLocationStrategy),
|
bind(LocationStrategy).toClass(MockLocationStrategy),
|
||||||
bind(XHR).toClass(MockXHR),
|
bind(XHR).toClass(MockXHR),
|
||||||
ComponentUrlMapper,
|
|
||||||
UrlResolver,
|
UrlResolver,
|
||||||
AnchorBasedAppRootUrl,
|
AnchorBasedAppRootUrl,
|
||||||
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
|
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
|
||||||
StyleUrlResolver,
|
|
||||||
StyleInliner,
|
|
||||||
TestComponentBuilder,
|
TestComponentBuilder,
|
||||||
bind(NgZone).toClass(MockNgZone),
|
bind(NgZone).toClass(MockNgZone),
|
||||||
bind(AnimationBuilder).toClass(MockAnimationBuilder),
|
bind(AnimationBuilder).toClass(MockAnimationBuilder),
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
export const ON_WEB_WORKER = CONST_EXPR(new OpaqueToken('WebWorker.onWebWorker'));
|
export const ON_WEB_WORKER = CONST_EXPR(new OpaqueToken('WebWorker.onWebWorker'));
|
||||||
|
|
||||||
export class WebWorkerElementRef implements RenderElementRef {
|
export class WebWorkerElementRef implements RenderElementRef {
|
||||||
constructor(public renderView: RenderViewRef, public renderBoundElementIndex: number) {}
|
constructor(public renderView: RenderViewRef, public boundElementIndex: number) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WebWorkerTemplateCmd implements RenderTemplateCmd {
|
export class WebWorkerTemplateCmd implements RenderTemplateCmd {
|
||||||
|
@ -15,21 +15,12 @@ import {
|
|||||||
MapWrapper
|
MapWrapper
|
||||||
} from "angular2/src/core/facade/collection";
|
} from "angular2/src/core/facade/collection";
|
||||||
import {
|
import {
|
||||||
ProtoViewDto,
|
|
||||||
RenderDirectiveMetadata,
|
|
||||||
RenderElementBinder,
|
|
||||||
DirectiveBinder,
|
|
||||||
ElementPropertyBinding,
|
|
||||||
EventBinding,
|
|
||||||
ViewDefinition,
|
|
||||||
RenderProtoViewRef,
|
RenderProtoViewRef,
|
||||||
RenderProtoViewMergeMapping,
|
|
||||||
RenderViewRef,
|
RenderViewRef,
|
||||||
RenderFragmentRef,
|
RenderFragmentRef,
|
||||||
RenderElementRef,
|
RenderElementRef,
|
||||||
ViewType,
|
ViewType,
|
||||||
ViewEncapsulation,
|
ViewEncapsulation,
|
||||||
PropertyBindingType,
|
|
||||||
RenderTemplateCmd,
|
RenderTemplateCmd,
|
||||||
RenderCommandVisitor,
|
RenderCommandVisitor,
|
||||||
RenderTextCmd,
|
RenderTextCmd,
|
||||||
@ -49,8 +40,6 @@ import {
|
|||||||
WebWorkerEndComponentCmd,
|
WebWorkerEndComponentCmd,
|
||||||
WebWorkerEmbeddedTemplateCmd
|
WebWorkerEmbeddedTemplateCmd
|
||||||
} from 'angular2/src/web_workers/shared/api';
|
} 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 {Injectable} from "angular2/src/core/di";
|
||||||
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
|
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
|
||||||
import {
|
import {
|
||||||
@ -64,7 +53,7 @@ export const PRIMITIVE: Type = String;
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class Serializer {
|
export class Serializer {
|
||||||
private _enumRegistry: Map<any, Map<number, any>>;
|
private _enumRegistry: Map<any, Map<number, any>>;
|
||||||
constructor(private _parser: Parser, private _protoViewStore: RenderProtoViewRefStore,
|
constructor(private _protoViewStore: RenderProtoViewRefStore,
|
||||||
private _renderViewStore: RenderViewWithFragmentsStore) {
|
private _renderViewStore: RenderViewWithFragmentsStore) {
|
||||||
this._enumRegistry = new Map<any, Map<number, any>>();
|
this._enumRegistry = new Map<any, Map<number, any>>();
|
||||||
|
|
||||||
@ -79,13 +68,6 @@ export class Serializer {
|
|||||||
viewEncapsulationMap[1] = ViewEncapsulation.Native;
|
viewEncapsulationMap[1] = ViewEncapsulation.Native;
|
||||||
viewEncapsulationMap[2] = ViewEncapsulation.None;
|
viewEncapsulationMap[2] = ViewEncapsulation.None;
|
||||||
this._enumRegistry.set(ViewEncapsulation, viewEncapsulationMap);
|
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 {
|
serialize(obj: any, type: Type): Object {
|
||||||
@ -100,32 +82,14 @@ export class Serializer {
|
|||||||
if (type == PRIMITIVE) {
|
if (type == PRIMITIVE) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
if (type == ViewDefinition) {
|
if (type == RenderProtoViewRef) {
|
||||||
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) {
|
|
||||||
return this._protoViewStore.serialize(obj);
|
return this._protoViewStore.serialize(obj);
|
||||||
} else if (type == RenderProtoViewMergeMapping) {
|
|
||||||
return this._serializeRenderProtoViewMergeMapping(obj);
|
|
||||||
} else if (type == RenderViewRef) {
|
} else if (type == RenderViewRef) {
|
||||||
return this._renderViewStore.serializeRenderViewRef(obj);
|
return this._renderViewStore.serializeRenderViewRef(obj);
|
||||||
} else if (type == RenderFragmentRef) {
|
} else if (type == RenderFragmentRef) {
|
||||||
return this._renderViewStore.serializeRenderFragmentRef(obj);
|
return this._renderViewStore.serializeRenderFragmentRef(obj);
|
||||||
} else if (type == WebWorkerElementRef) {
|
} else if (type == WebWorkerElementRef) {
|
||||||
return this._serializeWorkerElementRef(obj);
|
return this._serializeWorkerElementRef(obj);
|
||||||
} else if (type == ElementPropertyBinding) {
|
|
||||||
return this._serializeElementPropertyBinding(obj);
|
|
||||||
} else if (type == EventBinding) {
|
|
||||||
return this._serializeEventBinding(obj);
|
|
||||||
} else if (type == WebWorkerTemplateCmd) {
|
} else if (type == WebWorkerTemplateCmd) {
|
||||||
return serializeTemplateCmd(obj);
|
return serializeTemplateCmd(obj);
|
||||||
} else {
|
} else {
|
||||||
@ -146,32 +110,14 @@ export class Serializer {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == ViewDefinition) {
|
if (type == RenderProtoViewRef) {
|
||||||
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) {
|
|
||||||
return this._protoViewStore.deserialize(map);
|
return this._protoViewStore.deserialize(map);
|
||||||
} else if (type == RenderProtoViewMergeMapping) {
|
|
||||||
return this._deserializeRenderProtoViewMergeMapping(map);
|
|
||||||
} else if (type == RenderViewRef) {
|
} else if (type == RenderViewRef) {
|
||||||
return this._renderViewStore.deserializeRenderViewRef(map);
|
return this._renderViewStore.deserializeRenderViewRef(map);
|
||||||
} else if (type == RenderFragmentRef) {
|
} else if (type == RenderFragmentRef) {
|
||||||
return this._renderViewStore.deserializeRenderFragmentRef(map);
|
return this._renderViewStore.deserializeRenderFragmentRef(map);
|
||||||
} else if (type == WebWorkerElementRef) {
|
} else if (type == WebWorkerElementRef) {
|
||||||
return this._deserializeWorkerElementRef(map);
|
return this._deserializeWorkerElementRef(map);
|
||||||
} else if (type == EventBinding) {
|
|
||||||
return this._deserializeEventBinding(map);
|
|
||||||
} else if (type == ElementPropertyBinding) {
|
|
||||||
return this._deserializeElementPropertyBinding(map);
|
|
||||||
} else if (type == WebWorkerTemplateCmd) {
|
} else if (type == WebWorkerTemplateCmd) {
|
||||||
return deserializeTemplateCmd(map);
|
return deserializeTemplateCmd(map);
|
||||||
} else {
|
} else {
|
||||||
@ -211,220 +157,16 @@ export class Serializer {
|
|||||||
|
|
||||||
allocateRenderViews(fragmentCount: number) { this._renderViewStore.allocate(fragmentCount); }
|
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> {
|
private _serializeWorkerElementRef(elementRef: RenderElementRef): StringMap<string, any> {
|
||||||
return {
|
return {
|
||||||
'renderView': this.serialize(elementRef.renderView, RenderViewRef),
|
'renderView': this.serialize(elementRef.renderView, RenderViewRef),
|
||||||
'renderBoundElementIndex': elementRef.renderBoundElementIndex
|
'boundElementIndex': elementRef.boundElementIndex
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deserializeWorkerElementRef(map: StringMap<string, any>): RenderElementRef {
|
private _deserializeWorkerElementRef(map: StringMap<string, any>): RenderElementRef {
|
||||||
return new WebWorkerElementRef(this.deserialize(map['renderView'], RenderViewRef),
|
return new WebWorkerElementRef(this.deserialize(map['renderView'], RenderViewRef),
|
||||||
map['renderBoundElementIndex']);
|
map['boundElementIndex']);
|
||||||
}
|
|
||||||
|
|
||||||
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']
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,14 +5,7 @@ import {DEFAULT_PIPES} from 'angular2/src/core/pipes';
|
|||||||
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||||
import {BrowserDetails} from 'angular2/src/animate/browser_details';
|
import {BrowserDetails} from 'angular2/src/animate/browser_details';
|
||||||
import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
|
import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
import {
|
import {Parser, Lexer} from 'angular2/src/core/change_detection/change_detection';
|
||||||
Parser,
|
|
||||||
Lexer,
|
|
||||||
ChangeDetection,
|
|
||||||
DynamicChangeDetection,
|
|
||||||
JitChangeDetection,
|
|
||||||
PreGeneratedChangeDetection
|
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {
|
import {
|
||||||
EventManager,
|
EventManager,
|
||||||
DomEventsPlugin,
|
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 {KeyEventsPlugin} from 'angular2/src/core/render/dom/events/key_events';
|
||||||
import {HammerGesturesPlugin} from 'angular2/src/core/render/dom/events/hammer_gestures';
|
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 {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 {AppRootUrl} from 'angular2/src/core/services/app_root_url';
|
||||||
import {
|
import {DomRenderer, DOCUMENT, APP_ID_RANDOM_BINDING} from 'angular2/src/core/render/render';
|
||||||
DomRenderer,
|
|
||||||
DOCUMENT,
|
|
||||||
DefaultDomCompiler,
|
|
||||||
APP_ID_RANDOM_BINDING,
|
|
||||||
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
|
|
||||||
TemplateCloner
|
|
||||||
} from 'angular2/src/core/render/render';
|
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
||||||
import {
|
import {
|
||||||
DomElementSchemaRegistry
|
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 {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||||
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
||||||
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
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 {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {ExceptionHandler} from 'angular2/src/core/facade/exceptions';
|
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 {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 {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||||
import {Testability} from 'angular2/src/core/testability/testability';
|
import {Testability} from 'angular2/src/core/testability/testability';
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
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
|
// TODO: This code is nearly identical to core/application. There should be a way to only write it
|
||||||
// once
|
// once
|
||||||
function _injectorBindings(): any[] {
|
function _injectorBindings(): any[] {
|
||||||
var bestChangeDetection = new DynamicChangeDetection();
|
|
||||||
if (PreGeneratedChangeDetection.isSupported()) {
|
|
||||||
bestChangeDetection = new PreGeneratedChangeDetection();
|
|
||||||
} else if (JitChangeDetection.isSupported()) {
|
|
||||||
bestChangeDetection = new JitChangeDetection();
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
bind(DOCUMENT)
|
bind(DOCUMENT)
|
||||||
.toValue(DOM.defaultDoc()),
|
.toValue(DOM.defaultDoc()),
|
||||||
@ -98,10 +73,6 @@ function _injectorBindings(): any[] {
|
|||||||
DomRenderer,
|
DomRenderer,
|
||||||
bind(Renderer).toAlias(DomRenderer),
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
APP_ID_RANDOM_BINDING,
|
APP_ID_RANDOM_BINDING,
|
||||||
TemplateCloner,
|
|
||||||
bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(20),
|
|
||||||
DefaultDomCompiler,
|
|
||||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
|
||||||
DomSharedStylesHost,
|
DomSharedStylesHost,
|
||||||
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
||||||
Serializer,
|
Serializer,
|
||||||
@ -117,17 +88,12 @@ function _injectorBindings(): any[] {
|
|||||||
ProtoViewFactory,
|
ProtoViewFactory,
|
||||||
ViewResolver,
|
ViewResolver,
|
||||||
DEFAULT_PIPES,
|
DEFAULT_PIPES,
|
||||||
bind(ChangeDetection).toValue(bestChangeDetection),
|
|
||||||
ViewLoader,
|
|
||||||
DirectiveResolver,
|
DirectiveResolver,
|
||||||
Parser,
|
Parser,
|
||||||
Lexer,
|
Lexer,
|
||||||
bind(ExceptionHandler).toFactory(() => new ExceptionHandler(DOM), []),
|
bind(ExceptionHandler).toFactory(() => new ExceptionHandler(DOM), []),
|
||||||
bind(XHR).toValue(new XHRImpl()),
|
bind(XHR).toValue(new XHRImpl()),
|
||||||
ComponentUrlMapper,
|
|
||||||
UrlResolver,
|
UrlResolver,
|
||||||
StyleUrlResolver,
|
|
||||||
StyleInliner,
|
|
||||||
DynamicComponentLoader,
|
DynamicComponentLoader,
|
||||||
Testability,
|
Testability,
|
||||||
AnchorBasedAppRootUrl,
|
AnchorBasedAppRootUrl,
|
||||||
|
@ -15,7 +15,7 @@ import {XHR} from 'angular2/src/core/render/xhr';
|
|||||||
import {WebWorkerXHRImpl} from 'angular2/src/web_workers/worker/xhr_impl';
|
import {WebWorkerXHRImpl} from 'angular2/src/web_workers/worker/xhr_impl';
|
||||||
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
|
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
|
||||||
import {WebWorkerRenderer} from './renderer';
|
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 {ClientMessageBrokerFactory} from 'angular2/src/web_workers/shared/client_message_broker';
|
||||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||||
import {
|
import {
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
Renderer,
|
Renderer,
|
||||||
RenderCompiler,
|
|
||||||
RenderDirectiveMetadata,
|
|
||||||
ProtoViewDto,
|
|
||||||
ViewDefinition,
|
|
||||||
RenderProtoViewRef,
|
RenderProtoViewRef,
|
||||||
RenderViewRef,
|
RenderViewRef,
|
||||||
RenderElementRef,
|
RenderElementRef,
|
||||||
RenderEventDispatcher,
|
RenderEventDispatcher,
|
||||||
RenderProtoViewMergeMapping,
|
|
||||||
RenderViewWithFragments,
|
RenderViewWithFragments,
|
||||||
RenderFragmentRef,
|
RenderFragmentRef,
|
||||||
RenderTemplateCmd
|
RenderTemplateCmd
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {SpyProtoChangeDetector} from '../spies';
|
|
||||||
|
|
||||||
import {
|
|
||||||
PreGeneratedChangeDetection,
|
|
||||||
ChangeDetectorDefinition,
|
|
||||||
DynamicProtoChangeDetector
|
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe("PreGeneratedChangeDetection", () => {
|
|
||||||
var proto;
|
|
||||||
var def;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
proto = new SpyProtoChangeDetector();
|
|
||||||
def = new ChangeDetectorDefinition('id', null, [], [], [], [], null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return a proto change detector when one is available", () => {
|
|
||||||
var map = {'id': (def) => proto};
|
|
||||||
var cd = new PreGeneratedChangeDetection(null, map);
|
|
||||||
|
|
||||||
expect(cd.getProtoChangeDetector('id', def)).toBe(proto)
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should delegate to dynamic change detection otherwise", () => {
|
|
||||||
var cd = new PreGeneratedChangeDetection(null, {});
|
|
||||||
expect(cd.getProtoChangeDetector('id', def)).toBeAnInstanceOf(DynamicProtoChangeDetector);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
library angular2.test.core.compiler.component_url_mapper_spec;
|
|
||||||
|
|
||||||
import 'package:angular2/test_lib.dart';
|
|
||||||
import 'package:angular2/src/core/compiler/component_url_mapper.dart';
|
|
||||||
|
|
||||||
main() {
|
|
||||||
describe("ComponentUrlMapper", () {
|
|
||||||
it("should return the URL of the component's library", () {
|
|
||||||
var mapper = new ComponentUrlMapper();
|
|
||||||
expect(mapper
|
|
||||||
.getUrl(SomeComponent)
|
|
||||||
.endsWith("core/compiler/component_url_mapper_spec.dart")).toBeTrue();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class SomeComponent {}
|
|
@ -1,25 +0,0 @@
|
|||||||
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ComponentUrlMapper,
|
|
||||||
RuntimeComponentUrlMapper
|
|
||||||
} from 'angular2/src/core/compiler/component_url_mapper';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('RuntimeComponentUrlMapper', () => {
|
|
||||||
it('should return the registered URL', () => {
|
|
||||||
var url = 'http://path/to/component';
|
|
||||||
var mapper = new RuntimeComponentUrlMapper();
|
|
||||||
mapper.setComponentUrl(SomeComponent, url);
|
|
||||||
expect(mapper.getUrl(SomeComponent)).toEqual(url);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fallback to ComponentUrlMapper', () => {
|
|
||||||
var mapper = new ComponentUrlMapper();
|
|
||||||
var runtimeMapper = new RuntimeComponentUrlMapper();
|
|
||||||
expect(runtimeMapper.getUrl(SomeComponent)).toEqual(mapper.getUrl(SomeComponent));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class SomeComponent {}
|
|
@ -1,112 +1,102 @@
|
|||||||
library angular2.test.core.compiler.directive_lifecycle_spec;
|
library angular2.test.core.compiler.directive_lifecycle_spec;
|
||||||
|
|
||||||
import 'package:angular2/test_lib.dart';
|
import 'package:angular2/test_lib.dart';
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/src/core/compiler/directive_lifecycle_reflector.dart';
|
||||||
import 'package:angular2/src/core/compiler/element_injector.dart';
|
import 'package:angular2/src/core/compiler/interfaces.dart';
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
describe('Create DirectiveMetadata', () {
|
describe('Create DirectiveMetadata', () {
|
||||||
describe('lifecycle', () {
|
describe('lifecycle', () {
|
||||||
metadata(type, annotation) =>
|
|
||||||
DirectiveBinding.createFromType(type, annotation).metadata;
|
|
||||||
|
|
||||||
describe("onChanges", () {
|
describe("onChanges", () {
|
||||||
it("should be true when the directive implements OnChanges", () {
|
it("should be true when the directive has the onChanges method", () {
|
||||||
expect(metadata(DirectiveImplementingOnChanges, new Directive())
|
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveImplementingOnChanges))
|
||||||
.callOnChanges).toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () {
|
it("should be false otherwise", () {
|
||||||
expect(metadata(DirectiveNoHooks, new Directive()).callOnChanges)
|
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks)).toBe(false);
|
||||||
.toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("onDestroy", () {
|
describe("onDestroy", () {
|
||||||
it("should be true when the directive implements OnDestroy", () {
|
it("should be true when the directive has the onDestroy method", () {
|
||||||
expect(metadata(DirectiveImplementingOnDestroy, new Directive())
|
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveImplementingOnDestroy))
|
||||||
.callOnDestroy).toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () {
|
it("should be false otherwise", () {
|
||||||
expect(metadata(DirectiveNoHooks, new Directive()).callOnDestroy)
|
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks)).toBe(false);
|
||||||
.toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("doCheck", () {
|
|
||||||
it("should be true when the directive implements DoCheck", () {
|
|
||||||
expect(metadata(DirectiveImplementingOnCheck, new Directive())
|
|
||||||
.callDoCheck).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be false otherwise", () {
|
|
||||||
expect(metadata(DirectiveNoHooks, new Directive()).callDoCheck)
|
|
||||||
.toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("onInit", () {
|
describe("onInit", () {
|
||||||
it("should be true when the directive implements OnInit", () {
|
it("should be true when the directive has the onInit method", () {
|
||||||
expect(metadata(DirectiveImplementingOnInit, new Directive())
|
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveImplementingOnInit))
|
||||||
.callOnInit).toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () {
|
it("should be false otherwise", () {
|
||||||
expect(metadata(DirectiveNoHooks, new Directive()).callOnInit)
|
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks)).toBe(false);
|
||||||
.toBe(false);
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("doCheck", () {
|
||||||
|
it("should be true when the directive has the doCheck method", () {
|
||||||
|
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveImplementingOnCheck))
|
||||||
|
.toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be false otherwise", () {
|
||||||
|
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("afterContentInit", () {
|
describe("afterContentInit", () {
|
||||||
it("should be true when the directive implements AfterContentInit", () {
|
it("should be true when the directive has the afterContentInit method", () {
|
||||||
expect(
|
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveImplementingAfterContentInit))
|
||||||
metadata(DirectiveImplementingAfterContentInit, new Directive())
|
.toBe(true);
|
||||||
.callAfterContentInit).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () {
|
it("should be false otherwise", () {
|
||||||
expect(metadata(DirectiveNoHooks, new Directive())
|
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveNoHooks))
|
||||||
.callAfterContentInit).toBe(false);
|
.toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("afterContentChecked", () {
|
describe("afterContentChecked", () {
|
||||||
it("should be true when the directive implements AfterContentChecked", () {
|
it("should be true when the directive has the afterContentChecked method", () {
|
||||||
expect(
|
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveImplementingAfterContentChecked))
|
||||||
metadata(DirectiveImplementingAfterContentChecked, new Directive())
|
.toBe(true);
|
||||||
.callAfterContentChecked).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () {
|
it("should be false otherwise", () {
|
||||||
expect(metadata(DirectiveNoHooks, new Directive())
|
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
|
||||||
.callAfterContentChecked).toBe(false);
|
.toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe("afterViewInit", () {
|
describe("afterViewInit", () {
|
||||||
it("should be true when the directive implements AfterViewInit", () {
|
it("should be true when the directive has the afterViewInit method", () {
|
||||||
expect(
|
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveImplementingAfterViewInit))
|
||||||
metadata(DirectiveImplementingAfterViewInit, new Directive())
|
.toBe(true);
|
||||||
.callAfterViewInit).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () {
|
it("should be false otherwise", () {
|
||||||
expect(metadata(DirectiveNoHooks, new Directive())
|
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
|
||||||
.callAfterViewInit).toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("afterViewChecked", () {
|
describe("afterViewChecked", () {
|
||||||
it("should be true when the directive implements AfterViewChecked", () {
|
it("should be true when the directive has the afterViewChecked method", () {
|
||||||
expect(
|
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveImplementingAfterViewChecked))
|
||||||
metadata(DirectiveImplementingAfterViewChecked, new Directive())
|
.toBe(true);
|
||||||
.callAfterViewChecked).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () {
|
it("should be false otherwise", () {
|
||||||
expect(metadata(DirectiveNoHooks, new Directive())
|
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveNoHooks))
|
||||||
.callAfterViewChecked).toBe(false);
|
.toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -13,83 +13,76 @@ import {
|
|||||||
proxy
|
proxy
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {DirectiveMetadata} from 'angular2/src/core/metadata';
|
import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector';
|
||||||
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
||||||
import {RenderDirectiveMetadata} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('Create DirectiveMetadata', () => {
|
describe('Create DirectiveMetadata', () => {
|
||||||
describe('lifecycle', () => {
|
describe('lifecycle', () => {
|
||||||
function metadata(type, annotation): RenderDirectiveMetadata {
|
|
||||||
return DirectiveBinding.createFromType(type, annotation).metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("onChanges", () => {
|
describe("onChanges", () => {
|
||||||
it("should be true when the directive has the onChanges method", () => {
|
it("should be true when the directive has the onChanges method", () => {
|
||||||
expect(metadata(DirectiveWithOnChangesMethod, new DirectiveMetadata({})).callOnChanges)
|
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveWithOnChangesMethod))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () => {
|
it("should be false otherwise", () => {
|
||||||
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callOnChanges).toBe(false);
|
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("onDestroy", () => {
|
describe("onDestroy", () => {
|
||||||
it("should be true when the directive has the onDestroy method", () => {
|
it("should be true when the directive has the onDestroy method", () => {
|
||||||
expect(metadata(DirectiveWithOnDestroyMethod, new DirectiveMetadata({})).callOnDestroy)
|
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveWithOnDestroyMethod))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () => {
|
it("should be false otherwise", () => {
|
||||||
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callOnDestroy).toBe(false);
|
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("onInit", () => {
|
describe("onInit", () => {
|
||||||
it("should be true when the directive has the onInit method", () => {
|
it("should be true when the directive has the onInit method", () => {
|
||||||
expect(metadata(DirectiveWithOnInitMethod, new DirectiveMetadata({})).callOnInit)
|
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveWithOnInitMethod)).toBe(true);
|
||||||
.toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () => {
|
it("should be false otherwise", () => {
|
||||||
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callOnInit).toBe(false);
|
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("doCheck", () => {
|
describe("doCheck", () => {
|
||||||
it("should be true when the directive has the doCheck method", () => {
|
it("should be true when the directive has the doCheck method", () => {
|
||||||
expect(metadata(DirectiveWithOnCheckMethod, new DirectiveMetadata({})).callDoCheck)
|
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveWithOnCheckMethod)).toBe(true);
|
||||||
.toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () => {
|
it("should be false otherwise", () => {
|
||||||
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callDoCheck).toBe(false);
|
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("afterContentInit", () => {
|
describe("afterContentInit", () => {
|
||||||
it("should be true when the directive has the afterContentInit method", () => {
|
it("should be true when the directive has the afterContentInit method", () => {
|
||||||
expect(metadata(DirectiveWithAfterContentInitMethod, new DirectiveMetadata({}))
|
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit,
|
||||||
.callAfterContentInit)
|
DirectiveWithAfterContentInitMethod))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () => {
|
it("should be false otherwise", () => {
|
||||||
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callAfterContentInit)
|
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveNoHooks)).toBe(false);
|
||||||
.toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("afterContentChecked", () => {
|
describe("afterContentChecked", () => {
|
||||||
it("should be true when the directive has the afterContentChecked method", () => {
|
it("should be true when the directive has the afterContentChecked method", () => {
|
||||||
expect(metadata(DirectiveWithAfterContentCheckedMethod, new DirectiveMetadata({}))
|
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked,
|
||||||
.callAfterContentChecked)
|
DirectiveWithAfterContentCheckedMethod))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () => {
|
it("should be false otherwise", () => {
|
||||||
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callAfterContentChecked)
|
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
|
||||||
.toBe(false);
|
.toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -97,26 +90,24 @@ export function main() {
|
|||||||
|
|
||||||
describe("afterViewInit", () => {
|
describe("afterViewInit", () => {
|
||||||
it("should be true when the directive has the afterViewInit method", () => {
|
it("should be true when the directive has the afterViewInit method", () => {
|
||||||
expect(metadata(DirectiveWithAfterViewInitMethod, new DirectiveMetadata({}))
|
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveWithAfterViewInitMethod))
|
||||||
.callAfterViewInit)
|
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () => {
|
it("should be false otherwise", () => {
|
||||||
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callAfterViewInit).toBe(false);
|
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("afterViewChecked", () => {
|
describe("afterViewChecked", () => {
|
||||||
it("should be true when the directive has the afterViewChecked method", () => {
|
it("should be true when the directive has the afterViewChecked method", () => {
|
||||||
expect(metadata(DirectiveWithAfterViewCheckedMethod, new DirectiveMetadata({}))
|
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked,
|
||||||
.callAfterViewChecked)
|
DirectiveWithAfterViewCheckedMethod))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () => {
|
it("should be false otherwise", () => {
|
||||||
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callAfterViewChecked)
|
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveNoHooks)).toBe(false);
|
||||||
.toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -62,8 +62,6 @@ import {
|
|||||||
PipeTransform,
|
PipeTransform,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetection,
|
|
||||||
DynamicChangeDetection,
|
|
||||||
ChangeDetectorGenConfig
|
ChangeDetectorGenConfig
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ import {
|
|||||||
ViewMetadata
|
ViewMetadata
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
import {By} from 'angular2/src/core/debug';
|
import {By} from 'angular2/src/core/debug';
|
||||||
import {MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE} from 'angular2/src/core/render';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('projection', () => {
|
describe('projection', () => {
|
||||||
@ -421,11 +420,8 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('different proto view storages', () => {
|
|
||||||
function runTests() {
|
|
||||||
it('should support nested conditionals that contain ng-contents',
|
it('should support nested conditionals that contain ng-contents',
|
||||||
inject(
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
|
||||||
tcb.overrideView(MainComp, new ViewMetadata({
|
tcb.overrideView(MainComp, new ViewMetadata({
|
||||||
template: `<conditional-text>a</conditional-text>`,
|
template: `<conditional-text>a</conditional-text>`,
|
||||||
directives: [ConditionalTextComponent]
|
directives: [ConditionalTextComponent]
|
||||||
@ -447,19 +443,6 @@ export function main() {
|
|||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
|
|
||||||
describe('serialize templates', () => {
|
|
||||||
beforeEachBindings(() => [bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(0)]);
|
|
||||||
runTests();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("don't serialize templates", () => {
|
|
||||||
beforeEachBindings(() => [bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(-1)]);
|
|
||||||
runTests();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,133 +11,9 @@ import {
|
|||||||
it
|
it
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {SpyChangeDetection} from '../spies';
|
|
||||||
import {isBlank, stringify} from 'angular2/src/core/facade/lang';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ChangeDetection,
|
|
||||||
ChangeDetectorDefinition,
|
|
||||||
BindingRecord,
|
|
||||||
DirectiveIndex,
|
|
||||||
Parser
|
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {
|
|
||||||
BindingRecordsCreator,
|
|
||||||
getChangeDetectorDefinitions
|
|
||||||
} from 'angular2/src/core/compiler/proto_view_factory';
|
|
||||||
import {Component, Directive} from 'angular2/src/core/metadata';
|
|
||||||
import {Key, Binding} from 'angular2/core';
|
|
||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
|
||||||
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
|
||||||
import {
|
|
||||||
RenderElementBinder,
|
|
||||||
EventBinding,
|
|
||||||
RenderDirectiveMetadata,
|
|
||||||
ViewType,
|
|
||||||
ProtoViewDto,
|
|
||||||
DirectiveBinder
|
|
||||||
} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('ProtoViewFactory', () => {
|
describe('ProtoViewFactory', () => {
|
||||||
var changeDetection;
|
// TODO
|
||||||
var directiveResolver;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
directiveResolver = new DirectiveResolver();
|
|
||||||
changeDetection = new SpyChangeDetection();
|
|
||||||
changeDetection.prop("generateDetectors", true);
|
|
||||||
});
|
|
||||||
|
|
||||||
function bindDirective(type) {
|
|
||||||
return DirectiveBinding.createFromType(type, directiveResolver.resolve(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('getChangeDetectorDefinitions', () => {
|
|
||||||
|
|
||||||
it('should create a ChangeDetectorDefinition for the root render proto view', () => {
|
|
||||||
var renderPv = createRenderProtoView();
|
|
||||||
var defs =
|
|
||||||
getChangeDetectorDefinitions(bindDirective(MainComponent).metadata, renderPv, [], null);
|
|
||||||
expect(defs.length).toBe(1);
|
|
||||||
expect(defs[0].id).toEqual(`${stringify(MainComponent)}_comp_0`);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('BindingRecordsCreator', () => {
|
|
||||||
var creator: BindingRecordsCreator;
|
|
||||||
|
|
||||||
beforeEach(() => { creator = new BindingRecordsCreator(); });
|
|
||||||
|
|
||||||
describe('getEventBindingRecords', () => {
|
|
||||||
it("should return template event records", inject([Parser], (p: Parser) => {
|
|
||||||
var ast1 = p.parseAction("1", null);
|
|
||||||
var ast2 = p.parseAction("2", null);
|
|
||||||
|
|
||||||
var rec = creator.getEventBindingRecords(
|
|
||||||
[
|
|
||||||
new RenderElementBinder(
|
|
||||||
{eventBindings: [new EventBinding("a", ast1)], directives: []}),
|
|
||||||
new RenderElementBinder(
|
|
||||||
{eventBindings: [new EventBinding("b", ast2)], directives: []})
|
|
||||||
],
|
|
||||||
[]);
|
|
||||||
|
|
||||||
expect(rec).toEqual([
|
|
||||||
BindingRecord.createForEvent(ast1, "a", 0),
|
|
||||||
BindingRecord.createForEvent(ast2, "b", 1)
|
|
||||||
]);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should return host event records', inject([Parser], (p: Parser) => {
|
|
||||||
var ast1 = p.parseAction("1", null);
|
|
||||||
|
|
||||||
var rec = creator.getEventBindingRecords(
|
|
||||||
[
|
|
||||||
new RenderElementBinder({
|
|
||||||
eventBindings: [],
|
|
||||||
directives: [
|
|
||||||
new DirectiveBinder(
|
|
||||||
{directiveIndex: 0, eventBindings: [new EventBinding("a", ast1)]})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
[RenderDirectiveMetadata.create({id: 'some-id'})]);
|
|
||||||
|
|
||||||
expect(rec.length).toEqual(1);
|
|
||||||
expect(rec[0].target.name).toEqual("a");
|
|
||||||
expect(rec[0].implicitReceiver).toBeAnInstanceOf(DirectiveIndex);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function directiveBinding({metadata}: {metadata?: any} = {}) {
|
|
||||||
return new DirectiveBinding(Key.get("dummy"), null, null, metadata, [], []);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRenderProtoView(elementBinders = null, type: ViewType = null,
|
|
||||||
variableBindings = null) {
|
|
||||||
if (isBlank(type)) {
|
|
||||||
type = ViewType.COMPONENT;
|
|
||||||
}
|
|
||||||
if (isBlank(elementBinders)) {
|
|
||||||
elementBinders = [];
|
|
||||||
}
|
|
||||||
if (isBlank(variableBindings)) {
|
|
||||||
variableBindings = new Map();
|
|
||||||
}
|
|
||||||
return new ProtoViewDto({
|
|
||||||
elementBinders: elementBinders,
|
|
||||||
type: type,
|
|
||||||
variableBindings: variableBindings,
|
|
||||||
textBindings: [],
|
|
||||||
transitiveNgContentCount: 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'main-comp'})
|
|
||||||
class MainComponent {
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ import {
|
|||||||
RenderProtoViewRef,
|
RenderProtoViewRef,
|
||||||
RenderFragmentRef,
|
RenderFragmentRef,
|
||||||
ViewType,
|
ViewType,
|
||||||
RenderProtoViewMergeMapping,
|
|
||||||
RenderViewWithFragments
|
RenderViewWithFragments
|
||||||
} from 'angular2/src/core/render/api';
|
} from 'angular2/src/core/render/api';
|
||||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
|
@ -37,11 +37,7 @@ import {
|
|||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {Component} from 'angular2/src/core/metadata';
|
import {Component} from 'angular2/src/core/metadata';
|
||||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||||
import {
|
import {ViewType, RenderViewWithFragments} from 'angular2/src/core/render/render';
|
||||||
RenderProtoViewMergeMapping,
|
|
||||||
ViewType,
|
|
||||||
RenderViewWithFragments
|
|
||||||
} from 'angular2/src/core/render/render';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
// TODO(tbosch): add more tests here!
|
// TODO(tbosch): add more tests here!
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import {RenderDirectiveMetadata} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
import {ddescribe, describe, expect, it} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('Metadata', () => {
|
|
||||||
describe('host', () => {
|
|
||||||
it('should parse host configuration', () => {
|
|
||||||
var md = RenderDirectiveMetadata.create({
|
|
||||||
host: MapWrapper.createFromPairs(
|
|
||||||
[['(event)', 'eventVal'], ['[prop]', 'propVal'], ['attr', 'attrVal']])
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(md.hostListeners).toEqual(MapWrapper.createFromPairs([['event', 'eventVal']]));
|
|
||||||
expect(md.hostProperties).toEqual(MapWrapper.createFromPairs([['prop', 'propVal']]));
|
|
||||||
expect(md.hostAttributes).toEqual(MapWrapper.createFromPairs([['attr', 'attrVal']]));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
/*
|
|
||||||
* Runs compiler tests using in-browser DOM adapter.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {runCompilerCommonTests} from './compiler_common_tests';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
runCompilerCommonTests();
|
|
||||||
}
|
|
@ -1,339 +0,0 @@
|
|||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
import {ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
import {Type, isBlank, stringify, isPresent} from 'angular2/src/core/facade/lang';
|
|
||||||
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
|
||||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
|
||||||
|
|
||||||
import {DomCompiler} from 'angular2/src/core/render/dom/compiler/compiler';
|
|
||||||
import {
|
|
||||||
ProtoViewDto,
|
|
||||||
ViewDefinition,
|
|
||||||
RenderDirectiveMetadata,
|
|
||||||
ViewType,
|
|
||||||
ViewEncapsulation
|
|
||||||
} from 'angular2/src/core/render/api';
|
|
||||||
import {CompileStep} from 'angular2/src/core/render/dom/compiler/compile_step';
|
|
||||||
import {CompileStepFactory} from 'angular2/src/core/render/dom/compiler/compile_step_factory';
|
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
|
||||||
import {ViewLoader, TemplateAndStyles} from 'angular2/src/core/render/dom/compiler/view_loader';
|
|
||||||
|
|
||||||
import {resolveInternalDomProtoView} from 'angular2/src/core/render/dom/view/proto_view';
|
|
||||||
import {SharedStylesHost} from 'angular2/src/core/render/dom/view/shared_styles_host';
|
|
||||||
import {TemplateCloner} from 'angular2/src/core/render/dom/template_cloner';
|
|
||||||
|
|
||||||
import {MockStep} from './pipeline_spec';
|
|
||||||
|
|
||||||
export function runCompilerCommonTests() {
|
|
||||||
describe('DomCompiler', function() {
|
|
||||||
var mockStepFactory: MockStepFactory;
|
|
||||||
var sharedStylesHost: SharedStylesHost;
|
|
||||||
|
|
||||||
beforeEach(() => {sharedStylesHost = new SharedStylesHost()});
|
|
||||||
|
|
||||||
function createCompiler(processElementClosure = null, processStyleClosure = null,
|
|
||||||
urlData = null) {
|
|
||||||
if (isBlank(urlData)) {
|
|
||||||
urlData = new Map();
|
|
||||||
}
|
|
||||||
var tplLoader = new FakeViewLoader(urlData);
|
|
||||||
mockStepFactory =
|
|
||||||
new MockStepFactory([new MockStep(processElementClosure, processStyleClosure)]);
|
|
||||||
return new DomCompiler(new ElementSchemaRegistry(), new TemplateCloner(-1), mockStepFactory,
|
|
||||||
tplLoader, sharedStylesHost);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('compile', () => {
|
|
||||||
|
|
||||||
it('should run the steps and build the AppProtoView of the root element',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler((parent, current, control) => {
|
|
||||||
current.inheritedProtoView.bindVariable('b', 'a');
|
|
||||||
});
|
|
||||||
compiler.compile(
|
|
||||||
new ViewDefinition({componentId: 'someComponent', template: '<div></div>'}))
|
|
||||||
.then((protoView) => {
|
|
||||||
expect(protoView.variableBindings)
|
|
||||||
.toEqual(MapWrapper.createFromStringMap({'a': 'b'}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should run the steps and build the proto view', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler((parent, current, control) => {
|
|
||||||
current.inheritedProtoView.bindVariable('b', 'a');
|
|
||||||
});
|
|
||||||
|
|
||||||
var dirMetadata = RenderDirectiveMetadata.create(
|
|
||||||
{id: 'id', selector: 'custom', type: RenderDirectiveMetadata.COMPONENT_TYPE});
|
|
||||||
compiler.compileHost(dirMetadata)
|
|
||||||
.then((protoView) => {
|
|
||||||
expect(DOM.tagName(DOM.firstChild(DOM.content(templateRoot(protoView))))
|
|
||||||
.toLowerCase())
|
|
||||||
.toEqual('custom');
|
|
||||||
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
|
|
||||||
expect(protoView.variableBindings)
|
|
||||||
.toEqual(MapWrapper.createFromStringMap({'a': 'b'}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create element from component selector', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler((parent, current, control) => {
|
|
||||||
current.inheritedProtoView.bindVariable('b', 'a');
|
|
||||||
});
|
|
||||||
|
|
||||||
var dirMetadata = RenderDirectiveMetadata.create({
|
|
||||||
id: 'id',
|
|
||||||
selector: 'marquee.jazzy[size=huge]',
|
|
||||||
type: RenderDirectiveMetadata.COMPONENT_TYPE
|
|
||||||
});
|
|
||||||
|
|
||||||
compiler.compileHost(dirMetadata)
|
|
||||||
.then((protoView) => {
|
|
||||||
let element = DOM.firstChild(DOM.content(templateRoot(protoView)));
|
|
||||||
expect(DOM.tagName(element).toLowerCase()).toEqual('marquee');
|
|
||||||
expect(DOM.hasClass(element, 'jazzy')).toBe(true);
|
|
||||||
expect(DOM.getAttribute(element, 'size')).toEqual('huge');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should use the inline template and compile in sync',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler(EMPTY_STEP);
|
|
||||||
compiler.compile(
|
|
||||||
new ViewDefinition({componentId: 'someId', template: 'inline component'}))
|
|
||||||
.then((protoView) => {
|
|
||||||
expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('inline component');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should load url templates', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var urlData = MapWrapper.createFromStringMap({'someUrl': 'url component'});
|
|
||||||
var compiler = createCompiler(EMPTY_STEP, null, urlData);
|
|
||||||
compiler.compile(new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'}))
|
|
||||||
.then((protoView) => {
|
|
||||||
expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('url component');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should remove script tags from templates', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler(EMPTY_STEP);
|
|
||||||
compiler.compile(new ViewDefinition(
|
|
||||||
{componentId: 'someId', template: '<div></div><script></script>'}))
|
|
||||||
.then((protoView) => {
|
|
||||||
expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('<div></div>');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report loading errors', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler(EMPTY_STEP, null, new Map());
|
|
||||||
PromiseWrapper.catchError(
|
|
||||||
compiler.compile(
|
|
||||||
new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'})),
|
|
||||||
(e) => {
|
|
||||||
expect(e.message).toEqual(
|
|
||||||
'Failed to load the template for "someId" : Failed to fetch url "someUrl"');
|
|
||||||
async.done();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should return ProtoViews of type COMPONENT_VIEW_TYPE',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler(EMPTY_STEP);
|
|
||||||
compiler.compile(
|
|
||||||
new ViewDefinition({componentId: 'someId', template: 'inline component'}))
|
|
||||||
.then((protoView) => {
|
|
||||||
expect(protoView.type).toEqual(ViewType.COMPONENT);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('compileHost', () => {
|
|
||||||
|
|
||||||
it('should return ProtoViews of type HOST_VIEW_TYPE',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler(EMPTY_STEP);
|
|
||||||
compiler.compileHost(someComponent)
|
|
||||||
.then((protoView) => {
|
|
||||||
expect(protoView.type).toEqual(ViewType.HOST);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('compile styles', () => {
|
|
||||||
it('should run the steps', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler(null, (style) => { return style + 'b {};'; });
|
|
||||||
compiler.compile(new ViewDefinition(
|
|
||||||
{componentId: 'someComponent', template: '', styles: ['a {};']}))
|
|
||||||
.then((protoViewDto) => {
|
|
||||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};b {};']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should store the styles in the SharedStylesHost for ViewEncapsulation.None',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler();
|
|
||||||
compiler.compile(new ViewDefinition({
|
|
||||||
componentId: 'someComponent',
|
|
||||||
template: '',
|
|
||||||
styles: ['a {};'],
|
|
||||||
encapsulation: ViewEncapsulation.None
|
|
||||||
}))
|
|
||||||
.then((protoViewDto) => {
|
|
||||||
expect(DOM.getInnerHTML(templateRoot(protoViewDto))).toEqual('');
|
|
||||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should store the styles in the SharedStylesHost for ViewEncapsulation.Emulated',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler();
|
|
||||||
compiler.compile(new ViewDefinition({
|
|
||||||
componentId: 'someComponent',
|
|
||||||
template: '',
|
|
||||||
styles: ['a {};'],
|
|
||||||
encapsulation: ViewEncapsulation.Emulated
|
|
||||||
}))
|
|
||||||
.then((protoViewDto) => {
|
|
||||||
expect(DOM.getInnerHTML(templateRoot(protoViewDto))).toEqual('');
|
|
||||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (DOM.supportsNativeShadowDOM()) {
|
|
||||||
it('should store the styles in the template for ViewEncapsulation.Native',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler();
|
|
||||||
compiler.compile(new ViewDefinition({
|
|
||||||
componentId: 'someComponent',
|
|
||||||
template: '',
|
|
||||||
styles: ['a {};'],
|
|
||||||
encapsulation: ViewEncapsulation.Native
|
|
||||||
}))
|
|
||||||
.then((protoViewDto) => {
|
|
||||||
expect(DOM.getInnerHTML(templateRoot(protoViewDto)))
|
|
||||||
.toEqual('<style>a {};</style>');
|
|
||||||
expect(sharedStylesHost.getAllStyles()).toEqual([]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should default to ViewEncapsulation.None if no styles are specified',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler();
|
|
||||||
compiler.compile(
|
|
||||||
new ViewDefinition({componentId: 'someComponent', template: '', styles: []}))
|
|
||||||
.then((protoView) => {
|
|
||||||
expect(mockStepFactory.viewDef.encapsulation).toBe(ViewEncapsulation.None);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should default to ViewEncapsulation.Emulated if styles are specified',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler();
|
|
||||||
compiler.compile(new ViewDefinition(
|
|
||||||
{componentId: 'someComponent', template: '', styles: ['a {};']}))
|
|
||||||
.then((protoView) => {
|
|
||||||
expect(mockStepFactory.viewDef.encapsulation).toBe(ViewEncapsulation.Emulated);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('mergeProtoViews', () => {
|
|
||||||
it('should store the styles of the merged ProtoView in the SharedStylesHost',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler();
|
|
||||||
compiler.compile(new ViewDefinition(
|
|
||||||
{componentId: 'someComponent', template: '', styles: ['a {};']}))
|
|
||||||
.then(protoViewDto => compiler.mergeProtoViewsRecursively([protoViewDto.render]))
|
|
||||||
.then(_ => {
|
|
||||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function templateRoot(protoViewDto: ProtoViewDto): Element {
|
|
||||||
var pv = resolveInternalDomProtoView(protoViewDto.render);
|
|
||||||
return (<Element>pv.cloneableTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
class MockStepFactory extends CompileStepFactory {
|
|
||||||
steps: CompileStep[];
|
|
||||||
subTaskPromises: Array<Promise<any>>;
|
|
||||||
viewDef: ViewDefinition;
|
|
||||||
|
|
||||||
constructor(steps) {
|
|
||||||
super();
|
|
||||||
this.steps = steps;
|
|
||||||
}
|
|
||||||
createSteps(viewDef): CompileStep[] {
|
|
||||||
this.viewDef = viewDef;
|
|
||||||
return this.steps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var EMPTY_STEP = (parent, current, control) => {
|
|
||||||
if (isPresent(parent)) {
|
|
||||||
current.inheritedProtoView = parent.inheritedProtoView;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class FakeViewLoader extends ViewLoader {
|
|
||||||
_urlData: Map<string, string>;
|
|
||||||
constructor(urlData) {
|
|
||||||
super(null, null, null);
|
|
||||||
this._urlData = urlData;
|
|
||||||
}
|
|
||||||
|
|
||||||
load(viewDef): Promise<any> {
|
|
||||||
var styles = isPresent(viewDef.styles) ? viewDef.styles : [];
|
|
||||||
if (isPresent(viewDef.template)) {
|
|
||||||
return PromiseWrapper.resolve(new TemplateAndStyles(viewDef.template, styles));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPresent(viewDef.templateAbsUrl)) {
|
|
||||||
var content = this._urlData.get(viewDef.templateAbsUrl);
|
|
||||||
return isPresent(content) ?
|
|
||||||
PromiseWrapper.resolve(new TemplateAndStyles(content, styles)) :
|
|
||||||
PromiseWrapper.reject(`Failed to fetch url "${viewDef.templateAbsUrl}"`, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new BaseException('View should have either the templateUrl or template property set');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var someComponent = RenderDirectiveMetadata.create(
|
|
||||||
{selector: 'some-comp', id: 'someComponent', type: RenderDirectiveMetadata.COMPONENT_TYPE});
|
|
@ -1,11 +0,0 @@
|
|||||||
library angular2.compiler.html5lib_dom_adapter.test;
|
|
||||||
|
|
||||||
import 'package:angular2/src/core/dom/html_adapter.dart';
|
|
||||||
import 'package:angular2/src/test_lib/test_lib.dart' show testSetup;
|
|
||||||
import 'compiler_common_tests.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
Html5LibDomAdapter.makeCurrent();
|
|
||||||
testSetup();
|
|
||||||
runCompilerCommonTests();
|
|
||||||
}
|
|
@ -1,251 +0,0 @@
|
|||||||
import {describe, beforeEach, it, xit, expect, iit, ddescribe, el} from 'angular2/test_lib';
|
|
||||||
import {isPresent, isBlank, assertionsEnabled} from 'angular2/src/core/facade/lang';
|
|
||||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
import {DirectiveParser} from 'angular2/src/core/render/dom/compiler/directive_parser';
|
|
||||||
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
|
|
||||||
import {ViewDefinition, RenderDirectiveMetadata, ViewType} from 'angular2/src/core/render/api';
|
|
||||||
import {Lexer, Parser} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {ElementBinderBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
|
|
||||||
import {MockStep} from './pipeline_spec';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('DirectiveParser', () => {
|
|
||||||
var parser, annotatedDirectives;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
annotatedDirectives = [
|
|
||||||
someComponent,
|
|
||||||
someComponent2,
|
|
||||||
someDirective,
|
|
||||||
someDirectiveIgnoringChildren,
|
|
||||||
decoratorWithMultipleAttrs,
|
|
||||||
someDirectiveWithProps,
|
|
||||||
someDirectiveWithHostProperties,
|
|
||||||
someDirectiveWithInvalidHostProperties,
|
|
||||||
someDirectiveWithHostAttributes,
|
|
||||||
someDirectiveWithEvents,
|
|
||||||
someDirectiveWithGlobalEvents
|
|
||||||
];
|
|
||||||
parser = new Parser(new Lexer());
|
|
||||||
});
|
|
||||||
|
|
||||||
function createPipeline(propertyBindings = null, directives = null) {
|
|
||||||
if (isBlank(directives)) directives = annotatedDirectives;
|
|
||||||
|
|
||||||
return new CompilePipeline([
|
|
||||||
new MockStep((parent, current, control) => {
|
|
||||||
if (isPresent(propertyBindings)) {
|
|
||||||
StringMapWrapper.forEach(propertyBindings, (ast, name) => {
|
|
||||||
current.bindElement().bindProperty(name, ast);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
new DirectiveParser(parser, directives)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createViewDefinition(): ViewDefinition {
|
|
||||||
return new ViewDefinition({componentId: 'someComponent'});
|
|
||||||
}
|
|
||||||
|
|
||||||
function process(el, propertyBindings = null, directives = null): ElementBinderBuilder[] {
|
|
||||||
var pipeline = createPipeline(propertyBindings, directives);
|
|
||||||
return ListWrapper.map(
|
|
||||||
pipeline.processElements(el, ViewType.COMPONENT, createViewDefinition()),
|
|
||||||
(ce) => ce.inheritedElementBinder);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should not add directives if they are not used', () => {
|
|
||||||
var results = process(el('<div></div>'));
|
|
||||||
expect(results[0]).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect directives in attributes', () => {
|
|
||||||
var results = process(el('<div some-decor></div>'));
|
|
||||||
expect(results[0].directives[0].directiveIndex)
|
|
||||||
.toBe(annotatedDirectives.indexOf(someDirective));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect directives with multiple attributes', () => {
|
|
||||||
var results = process(el('<input type=text control=one></input>'));
|
|
||||||
expect(results[0].directives[0].directiveIndex)
|
|
||||||
.toBe(annotatedDirectives.indexOf(decoratorWithMultipleAttrs));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should compile children by default', () => {
|
|
||||||
var results = createPipeline().processElements(el('<div some-decor></div>'),
|
|
||||||
ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(results[0].compileChildren).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should stop compiling children when specified in the directive config', () => {
|
|
||||||
var results = createPipeline().processElements(el('<div some-decor-ignoring-children></div>'),
|
|
||||||
ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(results[0].compileChildren).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bind directive properties from bound properties', () => {
|
|
||||||
var results = process(el('<div some-decor-props></div>'),
|
|
||||||
{'elProp': parser.parseBinding('someExpr', '')});
|
|
||||||
var directiveBinding = results[0].directives[0];
|
|
||||||
expect(directiveBinding.propertyBindings.get('dirProp').source).toEqual('someExpr');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bind directive properties from attribute values', () => {
|
|
||||||
var results = process(el('<div some-decor-props el-prop="someValue"></div>'));
|
|
||||||
var directiveBinding = results[0].directives[0];
|
|
||||||
var simpleProp = directiveBinding.propertyBindings.get('dirProp');
|
|
||||||
expect(simpleProp.source).toEqual('someValue');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bind host directive properties', () => {
|
|
||||||
var element = el('<input some-decor-with-host-props>');
|
|
||||||
var results = process(element);
|
|
||||||
|
|
||||||
var directiveBinding = results[0].directives[0];
|
|
||||||
|
|
||||||
var ast = directiveBinding.hostPropertyBindings.get('hostProp');
|
|
||||||
expect(ast.source).toEqual('dirProp');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw when parsing invalid host properties', () => {
|
|
||||||
expect(() => process(el('<input some-decor-with-invalid-host-props>')))
|
|
||||||
.toThrowError(
|
|
||||||
new RegExp('Simple binding expression can only contain field access and constants'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set host element attributes', () => {
|
|
||||||
var element = el('<input some-decor-with-host-attrs>');
|
|
||||||
var results = process(element);
|
|
||||||
|
|
||||||
expect(DOM.getAttribute(results[0].element, 'attr_name')).toEqual('attr_val');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not set host element attribute if an attribute already exists', () => {
|
|
||||||
var element = el('<input attr_name="initial" some-decor-with-host-attrs>');
|
|
||||||
var results = process(element);
|
|
||||||
|
|
||||||
expect(DOM.getAttribute(results[0].element, 'attr_name')).toEqual('initial');
|
|
||||||
|
|
||||||
DOM.removeAttribute(element, 'attr_name');
|
|
||||||
results = process(element);
|
|
||||||
expect(DOM.getAttribute(results[0].element, 'attr_name')).toEqual('attr_val');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add CSS classes if "class" specified in host element attributes', () => {
|
|
||||||
var element = el('<input class="foo baz" some-decor-with-host-attrs>');
|
|
||||||
var results = process(element);
|
|
||||||
|
|
||||||
expect(DOM.hasClass(results[0].element, 'foo')).toBeTruthy();
|
|
||||||
expect(DOM.hasClass(results[0].element, 'bar')).toBeTruthy();
|
|
||||||
expect(DOM.hasClass(results[0].element, 'baz')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should read attribute values', () => {
|
|
||||||
var element = el('<input some-decor-props some-attr="someValue">');
|
|
||||||
var results = process(element);
|
|
||||||
expect(results[0].readAttributes.get('some-attr')).toEqual('someValue');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bind directive events', () => {
|
|
||||||
var results = process(el('<div some-decor-events></div>'));
|
|
||||||
var directiveBinding = results[0].directives[0];
|
|
||||||
expect(directiveBinding.eventBindings.length).toEqual(1);
|
|
||||||
var eventBinding = directiveBinding.eventBindings[0];
|
|
||||||
expect(eventBinding.fullName).toEqual('click');
|
|
||||||
expect(eventBinding.source.source).toEqual('doIt()');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bind directive global events', () => {
|
|
||||||
var results = process(el('<div some-decor-globalevents></div>'));
|
|
||||||
var directiveBinding = results[0].directives[0];
|
|
||||||
expect(directiveBinding.eventBindings.length).toEqual(1);
|
|
||||||
var eventBinding = directiveBinding.eventBindings[0];
|
|
||||||
expect(eventBinding.fullName).toEqual('window:resize');
|
|
||||||
expect(eventBinding.source.source).toEqual('doItGlobal()');
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: assertions should be enabled when running tests:
|
|
||||||
// https://github.com/angular/angular/issues/1340
|
|
||||||
describe('component directives', () => {
|
|
||||||
it('should save the component id', () => {
|
|
||||||
var results = process(el('<some-comp></some-comp>'));
|
|
||||||
expect(results[0].componentId).toEqual('someComponent');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not allow multiple component directives on the same element', () => {
|
|
||||||
expect(() => {
|
|
||||||
process(el('<some-comp></some-comp>'), null, [someComponent, someComponentDup]);
|
|
||||||
}).toThrowError(new RegExp('Only one component directive is allowed per element'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should sort the directives and store the component as the first directive', () => {
|
|
||||||
var results = process(el('<some-comp some-decor></some-comp>'));
|
|
||||||
expect(annotatedDirectives[results[0].directives[0].directiveIndex].id)
|
|
||||||
.toEqual('someComponent');
|
|
||||||
expect(annotatedDirectives[results[0].directives[1].directiveIndex].id)
|
|
||||||
.toEqual('someDirective');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var someComponent = RenderDirectiveMetadata.create(
|
|
||||||
{selector: 'some-comp', id: 'someComponent', type: RenderDirectiveMetadata.COMPONENT_TYPE});
|
|
||||||
|
|
||||||
var someComponentDup = RenderDirectiveMetadata.create(
|
|
||||||
{selector: 'some-comp', id: 'someComponentDup', type: RenderDirectiveMetadata.COMPONENT_TYPE});
|
|
||||||
|
|
||||||
var someComponent2 = RenderDirectiveMetadata.create(
|
|
||||||
{selector: 'some-comp2', id: 'someComponent2', type: RenderDirectiveMetadata.COMPONENT_TYPE});
|
|
||||||
|
|
||||||
var someDirective = RenderDirectiveMetadata.create(
|
|
||||||
{selector: '[some-decor]', id: 'someDirective', type: RenderDirectiveMetadata.DIRECTIVE_TYPE});
|
|
||||||
|
|
||||||
var someDirectiveIgnoringChildren = RenderDirectiveMetadata.create({
|
|
||||||
selector: '[some-decor-ignoring-children]',
|
|
||||||
compileChildren: false,
|
|
||||||
type: RenderDirectiveMetadata.DIRECTIVE_TYPE
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
var decoratorWithMultipleAttrs = RenderDirectiveMetadata.create({
|
|
||||||
selector: 'input[type=text][control]',
|
|
||||||
id: 'decoratorWithMultipleAttrs',
|
|
||||||
type: RenderDirectiveMetadata.DIRECTIVE_TYPE
|
|
||||||
});
|
|
||||||
|
|
||||||
var someDirectiveWithProps = RenderDirectiveMetadata.create(
|
|
||||||
{selector: '[some-decor-props]', inputs: ['dirProp: elProp'], readAttributes: ['some-attr']});
|
|
||||||
|
|
||||||
var someDirectiveWithHostProperties = RenderDirectiveMetadata.create({
|
|
||||||
selector: '[some-decor-with-host-props]',
|
|
||||||
host: MapWrapper.createFromStringMap<string>({'[hostProp]': 'dirProp'})
|
|
||||||
});
|
|
||||||
|
|
||||||
var someDirectiveWithInvalidHostProperties = RenderDirectiveMetadata.create({
|
|
||||||
selector: '[some-decor-with-invalid-host-props]',
|
|
||||||
host: MapWrapper.createFromStringMap<string>({'[hostProp]': 'dirProp + dirProp2'})
|
|
||||||
});
|
|
||||||
|
|
||||||
var someDirectiveWithHostAttributes = RenderDirectiveMetadata.create({
|
|
||||||
selector: '[some-decor-with-host-attrs]',
|
|
||||||
host: MapWrapper.createFromStringMap<string>({'attr_name': 'attr_val', 'class': 'foo bar'})
|
|
||||||
});
|
|
||||||
|
|
||||||
var someDirectiveWithEvents = RenderDirectiveMetadata.create({
|
|
||||||
selector: '[some-decor-events]',
|
|
||||||
host: MapWrapper.createFromStringMap<string>({'(click)': 'doIt()'})
|
|
||||||
});
|
|
||||||
|
|
||||||
var someDirectiveWithGlobalEvents = RenderDirectiveMetadata.create({
|
|
||||||
selector: '[some-decor-globalevents]',
|
|
||||||
host: MapWrapper.createFromStringMap<string>({'(window:resize)': 'doItGlobal()'})
|
|
||||||
});
|
|
||||||
|
|
||||||
var componentWithNonElementSelector = RenderDirectiveMetadata.create({
|
|
||||||
id: 'componentWithNonElementSelector',
|
|
||||||
selector: '[attr]',
|
|
||||||
type: RenderDirectiveMetadata.COMPONENT_TYPE
|
|
||||||
});
|
|
@ -1,287 +0,0 @@
|
|||||||
import {describe, beforeEach, it, expect, iit, ddescribe, el} from 'angular2/test_lib';
|
|
||||||
import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
import {isPresent, NumberWrapper, StringWrapper} from 'angular2/src/core/facade/lang';
|
|
||||||
|
|
||||||
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
|
|
||||||
import {CompileElement} from 'angular2/src/core/render/dom/compiler/compile_element';
|
|
||||||
import {CompileStep} from 'angular2/src/core/render/dom/compiler/compile_step';
|
|
||||||
import {CompileControl} from 'angular2/src/core/render/dom/compiler/compile_control';
|
|
||||||
|
|
||||||
import {ProtoViewBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
|
|
||||||
import {
|
|
||||||
ProtoViewDto,
|
|
||||||
ViewType,
|
|
||||||
ViewEncapsulation,
|
|
||||||
ViewDefinition
|
|
||||||
} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('compile_pipeline', () => {
|
|
||||||
function createViewDefinition(): ViewDefinition {
|
|
||||||
return new ViewDefinition({componentId: 'someComponent'});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('children compilation', () => {
|
|
||||||
it('should walk the tree in depth first order including template contents', () => {
|
|
||||||
var element = el('<div id="1"><template id="2"><span id="3"></span></template></div>');
|
|
||||||
|
|
||||||
var step0Log = [];
|
|
||||||
var results = new CompilePipeline([createLoggerStep(step0Log)])
|
|
||||||
.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
|
|
||||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
|
||||||
expect(resultIdLog(results)).toEqual(['1', '2', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should stop walking the tree when compileChildren is false', () => {
|
|
||||||
var element = el(
|
|
||||||
'<div id="1"><template id="2" ignore-children><span id="3"></span></template></div>');
|
|
||||||
|
|
||||||
var step0Log = [];
|
|
||||||
var pipeline = new CompilePipeline([new IgnoreChildrenStep(), createLoggerStep(step0Log)]);
|
|
||||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
|
|
||||||
expect(step0Log).toEqual(['1', '1<2']);
|
|
||||||
expect(resultIdLog(results)).toEqual(['1', '2']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inherit protoViewBuilders to children', () => {
|
|
||||||
var element = el('<div><div><span viewroot><span></span></span></div></div>');
|
|
||||||
var pipeline = new CompilePipeline([
|
|
||||||
new MockStep((parent, current, control) => {
|
|
||||||
if (isPresent(DOM.getAttribute(current.element, 'viewroot'))) {
|
|
||||||
current.inheritedProtoView =
|
|
||||||
new ProtoViewBuilder(current.element, ViewType.EMBEDDED, ViewEncapsulation.None);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(results[0].inheritedProtoView).toBe(results[1].inheritedProtoView);
|
|
||||||
expect(results[2].inheritedProtoView).toBe(results[3].inheritedProtoView);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inherit elementBinderBuilders to children', () => {
|
|
||||||
var element = el('<div bind><div><span bind><span></span></span></div></div>');
|
|
||||||
var pipeline = new CompilePipeline([
|
|
||||||
new MockStep((parent, current, control) => {
|
|
||||||
if (isPresent(DOM.getAttribute(current.element, 'bind'))) {
|
|
||||||
current.bindElement();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(results[0].inheritedElementBinder).toBe(results[1].inheritedElementBinder);
|
|
||||||
expect(results[2].inheritedElementBinder).toBe(results[3].inheritedElementBinder);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should mark root elements as viewRoot', () => {
|
|
||||||
var rootElement = el('<div></div>');
|
|
||||||
var results = new CompilePipeline([])
|
|
||||||
.processElements(rootElement, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(results[0].isViewRoot).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should calculate distanceToParent / parent correctly', () => {
|
|
||||||
var element = el('<div bind><div bind></div><div><div bind></div></div></div>');
|
|
||||||
var pipeline = new CompilePipeline([
|
|
||||||
new MockStep((parent, current, control) => {
|
|
||||||
if (isPresent(DOM.getAttribute(current.element, 'bind'))) {
|
|
||||||
current.bindElement();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(results[0].inheritedElementBinder.distanceToParent).toBe(0);
|
|
||||||
expect(results[1].inheritedElementBinder.distanceToParent).toBe(1);
|
|
||||||
expect(results[3].inheritedElementBinder.distanceToParent).toBe(2);
|
|
||||||
expect(results[1].inheritedElementBinder.parent).toBe(results[0].inheritedElementBinder);
|
|
||||||
expect(results[3].inheritedElementBinder.parent).toBe(results[0].inheritedElementBinder);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not execute further steps when ignoreCurrentElement has been called', () => {
|
|
||||||
var element = el('<div id="1"><span id="2" ignore-current></span><span id="3"></span></div>');
|
|
||||||
var logs = [];
|
|
||||||
var pipeline = new CompilePipeline([
|
|
||||||
new IgnoreCurrentElementStep(),
|
|
||||||
createLoggerStep(logs),
|
|
||||||
]);
|
|
||||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
|
|
||||||
expect(results.length).toBe(2);
|
|
||||||
expect(logs).toEqual(['1', '1<3'])
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('control.addParent', () => {
|
|
||||||
it('should report the new parent to the following processor and the result', () => {
|
|
||||||
var element = el('<div id="1"><span wrap0="1" id="2"><b id="3"></b></span></div>');
|
|
||||||
var step0Log = [];
|
|
||||||
var step1Log = [];
|
|
||||||
var pipeline =
|
|
||||||
new CompilePipeline([createWrapperStep('wrap0', step0Log), createLoggerStep(step1Log)]);
|
|
||||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
|
||||||
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<3']);
|
|
||||||
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', '2', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow to add a parent by multiple processors to the same element', () => {
|
|
||||||
var element =
|
|
||||||
el('<div id="1"><span wrap0="1" wrap1="1" id="2"><b id="3"></b></span></div>');
|
|
||||||
var step0Log = [];
|
|
||||||
var step1Log = [];
|
|
||||||
var step2Log = [];
|
|
||||||
var pipeline = new CompilePipeline([
|
|
||||||
createWrapperStep('wrap0', step0Log),
|
|
||||||
createWrapperStep('wrap1', step1Log),
|
|
||||||
createLoggerStep(step2Log)
|
|
||||||
]);
|
|
||||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
|
||||||
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<3']);
|
|
||||||
expect(step2Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<wrap1#0', 'wrap1#0<2', '2<3']);
|
|
||||||
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', 'wrap1#0', '2', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow to add a parent by multiple processors to different elements', () => {
|
|
||||||
var element =
|
|
||||||
el('<div id="1"><span wrap0="1" id="2"><b id="3" wrap1="1"></b></span></div>');
|
|
||||||
var step0Log = [];
|
|
||||||
var step1Log = [];
|
|
||||||
var step2Log = [];
|
|
||||||
var pipeline = new CompilePipeline([
|
|
||||||
createWrapperStep('wrap0', step0Log),
|
|
||||||
createWrapperStep('wrap1', step1Log),
|
|
||||||
createLoggerStep(step2Log)
|
|
||||||
]);
|
|
||||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
|
||||||
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<3']);
|
|
||||||
expect(step2Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<wrap1#0', 'wrap1#0<3']);
|
|
||||||
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', '2', 'wrap1#0', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow to add multiple parents by the same processor', () => {
|
|
||||||
var element = el('<div id="1"><span wrap0="2" id="2"><b id="3"></b></span></div>');
|
|
||||||
var step0Log = [];
|
|
||||||
var step1Log = [];
|
|
||||||
var pipeline =
|
|
||||||
new CompilePipeline([createWrapperStep('wrap0', step0Log), createLoggerStep(step1Log)]);
|
|
||||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
|
||||||
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<wrap0#1', 'wrap0#1<2', '2<3']);
|
|
||||||
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', 'wrap0#1', '2', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('control.addChild', () => {
|
|
||||||
it('should report the new child to all processors and the result', () => {
|
|
||||||
var element = el('<div id="1"><div id="2"></div></div>');
|
|
||||||
var resultLog = [];
|
|
||||||
var newChild = new CompileElement(el('<div id="3"></div>'));
|
|
||||||
var pipeline = new CompilePipeline([
|
|
||||||
new MockStep((parent, current, control) => {
|
|
||||||
if (StringWrapper.equals(DOM.getAttribute(current.element, 'id'), '1')) {
|
|
||||||
control.addChild(newChild);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
createLoggerStep(resultLog)
|
|
||||||
]);
|
|
||||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(result[2]).toBe(newChild);
|
|
||||||
expect(resultLog).toEqual(['1', '1<2', '1<3']);
|
|
||||||
expect(resultIdLog(result)).toEqual(['1', '2', '3']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('processStyles', () => {
|
|
||||||
it('should call the steps for every style', () => {
|
|
||||||
var stepCalls = [];
|
|
||||||
var pipeline = new CompilePipeline([
|
|
||||||
new MockStep(null,
|
|
||||||
(style) => {
|
|
||||||
stepCalls.push(style);
|
|
||||||
return style;
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
var result = pipeline.processStyles(['a', 'b']);
|
|
||||||
expect(result[0]).toEqual('a');
|
|
||||||
expect(result[1]).toEqual('b');
|
|
||||||
expect(result).toEqual(stepCalls);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MockStep implements CompileStep {
|
|
||||||
constructor(private _processElementClosure: Function,
|
|
||||||
private _processStyleClosure: Function = null) {}
|
|
||||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
|
||||||
if (isPresent(this._processElementClosure)) {
|
|
||||||
this._processElementClosure(parent, current, control);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processStyle(style: string): string {
|
|
||||||
if (isPresent(this._processStyleClosure)) {
|
|
||||||
return this._processStyleClosure(style);
|
|
||||||
} else {
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class IgnoreChildrenStep implements CompileStep {
|
|
||||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
|
||||||
var attributeMap = DOM.attributeMap(current.element);
|
|
||||||
if (attributeMap.has('ignore-children')) {
|
|
||||||
current.compileChildren = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processStyle(style: string): string { return style; }
|
|
||||||
}
|
|
||||||
|
|
||||||
class IgnoreCurrentElementStep implements CompileStep {
|
|
||||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
|
||||||
var attributeMap = DOM.attributeMap(current.element);
|
|
||||||
if (attributeMap.has('ignore-current')) {
|
|
||||||
control.ignoreCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processStyle(style: string): string { return style; }
|
|
||||||
}
|
|
||||||
|
|
||||||
function logEntry(log: string[], parent, current) {
|
|
||||||
var parentId = '';
|
|
||||||
if (isPresent(parent)) {
|
|
||||||
parentId = DOM.getAttribute(parent.element, 'id') + '<';
|
|
||||||
}
|
|
||||||
log.push(parentId + DOM.getAttribute(current.element, 'id'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function createLoggerStep(log: string[]) {
|
|
||||||
return new MockStep((parent, current, control) => { logEntry(log, parent, current); });
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWrapperStep(wrapperId, log) {
|
|
||||||
var nextElementId = 0;
|
|
||||||
return new MockStep((parent, current, control) => {
|
|
||||||
var parentCountStr = DOM.getAttribute(current.element, wrapperId);
|
|
||||||
if (isPresent(parentCountStr)) {
|
|
||||||
var parentCount = NumberWrapper.parseInt(parentCountStr, 10);
|
|
||||||
while (parentCount > 0) {
|
|
||||||
control.addParent(new CompileElement(el(`<a id="${wrapperId}#${nextElementId++}"></a>`)));
|
|
||||||
parentCount--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logEntry(log, parent, current);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function resultIdLog(result) {
|
|
||||||
var idLog = [];
|
|
||||||
ListWrapper.forEach(result, (current) => { logEntry(idLog, null, current); });
|
|
||||||
return idLog;
|
|
||||||
}
|
|
@ -1,230 +0,0 @@
|
|||||||
import {describe, beforeEach, it, expect, iit, ddescribe, el} from 'angular2/test_lib';
|
|
||||||
import {PropertyBindingParser} from 'angular2/src/core/render/dom/compiler/property_binding_parser';
|
|
||||||
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
|
|
||||||
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
import {Lexer, Parser} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {ElementBinderBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
|
|
||||||
import {ViewDefinition, ViewType} from 'angular2/src/core/render/api';
|
|
||||||
import {MockStep} from './pipeline_spec';
|
|
||||||
|
|
||||||
var EMPTY_MAP = new Map();
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('PropertyBindingParser', () => {
|
|
||||||
function createPipeline(hasNestedProtoView = false) {
|
|
||||||
return new CompilePipeline([
|
|
||||||
new MockStep((parent, current, control) => {
|
|
||||||
if (hasNestedProtoView) {
|
|
||||||
current.bindElement().bindNestedProtoView(el('<template></template>'));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
new PropertyBindingParser(new Parser(new Lexer()))
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createViewDefinition(): ViewDefinition {
|
|
||||||
return new ViewDefinition({componentId: 'someComponent'});
|
|
||||||
}
|
|
||||||
|
|
||||||
function process(element, hasNestedProtoView = false): ElementBinderBuilder[] {
|
|
||||||
return ListWrapper.map(
|
|
||||||
createPipeline(hasNestedProtoView)
|
|
||||||
.processElements(element, ViewType.COMPONENT, createViewDefinition()),
|
|
||||||
(compileElement) => compileElement.inheritedElementBinder);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should detect [] syntax', () => {
|
|
||||||
var results = process(el('<div [a]="b"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect [] syntax with data- prefix', () => {
|
|
||||||
var results = process(el('<div data-[a]="b"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect [] syntax only if an attribute name starts and ends with []', () => {
|
|
||||||
expect(process(el('<div z[a]="b"></div>'))[0]).toBe(null);
|
|
||||||
expect(process(el('<div [a]v="b"></div>'))[0]).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw when [] binding contains interpolation', () => {
|
|
||||||
expect(() => process(el('<div [a]="a + {{b()}}"></div>'))[0])
|
|
||||||
.toThrowErrorWith(
|
|
||||||
'Got interpolation ({{}}) where expression was expected at column 4 in [a + {{b()}}] in someComponent');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect bind- syntax', () => {
|
|
||||||
var results = process(el('<div bind-a="b"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect bind- syntax with data- prefix', () => {
|
|
||||||
var results = process(el('<div data-bind-a="b"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect bind- syntax only if an attribute name starts with bind',
|
|
||||||
() => { expect(process(el('<div _bind-a="b"></div>'))[0]).toEqual(null); });
|
|
||||||
|
|
||||||
it('should detect interpolation syntax', () => {
|
|
||||||
var results = process(el('<div a="{{b}}"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('{{b}}');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect interpolation syntax with data- prefix', () => {
|
|
||||||
var results = process(el('<div data-a="{{b}}"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('{{b}}');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store property setters as camel case', () => {
|
|
||||||
var element = el('<div bind-some-prop="1">');
|
|
||||||
var results = process(element);
|
|
||||||
expect(results[0].propertyBindings.get('someProp')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect var- syntax', () => {
|
|
||||||
var results = process(el('<template var-a="b"></template>'));
|
|
||||||
expect(results[0].variableBindings.get('b')).toEqual('a');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect var- syntax with data- prefix', () => {
|
|
||||||
var results = process(el('<template data-var-a="b"></template>'));
|
|
||||||
expect(results[0].variableBindings.get('b')).toEqual('a');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store variable binding for a template element on the nestedProtoView', () => {
|
|
||||||
var results = process(el('<template var-george="washington"></p>'), true);
|
|
||||||
expect(results[0].variableBindings).toEqual(EMPTY_MAP);
|
|
||||||
expect(results[0].nestedProtoView.variableBindings.get('washington')).toEqual('george');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store variable binding for a non-template element using shorthand syntax on the nestedProtoView',
|
|
||||||
() => {
|
|
||||||
var results = process(el('<template #george="washington"></template>'), true);
|
|
||||||
expect(results[0].variableBindings).toEqual(EMPTY_MAP);
|
|
||||||
expect(results[0].nestedProtoView.variableBindings.get('washington')).toEqual('george');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store variable binding for a non-template element', () => {
|
|
||||||
var results = process(el('<p var-george="washington"></p>'));
|
|
||||||
expect(results[0].variableBindings.get('washington')).toEqual('george');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store variable binding for a non-template element using shorthand syntax', () => {
|
|
||||||
var results = process(el('<p #george="washington"></p>'));
|
|
||||||
expect(results[0].variableBindings.get('washington')).toEqual('george');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store a variable binding with an implicit value', () => {
|
|
||||||
var results = process(el('<p var-george></p>'));
|
|
||||||
expect(results[0].variableBindings.get('\$implicit')).toEqual('george');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store a variable binding with an implicit value using shorthand syntax', () => {
|
|
||||||
var results = process(el('<p #george></p>'));
|
|
||||||
expect(results[0].variableBindings.get('\$implicit')).toEqual('george');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect variable bindings only if an attribute name starts with #', () => {
|
|
||||||
var results = process(el('<p b#george></p>'));
|
|
||||||
expect(results[0]).toEqual(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect () syntax', () => {
|
|
||||||
var results = process(el('<div (click)="b()"></div>'));
|
|
||||||
var eventBinding = results[0].eventBindings[0];
|
|
||||||
expect(eventBinding.source.source).toEqual('b()');
|
|
||||||
expect(eventBinding.fullName).toEqual('click');
|
|
||||||
// "(click[])" is not an expected syntax and is only used to validate the regexp
|
|
||||||
results = process(el('<div (click[])="b()"></div>'));
|
|
||||||
eventBinding = results[0].eventBindings[0];
|
|
||||||
expect(eventBinding.source.source).toEqual('b()');
|
|
||||||
expect(eventBinding.fullName).toEqual('click[]');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect () syntax with data- prefix', () => {
|
|
||||||
var results = process(el('<div data-(click)="b()"></div>'));
|
|
||||||
var eventBinding = results[0].eventBindings[0];
|
|
||||||
expect(eventBinding.source.source).toEqual('b()');
|
|
||||||
expect(eventBinding.fullName).toEqual('click');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect () syntax only if an attribute name starts and ends with ()', () => {
|
|
||||||
expect(process(el('<div z(a)="b()"></div>'))[0]).toEqual(null);
|
|
||||||
expect(process(el('<div (a)v="b()"></div>'))[0]).toEqual(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse event handlers using () syntax as actions', () => {
|
|
||||||
var results = process(el('<div (click)="foo=bar"></div>'));
|
|
||||||
var eventBinding = results[0].eventBindings[0];
|
|
||||||
expect(eventBinding.source.source).toEqual('foo=bar');
|
|
||||||
expect(eventBinding.fullName).toEqual('click');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw when () action contains interpolation', () => {
|
|
||||||
expect(() => process(el('<div (a)="{{b()}}"></div>'))[0])
|
|
||||||
.toThrowErrorWith(
|
|
||||||
'Got interpolation ({{}}) where expression was expected at column 0 in [{{b()}}] in someComponent');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect on- syntax', () => {
|
|
||||||
var results = process(el('<div on-click="b()"></div>'));
|
|
||||||
var eventBinding = results[0].eventBindings[0];
|
|
||||||
expect(eventBinding.source.source).toEqual('b()');
|
|
||||||
expect(eventBinding.fullName).toEqual('click');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect on- syntax with data- prefix', () => {
|
|
||||||
var results = process(el('<div data-on-click="b()"></div>'));
|
|
||||||
var eventBinding = results[0].eventBindings[0];
|
|
||||||
expect(eventBinding.source.source).toEqual('b()');
|
|
||||||
expect(eventBinding.fullName).toEqual('click');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse event handlers using on- syntax as actions', () => {
|
|
||||||
var results = process(el('<div on-click="foo=bar"></div>'));
|
|
||||||
var eventBinding = results[0].eventBindings[0];
|
|
||||||
expect(eventBinding.source.source).toEqual('foo=bar');
|
|
||||||
expect(eventBinding.fullName).toEqual('click');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store bound properties as temporal attributes', () => {
|
|
||||||
var results = createPipeline().processElements(el('<div bind-a="b" [c]="d"></div>'),
|
|
||||||
ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(results[0].attrs().get('a')).toEqual('b');
|
|
||||||
expect(results[0].attrs().get('c')).toEqual('d');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store variables as temporal attributes', () => {
|
|
||||||
var results = createPipeline().processElements(el('<div var-a="b" #c="d"></div>'),
|
|
||||||
ViewType.COMPONENT, createViewDefinition());
|
|
||||||
expect(results[0].attrs().get('a')).toEqual('b');
|
|
||||||
expect(results[0].attrs().get('c')).toEqual('d');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect [()] syntax', () => {
|
|
||||||
var results = process(el('<div [(a)]="b"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
|
||||||
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect [()] syntax with data- prefix', () => {
|
|
||||||
var results = process(el('<div data-[(a)]="b"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
|
||||||
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect bindon- syntax', () => {
|
|
||||||
var results = process(el('<div bindon-a="b"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
|
||||||
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect bindon- syntax with data- prefix', () => {
|
|
||||||
var results = process(el('<div data-bindon-a="b"></div>'));
|
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
|
||||||
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
SpyObject,
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
|
|
||||||
|
|
||||||
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
import {
|
|
||||||
ProtoViewBuilder,
|
|
||||||
ElementBinderBuilder
|
|
||||||
} from 'angular2/src/core/render/dom/view/proto_view_builder';
|
|
||||||
import {ViewDefinition, ViewType, ViewEncapsulation} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
import {StyleEncapsulator} from 'angular2/src/core/render/dom/compiler/style_encapsulator';
|
|
||||||
import {MockStep} from './pipeline_spec';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('StyleEncapsulator', () => {
|
|
||||||
var componentIdCache;
|
|
||||||
|
|
||||||
beforeEach(() => { componentIdCache = new Map(); });
|
|
||||||
|
|
||||||
function createPipeline(viewDef: ViewDefinition) {
|
|
||||||
return new CompilePipeline([
|
|
||||||
new MockStep((parent, current, control) => {
|
|
||||||
var tagName = DOM.tagName(current.element).toLowerCase();
|
|
||||||
if (tagName.startsWith('comp-')) {
|
|
||||||
current.bindElement().setComponentId(tagName);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
new StyleEncapsulator('someapp', viewDef, componentIdCache)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createViewDefinition(encapsulation: ViewEncapsulation, componentId: string):
|
|
||||||
ViewDefinition {
|
|
||||||
return new ViewDefinition({encapsulation: encapsulation, componentId: componentId});
|
|
||||||
}
|
|
||||||
|
|
||||||
function processStyles(encapsulation: ViewEncapsulation, componentId: string, styles: string[]):
|
|
||||||
string[] {
|
|
||||||
var viewDef = createViewDefinition(encapsulation, componentId);
|
|
||||||
return createPipeline(viewDef).processStyles(styles);
|
|
||||||
}
|
|
||||||
|
|
||||||
function processElements(encapsulation: ViewEncapsulation, componentId: string,
|
|
||||||
template: Element, viewType: ViewType = ViewType.COMPONENT):
|
|
||||||
ProtoViewBuilder {
|
|
||||||
var viewDef = createViewDefinition(encapsulation, componentId);
|
|
||||||
var compileElements = createPipeline(viewDef).processElements(template, viewType, viewDef);
|
|
||||||
return compileElements[0].inheritedProtoView;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('ViewEncapsulation.None', () => {
|
|
||||||
it('should not change the styles', () => {
|
|
||||||
var cs = processStyles(ViewEncapsulation.None, 'someComponent', ['.one {}']);
|
|
||||||
expect(cs[0]).toEqual('.one {}');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ViewEncapsulation.Native', () => {
|
|
||||||
it('should not change the styles', () => {
|
|
||||||
var cs = processStyles(ViewEncapsulation.Native, 'someComponent', ['.one {}']);
|
|
||||||
expect(cs[0]).toEqual('.one {}');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ViewEncapsulation.Emulated', () => {
|
|
||||||
it('should scope styles', () => {
|
|
||||||
var cs = processStyles(ViewEncapsulation.Emulated, 'someComponent', ['.foo {} :host {}']);
|
|
||||||
expect(cs[0]).toEqual(".foo[_ngcontent-someapp-0] {\n\n}\n\n[_nghost-someapp-0] {\n\n}");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return the same style given the same component', () => {
|
|
||||||
var style = '.foo {} :host {}';
|
|
||||||
var cs1 = processStyles(ViewEncapsulation.Emulated, 'someComponent', [style]);
|
|
||||||
var cs2 = processStyles(ViewEncapsulation.Emulated, 'someComponent', [style]);
|
|
||||||
|
|
||||||
expect(cs1[0]).toEqual(cs2[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return different styles given different components', () => {
|
|
||||||
var style = '.foo {} :host {}';
|
|
||||||
var cs1 = processStyles(ViewEncapsulation.Emulated, 'someComponent1', [style]);
|
|
||||||
var cs2 = processStyles(ViewEncapsulation.Emulated, 'someComponent2', [style]);
|
|
||||||
|
|
||||||
expect(cs1[0]).not.toEqual(cs2[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add a host attribute to component proto views', () => {
|
|
||||||
var template = DOM.createTemplate('<div></div>');
|
|
||||||
var protoViewBuilder =
|
|
||||||
processElements(ViewEncapsulation.Emulated, 'someComponent', template);
|
|
||||||
expect(protoViewBuilder.hostAttributes.get('_nghost-someapp-0')).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not add a host attribute to embedded proto views', () => {
|
|
||||||
var template = DOM.createTemplate('<div></div>');
|
|
||||||
var protoViewBuilder = processElements(ViewEncapsulation.Emulated, 'someComponent',
|
|
||||||
template, ViewType.EMBEDDED);
|
|
||||||
expect(protoViewBuilder.hostAttributes.size).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not add a host attribute to host proto views', () => {
|
|
||||||
var template = DOM.createTemplate('<div></div>');
|
|
||||||
var protoViewBuilder =
|
|
||||||
processElements(ViewEncapsulation.Emulated, 'someComponent', template, ViewType.HOST);
|
|
||||||
expect(protoViewBuilder.hostAttributes.size).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add an attribute to the content elements', () => {
|
|
||||||
var template = DOM.createTemplate('<div></div>');
|
|
||||||
processElements(ViewEncapsulation.Emulated, 'someComponent', template);
|
|
||||||
expect(DOM.getInnerHTML(template)).toEqual('<div _ngcontent-someapp-0=""></div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not add an attribute to the content elements for host views', () => {
|
|
||||||
var template = DOM.createTemplate('<div></div>');
|
|
||||||
processElements(ViewEncapsulation.Emulated, 'someComponent', template, ViewType.HOST);
|
|
||||||
expect(DOM.getInnerHTML(template)).toEqual('<div></div>');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
beforeEachBindings,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
import {StyleInliner} from 'angular2/src/core/render/dom/compiler/style_inliner';
|
|
||||||
|
|
||||||
import {isBlank} from 'angular2/src/core/facade/lang';
|
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
|
||||||
import {Map, MapWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
|
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
|
||||||
|
|
||||||
import {bind} from 'angular2/core';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('StyleInliner', () => {
|
|
||||||
beforeEachBindings(() => [
|
|
||||||
bind(XHR)
|
|
||||||
.toClass(FakeXHR),
|
|
||||||
]);
|
|
||||||
|
|
||||||
describe('loading', () => {
|
|
||||||
|
|
||||||
it('should return a string when there is no import statement',
|
|
||||||
inject([StyleInliner], (inliner) => {
|
|
||||||
var css = '.main {}';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
|
||||||
expect(loadedCss).toEqual(css);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should inline @import rules',
|
|
||||||
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
|
|
||||||
xhr.reply('http://base/one.css', '.one {}');
|
|
||||||
var css = '@import url("one.css");.main {}';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
|
||||||
expect(loadedCss).toBePromise();
|
|
||||||
PromiseWrapper.then(loadedCss,
|
|
||||||
function(css) {
|
|
||||||
expect(css).toEqual('.one {}\n.main {}');
|
|
||||||
async.done();
|
|
||||||
},
|
|
||||||
function(e) { throw 'fail;' });
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should support url([unquoted url]) in @import rules',
|
|
||||||
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
|
|
||||||
xhr.reply('http://base/one.css', '.one {}');
|
|
||||||
var css = '@import url(one.css);.main {}';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
|
||||||
expect(loadedCss).toBePromise();
|
|
||||||
PromiseWrapper.then(loadedCss,
|
|
||||||
function(css) {
|
|
||||||
expect(css).toEqual('.one {}\n.main {}');
|
|
||||||
async.done();
|
|
||||||
},
|
|
||||||
function(e) { throw 'fail;' });
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should handle @import error gracefuly',
|
|
||||||
inject([StyleInliner, AsyncTestCompleter], (inliner, async) => {
|
|
||||||
var css = '@import "one.css";.main {}';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
|
||||||
expect(loadedCss).toBePromise();
|
|
||||||
PromiseWrapper.then(
|
|
||||||
loadedCss,
|
|
||||||
function(css) {
|
|
||||||
expect(css).toEqual('/* failed to import http://base/one.css */\n.main {}');
|
|
||||||
async.done();
|
|
||||||
},
|
|
||||||
function(e) { throw 'fail;' });
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should inline multiple @import rules',
|
|
||||||
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
|
|
||||||
xhr.reply('http://base/one.css', '.one {}');
|
|
||||||
xhr.reply('http://base/two.css', '.two {}');
|
|
||||||
var css = '@import "one.css";@import "two.css";.main {}';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
|
||||||
expect(loadedCss).toBePromise();
|
|
||||||
PromiseWrapper.then(loadedCss,
|
|
||||||
function(css) {
|
|
||||||
expect(css).toEqual('.one {}\n.two {}\n.main {}');
|
|
||||||
async.done();
|
|
||||||
},
|
|
||||||
function(e) { throw 'fail;' });
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should inline nested @import rules',
|
|
||||||
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
|
|
||||||
xhr.reply('http://base/one.css', '@import "two.css";.one {}');
|
|
||||||
xhr.reply('http://base/two.css', '.two {}');
|
|
||||||
var css = '@import "one.css";.main {}';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
|
||||||
expect(loadedCss).toBePromise();
|
|
||||||
PromiseWrapper.then(loadedCss,
|
|
||||||
function(css) {
|
|
||||||
expect(css).toEqual('.two {}\n.one {}\n.main {}');
|
|
||||||
async.done();
|
|
||||||
},
|
|
||||||
function(e) { throw 'fail;' });
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should handle circular dependencies gracefuly',
|
|
||||||
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
|
|
||||||
xhr.reply('http://base/one.css', '@import "two.css";.one {}');
|
|
||||||
xhr.reply('http://base/two.css', '@import "one.css";.two {}');
|
|
||||||
var css = '@import "one.css";.main {}';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
|
||||||
expect(loadedCss).toBePromise();
|
|
||||||
PromiseWrapper.then(loadedCss,
|
|
||||||
function(css) {
|
|
||||||
expect(css).toEqual('.two {}\n.one {}\n.main {}');
|
|
||||||
async.done();
|
|
||||||
},
|
|
||||||
function(e) { throw 'fail;' });
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should handle invalid @import fracefuly',
|
|
||||||
inject([StyleInliner, AsyncTestCompleter], (inliner, async) => {
|
|
||||||
// Invalid rule: the url is not quoted
|
|
||||||
var css = '@import one.css;.main {}';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
|
||||||
expect(loadedCss).toBePromise();
|
|
||||||
PromiseWrapper.then(
|
|
||||||
loadedCss,
|
|
||||||
function(css) {
|
|
||||||
expect(css).toEqual('/* Invalid import rule: "@import one.css;" */.main {}');
|
|
||||||
async.done();
|
|
||||||
},
|
|
||||||
function(e) { throw 'fail;' });
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('media query', () => {
|
|
||||||
it('should wrap inlined content in media query',
|
|
||||||
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
|
|
||||||
xhr.reply('http://base/one.css', '.one {}');
|
|
||||||
var css = '@import "one.css" (min-width: 700px) and (orientation: landscape);';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
|
||||||
expect(loadedCss).toBePromise();
|
|
||||||
PromiseWrapper.then(
|
|
||||||
loadedCss,
|
|
||||||
function(css) {
|
|
||||||
expect(css).toEqual(
|
|
||||||
'@media (min-width: 700px) and (orientation: landscape) {\n.one {}\n}\n');
|
|
||||||
async.done();
|
|
||||||
},
|
|
||||||
function(e) { throw 'fail;' });
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('url rewritting', () => {
|
|
||||||
it('should rewrite url in inlined content',
|
|
||||||
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
|
|
||||||
// it should rewrite both '@import' and 'url()'
|
|
||||||
xhr.reply('http://base/one.css',
|
|
||||||
'@import "./nested/two.css";.one {background-image: url("one.jpg");}');
|
|
||||||
xhr.reply('http://base/nested/two.css',
|
|
||||||
'.two {background-image: url("../img/two.jpg");}');
|
|
||||||
var css = '@import "one.css";';
|
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
|
||||||
expect(loadedCss).toBePromise();
|
|
||||||
PromiseWrapper.then(
|
|
||||||
loadedCss,
|
|
||||||
function(css) {
|
|
||||||
expect(css).toEqual(".two {background-image: url('http://base/img/two.jpg');}\n" +
|
|
||||||
".one {background-image: url('http://base/one.jpg');}\n");
|
|
||||||
async.done();
|
|
||||||
},
|
|
||||||
function(e) { throw 'fail;' });
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class FakeXHR extends XHR {
|
|
||||||
_responses = new Map<string, string>();
|
|
||||||
|
|
||||||
constructor() { super(); }
|
|
||||||
|
|
||||||
get(url: string): Promise<string> {
|
|
||||||
var response = this._responses.get(url);
|
|
||||||
if (isBlank(response)) {
|
|
||||||
return PromiseWrapper.reject('xhr error', null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return PromiseWrapper.resolve(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
reply(url: string, response: string) { this._responses.set(url, response); }
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/test_lib';
|
|
||||||
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
|
|
||||||
|
|
||||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('StyleUrlResolver', () => {
|
|
||||||
let styleUrlResolver;
|
|
||||||
|
|
||||||
beforeEach(() => { styleUrlResolver = new StyleUrlResolver(new UrlResolver()); });
|
|
||||||
|
|
||||||
it('should resolve "url()" urls', () => {
|
|
||||||
var css = `
|
|
||||||
.foo {
|
|
||||||
background-image: url("double.jpg");
|
|
||||||
background-image: url('simple.jpg');
|
|
||||||
background-image: url(noquote.jpg);
|
|
||||||
}`;
|
|
||||||
var expectedCss = `
|
|
||||||
.foo {
|
|
||||||
background-image: url('http://ng.io/double.jpg');
|
|
||||||
background-image: url('http://ng.io/simple.jpg');
|
|
||||||
background-image: url('http://ng.io/noquote.jpg');
|
|
||||||
}`;
|
|
||||||
|
|
||||||
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
|
|
||||||
expect(resolvedCss).toEqual(expectedCss);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve "@import" urls', () => {
|
|
||||||
var css = `
|
|
||||||
@import '1.css';
|
|
||||||
@import "2.css";
|
|
||||||
`;
|
|
||||||
var expectedCss = `
|
|
||||||
@import 'http://ng.io/1.css';
|
|
||||||
@import 'http://ng.io/2.css';
|
|
||||||
`;
|
|
||||||
|
|
||||||
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
|
|
||||||
expect(resolvedCss).toEqual(expectedCss);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve "@import url()" urls', () => {
|
|
||||||
var css = `
|
|
||||||
@import url('3.css');
|
|
||||||
@import url("4.css");
|
|
||||||
@import url(5.css);
|
|
||||||
`;
|
|
||||||
var expectedCss = `
|
|
||||||
@import url('http://ng.io/3.css');
|
|
||||||
@import url('http://ng.io/4.css');
|
|
||||||
@import url('http://ng.io/5.css');
|
|
||||||
`;
|
|
||||||
|
|
||||||
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
|
|
||||||
expect(resolvedCss).toEqual(expectedCss);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support media query in "@import"', () => {
|
|
||||||
var css = `
|
|
||||||
@import 'print.css' print;
|
|
||||||
@import url(print.css) print;
|
|
||||||
`;
|
|
||||||
var expectedCss = `
|
|
||||||
@import 'http://ng.io/print.css' print;
|
|
||||||
@import url('http://ng.io/print.css') print;
|
|
||||||
`;
|
|
||||||
|
|
||||||
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
|
|
||||||
expect(resolvedCss).toEqual(expectedCss);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not strip quotes from inlined SVG styles', () => {
|
|
||||||
var css = `
|
|
||||||
.selector {
|
|
||||||
background:rgb(55,71,79) url('data:image/svg+xml;utf8,<?xml version="1.0"?>');
|
|
||||||
background:rgb(55,71,79) url("data:image/svg+xml;utf8,<?xml version='1.0'?>");
|
|
||||||
background:rgb(55,71,79) url("/some/data:image");
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
var expectedCss = `
|
|
||||||
.selector {
|
|
||||||
background:rgb(55,71,79) url('data:image/svg+xml;utf8,<?xml version="1.0"?>');
|
|
||||||
background:rgb(55,71,79) url("data:image/svg+xml;utf8,<?xml version='1.0'?>");
|
|
||||||
background:rgb(55,71,79) url('http://ng.io/some/data:image');
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
|
|
||||||
expect(resolvedCss).toEqual(expectedCss);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
import {describe, beforeEach, expect, it, iit, ddescribe, el} from 'angular2/test_lib';
|
|
||||||
import {
|
|
||||||
TextInterpolationParser
|
|
||||||
} from 'angular2/src/core/render/dom/compiler/text_interpolation_parser';
|
|
||||||
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
|
|
||||||
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
import {Lexer, Parser, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {IgnoreChildrenStep} from './pipeline_spec';
|
|
||||||
import {
|
|
||||||
ProtoViewBuilder,
|
|
||||||
ElementBinderBuilder
|
|
||||||
} from 'angular2/src/core/render/dom/view/proto_view_builder';
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
import {ViewDefinition, ViewType} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('TextInterpolationParser', () => {
|
|
||||||
function createPipeline() {
|
|
||||||
return new CompilePipeline(
|
|
||||||
[new IgnoreChildrenStep(), new TextInterpolationParser(new Parser(new Lexer()))]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createViewDefinition(): ViewDefinition {
|
|
||||||
return new ViewDefinition({componentId: 'someComponent'});
|
|
||||||
}
|
|
||||||
|
|
||||||
function process(templateString: string): ProtoViewBuilder {
|
|
||||||
var compileElements = createPipeline().processElements(
|
|
||||||
DOM.createTemplate(templateString), ViewType.COMPONENT, createViewDefinition());
|
|
||||||
return compileElements[0].inheritedProtoView;
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertRootTextBinding(protoViewBuilder: ProtoViewBuilder, nodeIndex: number,
|
|
||||||
expression: string) {
|
|
||||||
var node = DOM.childNodes(DOM.templateAwareRoot(protoViewBuilder.rootElement))[nodeIndex];
|
|
||||||
expect(protoViewBuilder.rootTextBindings.get(node).source).toEqual(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertElementTextBinding(elementBinderBuilder: ElementBinderBuilder, nodeIndex: number,
|
|
||||||
expression: string) {
|
|
||||||
var node = DOM.childNodes(DOM.templateAwareRoot(elementBinderBuilder.element))[nodeIndex];
|
|
||||||
expect(elementBinderBuilder.textBindings.get(node).source).toEqual(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should find root text interpolations', () => {
|
|
||||||
var result = process('{{expr1}}{{expr2}}<div></div>{{expr3}}');
|
|
||||||
assertRootTextBinding(result, 0, "{{expr1}}{{expr2}}");
|
|
||||||
assertRootTextBinding(result, 2, "{{expr3}}");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should find text interpolation in normal elements', () => {
|
|
||||||
var result = process('<div>{{expr1}}<span></span>{{expr2}}</div>');
|
|
||||||
assertElementTextBinding(result.elements[0], 0, "{{expr1}}");
|
|
||||||
assertElementTextBinding(result.elements[0], 2, "{{expr2}}");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow multiple expressions', () => {
|
|
||||||
var result = process('<div>{{expr1}}{{expr2}}</div>');
|
|
||||||
assertElementTextBinding(result.elements[0], 0, "{{expr1}}{{expr2}}");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not interpolate when compileChildren is false', () => {
|
|
||||||
var results = process('<div>{{included}}<span ignore-children>{{excluded}}</span></div>');
|
|
||||||
assertElementTextBinding(results.elements[0], 0, "{{included}}");
|
|
||||||
expect(results.elements.length).toBe(1);
|
|
||||||
expect(results.elements[0].textBindings.size).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow fixed text before, in between and after expressions', () => {
|
|
||||||
var result = process('<div>a{{expr1}}b{{expr2}}c</div>');
|
|
||||||
assertElementTextBinding(result.elements[0], 0, "a{{expr1}}b{{expr2}}c");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should escape quotes in fixed parts', () => {
|
|
||||||
var result = process("<div>'\"a{{expr1}}</div>");
|
|
||||||
assertElementTextBinding(result.elements[0], 0, "'\"a{{expr1}}");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,207 +0,0 @@
|
|||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
import {ViewLoader, TemplateAndStyles} from 'angular2/src/core/render/dom/compiler/view_loader';
|
|
||||||
import {StyleInliner} from 'angular2/src/core/render/dom/compiler/style_inliner';
|
|
||||||
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
|
|
||||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
|
||||||
|
|
||||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
|
||||||
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
|
||||||
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
|
|
||||||
import {ViewDefinition} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('ViewLoader', () => {
|
|
||||||
var loader: ViewLoader;
|
|
||||||
var xhr, styleUrlResolver, urlResolver;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
xhr = new MockXHR();
|
|
||||||
urlResolver = new UrlResolver();
|
|
||||||
styleUrlResolver = new StyleUrlResolver(urlResolver);
|
|
||||||
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
|
||||||
loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('html', () => {
|
|
||||||
it('should load inline templates', inject([AsyncTestCompleter], (async) => {
|
|
||||||
loader.load(new ViewDefinition({template: 'template template'}))
|
|
||||||
.then((el) => {
|
|
||||||
expect(el.template).toEqual('template template');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should load templates through XHR', inject([AsyncTestCompleter], (async) => {
|
|
||||||
xhr.expect('http://ng.io/foo.html', 'xhr template');
|
|
||||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'}))
|
|
||||||
.then((el) => {
|
|
||||||
expect(el.template).toEqual('xhr template');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should resolve urls in styles', inject([AsyncTestCompleter], (async) => {
|
|
||||||
xhr.expect('http://ng.io/foo.html',
|
|
||||||
'<style>.foo { background-image: url("double.jpg"); }</style>');
|
|
||||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'}))
|
|
||||||
.then((el) => {
|
|
||||||
expect(el.template).toEqual('');
|
|
||||||
expect(el.styles)
|
|
||||||
.toEqual([".foo { background-image: url('http://ng.io/double.jpg'); }"]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should inline styles', inject([AsyncTestCompleter], (async) => {
|
|
||||||
let xhr = new FakeXHR();
|
|
||||||
xhr.reply('http://ng.io/foo.html', '<style>@import "foo.css";</style>');
|
|
||||||
xhr.reply('http://ng.io/foo.css', '/* foo.css */');
|
|
||||||
|
|
||||||
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
|
||||||
let loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
|
|
||||||
|
|
||||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'}))
|
|
||||||
.then((el) => {
|
|
||||||
expect(el.template).toEqual('');
|
|
||||||
expect(el.styles).toEqual(["/* foo.css */\n"]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should throw when no template is defined', () => {
|
|
||||||
expect(() => loader.load(new ViewDefinition(
|
|
||||||
{componentId: 'TestComponent', template: null, templateAbsUrl: null})))
|
|
||||||
.toThrowError(
|
|
||||||
'View should have either the templateUrl or template property set but none was found for the \'TestComponent\' component');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return a rejected Promise when XHR loading fails',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
xhr.expect('http://ng.io/foo.html', null);
|
|
||||||
PromiseWrapper.then(
|
|
||||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'})),
|
|
||||||
function(_) { throw 'Unexpected response'; },
|
|
||||||
function(error) {
|
|
||||||
expect(error.message).toEqual('Failed to fetch url "http://ng.io/foo.html"');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should replace $baseUrl in attributes with the template base url',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
xhr.expect('http://ng.io/path/foo.html', '<img src="$baseUrl/logo.png">');
|
|
||||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/path/foo.html'}))
|
|
||||||
.then((el) => {
|
|
||||||
expect(el.template).toEqual('<img src="http://ng.io/path/logo.png">');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('css', () => {
|
|
||||||
it('should load inline styles', inject([AsyncTestCompleter], (async) => {
|
|
||||||
loader.load(new ViewDefinition({template: 'html', styles: ['style 1', 'style 2']}))
|
|
||||||
.then((el) => {
|
|
||||||
expect(el.template).toEqual('html');
|
|
||||||
expect(el.styles).toEqual(['style 1', 'style 2']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should resolve urls in inline styles', inject([AsyncTestCompleter], (async) => {
|
|
||||||
xhr.expect('http://ng.io/foo.html', 'html');
|
|
||||||
loader.load(new ViewDefinition({
|
|
||||||
templateAbsUrl: 'http://ng.io/foo.html',
|
|
||||||
styles: ['.foo { background-image: url("double.jpg"); }']
|
|
||||||
}))
|
|
||||||
.then((el) => {
|
|
||||||
expect(el.template).toEqual('html');
|
|
||||||
expect(el.styles)
|
|
||||||
.toEqual([".foo { background-image: url('http://ng.io/double.jpg'); }"]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should load templates through XHR', inject([AsyncTestCompleter], (async) => {
|
|
||||||
xhr.expect('http://ng.io/foo.html', 'xhr template');
|
|
||||||
xhr.expect('http://ng.io/foo-1.css', '1');
|
|
||||||
xhr.expect('http://ng.io/foo-2.css', '2');
|
|
||||||
loader.load(new ViewDefinition({
|
|
||||||
templateAbsUrl: 'http://ng.io/foo.html',
|
|
||||||
styles: ['i1'],
|
|
||||||
styleAbsUrls: ['http://ng.io/foo-1.css', 'http://ng.io/foo-2.css']
|
|
||||||
}))
|
|
||||||
.then((el) => {
|
|
||||||
expect(el.template).toEqual('xhr template');
|
|
||||||
expect(el.styles).toEqual(['i1', '1', '2']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should inline styles', inject([AsyncTestCompleter], (async) => {
|
|
||||||
let xhr = new FakeXHR();
|
|
||||||
xhr.reply('http://ng.io/foo.html', '<p>template</p>');
|
|
||||||
xhr.reply('http://ng.io/foo.css', '/* foo.css */');
|
|
||||||
|
|
||||||
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
|
||||||
let loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
|
|
||||||
|
|
||||||
loader.load(
|
|
||||||
new ViewDefinition(
|
|
||||||
{templateAbsUrl: 'http://ng.io/foo.html', styles: ['@import "foo.css";']}))
|
|
||||||
.then((el) => {
|
|
||||||
expect(el.template).toEqual("<p>template</p>");
|
|
||||||
expect(el.styles).toEqual(["/* foo.css */\n"]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should return a rejected Promise when XHR loading fails',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
xhr.expect('http://ng.io/foo.css', null);
|
|
||||||
PromiseWrapper.then(
|
|
||||||
loader.load(
|
|
||||||
new ViewDefinition({template: '', styleAbsUrls: ['http://ng.io/foo.css']})),
|
|
||||||
function(_) { throw 'Unexpected response'; },
|
|
||||||
function(error) {
|
|
||||||
expect(error.message).toEqual('Failed to fetch url "http://ng.io/foo.css"');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class SomeComponent {}
|
|
||||||
|
|
||||||
class FakeXHR extends XHR {
|
|
||||||
_responses = new Map<string, string>();
|
|
||||||
|
|
||||||
constructor() { super(); }
|
|
||||||
|
|
||||||
get(url: string): Promise<string> {
|
|
||||||
return this._responses.has(url) ? PromiseWrapper.resolve(this._responses.get(url)) :
|
|
||||||
PromiseWrapper.reject('xhr error', null);
|
|
||||||
}
|
|
||||||
|
|
||||||
reply(url: string, response: string): void { this._responses.set(url, response); }
|
|
||||||
}
|
|
@ -1,262 +0,0 @@
|
|||||||
import {
|
|
||||||
describe,
|
|
||||||
beforeEach,
|
|
||||||
it,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
ddescribe,
|
|
||||||
el,
|
|
||||||
stringifyElement
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
|
|
||||||
import {ViewSplitter} from 'angular2/src/core/render/dom/compiler/view_splitter';
|
|
||||||
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
|
|
||||||
import {CompileElement} from 'angular2/src/core/render/dom/compiler/compile_element';
|
|
||||||
import {ProtoViewDto, ViewType, ViewDefinition} from 'angular2/src/core/render/api';
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
|
|
||||||
import {Lexer, Parser} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('ViewSplitter', () => {
|
|
||||||
|
|
||||||
function createViewDefinition(): ViewDefinition {
|
|
||||||
return new ViewDefinition({componentId: 'someComponent'});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPipeline() {
|
|
||||||
return new CompilePipeline([new ViewSplitter(new Parser(new Lexer()))]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function proceess(el): CompileElement[] {
|
|
||||||
return createPipeline().processElements(el, ViewType.COMPONENT, createViewDefinition());
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('<template> elements', () => {
|
|
||||||
|
|
||||||
it('should move the content into a new <template> element and mark that as viewRoot', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
|
|
||||||
expect(stringifyElement(results[1].element))
|
|
||||||
.toEqual('<template class="ng-binding" if="true"></template>');
|
|
||||||
expect(results[1].isViewRoot).toBe(false);
|
|
||||||
expect(stringifyElement(results[2].element)).toEqual('<template>a</template>');
|
|
||||||
expect(results[2].isViewRoot).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should mark the new <template> element as viewRoot', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].isViewRoot).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not wrap the root element', () => {
|
|
||||||
var rootElement = DOM.createTemplate('');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results.length).toBe(1);
|
|
||||||
expect(stringifyElement(rootElement)).toEqual('<template></template>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should copy over the elementDescription', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].elementDescription).toBe(results[1].elementDescription);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should clean out the inheritedElementBinder', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].inheritedElementBinder).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a nestedProtoView', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
|
||||||
expect(results[2].inheritedProtoView)
|
|
||||||
.toBe(results[1].inheritedElementBinder.nestedProtoView);
|
|
||||||
expect(results[2].inheritedProtoView.type).toBe(ViewType.EMBEDDED);
|
|
||||||
expect(stringifyElement(results[2].inheritedProtoView.rootElement))
|
|
||||||
.toEqual('<template>a</template>');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('elements with template attribute', () => {
|
|
||||||
|
|
||||||
it('should replace the element with an empty <template> element', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
|
||||||
var originalChild = DOM.firstChild(DOM.content(rootElement));
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[0].element).toBe(rootElement);
|
|
||||||
expect(stringifyElement(results[0].element))
|
|
||||||
.toEqual('<template><template class="ng-binding"></template></template>');
|
|
||||||
expect(stringifyElement(results[2].element))
|
|
||||||
.toEqual('<template><span template=""></span></template>');
|
|
||||||
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work with top-level template node', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div template>x</div>');
|
|
||||||
var originalChild = DOM.content(rootElement).childNodes[0];
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
|
|
||||||
expect(results[0].element).toBe(rootElement);
|
|
||||||
expect(results[0].isViewRoot).toBe(true);
|
|
||||||
expect(results[2].isViewRoot).toBe(true);
|
|
||||||
expect(stringifyElement(results[0].element))
|
|
||||||
.toEqual('<template><template class="ng-binding"></template></template>');
|
|
||||||
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should mark the element as viewRoot', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div template></div>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].isViewRoot).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add property bindings from the template attribute', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div template="some-prop:expr"></div>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[1].inheritedElementBinder.propertyBindings.get('someProp').source)
|
|
||||||
.toEqual('expr');
|
|
||||||
expect(results[1].attrs().get('some-prop')).toEqual('expr');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add variable mappings from the template attribute to the nestedProtoView', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div template="var var-name=mapName"></div>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].inheritedProtoView.variableBindings)
|
|
||||||
.toEqual(MapWrapper.createFromStringMap({'mapName': 'varName'}));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add entries without value as attributes to the element', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div template="varname"></div>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[1].attrs().get('varname')).toEqual('');
|
|
||||||
expect(results[1].inheritedElementBinder.propertyBindings).toEqual(new Map());
|
|
||||||
expect(results[1].inheritedElementBinder.variableBindings).toEqual(new Map());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should iterate properly after a template dom modification', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div template></div><after></after>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
// 1 root + 2 initial + 2 generated template elements
|
|
||||||
expect(results.length).toEqual(5);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should copy over the elementDescription', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].elementDescription).toBe(results[1].elementDescription);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should clean out the inheritedElementBinder', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].inheritedElementBinder).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a nestedProtoView', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
|
||||||
expect(results[2].inheritedProtoView)
|
|
||||||
.toBe(results[1].inheritedElementBinder.nestedProtoView);
|
|
||||||
expect(stringifyElement(results[2].inheritedProtoView.rootElement))
|
|
||||||
.toEqual('<template><span template=""></span></template>');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('elements with *directive_name attribute', () => {
|
|
||||||
|
|
||||||
it('should replace the element with an empty <template> element', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<span *ng-if></span>');
|
|
||||||
var originalChild = DOM.firstChild(DOM.content(rootElement));
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[0].element).toBe(rootElement);
|
|
||||||
expect(stringifyElement(results[0].element))
|
|
||||||
.toEqual('<template><template class="ng-binding" ng-if=""></template></template>');
|
|
||||||
expect(stringifyElement(results[2].element))
|
|
||||||
.toEqual('<template><span *ng-if=""></span></template>');
|
|
||||||
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should mark the element as viewRoot', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div *foo="bar"></div>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].isViewRoot).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work with top-level template node', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div *foo>x</div>');
|
|
||||||
var originalChild = DOM.content(rootElement).childNodes[0];
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
|
|
||||||
expect(results[0].element).toBe(rootElement);
|
|
||||||
expect(results[0].isViewRoot).toBe(true);
|
|
||||||
expect(results[2].isViewRoot).toBe(true);
|
|
||||||
expect(stringifyElement(results[0].element))
|
|
||||||
.toEqual('<template><template class="ng-binding" foo=""></template></template>');
|
|
||||||
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add property bindings from the template attribute', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div *prop="expr"></div>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[1].inheritedElementBinder.propertyBindings.get('prop').source)
|
|
||||||
.toEqual('expr');
|
|
||||||
expect(results[1].attrs().get('prop')).toEqual('expr');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add variable mappings from the template attribute to the nestedProtoView', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div *foreach="var varName=mapName"></div>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].inheritedProtoView.variableBindings)
|
|
||||||
.toEqual(MapWrapper.createFromStringMap({'mapName': 'varName'}));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add entries without value as attribute to the element', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div *varname></div>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[1].attrs().get('varname')).toEqual('');
|
|
||||||
expect(results[1].inheritedElementBinder.propertyBindings).toEqual(new Map());
|
|
||||||
expect(results[1].inheritedElementBinder.variableBindings).toEqual(new Map());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should iterate properly after a template dom modification', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<div *foo></div><after></after>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
// 1 root + 2 initial + 2 generated template elements
|
|
||||||
expect(results.length).toEqual(5);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should copy over the elementDescription', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<span *foo></span>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].elementDescription).toBe(results[1].elementDescription);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should clean out the inheritedElementBinder', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<span *foo></span>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].inheritedElementBinder).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a nestedProtoView', () => {
|
|
||||||
var rootElement = DOM.createTemplate('<span *foo></span>');
|
|
||||||
var results = proceess(rootElement);
|
|
||||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
|
||||||
expect(results[2].inheritedProtoView)
|
|
||||||
.toBe(results[1].inheritedElementBinder.nestedProtoView);
|
|
||||||
expect(stringifyElement(results[2].inheritedProtoView.rootElement))
|
|
||||||
.toEqual('<template><span *foo=""></span></template>');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
import {Inject, Injectable} from 'angular2/core';
|
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
|
||||||
import {MapWrapper, ListWrapper, Map} 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 {DomRenderer} from 'angular2/src/core/render/dom/dom_renderer';
|
|
||||||
import {DOCUMENT} from 'angular2/src/core/render/dom/dom_tokens';
|
|
||||||
import {DefaultDomCompiler} from 'angular2/src/core/render/dom/compiler/compiler';
|
|
||||||
import {
|
|
||||||
RenderViewWithFragments,
|
|
||||||
RenderFragmentRef,
|
|
||||||
RenderViewRef,
|
|
||||||
ProtoViewDto,
|
|
||||||
ViewDefinition,
|
|
||||||
RenderEventDispatcher,
|
|
||||||
RenderDirectiveMetadata,
|
|
||||||
RenderElementRef,
|
|
||||||
RenderProtoViewMergeMapping,
|
|
||||||
RenderProtoViewRef
|
|
||||||
} from 'angular2/src/core/render/api';
|
|
||||||
import {resolveInternalDomView} from 'angular2/src/core/render/dom/view/view';
|
|
||||||
import {resolveInternalDomFragment} from 'angular2/src/core/render/dom/view/fragment';
|
|
||||||
import {el, dispatchEvent} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
export class TestRootView {
|
|
||||||
viewRef: RenderViewRef;
|
|
||||||
fragments: RenderFragmentRef[];
|
|
||||||
hostElement: Element;
|
|
||||||
events: any[][];
|
|
||||||
|
|
||||||
constructor(viewWithFragments: RenderViewWithFragments) {
|
|
||||||
this.viewRef = viewWithFragments.viewRef;
|
|
||||||
this.fragments = viewWithFragments.fragmentRefs;
|
|
||||||
this.hostElement = <Element>resolveInternalDomFragment(this.fragments[0])[0];
|
|
||||||
this.events = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TestRenderElementRef implements RenderElementRef {
|
|
||||||
constructor(public renderView: RenderViewRef, public renderBoundElementIndex: number) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function elRef(renderView: RenderViewRef, boundElementIndex: number) {
|
|
||||||
return new TestRenderElementRef(renderView, boundElementIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function rootNodes(view: RenderViewRef) {}
|
|
||||||
|
|
||||||
class LoggingEventDispatcher implements RenderEventDispatcher {
|
|
||||||
log: any[][];
|
|
||||||
|
|
||||||
constructor(log: any[][]) { this.log = log; }
|
|
||||||
|
|
||||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>): boolean {
|
|
||||||
this.log.push([elementIndex, eventName, locals]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class DomTestbed {
|
|
||||||
renderer: DomRenderer;
|
|
||||||
compiler: DefaultDomCompiler;
|
|
||||||
rootEl;
|
|
||||||
|
|
||||||
constructor(renderer: DomRenderer, compiler: DefaultDomCompiler, @Inject(DOCUMENT) document) {
|
|
||||||
this.renderer = renderer;
|
|
||||||
this.compiler = compiler;
|
|
||||||
this.rootEl = el('<div id="root" class="rootElem"></div>');
|
|
||||||
var oldRoots = DOM.querySelectorAll(document, '#root');
|
|
||||||
for (var i = 0; i < oldRoots.length; i++) {
|
|
||||||
DOM.remove(oldRoots[i]);
|
|
||||||
}
|
|
||||||
DOM.appendChild(DOM.querySelector(document, 'body'), this.rootEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
compile(host: RenderDirectiveMetadata,
|
|
||||||
componentViews: ViewDefinition[]): Promise<ProtoViewDto[]> {
|
|
||||||
var promises = [this.compiler.compileHost(host)];
|
|
||||||
componentViews.forEach(view => promises.push(this.compiler.compile(view)));
|
|
||||||
return PromiseWrapper.all(promises);
|
|
||||||
}
|
|
||||||
|
|
||||||
merge(protoViews:
|
|
||||||
Array<ProtoViewDto | RenderProtoViewRef>): Promise<RenderProtoViewMergeMapping> {
|
|
||||||
return this.compiler.mergeProtoViewsRecursively(collectMergeRenderProtoViewsRecurse(
|
|
||||||
<ProtoViewDto>protoViews[0], ListWrapper.slice(protoViews, 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
compileAndMerge(host: RenderDirectiveMetadata,
|
|
||||||
componentViews: ViewDefinition[]): Promise<RenderProtoViewMergeMapping> {
|
|
||||||
return this.compile(host, componentViews).then(protoViewDtos => this.merge(protoViewDtos));
|
|
||||||
}
|
|
||||||
|
|
||||||
_createTestView(viewWithFragments: RenderViewWithFragments) {
|
|
||||||
var testView = new TestRootView(viewWithFragments);
|
|
||||||
this.renderer.setEventDispatcher(viewWithFragments.viewRef,
|
|
||||||
new LoggingEventDispatcher(testView.events));
|
|
||||||
return testView;
|
|
||||||
}
|
|
||||||
|
|
||||||
createView(protoView: RenderProtoViewMergeMapping): TestRootView {
|
|
||||||
var viewWithFragments = this.renderer.createView(protoView.mergedProtoViewRef, 0);
|
|
||||||
this.renderer.hydrateView(viewWithFragments.viewRef);
|
|
||||||
return this._createTestView(viewWithFragments);
|
|
||||||
}
|
|
||||||
|
|
||||||
triggerEvent(elementRef: RenderElementRef, eventName: string) {
|
|
||||||
var element = resolveInternalDomView(elementRef.renderView)
|
|
||||||
.boundElements[elementRef.renderBoundElementIndex];
|
|
||||||
dispatchEvent(element, eventName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectMergeRenderProtoViewsRecurse(current: ProtoViewDto,
|
|
||||||
components: Array<ProtoViewDto | RenderProtoViewRef>):
|
|
||||||
Array<RenderProtoViewRef | any[]> {
|
|
||||||
var result = [current.render];
|
|
||||||
current.elementBinders.forEach((elementBinder) => {
|
|
||||||
if (isPresent(elementBinder.nestedProtoView)) {
|
|
||||||
result.push(collectMergeRenderProtoViewsRecurse(elementBinder.nestedProtoView, components));
|
|
||||||
} else if (elementBinder.directives.length > 0) {
|
|
||||||
if (components.length > 0) {
|
|
||||||
var comp = components.shift();
|
|
||||||
if (comp instanceof ProtoViewDto) {
|
|
||||||
result.push(collectMergeRenderProtoViewsRecurse(comp, components));
|
|
||||||
} else {
|
|
||||||
result.push(comp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result.push(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
beforeEachBindings,
|
|
||||||
SpyObject,
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
import {TemplateCloner} from 'angular2/src/core/render/dom/template_cloner';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('TemplateCloner', () => {
|
|
||||||
var cloner: TemplateCloner;
|
|
||||||
var bigTemplate: Element;
|
|
||||||
var smallTemplate: Element;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cloner = new TemplateCloner(1);
|
|
||||||
bigTemplate = DOM.createTemplate('a<div></div>');
|
|
||||||
smallTemplate = DOM.createTemplate('a');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('prepareForClone', () => {
|
|
||||||
it('should use a reference for small templates',
|
|
||||||
() => { expect(cloner.prepareForClone(smallTemplate)).toBe(smallTemplate); });
|
|
||||||
|
|
||||||
it('should use a reference if the max element count is -1', () => {
|
|
||||||
cloner = new TemplateCloner(-1);
|
|
||||||
expect(cloner.prepareForClone(bigTemplate)).toBe(bigTemplate);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use a string for big templates', () => {
|
|
||||||
expect(cloner.prepareForClone(bigTemplate)).toEqual(DOM.getInnerHTML(bigTemplate));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('cloneTemplate', () => {
|
|
||||||
|
|
||||||
function shouldReturnTemplateContentNodes(template: Element, importIntoDoc: boolean) {
|
|
||||||
var clone = cloner.cloneContent(cloner.prepareForClone(template), importIntoDoc);
|
|
||||||
expect(clone).not.toBe(DOM.content(template));
|
|
||||||
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should return template.content nodes (small template, no import)',
|
|
||||||
() => { shouldReturnTemplateContentNodes(smallTemplate, false); });
|
|
||||||
|
|
||||||
it('should return template.content nodes (small template, import)',
|
|
||||||
() => { shouldReturnTemplateContentNodes(smallTemplate, true); });
|
|
||||||
|
|
||||||
it('should return template.content nodes (big template, no import)',
|
|
||||||
() => { shouldReturnTemplateContentNodes(bigTemplate, false); });
|
|
||||||
|
|
||||||
it('should return template.content nodes (big template, import)',
|
|
||||||
() => { shouldReturnTemplateContentNodes(bigTemplate, true); });
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
import {
|
|
||||||
describe,
|
|
||||||
ddescribe,
|
|
||||||
it,
|
|
||||||
iit,
|
|
||||||
xit,
|
|
||||||
xdescribe,
|
|
||||||
expect,
|
|
||||||
beforeEach,
|
|
||||||
el
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {
|
|
||||||
DomElementSchemaRegistry
|
|
||||||
} from 'angular2/src/core/render/dom/schema/dom_element_schema_registry';
|
|
||||||
import {TemplateCloner} from 'angular2/src/core/render/dom/template_cloner';
|
|
||||||
import {ProtoViewBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
|
|
||||||
import {ASTWithSource, AST} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {PropertyBindingType, ViewType, ViewEncapsulation} from 'angular2/src/core/render/api';
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
import {IS_DART} from '../../../../platform';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
function emptyExpr() { return new ASTWithSource(new AST(), 'empty', 'empty'); }
|
|
||||||
|
|
||||||
describe('ProtoViewBuilder', () => {
|
|
||||||
var builder;
|
|
||||||
var templateCloner;
|
|
||||||
beforeEach(() => {
|
|
||||||
templateCloner = new TemplateCloner(-1);
|
|
||||||
builder =
|
|
||||||
new ProtoViewBuilder(DOM.createTemplate(''), ViewType.EMBEDDED, ViewEncapsulation.None);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!IS_DART) {
|
|
||||||
describe('verification of properties', () => {
|
|
||||||
|
|
||||||
it('should throw for unknown properties', () => {
|
|
||||||
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
|
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner))
|
|
||||||
.toThrowError(
|
|
||||||
`Can't bind to 'unknownProperty' since it isn't a known property of the '<div>' element and there are no matching directives with a corresponding property`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow unknown properties if a directive uses it', () => {
|
|
||||||
var binder = builder.bindElement(el('<div/>'));
|
|
||||||
binder.bindDirective(0).bindProperty('someDirProperty', emptyExpr(), 'directiveProperty');
|
|
||||||
binder.bindProperty('directiveProperty', emptyExpr());
|
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner)).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw for unknown host properties even if another directive uses it', () => {
|
|
||||||
var binder = builder.bindElement(el('<div/>'));
|
|
||||||
binder.bindDirective(0).bindProperty('someDirProperty', emptyExpr(), 'someDirProperty');
|
|
||||||
binder.bindDirective(1).bindHostProperty('someDirProperty', emptyExpr());
|
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry()))
|
|
||||||
.toThrowError(
|
|
||||||
`Can't bind to 'someDirProperty' since it isn't a known property of the '<div>' element`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow unknown properties on custom elements', () => {
|
|
||||||
var binder = builder.bindElement(el('<some-custom/>'));
|
|
||||||
binder.bindProperty('unknownProperty', emptyExpr());
|
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner)).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw for unknown properties on custom elements if there is an ng component', () => {
|
|
||||||
var binder = builder.bindElement(el('<some-custom/>'));
|
|
||||||
binder.bindProperty('unknownProperty', emptyExpr());
|
|
||||||
binder.setComponentId('someComponent');
|
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner))
|
|
||||||
.toThrowError(
|
|
||||||
`Can't bind to 'unknownProperty' since it isn't a known property of the '<some-custom>' element and there are no matching directives with a corresponding property`);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
describe('verification of properties', () => {
|
|
||||||
|
|
||||||
// TODO(tbosch): This is just a temporary test that makes sure that the dart server and
|
|
||||||
// dart browser is in sync. Change this to "not contains notifyBinding"
|
|
||||||
// when https://github.com/angular/angular/issues/3019 is solved.
|
|
||||||
it('should not throw for unknown properties', () => {
|
|
||||||
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
|
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner)).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('property normalization', () => {
|
|
||||||
it('should normalize "innerHtml" to "innerHTML"', () => {
|
|
||||||
builder.bindElement(el('<div/>')).bindProperty('innerHtml', emptyExpr());
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('innerHTML');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should normalize "tabindex" to "tabIndex"', () => {
|
|
||||||
builder.bindElement(el('<div/>')).bindProperty('tabindex', emptyExpr());
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('tabIndex');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should normalize "readonly" to "readOnly"', () => {
|
|
||||||
builder.bindElement(el('<input/>')).bindProperty('readonly', emptyExpr());
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('readOnly');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should normalize "class" to "className"', () => {
|
|
||||||
builder.bindElement(el('<div></div>')).bindProperty('class', emptyExpr());
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('className');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('property binding', () => {
|
|
||||||
describe('types', () => {
|
|
||||||
it('should detect property names', () => {
|
|
||||||
builder.bindElement(el('<div/>')).bindProperty('tabindex', emptyExpr());
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].type)
|
|
||||||
.toEqual(PropertyBindingType.PROPERTY);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect attribute names', () => {
|
|
||||||
builder.bindElement(el('<div/>')).bindProperty('attr.someName', emptyExpr());
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].type)
|
|
||||||
.toEqual(PropertyBindingType.ATTRIBUTE);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect class names', () => {
|
|
||||||
builder.bindElement(el('<div/>')).bindProperty('class.someName', emptyExpr());
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.CLASS);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect style names', () => {
|
|
||||||
builder.bindElement(el('<div/>')).bindProperty('style.someName', emptyExpr());
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.STYLE);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect style units', () => {
|
|
||||||
builder.bindElement(el('<div/>')).bindProperty('style.someName.someUnit', emptyExpr());
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].unit).toEqual('someUnit');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not create a property binding when there is already same directive property binding',
|
|
||||||
() => {
|
|
||||||
var binder = builder.bindElement(el('<div/>'));
|
|
||||||
|
|
||||||
binder.bindProperty('tabindex', emptyExpr());
|
|
||||||
binder.bindDirective(0).bindProperty('tabindex', emptyExpr(), 'tabindex');
|
|
||||||
|
|
||||||
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
|
||||||
expect(pv.elementBinders[0].propertyBindings.length).toEqual(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,346 +0,0 @@
|
|||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
xdescribe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
beforeEachBindings,
|
|
||||||
SpyObject,
|
|
||||||
stringifyElement
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
|
||||||
|
|
||||||
import {DomTestbed} from '../../../../core/render/dom/dom_testbed';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ViewDefinition,
|
|
||||||
RenderDirectiveMetadata,
|
|
||||||
RenderProtoViewMergeMapping,
|
|
||||||
ViewEncapsulation,
|
|
||||||
ViewType
|
|
||||||
} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
import {cloneAndQueryProtoView} from 'angular2/src/core/render/dom/util';
|
|
||||||
import {TemplateCloner} from 'angular2/src/core/render/dom/template_cloner';
|
|
||||||
import {
|
|
||||||
resolveInternalDomProtoView,
|
|
||||||
DomProtoView
|
|
||||||
} from 'angular2/src/core/render/dom/view/proto_view';
|
|
||||||
import {ProtoViewBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
|
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('ProtoViewMerger integration test', () => {
|
|
||||||
beforeEachBindings(() => [DomTestbed]);
|
|
||||||
|
|
||||||
describe('component views', () => {
|
|
||||||
it('should merge a component view',
|
|
||||||
runAndAssert('root', ['a'], ['<root class="ng-binding" idx="0">a</root>']));
|
|
||||||
|
|
||||||
it('should merge component views with interpolation at root level',
|
|
||||||
runAndAssert('root', ['{{a}}'], ['<root class="ng-binding" idx="0">{0}</root>']));
|
|
||||||
|
|
||||||
it('should merge component views with interpolation not at root level',
|
|
||||||
runAndAssert('root', ['<div>{{a}}</div>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><div class="ng-binding" idx="1">{0}</div></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should merge component views with bound elements',
|
|
||||||
runAndAssert('root', ['<div #a></div>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><div #a="" class="ng-binding" idx="1"></div></root>'
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('embedded views', () => {
|
|
||||||
|
|
||||||
it('should merge embedded views as fragments',
|
|
||||||
runAndAssert('root', ['<template>a</template>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1"></template></root>',
|
|
||||||
'a'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should merge embedded views with interpolation at root level',
|
|
||||||
runAndAssert('root', ['<template>{{a}}</template>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1"></template></root>',
|
|
||||||
'{0}'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should merge embedded views with interpolation not at root level',
|
|
||||||
runAndAssert('root', ['<div *ng-if>{{a}}</div>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template></root>',
|
|
||||||
'<div *ng-if="" class="ng-binding" idx="2">{0}</div>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should merge embedded views with bound elements',
|
|
||||||
runAndAssert('root', ['<div *ng-if #a></div>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template></root>',
|
|
||||||
'<div #a="" *ng-if="" class="ng-binding" idx="2"></div>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('projection', () => {
|
|
||||||
|
|
||||||
it('should remove text nodes if there is no ng-content',
|
|
||||||
runAndAssert(
|
|
||||||
'root', ['<a>b</a>', ''],
|
|
||||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"></a></root>']));
|
|
||||||
|
|
||||||
it('should project static text',
|
|
||||||
runAndAssert('root', ['<a>b</a>', 'A(<ng-content></ng-content>)'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[-->b<!--]-->)</a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should project text interpolation',
|
|
||||||
runAndAssert('root', ['<a>{{b}}</a>', 'A(<ng-content></ng-content>)'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[-->{0}<!--]-->)</a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should project text interpolation to elements without bindings',
|
|
||||||
runAndAssert('root', ['<a>{{b}}</a>', '<div><ng-content></ng-content></div>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><div class="ng-binding"><!--[-->{0}<!--]--></div></a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should project elements',
|
|
||||||
runAndAssert('root', ['<a><div></div></a>', 'A(<ng-content></ng-content>)'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[--><div></div><!--]-->)</a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should project elements using the selector',
|
|
||||||
runAndAssert(
|
|
||||||
'root',
|
|
||||||
[
|
|
||||||
'<a><div class="x">a</div><span></span><div class="x">b</div></a>',
|
|
||||||
'A(<ng-content select=".x"></ng-content>)'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[--><div class="x">a</div><div class="x">b</div><!--]-->)</a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should reproject',
|
|
||||||
runAndAssert(
|
|
||||||
'root',
|
|
||||||
['<a>x</a>', 'A(<b><ng-content></ng-content></b>)', 'B(<ng-content></ng-content>)'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<!--[--><!--[-->x<!--]--><!--]-->)</b>)</a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should reproject text interpolation to sibling text nodes',
|
|
||||||
runAndAssert(
|
|
||||||
'root',
|
|
||||||
[
|
|
||||||
'<a>{{x}}</a>',
|
|
||||||
'<b>A(<ng-content></ng-content>)</b>)',
|
|
||||||
'B(<ng-content></ng-content>)'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><b class="ng-binding" idx="2">B(<!--[-->A(<!--[-->{0}<!--]-->)<!--]-->)</b>)</a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should reproject by combining selectors',
|
|
||||||
runAndAssert(
|
|
||||||
'root',
|
|
||||||
[
|
|
||||||
'<a><div class="x"></div><div class="x y"></div><div class="y"></div></a>',
|
|
||||||
'A(<b><ng-content select=".x"></ng-content></b>)',
|
|
||||||
'B(<ng-content select=".y"></ng-content>)'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<!--[--><div class="x y"></div><!--]-->)</b>)</a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should keep non projected embedded views as fragments (so that they can be moved manually)',
|
|
||||||
runAndAssert(
|
|
||||||
'root', ['<a><template class="x">b</template></a>', ''],
|
|
||||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"></a></root>', 'b']));
|
|
||||||
|
|
||||||
it('should project embedded views and match the template element',
|
|
||||||
runAndAssert(
|
|
||||||
'root', ['<a><template class="x">b</template></a>', 'A(<ng-content></ng-content>)'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[--><template class="x ng-binding" idx="2"></template><!--]-->)</a></root>',
|
|
||||||
'b'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should project nodes using the ng-content in embedded views',
|
|
||||||
runAndAssert('root', ['<a>b</a>', 'A(<ng-content *ng-if></ng-content>)'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>)</a></root>',
|
|
||||||
'<!--[-->b<!--]-->'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should allow to use wildcard selector after embedded view with non wildcard selector',
|
|
||||||
runAndAssert(
|
|
||||||
'root',
|
|
||||||
[
|
|
||||||
'<a><div class="x">a</div>b</a>',
|
|
||||||
'A(<ng-content select=".x" *ng-if></ng-content>, <ng-content></ng-content>)'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>, <!--[-->b<!--]-->)</a></root>',
|
|
||||||
'<!--[--><div class="x">a</div><!--]-->'
|
|
||||||
]));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('composition', () => {
|
|
||||||
it('should merge multiple component views',
|
|
||||||
runAndAssert('root', ['<a></a><b></b>', 'c', 'd'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">c</a><b class="ng-binding" idx="2">d</b></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should merge multiple embedded views as fragments',
|
|
||||||
runAndAssert('root', ['<div *ng-if></div><span *ng-for></span>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template><template class="ng-binding" idx="2" ng-for=""></template></root>',
|
|
||||||
'<div *ng-if=""></div>',
|
|
||||||
'<span *ng-for=""></span>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should merge nested embedded views as fragments',
|
|
||||||
runAndAssert('root', ['<div *ng-if><span *ng-for></span></div>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template></root>',
|
|
||||||
'<div *ng-if=""><template class="ng-binding" idx="2" ng-for=""></template></div>',
|
|
||||||
'<span *ng-for=""></span>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('element index mapping should be grouped by view and view depth first', () => {
|
|
||||||
|
|
||||||
it('should map component views correctly',
|
|
||||||
runAndAssert('root', ['<a></a><b></b>', '<c></c>'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><c class="ng-binding" idx="3"></c></a><b class="ng-binding" idx="2"></b></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should map moved projected elements correctly',
|
|
||||||
runAndAssert(
|
|
||||||
'root',
|
|
||||||
[
|
|
||||||
'<a><b></b><c></c></a>',
|
|
||||||
'<ng-content select="c"></ng-content><ng-content select="b"></ng-content>'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><!--[--><c class="ng-binding" idx="3"></c><!--]--><!--[--><b class="ng-binding" idx="2"></b><!--]--></a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('text index mapping should be grouped by view and view depth first', () => {
|
|
||||||
|
|
||||||
it('should map component views correctly', runAndAssert('root', ['<a></a>{{b}}', '{{c}}'], [
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">{1}</a>{0}</root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
it('should map moved projected elements correctly',
|
|
||||||
runAndAssert(
|
|
||||||
'root',
|
|
||||||
[
|
|
||||||
'<a><div x>{{x}}</div><div y>{{y}}</div></a>',
|
|
||||||
'<ng-content select="[y]"></ng-content><ng-content select="[x]"></ng-content>'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><!--[--><div class="ng-binding" idx="3" y="">{1}</div><!--]--><!--[--><div class="ng-binding" idx="2" x="">{0}</div><!--]--></a></root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('native shadow dom support', () => {
|
|
||||||
it('should keep the non projected light dom and wrap the component view into a shadow-root element',
|
|
||||||
runAndAssert('native-root', ['<a>b</a>', 'c'], [
|
|
||||||
'<native-root class="ng-binding" idx="0"><shadow-root><a class="ng-binding" idx="1"><shadow-root>c</shadow-root>b</a></shadow-root></native-root>'
|
|
||||||
]));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('host attributes', () => {
|
|
||||||
it('should set host attributes while merging',
|
|
||||||
inject([AsyncTestCompleter, DomTestbed, TemplateCloner], (async, tb: DomTestbed,
|
|
||||||
cloner: TemplateCloner) => {
|
|
||||||
tb.compiler.compileHost(rootDirective('root'))
|
|
||||||
.then((rootProtoViewDto) => {
|
|
||||||
var builder = new ProtoViewBuilder(DOM.createTemplate(''), ViewType.COMPONENT,
|
|
||||||
ViewEncapsulation.None);
|
|
||||||
builder.setHostAttribute('a', 'b');
|
|
||||||
var componentProtoViewDto = builder.build(new ElementSchemaRegistry(), cloner);
|
|
||||||
tb.merge([rootProtoViewDto, componentProtoViewDto])
|
|
||||||
.then(mergeMappings => {
|
|
||||||
var domPv = resolveInternalDomProtoView(mergeMappings.mergedProtoViewRef);
|
|
||||||
expect(stringifyElement(templateRoot(domPv)))
|
|
||||||
.toEqual('<template><root a="b" class="ng-binding"></root></template>');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function templateRoot(pv: DomProtoView) {
|
|
||||||
return <Element>pv.cloneableTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
function runAndAssert(hostElementName: string, componentTemplates: string[],
|
|
||||||
expectedFragments: string[]) {
|
|
||||||
var useNativeEncapsulation = hostElementName.startsWith('native-');
|
|
||||||
var rootComp = rootDirective(hostElementName);
|
|
||||||
return inject([AsyncTestCompleter, DomTestbed, TemplateCloner], (async, tb: DomTestbed,
|
|
||||||
cloner: TemplateCloner) => {
|
|
||||||
tb.compileAndMerge(rootComp, componentTemplates.map(template => componentView(
|
|
||||||
template, useNativeEncapsulation ?
|
|
||||||
ViewEncapsulation.Native :
|
|
||||||
ViewEncapsulation.None)))
|
|
||||||
.then((mergeMappings) => {
|
|
||||||
expect(stringify(cloner, mergeMappings)).toEqual(expectedFragments);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function rootDirective(hostElementName: string) {
|
|
||||||
return RenderDirectiveMetadata.create(
|
|
||||||
{id: 'rootComp', type: RenderDirectiveMetadata.COMPONENT_TYPE, selector: hostElementName});
|
|
||||||
}
|
|
||||||
|
|
||||||
function componentView(template: string,
|
|
||||||
encapsulation: ViewEncapsulation = ViewEncapsulation.None) {
|
|
||||||
return new ViewDefinition({
|
|
||||||
componentId: 'someComp',
|
|
||||||
template: template,
|
|
||||||
directives: [aComp, bComp, cComp],
|
|
||||||
encapsulation: encapsulation
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringify(cloner: TemplateCloner, protoViewMergeMapping: RenderProtoViewMergeMapping):
|
|
||||||
string[] {
|
|
||||||
var testView = cloneAndQueryProtoView(
|
|
||||||
cloner, resolveInternalDomProtoView(protoViewMergeMapping.mergedProtoViewRef), false);
|
|
||||||
for (var i = 0; i < protoViewMergeMapping.mappedElementIndices.length; i++) {
|
|
||||||
var renderElIdx = protoViewMergeMapping.mappedElementIndices[i];
|
|
||||||
if (isPresent(renderElIdx)) {
|
|
||||||
DOM.setAttribute(testView.boundElements[renderElIdx], 'idx', `${i}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var i = 0; i < protoViewMergeMapping.mappedTextIndices.length; i++) {
|
|
||||||
var renderTextIdx = protoViewMergeMapping.mappedTextIndices[i];
|
|
||||||
if (isPresent(renderTextIdx)) {
|
|
||||||
DOM.setText(testView.boundTextNodes[renderTextIdx], `{${i}}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect(protoViewMergeMapping.fragmentCount).toEqual(testView.fragments.length);
|
|
||||||
return testView.fragments.map(nodes => nodes.map(node => stringifyElement(node)).join(''));
|
|
||||||
}
|
|
||||||
|
|
||||||
var aComp = RenderDirectiveMetadata.create(
|
|
||||||
{id: 'aComp', type: RenderDirectiveMetadata.COMPONENT_TYPE, selector: 'a'});
|
|
||||||
var bComp = RenderDirectiveMetadata.create(
|
|
||||||
{id: 'bComp', type: RenderDirectiveMetadata.COMPONENT_TYPE, selector: 'b'});
|
|
||||||
var cComp = RenderDirectiveMetadata.create(
|
|
||||||
{id: 'cComp', type: RenderDirectiveMetadata.COMPONENT_TYPE, selector: 'c'});
|
|
@ -1,143 +0,0 @@
|
|||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
xdescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
dispatchEvent,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
beforeEachBindings,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
SpyObject,
|
|
||||||
proxy
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
import {isBlank} from 'angular2/src/core/facade/lang';
|
|
||||||
import {ListWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
|
|
||||||
import {DomProtoView} from 'angular2/src/core/render/dom/view/proto_view';
|
|
||||||
import {DomElementBinder} from 'angular2/src/core/render/dom/view/element_binder';
|
|
||||||
import {DomView} from 'angular2/src/core/render/dom/view/view';
|
|
||||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
|
||||||
import {TemplateCloner} from 'angular2/src/core/render/dom/template_cloner';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('DomView', () => {
|
|
||||||
function createProtoView(binders = null) {
|
|
||||||
if (isBlank(binders)) {
|
|
||||||
binders = [];
|
|
||||||
}
|
|
||||||
var rootEl = DOM.createTemplate('<div></div>');
|
|
||||||
return DomProtoView.create(new TemplateCloner(-1), null, <Element>rootEl, null, [1], [],
|
|
||||||
binders, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createElementBinder() { return new DomElementBinder({textNodeIndices: []}); }
|
|
||||||
|
|
||||||
function createView(pv = null, boundElementCount = 0) {
|
|
||||||
if (isBlank(pv)) {
|
|
||||||
var elementBinders = ListWrapper.createFixedSize(boundElementCount);
|
|
||||||
for (var i = 0; i < boundElementCount; i++) {
|
|
||||||
elementBinders[i] = createElementBinder();
|
|
||||||
}
|
|
||||||
pv = createProtoView(elementBinders);
|
|
||||||
}
|
|
||||||
var root = el('<div><div></div></div>');
|
|
||||||
var boundElements = [];
|
|
||||||
for (var i = 0; i < boundElementCount; i++) {
|
|
||||||
boundElements.push(el('<span></span'));
|
|
||||||
}
|
|
||||||
return new DomView(pv, [DOM.childNodes(root)[0]], boundElements);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('setElementProperty', () => {
|
|
||||||
var el, view;
|
|
||||||
beforeEach(() => {
|
|
||||||
view = createView(null, 1);
|
|
||||||
el = view.boundElements[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update the property value', () => {
|
|
||||||
view.setElementProperty(0, 'title', 'Hello');
|
|
||||||
expect(el.title).toEqual('Hello');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setElementAttribute', () => {
|
|
||||||
var el, view;
|
|
||||||
beforeEach(() => {
|
|
||||||
view = createView(null, 1);
|
|
||||||
el = view.boundElements[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update and remove an attribute', () => {
|
|
||||||
view.setElementAttribute(0, 'role', 'button');
|
|
||||||
expect(DOM.getAttribute(el, 'role')).toEqual('button');
|
|
||||||
view.setElementAttribute(0, 'role', null);
|
|
||||||
expect(DOM.getAttribute(el, 'role')).toEqual(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should de-normalize attribute names', () => {
|
|
||||||
view.setElementAttribute(0, 'ariaLabel', 'fancy button');
|
|
||||||
expect(DOM.getAttribute(el, 'aria-label')).toEqual('fancy button');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setElementClass', () => {
|
|
||||||
var el, view;
|
|
||||||
beforeEach(() => {
|
|
||||||
view = createView(null, 1);
|
|
||||||
el = view.boundElements[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set and remove a class', () => {
|
|
||||||
view.setElementClass(0, 'active', true);
|
|
||||||
expect(DOM.hasClass(el, 'active')).toEqual(true);
|
|
||||||
|
|
||||||
view.setElementClass(0, 'active', false);
|
|
||||||
expect(DOM.hasClass(el, 'active')).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not de-normalize class names', () => {
|
|
||||||
view.setElementClass(0, 'veryActive', true);
|
|
||||||
view.setElementClass(0, 'very-active', true);
|
|
||||||
expect(DOM.hasClass(el, 'veryActive')).toEqual(true);
|
|
||||||
expect(DOM.hasClass(el, 'very-active')).toEqual(true);
|
|
||||||
|
|
||||||
view.setElementClass(0, 'veryActive', false);
|
|
||||||
view.setElementClass(0, 'very-active', false);
|
|
||||||
expect(DOM.hasClass(el, 'veryActive')).toEqual(false);
|
|
||||||
expect(DOM.hasClass(el, 'very-active')).toEqual(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setElementStyle', () => {
|
|
||||||
var el, view;
|
|
||||||
beforeEach(() => {
|
|
||||||
view = createView(null, 1);
|
|
||||||
el = view.boundElements[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set and remove styles', () => {
|
|
||||||
view.setElementStyle(0, 'width', '40px');
|
|
||||||
expect(DOM.getStyle(el, 'width')).toEqual('40px');
|
|
||||||
|
|
||||||
view.setElementStyle(0, 'width', null);
|
|
||||||
expect(DOM.getStyle(el, 'width')).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should de-normalize style names', () => {
|
|
||||||
view.setElementStyle(0, 'maxWidth', '40px');
|
|
||||||
expect(DOM.getStyle(el, 'max-width')).toEqual('40px');
|
|
||||||
view.setElementStyle(0, 'maxWidth', null);
|
|
||||||
expect(DOM.getStyle(el, 'max-width')).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
@ -21,21 +21,11 @@ class SpyDependencyProvider extends SpyObject implements DependencyProvider {
|
|||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
|
||||||
class SpyChangeDetection extends SpyObject implements ChangeDetection {
|
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
class SpyChangeDetector extends SpyObject implements ChangeDetector {
|
class SpyChangeDetector extends SpyObject implements ChangeDetector {
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
|
||||||
class SpyProtoChangeDetector extends SpyObject implements ProtoChangeDetector {
|
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
class SpyChangeDispatcher extends SpyObject implements ChangeDispatcher {
|
class SpyChangeDispatcher extends SpyObject implements ChangeDispatcher {
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
@ -52,11 +42,6 @@ class SpyInjector extends SpyObject implements Injector {
|
|||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
|
||||||
class SpyRenderCompiler extends SpyObject implements RenderCompiler {
|
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
class SpyDirectiveResolver extends SpyObject implements DirectiveResolver {
|
class SpyDirectiveResolver extends SpyObject implements DirectiveResolver {
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetection,
|
|
||||||
ChangeDetector,
|
ChangeDetector,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
ProtoChangeDetector,
|
ProtoChangeDetector,
|
||||||
DynamicChangeDetector
|
DynamicChangeDetector
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
|
||||||
import {RenderCompiler, Renderer, RenderEventDispatcher} from 'angular2/src/core/render/api';
|
import {Renderer, RenderEventDispatcher} from 'angular2/src/core/render/api';
|
||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
|
||||||
import {AppView} from 'angular2/src/core/compiler/view';
|
import {AppView} from 'angular2/src/core/compiler/view';
|
||||||
@ -29,26 +28,14 @@ import {SpyObject, proxy} from 'angular2/test_lib';
|
|||||||
|
|
||||||
export class SpyDependencyProvider extends SpyObject {}
|
export class SpyDependencyProvider extends SpyObject {}
|
||||||
|
|
||||||
export class SpyChangeDetection extends SpyObject {
|
|
||||||
constructor() { super(ChangeDetection); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpyChangeDetector extends SpyObject {
|
export class SpyChangeDetector extends SpyObject {
|
||||||
constructor() { super(DynamicChangeDetector); }
|
constructor() { super(DynamicChangeDetector); }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SpyProtoChangeDetector extends SpyObject {
|
|
||||||
constructor() { super(DynamicChangeDetector); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpyChangeDispatcher extends SpyObject {}
|
export class SpyChangeDispatcher extends SpyObject {}
|
||||||
|
|
||||||
export class SpyIterableDifferFactory extends SpyObject {}
|
export class SpyIterableDifferFactory extends SpyObject {}
|
||||||
|
|
||||||
export class SpyRenderCompiler extends SpyObject {
|
|
||||||
constructor() { super(RenderCompiler); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpyDirectiveResolver extends SpyObject {
|
export class SpyDirectiveResolver extends SpyObject {
|
||||||
constructor() { super(DirectiveResolver); }
|
constructor() { super(DirectiveResolver); }
|
||||||
}
|
}
|
||||||
|
@ -188,8 +188,6 @@ var NG_API = [
|
|||||||
'ComponentRef.instance=',
|
'ComponentRef.instance=',
|
||||||
'ComponentRef.location',
|
'ComponentRef.location',
|
||||||
'ComponentRef.location=',
|
'ComponentRef.location=',
|
||||||
'ComponentUrlMapper',
|
|
||||||
'ComponentUrlMapper.getUrl()',
|
|
||||||
'ContentChild',
|
'ContentChild',
|
||||||
'ContentChild.descendants',
|
'ContentChild.descendants',
|
||||||
'ContentChild.first',
|
'ContentChild.first',
|
||||||
@ -400,8 +398,6 @@ var NG_API = [
|
|||||||
'ElementRef.nativeElement',
|
'ElementRef.nativeElement',
|
||||||
'ElementRef.parentView',
|
'ElementRef.parentView',
|
||||||
'ElementRef.parentView=',
|
'ElementRef.parentView=',
|
||||||
'ElementRef.renderBoundElementIndex',
|
|
||||||
'ElementRef.renderBoundElementIndex=',
|
|
||||||
'ElementRef.renderView',
|
'ElementRef.renderView',
|
||||||
'ElementRef.renderView=',
|
'ElementRef.renderView=',
|
||||||
'Output',
|
'Output',
|
||||||
@ -534,7 +530,6 @@ var NG_API = [
|
|||||||
'LifeCycle.tick()',
|
'LifeCycle.tick()',
|
||||||
'LowerCasePipe',
|
'LowerCasePipe',
|
||||||
'LowerCasePipe.transform()',
|
'LowerCasePipe.transform()',
|
||||||
'MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE',
|
|
||||||
'NG_VALIDATORS',
|
'NG_VALIDATORS',
|
||||||
'NgClass',
|
'NgClass',
|
||||||
'NgClass.doCheck()',
|
'NgClass.doCheck()',
|
||||||
@ -838,52 +833,6 @@ var NG_API = [
|
|||||||
'QueryMetadata.selector',
|
'QueryMetadata.selector',
|
||||||
'QueryMetadata.token',
|
'QueryMetadata.token',
|
||||||
'QueryMetadata.varBindings',
|
'QueryMetadata.varBindings',
|
||||||
'RenderDirectiveMetadata#COMPONENT_TYPE',
|
|
||||||
'RenderDirectiveMetadata#DIRECTIVE_TYPE',
|
|
||||||
'RenderDirectiveMetadata#create()',
|
|
||||||
'RenderDirectiveMetadata',
|
|
||||||
'RenderDirectiveMetadata.callAfterContentChecked',
|
|
||||||
'RenderDirectiveMetadata.callAfterContentChecked=',
|
|
||||||
'RenderDirectiveMetadata.callAfterContentInit',
|
|
||||||
'RenderDirectiveMetadata.callAfterContentInit=',
|
|
||||||
'RenderDirectiveMetadata.callAfterViewChecked',
|
|
||||||
'RenderDirectiveMetadata.callAfterViewChecked=',
|
|
||||||
'RenderDirectiveMetadata.callAfterViewInit',
|
|
||||||
'RenderDirectiveMetadata.callAfterViewInit=',
|
|
||||||
'RenderDirectiveMetadata.callDoCheck',
|
|
||||||
'RenderDirectiveMetadata.callDoCheck=',
|
|
||||||
'RenderDirectiveMetadata.callOnChanges',
|
|
||||||
'RenderDirectiveMetadata.callOnChanges=',
|
|
||||||
'RenderDirectiveMetadata.callOnDestroy',
|
|
||||||
'RenderDirectiveMetadata.callOnDestroy=',
|
|
||||||
'RenderDirectiveMetadata.callOnInit',
|
|
||||||
'RenderDirectiveMetadata.callOnInit=',
|
|
||||||
'RenderDirectiveMetadata.changeDetection',
|
|
||||||
'RenderDirectiveMetadata.changeDetection=',
|
|
||||||
'RenderDirectiveMetadata.compileChildren',
|
|
||||||
'RenderDirectiveMetadata.compileChildren=',
|
|
||||||
'RenderDirectiveMetadata.outputs',
|
|
||||||
'RenderDirectiveMetadata.outputs=',
|
|
||||||
'RenderDirectiveMetadata.exportAs',
|
|
||||||
'RenderDirectiveMetadata.exportAs=',
|
|
||||||
'RenderDirectiveMetadata.hostAttributes',
|
|
||||||
'RenderDirectiveMetadata.hostAttributes=',
|
|
||||||
'RenderDirectiveMetadata.hostListeners',
|
|
||||||
'RenderDirectiveMetadata.hostListeners=',
|
|
||||||
'RenderDirectiveMetadata.hostProperties',
|
|
||||||
'RenderDirectiveMetadata.hostProperties=',
|
|
||||||
'RenderDirectiveMetadata.id',
|
|
||||||
'RenderDirectiveMetadata.id=',
|
|
||||||
'RenderDirectiveMetadata.inputs',
|
|
||||||
'RenderDirectiveMetadata.inputs=',
|
|
||||||
'RenderDirectiveMetadata.queries',
|
|
||||||
'RenderDirectiveMetadata.queries=',
|
|
||||||
'RenderDirectiveMetadata.readAttributes',
|
|
||||||
'RenderDirectiveMetadata.readAttributes=',
|
|
||||||
'RenderDirectiveMetadata.selector',
|
|
||||||
'RenderDirectiveMetadata.selector=',
|
|
||||||
'RenderDirectiveMetadata.type',
|
|
||||||
'RenderDirectiveMetadata.type=',
|
|
||||||
'RenderFragmentRef',
|
'RenderFragmentRef',
|
||||||
'RenderProtoViewRef',
|
'RenderProtoViewRef',
|
||||||
'RenderViewRef',
|
'RenderViewRef',
|
||||||
@ -1026,21 +975,6 @@ var NG_API = [
|
|||||||
'ViewContainerRef.remove()',
|
'ViewContainerRef.remove()',
|
||||||
'ViewContainerRef.viewManager',
|
'ViewContainerRef.viewManager',
|
||||||
'ViewContainerRef.viewManager=',
|
'ViewContainerRef.viewManager=',
|
||||||
'ViewDefinition',
|
|
||||||
'ViewDefinition.componentId',
|
|
||||||
'ViewDefinition.componentId=',
|
|
||||||
'ViewDefinition.directives',
|
|
||||||
'ViewDefinition.directives=',
|
|
||||||
'ViewDefinition.encapsulation',
|
|
||||||
'ViewDefinition.encapsulation=',
|
|
||||||
'ViewDefinition.styleAbsUrls',
|
|
||||||
'ViewDefinition.styleAbsUrls=',
|
|
||||||
'ViewDefinition.styles',
|
|
||||||
'ViewDefinition.styles=',
|
|
||||||
'ViewDefinition.template',
|
|
||||||
'ViewDefinition.template=',
|
|
||||||
'ViewDefinition.templateAbsUrl',
|
|
||||||
'ViewDefinition.templateAbsUrl=',
|
|
||||||
'ViewEncapsulation#Emulated',
|
'ViewEncapsulation#Emulated',
|
||||||
'ViewEncapsulation#Native',
|
'ViewEncapsulation#Native',
|
||||||
'ViewEncapsulation#None',
|
'ViewEncapsulation#None',
|
||||||
@ -1157,8 +1091,8 @@ var NG_API = [
|
|||||||
'{RenderTextCmd}.value',
|
'{RenderTextCmd}.value',
|
||||||
'{RenderTextCmd}.value=',
|
'{RenderTextCmd}.value=',
|
||||||
'{RenderElementRef}',
|
'{RenderElementRef}',
|
||||||
'{RenderElementRef}.renderBoundElementIndex',
|
'{RenderElementRef}.boundElementIndex',
|
||||||
'{RenderElementRef}.renderBoundElementIndex=',
|
'{RenderElementRef}.boundElementIndex=',
|
||||||
'{RenderElementRef}.renderView',
|
'{RenderElementRef}.renderView',
|
||||||
'{RenderElementRef}.renderView=',
|
'{RenderElementRef}.renderView=',
|
||||||
'{RenderEventDispatcher}',
|
'{RenderEventDispatcher}',
|
||||||
|
@ -8,6 +8,7 @@ import "package:angular2/test_lib.dart"
|
|||||||
inject,
|
inject,
|
||||||
describe,
|
describe,
|
||||||
it,
|
it,
|
||||||
|
iit,
|
||||||
expect,
|
expect,
|
||||||
beforeEach,
|
beforeEach,
|
||||||
createTestInjector,
|
createTestInjector,
|
||||||
|
@ -7,10 +7,9 @@ import {
|
|||||||
Lexer,
|
Lexer,
|
||||||
Parser,
|
Parser,
|
||||||
ChangeDispatcher,
|
ChangeDispatcher,
|
||||||
ChangeDetection,
|
|
||||||
DebugContext,
|
DebugContext,
|
||||||
DynamicChangeDetection,
|
DynamicProtoChangeDetector,
|
||||||
JitChangeDetection,
|
JitProtoChangeDetector,
|
||||||
ChangeDetectorDefinition,
|
ChangeDetectorDefinition,
|
||||||
ChangeDetectorGenConfig,
|
ChangeDetectorGenConfig,
|
||||||
BindingRecord,
|
BindingRecord,
|
||||||
@ -245,13 +244,13 @@ function runBaselineWrites(baselineHead, numberOfRuns, object) {
|
|||||||
|
|
||||||
// ---- CHANGE DETECTION
|
// ---- CHANGE DETECTION
|
||||||
|
|
||||||
function setUpChangeDetection(changeDetection: ChangeDetection, iterations, object) {
|
function setUpChangeDetection(protoChangeDetectorFactory: Function, iterations, object) {
|
||||||
var dispatcher = new DummyDispatcher();
|
var dispatcher = new DummyDispatcher();
|
||||||
var parser = new Parser(new Lexer());
|
var parser = new Parser(new Lexer());
|
||||||
|
|
||||||
var genConfig = new ChangeDetectorGenConfig(false, false, false, true);
|
var genConfig = new ChangeDetectorGenConfig(false, false, false, true);
|
||||||
var parentProto = changeDetection.getProtoChangeDetector(
|
var parentProto = protoChangeDetectorFactory(
|
||||||
"id", new ChangeDetectorDefinition('parent', null, [], [], [], [], genConfig));
|
new ChangeDetectorDefinition('parent', null, [], [], [], [], genConfig));
|
||||||
var parentCd = parentProto.instantiate(dispatcher);
|
var parentCd = parentProto.instantiate(dispatcher);
|
||||||
|
|
||||||
var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)});
|
var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)});
|
||||||
@ -278,8 +277,7 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
|
|||||||
reflector.setter("field9"), directiveRecord)
|
reflector.setter("field9"), directiveRecord)
|
||||||
];
|
];
|
||||||
|
|
||||||
var proto = changeDetection.getProtoChangeDetector(
|
var proto = protoChangeDetectorFactory(
|
||||||
"id",
|
|
||||||
new ChangeDetectorDefinition("proto", null, [], bindings, [], [directiveRecord], genConfig));
|
new ChangeDetectorDefinition("proto", null, [], bindings, [], [directiveRecord], genConfig));
|
||||||
|
|
||||||
var targetObj = new Obj();
|
var targetObj = new Obj();
|
||||||
@ -334,8 +332,9 @@ export function main() {
|
|||||||
|
|
||||||
|
|
||||||
// -- DYNAMIC
|
// -- DYNAMIC
|
||||||
var ng2DynamicChangeDetector =
|
var ng2DynamicChangeDetector = setUpChangeDetection(
|
||||||
setUpChangeDetection(new DynamicChangeDetection(), numberOfDetectors, object);
|
(changeDetectorDefinition) => new DynamicProtoChangeDetector(changeDetectorDefinition),
|
||||||
|
numberOfDetectors, object);
|
||||||
|
|
||||||
runChangeDetectionReads(ng2DynamicChangeDetector, 1); // warmup
|
runChangeDetectionReads(ng2DynamicChangeDetector, 1); // warmup
|
||||||
|
|
||||||
@ -353,9 +352,10 @@ export function main() {
|
|||||||
|
|
||||||
// -- JIT
|
// -- JIT
|
||||||
// Reenable when we have transformers for Dart
|
// Reenable when we have transformers for Dart
|
||||||
if (JitChangeDetection.isSupported()) {
|
if (JitProtoChangeDetector.isSupported()) {
|
||||||
var ng2JitChangeDetector =
|
var ng2JitChangeDetector = setUpChangeDetection(
|
||||||
setUpChangeDetection(new JitChangeDetection(), numberOfDetectors, object);
|
(changeDetectorDefinition) => new JitProtoChangeDetector(changeDetectorDefinition),
|
||||||
|
numberOfDetectors, object);
|
||||||
|
|
||||||
runChangeDetectionReads(ng2JitChangeDetector, 1); // warmup
|
runChangeDetectionReads(ng2JitChangeDetector, 1); // warmup
|
||||||
|
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
library angular2.transform.common.convert;
|
|
||||||
|
|
||||||
import "package:angular2/src/core/facade/collection.dart"
|
|
||||||
show ListWrapper, MapWrapper;
|
|
||||||
import "package:angular2/src/core/facade/lang.dart" show isPresent, isArray;
|
|
||||||
import "package:angular2/src/core/render/api.dart" show RenderDirectiveMetadata;
|
|
||||||
import "package:angular2/src/core/change_detection/change_detection.dart"
|
|
||||||
show ChangeDetectionStrategy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a [DirectiveMetadata] to a map representation. This creates a copy,
|
|
||||||
* that is, subsequent changes to `meta` will not be mirrored in the map.
|
|
||||||
*/
|
|
||||||
Map<String, dynamic> directiveMetadataToMap(RenderDirectiveMetadata meta) {
|
|
||||||
return MapWrapper.createFromPairs([
|
|
||||||
["id", meta.id],
|
|
||||||
["selector", meta.selector],
|
|
||||||
["compileChildren", meta.compileChildren],
|
|
||||||
["hostProperties", _cloneIfPresent(meta.hostProperties)],
|
|
||||||
["hostListeners", _cloneIfPresent(meta.hostListeners)],
|
|
||||||
["hostAttributes", _cloneIfPresent(meta.hostAttributes)],
|
|
||||||
["inputs", _cloneIfPresent(meta.inputs)],
|
|
||||||
["readAttributes", _cloneIfPresent(meta.readAttributes)],
|
|
||||||
["type", meta.type],
|
|
||||||
["exportAs", meta.exportAs],
|
|
||||||
["callOnDestroy", meta.callOnDestroy],
|
|
||||||
["callDoCheck", meta.callDoCheck],
|
|
||||||
["callOnInit", meta.callOnInit],
|
|
||||||
["callOnChanges", meta.callOnChanges],
|
|
||||||
["callAfterContentInit", meta.callAfterContentInit],
|
|
||||||
["callAfterContentChecked", meta.callAfterContentChecked],
|
|
||||||
["callAfterViewInit", meta.callAfterViewInit],
|
|
||||||
["callAfterViewChecked", meta.callAfterViewChecked],
|
|
||||||
["outputs", meta.outputs],
|
|
||||||
["changeDetection", meta.changeDetection == null ? null : meta.changeDetection.index],
|
|
||||||
["version", 1]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Converts a map representation of [DirectiveMetadata] into a
|
|
||||||
* [DirectiveMetadata] object. This creates a copy, that is, subsequent changes
|
|
||||||
* to `map` will not be mirrored in the [DirectiveMetadata] object.
|
|
||||||
*/
|
|
||||||
RenderDirectiveMetadata directiveMetadataFromMap(Map<String, dynamic> map) {
|
|
||||||
return new RenderDirectiveMetadata(
|
|
||||||
id: (map["id"] as String),
|
|
||||||
selector: (map["selector"] as String),
|
|
||||||
compileChildren: (map["compileChildren"] as bool),
|
|
||||||
hostProperties: (_cloneIfPresent(
|
|
||||||
map["hostProperties"]) as Map<String, String>),
|
|
||||||
hostListeners: (_cloneIfPresent(
|
|
||||||
map["hostListeners"]) as Map<String, String>),
|
|
||||||
hostAttributes: (_cloneIfPresent(
|
|
||||||
map["hostAttributes"]) as Map<String, String>),
|
|
||||||
inputs: (_cloneIfPresent(map["inputs"]) as List<String>),
|
|
||||||
readAttributes: (_cloneIfPresent(map["readAttributes"]) as List<String>),
|
|
||||||
type: (map["type"] as num),
|
|
||||||
exportAs: (map["exportAs"] as String),
|
|
||||||
callOnDestroy: (map["callOnDestroy"] as bool),
|
|
||||||
callDoCheck: (map["callDoCheck"] as bool),
|
|
||||||
callOnChanges: (map["callOnChanges"] as bool),
|
|
||||||
callOnInit: (map["callOnInit"] as bool),
|
|
||||||
callAfterContentInit: (map["callAfterContentInit"] as bool),
|
|
||||||
callAfterContentChecked: (map["callAfterContentChecked"] as bool),
|
|
||||||
callAfterViewInit: (map["callAfterViewInit"] as bool),
|
|
||||||
callAfterViewChecked: (map["callAfterViewChecked"] as bool),
|
|
||||||
outputs: (_cloneIfPresent(map["outputs"]) as List<String>),
|
|
||||||
changeDetection: map["changeDetection"] == null ? null
|
|
||||||
: ChangeDetectionStrategy.values[map["changeDetection"] as int]);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Clones the [List] or [Map] `o` if it is present.
|
|
||||||
*/
|
|
||||||
dynamic _cloneIfPresent(o) {
|
|
||||||
if (!isPresent(o)) return null;
|
|
||||||
return isArray(o) ? ListWrapper.clone(o) : MapWrapper.clone(o);
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
library angular2.transform.common.registered_type;
|
library angular2.transform.common.registered_type;
|
||||||
|
|
||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
import 'package:angular2/src/core/render/api.dart';
|
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
|
||||||
/// A call to `Reflector#registerType` generated by `DirectiveProcessor`.
|
/// A call to `Reflector#registerType` generated by `DirectiveProcessor`.
|
||||||
@ -44,8 +43,6 @@ class RegisteredType {
|
|||||||
return new RegisteredType._(visitor.typeName, registerMethod, visitor.info,
|
return new RegisteredType._(visitor.typeName, registerMethod, visitor.info,
|
||||||
visitor.factoryFn, visitor.parameters, visitor.annotations, visitor.propMetadata);
|
visitor.factoryFn, visitor.parameters, visitor.annotations, visitor.propMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderDirectiveMetadata get directiveMetadata => null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ParseRegisterTypeVisitor extends Object
|
class _ParseRegisterTypeVisitor extends Object
|
||||||
|
@ -6,7 +6,6 @@ import 'dart:convert';
|
|||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
import 'package:angular2/src/compiler/directive_metadata.dart';
|
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||||
import 'package:angular2/src/compiler/template_compiler.dart';
|
import 'package:angular2/src/compiler/template_compiler.dart';
|
||||||
import 'package:angular2/src/core/render/api.dart';
|
|
||||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||||
import 'package:angular2/src/transform/common/logging.dart';
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
library angular2.test.transform.common.convert_spec;
|
|
||||||
|
|
||||||
import "package:angular2/src/core/facade/collection.dart" show MapWrapper;
|
|
||||||
import "package:angular2/src/core/render/api.dart" show RenderDirectiveMetadata;
|
|
||||||
import "package:angular2/src/transform/common/convert.dart"
|
|
||||||
show directiveMetadataFromMap, directiveMetadataToMap;
|
|
||||||
import "package:angular2/test_lib.dart" show ddescribe, describe, expect, it;
|
|
||||||
import "package:angular2/src/core/change_detection/change_detection.dart"
|
|
||||||
show ChangeDetectionStrategy;
|
|
||||||
|
|
||||||
main() {
|
|
||||||
describe("convert", () {
|
|
||||||
it("directiveMetadataToMap", () {
|
|
||||||
var someComponent = new RenderDirectiveMetadata(
|
|
||||||
compileChildren: false,
|
|
||||||
hostListeners: MapWrapper.createFromPairs([
|
|
||||||
["LKey", "LVal"]
|
|
||||||
]),
|
|
||||||
hostProperties: MapWrapper.createFromPairs([
|
|
||||||
["PKey", "PVal"]
|
|
||||||
]),
|
|
||||||
hostAttributes: MapWrapper.createFromPairs([
|
|
||||||
["AtKey", "AtVal"]
|
|
||||||
]),
|
|
||||||
id: "someComponent",
|
|
||||||
inputs: ["propKey: propVal"],
|
|
||||||
readAttributes: ["read1", "read2"],
|
|
||||||
selector: "some-comp",
|
|
||||||
type: RenderDirectiveMetadata.COMPONENT_TYPE,
|
|
||||||
exportAs: "aaa",
|
|
||||||
callOnDestroy: true,
|
|
||||||
callOnChanges: true,
|
|
||||||
callDoCheck: true,
|
|
||||||
callOnInit: true,
|
|
||||||
callAfterContentInit: true,
|
|
||||||
callAfterContentChecked: true,
|
|
||||||
callAfterViewInit: true,
|
|
||||||
callAfterViewChecked: true,
|
|
||||||
outputs: ["onFoo", "onBar"],
|
|
||||||
changeDetection: ChangeDetectionStrategy.CheckOnce);
|
|
||||||
var map = directiveMetadataToMap(someComponent);
|
|
||||||
expect(map["compileChildren"]).toEqual(false);
|
|
||||||
expect(map["hostListeners"]).toEqual(MapWrapper.createFromPairs([
|
|
||||||
["LKey", "LVal"]
|
|
||||||
]));
|
|
||||||
expect(map["hostProperties"]).toEqual(MapWrapper.createFromPairs([
|
|
||||||
["PKey", "PVal"]
|
|
||||||
]));
|
|
||||||
expect(map["hostAttributes"]).toEqual(MapWrapper.createFromPairs([
|
|
||||||
["AtKey", "AtVal"]
|
|
||||||
]));
|
|
||||||
expect(map["id"]).toEqual("someComponent");
|
|
||||||
expect(map["inputs"]).toEqual(["propKey: propVal"]);
|
|
||||||
expect(map["readAttributes"]).toEqual(["read1", "read2"]);
|
|
||||||
expect(map["selector"]).toEqual("some-comp");
|
|
||||||
expect(map["type"]).toEqual(RenderDirectiveMetadata.COMPONENT_TYPE);
|
|
||||||
expect(map["callOnDestroy"]).toEqual(true);
|
|
||||||
expect(map["callDoCheck"]).toEqual(true);
|
|
||||||
expect(map["callOnChanges"]).toEqual(true);
|
|
||||||
expect(map["callOnInit"]).toEqual(true);
|
|
||||||
expect(map["callAfterContentInit"]).toEqual(true);
|
|
||||||
expect(map["callAfterContentChecked"]).toEqual(true);
|
|
||||||
expect(map["callAfterViewInit"]).toEqual(true);
|
|
||||||
expect(map["callAfterViewChecked"]).toEqual(true);
|
|
||||||
expect(map["exportAs"]).toEqual("aaa");
|
|
||||||
expect(map["outputs"]).toEqual(["onFoo", "onBar"]);
|
|
||||||
expect(map["changeDetection"])
|
|
||||||
.toEqual(ChangeDetectionStrategy.CheckOnce.index);
|
|
||||||
});
|
|
||||||
it("mapToDirectiveMetadata", () {
|
|
||||||
var map = MapWrapper.createFromPairs([
|
|
||||||
["compileChildren", false],
|
|
||||||
[
|
|
||||||
"hostProperties",
|
|
||||||
MapWrapper.createFromPairs([
|
|
||||||
["PKey", "testVal"]
|
|
||||||
])
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"hostListeners",
|
|
||||||
MapWrapper.createFromPairs([
|
|
||||||
["LKey", "testVal"]
|
|
||||||
])
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"hostAttributes",
|
|
||||||
MapWrapper.createFromPairs([
|
|
||||||
["AtKey", "testVal"]
|
|
||||||
])
|
|
||||||
],
|
|
||||||
["id", "testId"],
|
|
||||||
[
|
|
||||||
"inputs",
|
|
||||||
["propKey: propVal"]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"readAttributes",
|
|
||||||
["readTest1", "readTest2"]
|
|
||||||
],
|
|
||||||
["selector", "testSelector"],
|
|
||||||
["type", RenderDirectiveMetadata.DIRECTIVE_TYPE],
|
|
||||||
["exportAs", "aaa"],
|
|
||||||
["callOnDestroy", true],
|
|
||||||
["callDoCheck", true],
|
|
||||||
["callOnInit", true],
|
|
||||||
["callOnChanges", true],
|
|
||||||
["callAfterContentInit", true],
|
|
||||||
["callAfterContentChecked", true],
|
|
||||||
["callAfterViewInit", true],
|
|
||||||
["callAfterViewChecked", true],
|
|
||||||
[
|
|
||||||
"outputs",
|
|
||||||
["onFoo", "onBar"]
|
|
||||||
],
|
|
||||||
["changeDetection", ChangeDetectionStrategy.CheckOnce.index]
|
|
||||||
]);
|
|
||||||
var meta = directiveMetadataFromMap(map);
|
|
||||||
expect(meta.compileChildren).toEqual(false);
|
|
||||||
expect(meta.hostProperties).toEqual(MapWrapper.createFromPairs([
|
|
||||||
["PKey", "testVal"]
|
|
||||||
]));
|
|
||||||
expect(meta.hostListeners).toEqual(MapWrapper.createFromPairs([
|
|
||||||
["LKey", "testVal"]
|
|
||||||
]));
|
|
||||||
expect(meta.hostAttributes).toEqual(MapWrapper.createFromPairs([
|
|
||||||
["AtKey", "testVal"]
|
|
||||||
]));
|
|
||||||
expect(meta.id).toEqual("testId");
|
|
||||||
expect(meta.inputs).toEqual(["propKey: propVal"]);
|
|
||||||
expect(meta.readAttributes).toEqual(["readTest1", "readTest2"]);
|
|
||||||
expect(meta.selector).toEqual("testSelector");
|
|
||||||
expect(meta.type).toEqual(RenderDirectiveMetadata.DIRECTIVE_TYPE);
|
|
||||||
expect(meta.exportAs).toEqual("aaa");
|
|
||||||
expect(meta.callOnDestroy).toEqual(true);
|
|
||||||
expect(meta.callDoCheck).toEqual(true);
|
|
||||||
expect(meta.callOnInit).toEqual(true);
|
|
||||||
expect(meta.callOnChanges).toEqual(true);
|
|
||||||
expect(meta.callAfterContentInit).toEqual(true);
|
|
||||||
expect(meta.callAfterContentChecked).toEqual(true);
|
|
||||||
expect(meta.callAfterViewInit).toEqual(true);
|
|
||||||
expect(meta.callAfterViewChecked).toEqual(true);
|
|
||||||
expect(meta.outputs).toEqual(["onFoo", "onBar"]);
|
|
||||||
expect(meta.changeDetection).toEqual(ChangeDetectionStrategy.CheckOnce);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ library angular2.test.transform.directive_metadata_linker.all_tests;
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:angular2/src/core/render/api.dart';
|
import 'package:angular2/src/core/render/api.dart';
|
||||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||||
import 'package:angular2/src/transform/common/convert.dart';
|
|
||||||
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
||||||
import 'package:angular2/src/transform/common/logging.dart';
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
import 'package:angular2/src/transform/common/ng_deps.dart';
|
import 'package:angular2/src/transform/common/ng_deps.dart';
|
||||||
|
Reference in New Issue
Block a user