angular/packages/core/src/view/services.ts

776 lines
28 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {isDevMode} from '../application_ref';
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
import {Injector} from '../di';
import {ErrorHandler} from '../error_handler';
import {NgModuleRef} from '../linker/ng_module_factory';
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
import {Sanitizer} from '../security';
import {Type} from '../type';
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
import {resolveDep} from './provider';
import {dirtyParentQueries, getQueryValue} from './query';
import {createInjector, createNgModuleRef} from './refs';
import {ArgumentType, BindingFlags, CheckType, DebugContext, DepDef, ElementData, NgModuleDefinition, NgModuleProviderDef, NodeDef, NodeFlags, NodeLogger, ProviderOverride, RootData, Services, ViewData, ViewDefinition, ViewState, asElementData, asPureExpressionData} from './types';
import {NOOP, isComponentView, renderNode, splitDepsDsl, viewParentEl} from './util';
import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createComponentView, createEmbeddedView, createRootView, destroyView} from './view';
let initialized = false;
export function initServicesIfNeeded() {
if (initialized) {
return;
}
initialized = true;
const services = isDevMode() ? createDebugServices() : createProdServices();
Services.setCurrentNode = services.setCurrentNode;
Services.createRootView = services.createRootView;
Services.createEmbeddedView = services.createEmbeddedView;
Services.createComponentView = services.createComponentView;
Services.createNgModuleRef = services.createNgModuleRef;
Services.overrideProvider = services.overrideProvider;
Services.clearProviderOverrides = services.clearProviderOverrides;
Services.checkAndUpdateView = services.checkAndUpdateView;
Services.checkNoChangesView = services.checkNoChangesView;
Services.destroyView = services.destroyView;
Services.resolveDep = resolveDep;
Services.createDebugContext = services.createDebugContext;
Services.handleEvent = services.handleEvent;
Services.updateDirectives = services.updateDirectives;
Services.updateRenderer = services.updateRenderer;
Services.dirtyParentQueries = dirtyParentQueries;
}
function createProdServices() {
return {
setCurrentNode: () => {},
createRootView: createProdRootView,
createEmbeddedView: createEmbeddedView,
createComponentView: createComponentView,
createNgModuleRef: createNgModuleRef,
overrideProvider: NOOP,
clearProviderOverrides: NOOP,
checkAndUpdateView: checkAndUpdateView,
checkNoChangesView: checkNoChangesView,
destroyView: destroyView,
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
handleEvent: (view: ViewData, nodeIndex: number, eventName: string, event: any) =>
view.def.handleEvent(view, nodeIndex, eventName, event),
updateDirectives: (view: ViewData, checkType: CheckType) => view.def.updateDirectives(
checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode :
prodCheckNoChangesNode,
view),
updateRenderer: (view: ViewData, checkType: CheckType) => view.def.updateRenderer(
checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode :
prodCheckNoChangesNode,
view),
};
}
function createDebugServices() {
return {
setCurrentNode: debugSetCurrentNode,
createRootView: debugCreateRootView,
createEmbeddedView: debugCreateEmbeddedView,
createComponentView: debugCreateComponentView,
createNgModuleRef: debugCreateNgModuleRef,
overrideProvider: debugOverrideProvider,
clearProviderOverrides: debugClearProviderOverrides,
checkAndUpdateView: debugCheckAndUpdateView,
checkNoChangesView: debugCheckNoChangesView,
destroyView: debugDestroyView,
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
handleEvent: debugHandleEvent,
updateDirectives: debugUpdateDirectives,
updateRenderer: debugUpdateRenderer,
};
}
function createProdRootView(
elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
def: ViewDefinition, ngModule: NgModuleRef<any>, context?: any): ViewData {
const rendererFactory: RendererFactory2 = ngModule.injector.get(RendererFactory2);
return createRootView(
createRootData(elInjector, ngModule, rendererFactory, projectableNodes, rootSelectorOrNode),
def, context);
}
function debugCreateRootView(
elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
def: ViewDefinition, ngModule: NgModuleRef<any>, context?: any): ViewData {
const rendererFactory: RendererFactory2 = ngModule.injector.get(RendererFactory2);
const root = createRootData(
elInjector, ngModule, new DebugRendererFactory2(rendererFactory), projectableNodes,
rootSelectorOrNode);
const defWithOverride = applyProviderOverridesToView(def);
return callWithDebugContext(
DebugAction.create, createRootView, null, [root, defWithOverride, context]);
}
function createRootData(
elInjector: Injector, ngModule: NgModuleRef<any>, rendererFactory: RendererFactory2,
projectableNodes: any[][], rootSelectorOrNode: any): RootData {
const sanitizer = ngModule.injector.get(Sanitizer);
const errorHandler = ngModule.injector.get(ErrorHandler);
const renderer = rendererFactory.createRenderer(null, null);
return {
ngModule,
injector: elInjector, projectableNodes,
selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer, errorHandler
};
}
function debugCreateEmbeddedView(
parentView: ViewData, anchorDef: NodeDef, viewDef: ViewDefinition, context?: any): ViewData {
const defWithOverride = applyProviderOverridesToView(viewDef);
return callWithDebugContext(
DebugAction.create, createEmbeddedView, null,
[parentView, anchorDef, defWithOverride, context]);
}
function debugCreateComponentView(
parentView: ViewData, nodeDef: NodeDef, viewDef: ViewDefinition, hostElement: any): ViewData {
const defWithOverride = applyProviderOverridesToView(viewDef);
return callWithDebugContext(
DebugAction.create, createComponentView, null,
[parentView, nodeDef, defWithOverride, hostElement]);
}
function debugCreateNgModuleRef(
moduleType: Type<any>, parentInjector: Injector, bootstrapComponents: Type<any>[],
def: NgModuleDefinition): NgModuleRef<any> {
const defWithOverride = applyProviderOverridesToNgModule(def);
return createNgModuleRef(moduleType, parentInjector, bootstrapComponents, defWithOverride);
}
const providerOverrides = new Map<any, ProviderOverride>();
function debugOverrideProvider(override: ProviderOverride) {
providerOverrides.set(override.token, override);
}
function debugClearProviderOverrides() {
providerOverrides.clear();
}
// Notes about the algorithm:
// 1) Locate the providers of an element and check if one of them was overwritten
// 2) Change the providers of that element
//
// We only create new datastructures if we need to, to keep perf impact
// reasonable.
function applyProviderOverridesToView(def: ViewDefinition): ViewDefinition {
if (providerOverrides.size === 0) {
return def;
}
const elementIndicesWithOverwrittenProviders = findElementIndicesWithOverwrittenProviders(def);
if (elementIndicesWithOverwrittenProviders.length === 0) {
return def;
}
// clone the whole view definition,
// as it maintains references between the nodes that are hard to update.
def = def.factory !(() => NOOP);
for (let i = 0; i < elementIndicesWithOverwrittenProviders.length; i++) {
applyProviderOverridesToElement(def, elementIndicesWithOverwrittenProviders[i]);
}
return def;
function findElementIndicesWithOverwrittenProviders(def: ViewDefinition): number[] {
const elIndicesWithOverwrittenProviders: number[] = [];
let lastElementDef: NodeDef|null = null;
for (let i = 0; i < def.nodes.length; i++) {
const nodeDef = def.nodes[i];
if (nodeDef.flags & NodeFlags.TypeElement) {
lastElementDef = nodeDef;
}
if (lastElementDef && nodeDef.flags & NodeFlags.CatProviderNoDirective &&
providerOverrides.has(nodeDef.provider !.token)) {
elIndicesWithOverwrittenProviders.push(lastElementDef !.index);
lastElementDef = null;
}
}
return elIndicesWithOverwrittenProviders;
}
function applyProviderOverridesToElement(viewDef: ViewDefinition, elIndex: number) {
for (let i = elIndex + 1; i < viewDef.nodes.length; i++) {
const nodeDef = viewDef.nodes[i];
if (nodeDef.flags & NodeFlags.TypeElement) {
// stop at the next element
return;
}
if (nodeDef.flags & NodeFlags.CatProviderNoDirective) {
// Make all providers lazy, so that we don't get into trouble
// with ordering problems of providers on the same element
nodeDef.flags |= NodeFlags.LazyProvider;
const provider = nodeDef.provider !;
const override = providerOverrides.get(provider.token);
if (override) {
nodeDef.flags = (nodeDef.flags & ~NodeFlags.CatProviderNoDirective) | override.flags;
provider.deps = splitDepsDsl(override.deps);
provider.value = override.value;
}
}
}
}
}
// Notes about the algorithm:
// We only create new datastructures if we need to, to keep perf impact
// reasonable.
function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefinition {
if (providerOverrides.size === 0 || !hasOverrrides(def)) {
return def;
}
// clone the whole view definition,
// as it maintains references between the nodes that are hard to update.
def = def.factory !(() => NOOP);
applyProviderOverrides(def);
return def;
function hasOverrrides(def: NgModuleDefinition): boolean {
return def.providers.some(
node =>
!!(node.flags & NodeFlags.CatProviderNoDirective) && providerOverrides.has(node.token));
}
function applyProviderOverrides(def: NgModuleDefinition) {
for (let i = 0; i < def.providers.length; i++) {
const provider = def.providers[i];
// Make all providers lazy, so that we don't get into trouble
// with ordering problems of providers on the same element
provider.flags |= NodeFlags.LazyProvider;
const override = providerOverrides.get(provider.token);
if (override) {
provider.flags = (provider.flags & ~NodeFlags.CatProviderNoDirective) | override.flags;
provider.deps = splitDepsDsl(override.deps);
provider.value = override.value;
}
}
}
}
function prodCheckAndUpdateNode(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any {
const nodeDef = view.def.nodes[nodeIndex];
checkAndUpdateNode(view, nodeDef, argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
return (nodeDef.flags & NodeFlags.CatPureExpression) ?
asPureExpressionData(view, nodeIndex).value :
undefined;
}
function prodCheckNoChangesNode(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any {
const nodeDef = view.def.nodes[nodeIndex];
checkNoChangesNode(view, nodeDef, argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
return (nodeDef.flags & NodeFlags.CatPureExpression) ?
asPureExpressionData(view, nodeIndex).value :
undefined;
}
function debugCheckAndUpdateView(view: ViewData) {
return callWithDebugContext(DebugAction.detectChanges, checkAndUpdateView, null, [view]);
}
function debugCheckNoChangesView(view: ViewData) {
return callWithDebugContext(DebugAction.checkNoChanges, checkNoChangesView, null, [view]);
}
function debugDestroyView(view: ViewData) {
return callWithDebugContext(DebugAction.destroy, destroyView, null, [view]);
}
enum DebugAction {
create,
detectChanges,
checkNoChanges,
destroy,
handleEvent
}
let _currentAction: DebugAction;
let _currentView: ViewData;
let _currentNodeIndex: number|null;
function debugSetCurrentNode(view: ViewData, nodeIndex: number | null) {
_currentView = view;
_currentNodeIndex = nodeIndex;
}
function debugHandleEvent(view: ViewData, nodeIndex: number, eventName: string, event: any) {
debugSetCurrentNode(view, nodeIndex);
return callWithDebugContext(
DebugAction.handleEvent, view.def.handleEvent, null, [view, nodeIndex, eventName, event]);
}
function debugUpdateDirectives(view: ViewData, checkType: CheckType) {
if (view.state & ViewState.Destroyed) {
throw viewDestroyedError(DebugAction[_currentAction]);
}
debugSetCurrentNode(view, nextDirectiveWithBinding(view, 0));
return view.def.updateDirectives(debugCheckDirectivesFn, view);
function debugCheckDirectivesFn(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) {
const nodeDef = view.def.nodes[nodeIndex];
if (checkType === CheckType.CheckAndUpdate) {
debugCheckAndUpdateNode(view, nodeDef, argStyle, values);
} else {
debugCheckNoChangesNode(view, nodeDef, argStyle, values);
}
if (nodeDef.flags & NodeFlags.TypeDirective) {
debugSetCurrentNode(view, nextDirectiveWithBinding(view, nodeIndex));
}
return (nodeDef.flags & NodeFlags.CatPureExpression) ?
asPureExpressionData(view, nodeDef.index).value :
undefined;
}
}
function debugUpdateRenderer(view: ViewData, checkType: CheckType) {
if (view.state & ViewState.Destroyed) {
throw viewDestroyedError(DebugAction[_currentAction]);
}
debugSetCurrentNode(view, nextRenderNodeWithBinding(view, 0));
return view.def.updateRenderer(debugCheckRenderNodeFn, view);
function debugCheckRenderNodeFn(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) {
const nodeDef = view.def.nodes[nodeIndex];
if (checkType === CheckType.CheckAndUpdate) {
debugCheckAndUpdateNode(view, nodeDef, argStyle, values);
} else {
debugCheckNoChangesNode(view, nodeDef, argStyle, values);
}
if (nodeDef.flags & NodeFlags.CatRenderNode) {
debugSetCurrentNode(view, nextRenderNodeWithBinding(view, nodeIndex));
}
return (nodeDef.flags & NodeFlags.CatPureExpression) ?
asPureExpressionData(view, nodeDef.index).value :
undefined;
}
}
function debugCheckAndUpdateNode(
view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, givenValues: any[]): void {
const changed = (<any>checkAndUpdateNode)(view, nodeDef, argStyle, ...givenValues);
if (changed) {
const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues;
if (nodeDef.flags & NodeFlags.TypeDirective) {
const bindingValues: {[key: string]: string} = {};
for (let i = 0; i < nodeDef.bindings.length; i++) {
const binding = nodeDef.bindings[i];
const value = values[i];
if (binding.flags & BindingFlags.TypeProperty) {
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName !)] =
normalizeDebugBindingValue(value);
}
}
const elDef = nodeDef.parent !;
const el = asElementData(view, elDef.index).renderElement;
if (!elDef.element !.name) {
// a comment.
view.renderer.setValue(el, `bindings=${JSON.stringify(bindingValues, null, 2)}`);
} else {
// a regular element.
for (let attr in bindingValues) {
const value = bindingValues[attr];
if (value != null) {
view.renderer.setAttribute(el, attr, value);
} else {
view.renderer.removeAttribute(el, attr);
}
}
}
}
}
}
function debugCheckNoChangesNode(
view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, values: any[]): void {
(<any>checkNoChangesNode)(view, nodeDef, argStyle, ...values);
}
function normalizeDebugBindingName(name: string) {
// Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
return `ng-reflect-${name}`;
}
const CAMEL_CASE_REGEXP = /([A-Z])/g;
function camelCaseToDashCase(input: string): string {
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
}
function normalizeDebugBindingValue(value: any): string {
try {
// Limit the size of the value as otherwise the DOM just gets polluted.
return value != null ? value.toString().slice(0, 30) : value;
} catch (e) {
return '[ERROR] Exception while trying to serialize the value';
}
}
function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number|null {
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
const nodeDef = view.def.nodes[i];
if (nodeDef.flags & NodeFlags.TypeDirective && nodeDef.bindings && nodeDef.bindings.length) {
return i;
}
}
return null;
}
function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number|null {
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
const nodeDef = view.def.nodes[i];
if ((nodeDef.flags & NodeFlags.CatRenderNode) && nodeDef.bindings && nodeDef.bindings.length) {
return i;
}
}
return null;
}
class DebugContext_ implements DebugContext {
private nodeDef: NodeDef;
private elView: ViewData;
private elDef: NodeDef;
constructor(public view: ViewData, public nodeIndex: number|null) {
if (nodeIndex == null) {
this.nodeIndex = nodeIndex = 0;
}
this.nodeDef = view.def.nodes[nodeIndex];
let elDef = this.nodeDef;
let elView = view;
while (elDef && (elDef.flags & NodeFlags.TypeElement) === 0) {
elDef = elDef.parent !;
}
if (!elDef) {
while (!elDef && elView) {
elDef = viewParentEl(elView) !;
elView = elView.parent !;
}
}
this.elDef = elDef;
this.elView = elView;
}
private get elOrCompView() {
// Has to be done lazily as we use the DebugContext also during creation of elements...
return asElementData(this.elView, this.elDef.index).componentView || this.view;
}
get injector(): Injector { return createInjector(this.elView, this.elDef); }
get component(): any { return this.elOrCompView.component; }
get context(): any { return this.elOrCompView.context; }
get providerTokens(): any[] {
const tokens: any[] = [];
if (this.elDef) {
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.elView.def.nodes[i];
if (childDef.flags & NodeFlags.CatProvider) {
tokens.push(childDef.provider !.token);
}
i += childDef.childCount;
}
}
return tokens;
}
get references(): {[key: string]: any} {
const references: {[key: string]: any} = {};
if (this.elDef) {
collectReferences(this.elView, this.elDef, references);
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.elView.def.nodes[i];
if (childDef.flags & NodeFlags.CatProvider) {
collectReferences(this.elView, childDef, references);
}
i += childDef.childCount;
}
}
return references;
}
get componentRenderElement() {
const elData = findHostElement(this.elOrCompView);
return elData ? elData.renderElement : undefined;
}
get renderNode(): any {
return this.nodeDef.flags & NodeFlags.TypeText ? renderNode(this.view, this.nodeDef) :
renderNode(this.elView, this.elDef);
}
logError(console: Console, ...values: any[]) {
let logViewDef: ViewDefinition;
let logNodeIndex: number;
if (this.nodeDef.flags & NodeFlags.TypeText) {
logViewDef = this.view.def;
logNodeIndex = this.nodeDef.index;
} else {
logViewDef = this.elView.def;
logNodeIndex = this.elDef.index;
}
// Note: we only generate a log function for text and element nodes
// to make the generated code as small as possible.
const renderNodeIndex = getRenderNodeIndex(logViewDef, logNodeIndex);
let currRenderNodeIndex = -1;
let nodeLogger: NodeLogger = () => {
currRenderNodeIndex++;
if (currRenderNodeIndex === renderNodeIndex) {
return console.error.bind(console, ...values);
} else {
return NOOP;
}
};
logViewDef.factory !(nodeLogger);
if (currRenderNodeIndex < renderNodeIndex) {
console.error('Illegal state: the ViewDefinitionFactory did not call the logger!');
(<any>console.error)(...values);
}
}
}
function getRenderNodeIndex(viewDef: ViewDefinition, nodeIndex: number): number {
let renderNodeIndex = -1;
for (let i = 0; i <= nodeIndex; i++) {
const nodeDef = viewDef.nodes[i];
if (nodeDef.flags & NodeFlags.CatRenderNode) {
renderNodeIndex++;
}
}
return renderNodeIndex;
}
function findHostElement(view: ViewData): ElementData|null {
while (view && !isComponentView(view)) {
view = view.parent !;
}
if (view.parent) {
return asElementData(view.parent, viewParentEl(view) !.index);
}
return null;
}
function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) {
for (let refName in nodeDef.references) {
references[refName] = getQueryValue(view, nodeDef, nodeDef.references[refName]);
}
}
function callWithDebugContext(action: DebugAction, fn: any, self: any, args: any[]) {
const oldAction = _currentAction;
const oldView = _currentView;
const oldNodeIndex = _currentNodeIndex;
try {
_currentAction = action;
const result = fn.apply(self, args);
_currentView = oldView;
_currentNodeIndex = oldNodeIndex;
_currentAction = oldAction;
return result;
} catch (e) {
if (isViewDebugError(e) || !_currentView) {
throw e;
}
throw viewWrappedDebugError(e, getCurrentDebugContext() !);
}
}
export function getCurrentDebugContext(): DebugContext|null {
return _currentView ? new DebugContext_(_currentView, _currentNodeIndex) : null;
}
class DebugRendererFactory2 implements RendererFactory2 {
constructor(private delegate: RendererFactory2) {}
createRenderer(element: any, renderData: RendererType2|null): Renderer2 {
return new DebugRenderer2(this.delegate.createRenderer(element, renderData));
}
begin() {
if (this.delegate.begin) {
this.delegate.begin();
}
}
end() {
if (this.delegate.end) {
this.delegate.end();
}
}
whenRenderingDone(): Promise<any> {
if (this.delegate.whenRenderingDone) {
return this.delegate.whenRenderingDone();
}
return Promise.resolve(null);
}
}
class DebugRenderer2 implements Renderer2 {
constructor(private delegate: Renderer2) {}
get data() { return this.delegate.data; }
destroyNode(node: any) {
removeDebugNodeFromIndex(getDebugNode(node) !);
if (this.delegate.destroyNode) {
this.delegate.destroyNode(node);
}
}
destroy() { this.delegate.destroy(); }
createElement(name: string, namespace?: string): any {
const el = this.delegate.createElement(name, namespace);
const debugCtx = getCurrentDebugContext();
if (debugCtx) {
const debugEl = new DebugElement(el, null, debugCtx);
debugEl.name = name;
indexDebugNode(debugEl);
}
return el;
}
createComment(value: string): any {
const comment = this.delegate.createComment(value);
const debugCtx = getCurrentDebugContext();
if (debugCtx) {
indexDebugNode(new DebugNode(comment, null, debugCtx));
}
return comment;
}
createText(value: string): any {
const text = this.delegate.createText(value);
const debugCtx = getCurrentDebugContext();
if (debugCtx) {
indexDebugNode(new DebugNode(text, null, debugCtx));
}
return text;
}
appendChild(parent: any, newChild: any): void {
const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(newChild);
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.addChild(debugChildEl);
}
this.delegate.appendChild(parent, newChild);
}
insertBefore(parent: any, newChild: any, refChild: any): void {
const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(newChild);
const debugRefEl = getDebugNode(refChild) !;
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.insertBefore(debugRefEl, debugChildEl);
}
this.delegate.insertBefore(parent, newChild, refChild);
}
removeChild(parent: any, oldChild: any): void {
const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(oldChild);
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.removeChild(debugChildEl);
}
this.delegate.removeChild(parent, oldChild);
}
selectRootElement(selectorOrNode: string|any): any {
const el = this.delegate.selectRootElement(selectorOrNode);
const debugCtx = getCurrentDebugContext();
if (debugCtx) {
indexDebugNode(new DebugElement(el, null, debugCtx));
}
return el;
}
setAttribute(el: any, name: string, value: string, namespace?: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
const fullName = namespace ? namespace + ':' + name : name;
debugEl.attributes[fullName] = value;
}
this.delegate.setAttribute(el, name, value, namespace);
}
removeAttribute(el: any, name: string, namespace?: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
const fullName = namespace ? namespace + ':' + name : name;
debugEl.attributes[fullName] = null;
}
this.delegate.removeAttribute(el, name, namespace);
}
addClass(el: any, name: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.classes[name] = true;
}
this.delegate.addClass(el, name);
}
removeClass(el: any, name: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.classes[name] = false;
}
this.delegate.removeClass(el, name);
}
setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.styles[style] = value;
}
this.delegate.setStyle(el, style, value, flags);
}
removeStyle(el: any, style: string, flags: RendererStyleFlags2): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.styles[style] = null;
}
this.delegate.removeStyle(el, style, flags);
}
setProperty(el: any, name: string, value: any): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.properties[name] = value;
}
this.delegate.setProperty(el, name, value);
}
listen(
target: 'document'|'windows'|'body'|any, eventName: string,
callback: (event: any) => boolean): () => void {
if (typeof target !== 'string') {
const debugEl = getDebugNode(target);
if (debugEl) {
debugEl.listeners.push(new EventListener(eventName, callback));
}
}
return this.delegate.listen(target, eventName, callback);
}
parentNode(node: any): any { return this.delegate.parentNode(node); }
nextSibling(node: any): any { return this.delegate.nextSibling(node); }
setValue(node: any, value: string): void { return this.delegate.setValue(node, value); }
}