diff --git a/modules/angular2/core.dart b/modules/angular2/core.dart index 54bb3df115..92ce45e5d6 100644 --- a/modules/angular2/core.dart +++ b/modules/angular2/core.dart @@ -15,7 +15,7 @@ export './src/core/application_tokens.dart' show APP_ID, export './src/core/zone.dart'; export './src/core/render.dart'; export './src/core/linker.dart'; -export './src/core/debug/debug_element.dart' show DebugElement, +export './src/core/debug/debug_node.dart' show DebugElement, Scope, inspectElement, asNativeElements; diff --git a/modules/angular2/core.ts b/modules/angular2/core.ts index 08f3d19d54..48c9387d0b 100644 --- a/modules/angular2/core.ts +++ b/modules/angular2/core.ts @@ -20,12 +20,7 @@ export { export * from './src/core/zone'; export * from './src/core/render'; export * from './src/core/linker'; -export { - DebugElement, - Scope, - inspectElement, - asNativeElements -} from './src/core/debug/debug_element'; +export {DebugElement, asNativeElements} from './src/core/debug/debug_node'; export * from './src/core/testability/testability'; export * from './src/core/change_detection'; export * from './src/core/platform_directives_and_pipes'; diff --git a/modules/angular2/examples/core/debug/ts/debug_element/debug_element.ts b/modules/angular2/examples/core/debug/ts/debug_element/debug_element.ts index ebda75d05c..4fc729dae1 100644 --- a/modules/angular2/examples/core/debug/ts/debug_element/debug_element.ts +++ b/modules/angular2/examples/core/debug/ts/debug_element/debug_element.ts @@ -1,16 +1,8 @@ -import {DebugElement, Scope} from 'angular2/core'; +import {DebugElement} from 'angular2/core'; var debugElement: DebugElement; var predicate; // #docregion scope_all -debugElement.query(predicate, Scope.all); -// #enddocregion - -// #docregion scope_light -debugElement.query(predicate, Scope.light); -// #enddocregion - -// #docregion scope_view -debugElement.query(predicate, Scope.view); +debugElement.query(predicate); // #enddocregion diff --git a/modules/angular2/examples/platform/dom/debug/ts/by/by.ts b/modules/angular2/examples/platform/dom/debug/ts/by/by.ts index 58c888328c..b4155a4e5d 100644 --- a/modules/angular2/examples/platform/dom/debug/ts/by/by.ts +++ b/modules/angular2/examples/platform/dom/debug/ts/by/by.ts @@ -1,17 +1,17 @@ import {By} from 'angular2/platform/browser'; -import {DebugElement, Scope} from 'angular2/core'; +import {DebugElement} from 'angular2/core'; var debugElement: DebugElement; class MyDirective {} // #docregion by_all -debugElement.query(By.all(), Scope.all); +debugElement.query(By.all()); // #enddocregion // #docregion by_css -debugElement.query(By.css('[attribute]'), Scope.all); +debugElement.query(By.css('[attribute]')); // #enddocregion // #docregion by_directive -debugElement.query(By.directive(MyDirective), Scope.all); +debugElement.query(By.directive(MyDirective)); // #enddocregion diff --git a/modules/angular2/platform/browser.ts b/modules/angular2/platform/browser.ts index 741f1f65e9..711a194ec1 100644 --- a/modules/angular2/platform/browser.ts +++ b/modules/angular2/platform/browser.ts @@ -1,8 +1,8 @@ export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint'; export { BROWSER_PROVIDERS, - ELEMENT_PROBE_BINDINGS, ELEMENT_PROBE_PROVIDERS, + ELEMENT_PROBE_PROVIDERS_PROD_MODE, inspectNativeElement, BrowserDomAdapter, By, diff --git a/modules/angular2/platform/browser_static.ts b/modules/angular2/platform/browser_static.ts index 732b08bf11..6722212af1 100644 --- a/modules/angular2/platform/browser_static.ts +++ b/modules/angular2/platform/browser_static.ts @@ -1,8 +1,8 @@ export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint'; export { BROWSER_PROVIDERS, - ELEMENT_PROBE_BINDINGS, ELEMENT_PROBE_PROVIDERS, + ELEMENT_PROBE_PROVIDERS_PROD_MODE, inspectNativeElement, BrowserDomAdapter, By, diff --git a/modules/angular2/platform/common_dom.ts b/modules/angular2/platform/common_dom.ts index c74e5ee84b..50d3b5ba04 100644 --- a/modules/angular2/platform/common_dom.ts +++ b/modules/angular2/platform/common_dom.ts @@ -12,4 +12,4 @@ export { EventManagerPlugin } from 'angular2/src/platform/dom/events/event_manager'; export * from 'angular2/src/platform/dom/debug/by'; -export * from 'angular2/src/platform/dom/debug/debug_element_view_listener'; \ No newline at end of file +export * from 'angular2/src/platform/dom/debug/ng_probe'; diff --git a/modules/angular2/src/core/application_common_providers.ts b/modules/angular2/src/core/application_common_providers.ts index bb75bf517e..778e5799f4 100644 --- a/modules/angular2/src/core/application_common_providers.ts +++ b/modules/angular2/src/core/application_common_providers.ts @@ -15,7 +15,6 @@ import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_ import {AppViewManager} from './linker/view_manager'; import {AppViewManager_} from "./linker/view_manager"; import {ViewResolver} from './linker/view_resolver'; -import {AppViewListener} from './linker/view_listener'; import {DirectiveResolver} from './linker/directive_resolver'; import {PipeResolver} from './linker/pipe_resolver'; import {Compiler} from './linker/compiler'; @@ -32,7 +31,6 @@ export const APPLICATION_COMMON_PROVIDERS: Array = CONS APP_ID_RANDOM_PROVIDER, ResolvedMetadataCache, new Provider(AppViewManager, {useClass: AppViewManager_}), - AppViewListener, ViewResolver, new Provider(IterableDiffers, {useValue: defaultIterableDiffers}), new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}), diff --git a/modules/angular2/src/core/debug/debug_element.ts b/modules/angular2/src/core/debug/debug_element.ts deleted file mode 100644 index 88b196258e..0000000000 --- a/modules/angular2/src/core/debug/debug_element.ts +++ /dev/null @@ -1,248 +0,0 @@ -import {Type, isPresent, isBlank} from 'angular2/src/facade/lang'; -import {ListWrapper, MapWrapper, Predicate} from 'angular2/src/facade/collection'; -import {unimplemented} from 'angular2/src/facade/exceptions'; - -import {AppElement} from 'angular2/src/core/linker/element'; -import {AppView} from 'angular2/src/core/linker/view'; -import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref'; - -/** - * A DebugElement contains information from the Angular compiler about an - * element and provides access to the corresponding ElementInjector and - * underlying DOM Element, as well as a way to query for children. - * - * A DebugElement can be obtained from a {@link ComponentFixture} or from an - * {@link ElementRef} via {@link inspectElement}. - */ -export abstract class DebugElement { - /** - * Return the instance of the component associated with this element, if any. - */ - get componentInstance(): any { return unimplemented(); }; - - /** - * Return the native HTML element for this DebugElement. - */ - get nativeElement(): any { return unimplemented(); }; - - /** - * Return an Angular {@link ElementRef} for this element. - */ - get elementRef(): ElementRef { return unimplemented(); }; - - /** - * Get the directive active for this element with the given index, if any. - */ - abstract getDirectiveInstance(directiveIndex: number): any; - - /** - * Get child DebugElements from within the Light DOM. - * - * @return {DebugElement[]} - */ - get children(): DebugElement[] { return unimplemented(); }; - - /** - * Get the root DebugElement children of a component. Returns an empty - * list if the current DebugElement is not a component root. - * - * @return {DebugElement[]} - */ - get componentViewChildren(): DebugElement[] { return unimplemented(); }; - - /** - * Simulate an event from this element as if the user had caused - * this event to fire from the page. - */ - abstract triggerEventHandler(eventName: string, eventObj: Event): void; - - /** - * Check whether the element has a directive with the given type. - */ - abstract hasDirective(type: Type): boolean; - - /** - * Inject the given type from the element injector. - */ - abstract inject(type: Type): any; - - - /** - * Read a local variable from the element (e.g. one defined with `#variable`). - */ - abstract getLocal(name: string): any; - - /** - * Return the first descendant TestElement matching the given predicate - * and scope. - * - * @param {Function: boolean} predicate - * @param {Scope} scope - * - * @return {DebugElement} - */ - query(predicate: Predicate, scope: Function = Scope.all): DebugElement { - var results = this.queryAll(predicate, scope); - return results.length > 0 ? results[0] : null; - } - - /** - * Return descendant TestElememts matching the given predicate - * and scope. - * - * @param {Function: boolean} predicate - * @param {Scope} scope - * - * @return {DebugElement[]} - */ - queryAll(predicate: Predicate, scope: Function = Scope.all): DebugElement[] { - var elementsInScope: any[] = scope(this); - - return elementsInScope.filter(predicate); - } -} - -export class DebugElement_ extends DebugElement { - constructor(private _appElement: AppElement) { super(); } - - get componentInstance(): any { - if (!isPresent(this._appElement)) { - return null; - } - return this._appElement.getComponent(); - } - - get nativeElement(): any { return this.elementRef.nativeElement; } - - get elementRef(): ElementRef { return this._appElement.ref; } - - getDirectiveInstance(directiveIndex: number): any { - return this._appElement.getDirectiveAtIndex(directiveIndex); - } - - get children(): DebugElement[] { - return this._getChildElements(this._appElement.parentView, this._appElement); - } - - get componentViewChildren(): DebugElement[] { - if (!isPresent(this._appElement.componentView)) { - // The current element is not a component. - return []; - } - - return this._getChildElements(this._appElement.componentView, null); - } - - triggerEventHandler(eventName: string, eventObj: Event): void { - this._appElement.parentView.triggerEventHandlers(eventName, eventObj, - this._appElement.proto.index); - } - - hasDirective(type: Type): boolean { - if (!isPresent(this._appElement)) { - return false; - } - return this._appElement.hasDirective(type); - } - - inject(type: Type): any { - if (!isPresent(this._appElement)) { - return null; - } - return this._appElement.get(type); - } - - getLocal(name: string): any { return this._appElement.parentView.locals.get(name); } - - /** @internal */ - _getChildElements(view: AppView, parentAppElement: AppElement): DebugElement[] { - var els = []; - for (var i = 0; i < view.appElements.length; ++i) { - var appEl = view.appElements[i]; - if (appEl.parent == parentAppElement) { - els.push(new DebugElement_(appEl)); - - var views = appEl.nestedViews; - if (isPresent(views)) { - views.forEach( - (nextView) => { els = els.concat(this._getChildElements(nextView, null)); }); - } - } - } - return els; - } -} - -/** - * Returns a {@link DebugElement} for an {@link ElementRef}. - * - * @param {ElementRef}: elementRef - * @return {DebugElement} - */ -export function inspectElement(elementRef: ElementRef): DebugElement { - return new DebugElement_((elementRef).internalElement); -} - -/** - * Maps an array of {@link DebugElement}s to an array of native DOM elements. - */ -export function asNativeElements(arr: DebugElement[]): any[] { - return arr.map((debugEl) => debugEl.nativeElement); -} - -/** - * Set of scope functions used with {@link DebugElement}'s query functionality. - */ -export class Scope { - /** - * Scope queries to both the light dom and view of an element and its - * children. - * - * ## Example - * - * {@example core/debug/ts/debug_element/debug_element.ts region='scope_all'} - */ - static all(debugElement: DebugElement): DebugElement[] { - var scope = []; - scope.push(debugElement); - - debugElement.children.forEach(child => scope = scope.concat(Scope.all(child))); - - debugElement.componentViewChildren.forEach(child => scope = scope.concat(Scope.all(child))); - - return scope; - } - - /** - * Scope queries to the light dom of an element and its children. - * - * ## Example - * - * {@example core/debug/ts/debug_element/debug_element.ts region='scope_light'} - */ - static light(debugElement: DebugElement): DebugElement[] { - var scope = []; - debugElement.children.forEach(child => { - scope.push(child); - scope = scope.concat(Scope.light(child)); - }); - return scope; - } - - /** - * Scope queries to the view of an element of its children. - * - * ## Example - * - * {@example core/debug/ts/debug_element/debug_element.ts region='scope_view'} - */ - static view(debugElement: DebugElement): DebugElement[] { - var scope = []; - - debugElement.componentViewChildren.forEach(child => { - scope.push(child); - scope = scope.concat(Scope.light(child)); - }); - return scope; - } -} diff --git a/modules/angular2/src/core/debug/debug_node.ts b/modules/angular2/src/core/debug/debug_node.ts new file mode 100644 index 0000000000..88918f69d2 --- /dev/null +++ b/modules/angular2/src/core/debug/debug_node.ts @@ -0,0 +1,171 @@ +import {isPresent} from 'angular2/src/facade/lang'; +import {Predicate} from 'angular2/src/facade/collection'; +import {Injector} from 'angular2/src/core/di'; +import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; +import {RenderDebugInfo} from 'angular2/src/core/render/api'; + +export class EventListener { constructor(public name: string, public callback: Function){}; } + +export class DebugNode { + nativeNode: any; + listeners: EventListener[]; + parent: DebugElement; + providerTokens: any[]; + locals: Map; + injector: Injector; + componentInstance: any; + + constructor(nativeNode: any, parent: DebugNode) { + this.nativeNode = nativeNode; + if (isPresent(parent) && parent instanceof DebugElement) { + parent.addChild(this); + } else { + this.parent = null; + } + this.listeners = []; + this.providerTokens = []; + } + + setDebugInfo(info: RenderDebugInfo) { + this.injector = info.injector; + this.providerTokens = info.providerTokens; + this.locals = info.locals; + this.componentInstance = info.component; + } + + inject(token: any): any { return this.injector.get(token); } + + getLocal(name: string): any { return this.locals.get(name); } +} + +export class DebugElement extends DebugNode { + name: string; + properties: Map; + attributes: Map; + childNodes: DebugNode[]; + nativeElement: any; + + constructor(nativeNode: any, parent: any) { + super(nativeNode, parent); + this.properties = new Map(); + this.attributes = new Map(); + this.childNodes = []; + this.nativeElement = nativeNode; + } + + addChild(child: DebugNode) { + if (isPresent(child)) { + this.childNodes.push(child); + child.parent = this; + } + } + + removeChild(child: DebugNode) { + var childIndex = this.childNodes.indexOf(child); + if (childIndex !== -1) { + child.parent = null; + this.childNodes.splice(childIndex, 1); + } + } + + insertChildrenAfter(child: DebugNode, newChildren: DebugNode[]) { + var siblingIndex = this.childNodes.indexOf(child); + if (siblingIndex !== -1) { + var previousChildren = this.childNodes.slice(0, siblingIndex + 1); + var nextChildren = this.childNodes.slice(siblingIndex + 1); + this.childNodes = + ListWrapper.concat(ListWrapper.concat(previousChildren, newChildren), nextChildren); + for (var i = 0; i < newChildren.length; ++i) { + var newChild = newChildren[i]; + if (isPresent(newChild.parent)) { + newChild.parent.removeChild(newChild); + } + newChild.parent = this; + } + } + } + + query(predicate: Predicate): DebugElement { + var results = this.queryAll(predicate); + return results.length > 0 ? results[0] : null; + } + + queryAll(predicate: Predicate): DebugElement[] { + var matches = []; + _queryElementChildren(this, predicate, matches); + return matches; + } + + queryAllNodes(predicate: Predicate): DebugNode[] { + var matches = []; + _queryNodeChildren(this, predicate, matches); + return matches; + } + + get children(): DebugElement[] { + var children = []; + this.childNodes.forEach((node) => { + if (node instanceof DebugElement) { + children.push(node); + } + }); + return children; + } + + triggerEventHandler(eventName: string, eventObj: Event) { + this.listeners.forEach((listener) => { + if (listener.name == eventName) { + listener.callback(eventObj); + } + }); + } +} + +export function asNativeElements(debugEls: DebugElement[]): any { + return debugEls.map((el) => el.nativeElement); +} + +function _queryElementChildren(element: DebugElement, predicate: Predicate, + matches: DebugElement[]) { + element.childNodes.forEach(node => { + if (node instanceof DebugElement) { + if (predicate(node)) { + matches.push(node); + } + _queryElementChildren(node, predicate, matches); + } + }); +} + +function _queryNodeChildren(parentNode: DebugNode, predicate: Predicate, + matches: DebugNode[]) { + if (parentNode instanceof DebugElement) { + parentNode.childNodes.forEach(node => { + if (predicate(node)) { + matches.push(node); + } + if (node instanceof DebugElement) { + _queryNodeChildren(node, predicate, matches); + } + }); + } +} + +// Need to keep the nodes in a global Map so that multiple angular apps are supported. +var _nativeNodeToDebugNode = new Map(); + +export function getDebugNode(nativeNode: any): DebugNode { + return _nativeNodeToDebugNode.get(nativeNode); +} + +export function getAllDebugNodes(): DebugNode[] { + return MapWrapper.values(_nativeNodeToDebugNode); +} + +export function indexDebugNode(node: DebugNode) { + _nativeNodeToDebugNode.set(node.nativeNode, node); +} + +export function removeDebugNodeFromIndex(node: DebugNode) { + _nativeNodeToDebugNode.delete(node.nativeNode); +} diff --git a/modules/angular2/src/core/debug/debug_renderer.ts b/modules/angular2/src/core/debug/debug_renderer.ts new file mode 100644 index 0000000000..22c9e67245 --- /dev/null +++ b/modules/angular2/src/core/debug/debug_renderer.ts @@ -0,0 +1,157 @@ +import {isPresent} from 'angular2/src/facade/lang'; +import { + Renderer, + RootRenderer, + RenderComponentType, + RenderDebugInfo +} from 'angular2/src/core/render/api'; +import { + DebugNode, + DebugElement, + EventListener, + getDebugNode, + indexDebugNode, + removeDebugNodeFromIndex +} from 'angular2/src/core/debug/debug_node'; + +export class DebugDomRootRenderer implements RootRenderer { + constructor(private _delegate: RootRenderer) {} + + renderComponent(componentProto: RenderComponentType): Renderer { + return new DebugDomRenderer(this, this._delegate.renderComponent(componentProto)); + } +} + +export class DebugDomRenderer implements Renderer { + constructor(private _rootRenderer: DebugDomRootRenderer, private _delegate: Renderer) {} + + renderComponent(componentType: RenderComponentType): Renderer { + return this._rootRenderer.renderComponent(componentType); + } + + selectRootElement(selector: string): any { + var nativeEl = this._delegate.selectRootElement(selector); + var debugEl = new DebugElement(nativeEl, null); + indexDebugNode(debugEl); + return nativeEl; + } + + createElement(parentElement: any, name: string): any { + var nativeEl = this._delegate.createElement(parentElement, name); + var debugEl = new DebugElement(nativeEl, getDebugNode(parentElement)); + debugEl.name = name; + indexDebugNode(debugEl); + return nativeEl; + } + + createViewRoot(hostElement: any): any { return this._delegate.createViewRoot(hostElement); } + + createTemplateAnchor(parentElement: any): any { + var comment = this._delegate.createTemplateAnchor(parentElement); + var debugEl = new DebugNode(comment, getDebugNode(parentElement)); + indexDebugNode(debugEl); + return comment; + } + + createText(parentElement: any, value: string): any { + var text = this._delegate.createText(parentElement, value); + var debugEl = new DebugNode(text, getDebugNode(parentElement)); + indexDebugNode(debugEl); + return text; + } + + projectNodes(parentElement: any, nodes: any[]) { + var debugParent = getDebugNode(parentElement); + if (isPresent(debugParent) && debugParent instanceof DebugElement) { + nodes.forEach((node) => { debugParent.addChild(getDebugNode(node)); }); + } + return this._delegate.projectNodes(parentElement, nodes); + } + + attachViewAfter(node: any, viewRootNodes: any[]) { + var debugNode = getDebugNode(node); + if (isPresent(debugNode)) { + var debugParent = debugNode.parent; + if (viewRootNodes.length > 0 && isPresent(debugParent)) { + var debugViewRootNodes = []; + viewRootNodes.forEach((rootNode) => debugViewRootNodes.push(getDebugNode(rootNode))); + debugParent.insertChildrenAfter(debugNode, debugViewRootNodes); + } + } + return this._delegate.attachViewAfter(node, viewRootNodes); + } + + detachView(viewRootNodes: any[]) { + viewRootNodes.forEach((node) => { + var debugNode = getDebugNode(node); + if (isPresent(debugNode) && isPresent(debugNode.parent)) { + debugNode.parent.removeChild(debugNode); + } + }); + return this._delegate.detachView(viewRootNodes); + } + + destroyView(hostElement: any, viewAllNodes: any[]) { + viewAllNodes.forEach((node) => { removeDebugNodeFromIndex(getDebugNode(node)); }); + return this._delegate.destroyView(hostElement, viewAllNodes); + } + + listen(renderElement: any, name: string, callback: Function) { + var debugEl = getDebugNode(renderElement); + if (isPresent(debugEl)) { + debugEl.listeners.push(new EventListener(name, callback)); + } + return this._delegate.listen(renderElement, name, callback); + } + + listenGlobal(target: string, name: string, callback: Function): Function { + return this._delegate.listenGlobal(target, name, callback); + } + + setElementProperty(renderElement: any, propertyName: string, propertyValue: any) { + var debugEl = getDebugNode(renderElement); + if (isPresent(debugEl) && debugEl instanceof DebugElement) { + debugEl.properties.set(propertyName, propertyValue); + } + return this._delegate.setElementProperty(renderElement, propertyName, propertyValue); + } + + setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) { + var debugEl = getDebugNode(renderElement); + if (isPresent(debugEl) && debugEl instanceof DebugElement) { + debugEl.attributes.set(attributeName, attributeValue); + } + return this._delegate.setElementAttribute(renderElement, attributeName, attributeValue); + } + + /** + * Used only in debug mode to serialize property changes to comment nodes, + * such as