refactor: move angular source to /packages rather than modules/@angular

This commit is contained in:
Jason Aden
2017-03-02 10:48:42 -08:00
parent 5ad5301a3e
commit 3e51a19983
1051 changed files with 18 additions and 18 deletions

View File

@ -0,0 +1,428 @@
/**
* @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 {ɵglobal as global} from '@angular/core';
import {setRootDomAdapter} from '../dom/dom_adapter';
import {GenericBrowserDomAdapter} from './generic_browser_adapter';
const _attrToPropMap = {
'class': 'className',
'innerHtml': 'innerHTML',
'readonly': 'readOnly',
'tabindex': 'tabIndex',
};
const DOM_KEY_LOCATION_NUMPAD = 3;
// Map to convert some key or keyIdentifier values to what will be returned by getEventKey
const _keyMap: {[k: string]: string} = {
// The following values are here for cross-browser compatibility and to match the W3C standard
// cf http://www.w3.org/TR/DOM-Level-3-Events-key/
'\b': 'Backspace',
'\t': 'Tab',
'\x7F': 'Delete',
'\x1B': 'Escape',
'Del': 'Delete',
'Esc': 'Escape',
'Left': 'ArrowLeft',
'Right': 'ArrowRight',
'Up': 'ArrowUp',
'Down': 'ArrowDown',
'Menu': 'ContextMenu',
'Scroll': 'ScrollLock',
'Win': 'OS'
};
// There is a bug in Chrome for numeric keypad keys:
// https://code.google.com/p/chromium/issues/detail?id=155654
// 1, 2, 3 ... are reported as A, B, C ...
const _chromeNumKeyPadMap = {
'A': '1',
'B': '2',
'C': '3',
'D': '4',
'E': '5',
'F': '6',
'G': '7',
'H': '8',
'I': '9',
'J': '*',
'K': '+',
'M': '-',
'N': '.',
'O': '/',
'\x60': '0',
'\x90': 'NumLock'
};
/**
* A `DomAdapter` powered by full browser DOM APIs.
*
* @security Tread carefully! Interacting with the DOM directly is dangerous and
* can introduce XSS risks.
*/
/* tslint:disable:requireParameterType no-console */
export class BrowserDomAdapter extends GenericBrowserDomAdapter {
parse(templateHtml: string) { throw new Error('parse not implemented'); }
static makeCurrent() { setRootDomAdapter(new BrowserDomAdapter()); }
hasProperty(element: Node, name: string): boolean { return name in element; }
setProperty(el: Node, name: string, value: any) { (<any>el)[name] = value; }
getProperty(el: Node, name: string): any { return (<any>el)[name]; }
invoke(el: Node, methodName: string, args: any[]): any { (<any>el)[methodName](...args); }
// TODO(tbosch): move this into a separate environment class once we have it
logError(error: string): void {
if (window.console) {
if (console.error) {
console.error(error);
} else {
console.log(error);
}
}
}
log(error: string): void {
if (window.console) {
window.console.log && window.console.log(error);
}
}
logGroup(error: string): void {
if (window.console) {
window.console.group && window.console.group(error);
}
}
logGroupEnd(): void {
if (window.console) {
window.console.groupEnd && window.console.groupEnd();
}
}
get attrToPropMap(): any { return _attrToPropMap; }
querySelector(el: Element, selector: string): any { return el.querySelector(selector); }
querySelectorAll(el: any, selector: string): any[] { return el.querySelectorAll(selector); }
on(el: Node, evt: any, listener: any) { el.addEventListener(evt, listener, false); }
onAndCancel(el: Node, evt: any, listener: any): Function {
el.addEventListener(evt, listener, false);
// Needed to follow Dart's subscription semantic, until fix of
// https://code.google.com/p/dart/issues/detail?id=17406
return () => { el.removeEventListener(evt, listener, false); };
}
dispatchEvent(el: Node, evt: any) { el.dispatchEvent(evt); }
createMouseEvent(eventType: string): MouseEvent {
const evt: MouseEvent = document.createEvent('MouseEvent');
evt.initEvent(eventType, true, true);
return evt;
}
createEvent(eventType: any): Event {
const evt: Event = document.createEvent('Event');
evt.initEvent(eventType, true, true);
return evt;
}
preventDefault(evt: Event) {
evt.preventDefault();
evt.returnValue = false;
}
isPrevented(evt: Event): boolean {
return evt.defaultPrevented || evt.returnValue != null && !evt.returnValue;
}
getInnerHTML(el: HTMLElement): string { return el.innerHTML; }
getTemplateContent(el: Node): Node {
return 'content' in el && el instanceof HTMLTemplateElement ? el.content : null;
}
getOuterHTML(el: HTMLElement): string { return el.outerHTML; }
nodeName(node: Node): string { return node.nodeName; }
nodeValue(node: Node): string { return node.nodeValue; }
type(node: HTMLInputElement): string { return node.type; }
content(node: Node): Node {
if (this.hasProperty(node, 'content')) {
return (<any>node).content;
} else {
return node;
}
}
firstChild(el: Node): Node { return el.firstChild; }
nextSibling(el: Node): Node { return el.nextSibling; }
parentElement(el: Node): Node { return el.parentNode; }
childNodes(el: any): Node[] { return el.childNodes; }
childNodesAsList(el: Node): any[] {
const childNodes = el.childNodes;
const res = new Array(childNodes.length);
for (let i = 0; i < childNodes.length; i++) {
res[i] = childNodes[i];
}
return res;
}
clearNodes(el: Node) {
while (el.firstChild) {
el.removeChild(el.firstChild);
}
}
appendChild(el: Node, node: Node) { el.appendChild(node); }
removeChild(el: Node, node: Node) { el.removeChild(node); }
replaceChild(el: Node, newChild: Node, oldChild: Node) { el.replaceChild(newChild, oldChild); }
remove(node: Node): Node {
if (node.parentNode) {
node.parentNode.removeChild(node);
}
return node;
}
insertBefore(parent: Node, ref: Node, node: Node) { parent.insertBefore(node, ref); }
insertAllBefore(parent: Node, ref: Node, nodes: Node[]) {
nodes.forEach((n: any) => parent.insertBefore(n, ref));
}
insertAfter(parent: Node, ref: Node, node: any) { parent.insertBefore(node, ref.nextSibling); }
setInnerHTML(el: Element, value: string) { el.innerHTML = value; }
getText(el: Node): string { return el.textContent; }
setText(el: Node, value: string) { el.textContent = value; }
getValue(el: any): string { return el.value; }
setValue(el: any, value: string) { el.value = value; }
getChecked(el: any): boolean { return el.checked; }
setChecked(el: any, value: boolean) { el.checked = value; }
createComment(text: string): Comment { return document.createComment(text); }
createTemplate(html: any): HTMLElement {
const t = document.createElement('template');
t.innerHTML = html;
return t;
}
createElement(tagName: string, doc = document): HTMLElement { return doc.createElement(tagName); }
createElementNS(ns: string, tagName: string, doc = document): Element {
return doc.createElementNS(ns, tagName);
}
createTextNode(text: string, doc = document): Text { return doc.createTextNode(text); }
createScriptTag(attrName: string, attrValue: string, doc = document): HTMLScriptElement {
const el = <HTMLScriptElement>doc.createElement('SCRIPT');
el.setAttribute(attrName, attrValue);
return el;
}
createStyleElement(css: string, doc = document): HTMLStyleElement {
const style = <HTMLStyleElement>doc.createElement('style');
this.appendChild(style, this.createTextNode(css));
return style;
}
createShadowRoot(el: HTMLElement): DocumentFragment { return (<any>el).createShadowRoot(); }
getShadowRoot(el: HTMLElement): DocumentFragment { return (<any>el).shadowRoot; }
getHost(el: HTMLElement): HTMLElement { return (<any>el).host; }
clone(node: Node): Node { return node.cloneNode(true); }
getElementsByClassName(element: any, name: string): HTMLElement[] {
return element.getElementsByClassName(name);
}
getElementsByTagName(element: any, name: string): HTMLElement[] {
return element.getElementsByTagName(name);
}
classList(element: any): any[] { return Array.prototype.slice.call(element.classList, 0); }
addClass(element: any, className: string) { element.classList.add(className); }
removeClass(element: any, className: string) { element.classList.remove(className); }
hasClass(element: any, className: string): boolean {
return element.classList.contains(className);
}
setStyle(element: any, styleName: string, styleValue: string) {
element.style[styleName] = styleValue;
}
removeStyle(element: any, stylename: string) {
// IE requires '' instead of null
// see https://github.com/angular/angular/issues/7916
element.style[stylename] = '';
}
getStyle(element: any, stylename: string): string { return element.style[stylename]; }
hasStyle(element: any, styleName: string, styleValue: string = null): boolean {
const value = this.getStyle(element, styleName) || '';
return styleValue ? value == styleValue : value.length > 0;
}
tagName(element: any): string { return element.tagName; }
attributeMap(element: any): Map<string, string> {
const res = new Map<string, string>();
const elAttrs = element.attributes;
for (let i = 0; i < elAttrs.length; i++) {
const attrib = elAttrs[i];
res.set(attrib.name, attrib.value);
}
return res;
}
hasAttribute(element: Element, attribute: string): boolean {
return element.hasAttribute(attribute);
}
hasAttributeNS(element: Element, ns: string, attribute: string): boolean {
return element.hasAttributeNS(ns, attribute);
}
getAttribute(element: Element, attribute: string): string {
return element.getAttribute(attribute);
}
getAttributeNS(element: Element, ns: string, name: string): string {
return element.getAttributeNS(ns, name);
}
setAttribute(element: Element, name: string, value: string) { element.setAttribute(name, value); }
setAttributeNS(element: Element, ns: string, name: string, value: string) {
element.setAttributeNS(ns, name, value);
}
removeAttribute(element: Element, attribute: string) { element.removeAttribute(attribute); }
removeAttributeNS(element: Element, ns: string, name: string) {
element.removeAttributeNS(ns, name);
}
templateAwareRoot(el: Node): any { return this.isTemplateElement(el) ? this.content(el) : el; }
createHtmlDocument(): HTMLDocument {
return document.implementation.createHTMLDocument('fakeTitle');
}
getBoundingClientRect(el: Element): any {
try {
return el.getBoundingClientRect();
} catch (e) {
return {top: 0, bottom: 0, left: 0, right: 0, width: 0, height: 0};
}
}
getTitle(doc: Document): string { return document.title; }
setTitle(doc: Document, newTitle: string) { document.title = newTitle || ''; }
elementMatches(n: any, selector: string): boolean {
if (n instanceof HTMLElement) {
return n.matches && n.matches(selector) ||
n.msMatchesSelector && n.msMatchesSelector(selector) ||
n.webkitMatchesSelector && n.webkitMatchesSelector(selector);
}
return false;
}
isTemplateElement(el: Node): boolean {
return el instanceof HTMLElement && el.nodeName == 'TEMPLATE';
}
isTextNode(node: Node): boolean { return node.nodeType === Node.TEXT_NODE; }
isCommentNode(node: Node): boolean { return node.nodeType === Node.COMMENT_NODE; }
isElementNode(node: Node): boolean { return node.nodeType === Node.ELEMENT_NODE; }
hasShadowRoot(node: any): boolean {
return node.shadowRoot != null && node instanceof HTMLElement;
}
isShadowRoot(node: any): boolean { return node instanceof DocumentFragment; }
importIntoDoc(node: Node): any { return document.importNode(this.templateAwareRoot(node), true); }
adoptNode(node: Node): any { return document.adoptNode(node); }
getHref(el: Element): string { return (<any>el).href; }
getEventKey(event: any): string {
let key = event.key;
if (key == null) {
key = event.keyIdentifier;
// keyIdentifier is defined in the old draft of DOM Level 3 Events implemented by Chrome and
// Safari cf
// http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/events.html#Events-KeyboardEvents-Interfaces
if (key == null) {
return 'Unidentified';
}
if (key.startsWith('U+')) {
key = String.fromCharCode(parseInt(key.substring(2), 16));
if (event.location === DOM_KEY_LOCATION_NUMPAD && _chromeNumKeyPadMap.hasOwnProperty(key)) {
// There is a bug in Chrome for numeric keypad keys:
// https://code.google.com/p/chromium/issues/detail?id=155654
// 1, 2, 3 ... are reported as A, B, C ...
key = (_chromeNumKeyPadMap as any)[key];
}
}
}
return _keyMap[key] || key;
}
getGlobalEventTarget(doc: Document, target: string): EventTarget {
if (target === 'window') {
return window;
}
if (target === 'document') {
return document;
}
if (target === 'body') {
return document.body;
}
}
getHistory(): History { return window.history; }
getLocation(): Location { return window.location; }
getBaseHref(doc: Document): string {
const href = getBaseElementHref();
return href == null ? null : relativePath(href);
}
resetBaseElement(): void { baseElement = null; }
getUserAgent(): string { return window.navigator.userAgent; }
setData(element: Element, name: string, value: string) {
this.setAttribute(element, 'data-' + name, value);
}
getData(element: Element, name: string): string {
return this.getAttribute(element, 'data-' + name);
}
getComputedStyle(element: any): any { return getComputedStyle(element); }
// TODO(tbosch): move this into a separate environment class once we have it
setGlobalVar(path: string, value: any) { setValueOnPath(global, path, value); }
supportsWebAnimation(): boolean {
return typeof(<any>Element).prototype['animate'] === 'function';
}
performanceNow(): number {
// performance.now() is not available in all browsers, see
// http://caniuse.com/#search=performance.now
return window.performance && window.performance.now ? window.performance.now() :
new Date().getTime();
}
supportsCookies(): boolean { return true; }
getCookie(name: string): string { return parseCookieValue(document.cookie, name); }
setCookie(name: string, value: string) {
// document.cookie is magical, assigning into it assigns/overrides one cookie value, but does
// not clear other cookies.
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);
}
}
let baseElement: HTMLElement = null;
function getBaseElementHref(): string {
if (!baseElement) {
baseElement = document.querySelector('base');
if (!baseElement) {
return null;
}
}
return baseElement.getAttribute('href');
}
// based on urlUtils.js in AngularJS 1
let urlParsingNode: any;
function relativePath(url: any): string {
if (!urlParsingNode) {
urlParsingNode = document.createElement('a');
}
urlParsingNode.setAttribute('href', url);
return (urlParsingNode.pathname.charAt(0) === '/') ? urlParsingNode.pathname :
'/' + urlParsingNode.pathname;
}
export function parseCookieValue(cookieStr: string, name: string): string {
name = encodeURIComponent(name);
for (const cookie of cookieStr.split(';')) {
const eqIndex = cookie.indexOf('=');
const [cookieName, cookieValue]: string[] =
eqIndex == -1 ? [cookie, ''] : [cookie.slice(0, eqIndex), cookie.slice(eqIndex + 1)];
if (cookieName.trim() === name) {
return decodeURIComponent(cookieValue);
}
}
return null;
}
export function setValueOnPath(global: any, path: string, value: any) {
const parts = path.split('.');
let obj: any = global;
while (parts.length > 1) {
const name = parts.shift();
if (obj.hasOwnProperty(name) && obj[name] != null) {
obj = obj[name];
} else {
obj = obj[name] = {};
}
}
if (obj === undefined || obj === null) {
obj = {};
}
obj[parts.shift()] = value;
}

View File

@ -0,0 +1,70 @@
/**
* @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 {DomAdapter} from '../dom/dom_adapter';
/**
* Provides DOM operations in any browser environment.
*
* @security Tread carefully! Interacting with the DOM directly is dangerous and
* can introduce XSS risks.
*/
export abstract class GenericBrowserDomAdapter extends DomAdapter {
private _animationPrefix: string = null;
private _transitionEnd: string = null;
constructor() {
super();
try {
const element = this.createElement('div', document);
if (this.getStyle(element, 'animationName') != null) {
this._animationPrefix = '';
} else {
const domPrefixes = ['Webkit', 'Moz', 'O', 'ms'];
for (let i = 0; i < domPrefixes.length; i++) {
if (this.getStyle(element, domPrefixes[i] + 'AnimationName') != null) {
this._animationPrefix = '-' + domPrefixes[i].toLowerCase() + '-';
break;
}
}
}
const transEndEventNames: {[key: string]: string} = {
WebkitTransition: 'webkitTransitionEnd',
MozTransition: 'transitionend',
OTransition: 'oTransitionEnd otransitionend',
transition: 'transitionend'
};
Object.keys(transEndEventNames).forEach((key: string) => {
if (this.getStyle(element, key) != null) {
this._transitionEnd = transEndEventNames[key];
}
});
} catch (e) {
this._animationPrefix = null;
this._transitionEnd = null;
}
}
getDistributedNodes(el: HTMLElement): Node[] { return (<any>el).getDistributedNodes(); }
resolveAndSetHref(el: HTMLAnchorElement, baseUrl: string, href: string) {
el.href = href == null ? baseUrl : baseUrl + '/../' + href;
}
supportsDOMEvents(): boolean { return true; }
supportsNativeShadowDOM(): boolean {
return typeof(<any>document.body).createShadowRoot === 'function';
}
getAnimationPrefix(): string { return this._animationPrefix ? this._animationPrefix : ''; }
getTransitionEnd(): string { return this._transitionEnd ? this._transitionEnd : ''; }
supportsAnimation(): boolean {
return this._animationPrefix != null && this._transitionEnd != null;
}
}

View File

@ -0,0 +1,77 @@
/**
* @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 {LocationChangeListener, PlatformLocation} from '@angular/common';
import {Inject, Injectable} from '@angular/core';
import {getDOM} from '../../dom/dom_adapter';
import {DOCUMENT} from '../../dom/dom_tokens';
import {supportsState} from './history';
/**
* `PlatformLocation` encapsulates all of the direct calls to platform APIs.
* This class should not be used directly by an application developer. Instead, use
* {@link Location}.
*/
@Injectable()
export class BrowserPlatformLocation extends PlatformLocation {
private _location: Location;
private _history: History;
constructor(@Inject(DOCUMENT) private _doc: any) {
super();
this._init();
}
// This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it
/** @internal */
_init() {
this._location = getDOM().getLocation();
this._history = getDOM().getHistory();
}
get location(): Location { return this._location; }
getBaseHrefFromDOM(): string { return getDOM().getBaseHref(this._doc); }
onPopState(fn: LocationChangeListener): void {
getDOM().getGlobalEventTarget(this._doc, 'window').addEventListener('popstate', fn, false);
}
onHashChange(fn: LocationChangeListener): void {
getDOM().getGlobalEventTarget(this._doc, 'window').addEventListener('hashchange', fn, false);
}
get pathname(): string { return this._location.pathname; }
get search(): string { return this._location.search; }
get hash(): string { return this._location.hash; }
set pathname(newPath: string) { this._location.pathname = newPath; }
pushState(state: any, title: string, url: string): void {
if (supportsState()) {
this._history.pushState(state, title, url);
} else {
this._location.hash = url;
}
}
replaceState(state: any, title: string, url: string): void {
if (supportsState()) {
this._history.replaceState(state, title, url);
} else {
this._location.hash = url;
}
}
forward(): void { this._history.forward(); }
back(): void { this._history.back(); }
}

View File

@ -0,0 +1,11 @@
/**
* @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
*/
export function supportsState(): boolean {
return !!window.history.pushState;
}

View File

@ -0,0 +1,116 @@
/**
* @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 {Inject, Injectable} from '@angular/core';
import {DomAdapter, getDOM} from '../dom/dom_adapter';
import {DOCUMENT} from '../dom/dom_tokens';
/**
* Represents a meta element.
*
* @experimental
*/
export type MetaDefinition = {
charset?: string; content?: string; httpEquiv?: string; id?: string; itemprop?: string;
name?: string;
property?: string;
scheme?: string;
url?: string;
} &
{
// TODO(IgorMinar): this type looks wrong
[prop: string]: string;
};
/**
* A service that can be used to get and add meta tags.
*
* @experimental
*/
@Injectable()
export class Meta {
private _dom: DomAdapter;
constructor(@Inject(DOCUMENT) private _doc: any) { this._dom = getDOM(); }
addTag(tag: MetaDefinition, forceCreation: boolean = false): HTMLMetaElement {
if (!tag) return null;
return this._getOrCreateElement(tag, forceCreation);
}
addTags(tags: MetaDefinition[], forceCreation: boolean = false): HTMLMetaElement[] {
if (!tags) return [];
return tags.reduce((result: HTMLMetaElement[], tag: MetaDefinition) => {
if (tag) {
result.push(this._getOrCreateElement(tag, forceCreation));
}
return result;
}, []);
}
getTag(attrSelector: string): HTMLMetaElement {
if (!attrSelector) return null;
return this._dom.querySelector(this._doc, `meta[${attrSelector}]`);
}
getTags(attrSelector: string): HTMLMetaElement[] {
if (!attrSelector) return [];
const list /*NodeList*/ = this._dom.querySelectorAll(this._doc, `meta[${attrSelector}]`);
return list ? [].slice.call(list) : [];
}
updateTag(tag: MetaDefinition, selector?: string): HTMLMetaElement {
if (!tag) return null;
selector = selector || this._parseSelector(tag);
const meta: HTMLMetaElement = this.getTag(selector);
if (meta) {
return this._setMetaElementAttributes(tag, meta);
}
return this._getOrCreateElement(tag, true);
}
removeTag(attrSelector: string): void { this.removeTagElement(this.getTag(attrSelector)); }
removeTagElement(meta: HTMLMetaElement): void {
if (meta) {
this._dom.remove(meta);
}
}
private _getOrCreateElement(meta: MetaDefinition, forceCreation: boolean = false):
HTMLMetaElement {
if (!forceCreation) {
const selector: string = this._parseSelector(meta);
const elem: HTMLMetaElement = this.getTag(selector);
// It's allowed to have multiple elements with the same name so it's not enough to
// just check that element with the same name already present on the page. We also need to
// check if element has tag attributes
if (elem && this._containsAttributes(meta, elem)) return elem;
}
const element: HTMLMetaElement = this._dom.createElement('meta') as HTMLMetaElement;
this._setMetaElementAttributes(meta, element);
const head = this._dom.getElementsByTagName(this._doc, 'head')[0];
this._dom.appendChild(head, element);
return element;
}
private _setMetaElementAttributes(tag: MetaDefinition, el: HTMLMetaElement): HTMLMetaElement {
Object.keys(tag).forEach((prop: string) => this._dom.setAttribute(el, prop, tag[prop]));
return el;
}
private _parseSelector(tag: MetaDefinition): string {
const attr: string = tag.name ? 'name' : 'property';
return `${attr}="${tag[attr]}"`;
}
private _containsAttributes(tag: MetaDefinition, elem: HTMLMetaElement): boolean {
return Object.keys(tag).every((key: string) => this._dom.getAttribute(elem, key) === tag[key]);
}
}

View File

@ -0,0 +1,38 @@
/**
* @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 {APP_INITIALIZER, Inject, InjectionToken, Provider} from '@angular/core';
import {getDOM} from '../dom/dom_adapter';
import {DOCUMENT} from '../dom/dom_tokens';
/**
* An id that identifies a particular application being bootstrapped, that should
* match across the client/server boundary.
*/
export const TRANSITION_ID = new InjectionToken('TRANSITION_ID');
export function bootstrapListenerFactory(transitionId: string, document: any) {
const factory = () => {
const dom = getDOM();
const styles: any[] =
Array.prototype.slice.apply(dom.querySelectorAll(document, `style[ng-transition]`));
styles.filter(el => dom.getAttribute(el, 'ng-transition') === transitionId)
.forEach(el => dom.remove(el));
};
return factory;
}
export const SERVER_TRANSITION_PROVIDERS: Provider[] = [
{
provide: APP_INITIALIZER,
useFactory: bootstrapListenerFactory,
deps: [TRANSITION_ID, DOCUMENT],
multi: true
},
];

View File

@ -0,0 +1,67 @@
/**
* @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 {GetTestability, Testability, TestabilityRegistry, setTestabilityGetter, ɵglobal as global} from '@angular/core';
import {getDOM} from '../dom/dom_adapter';
export class BrowserGetTestability implements GetTestability {
static init() { setTestabilityGetter(new BrowserGetTestability()); }
addToWindow(registry: TestabilityRegistry): void {
global['getAngularTestability'] = (elem: any, findInAncestors: boolean = true) => {
const testability = registry.findTestabilityInTree(elem, findInAncestors);
if (testability == null) {
throw new Error('Could not find testability for element.');
}
return testability;
};
global['getAllAngularTestabilities'] = () => registry.getAllTestabilities();
global['getAllAngularRootElements'] = () => registry.getAllRootElements();
const whenAllStable = (callback: any /** TODO #9100 */) => {
const testabilities = global['getAllAngularTestabilities']();
let count = testabilities.length;
let didWork = false;
const decrement = function(didWork_: any /** TODO #9100 */) {
didWork = didWork || didWork_;
count--;
if (count == 0) {
callback(didWork);
}
};
testabilities.forEach(function(testability: any /** TODO #9100 */) {
testability.whenStable(decrement);
});
};
if (!global['frameworkStabilizers']) {
global['frameworkStabilizers'] = [];
}
global['frameworkStabilizers'].push(whenAllStable);
}
findTestabilityInTree(registry: TestabilityRegistry, elem: any, findInAncestors: boolean):
Testability {
if (elem == null) {
return null;
}
const t = registry.getTestability(elem);
if (t != null) {
return t;
} else if (!findInAncestors) {
return null;
}
if (getDOM().isShadowRoot(elem)) {
return this.findTestabilityInTree(registry, getDOM().getHost(elem), true);
}
return this.findTestabilityInTree(registry, getDOM().parentElement(elem), true);
}
}

View File

@ -0,0 +1,39 @@
/**
* @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 {Inject, Injectable} from '@angular/core';
import {getDOM} from '../dom/dom_adapter';
import {DOCUMENT} from '../dom/dom_tokens';
/**
* A service that can be used to get and set the title of a current HTML document.
*
* Since an Angular application can't be bootstrapped on the entire HTML document (`<html>` tag)
* it is not possible to bind to the `text` property of the `HTMLTitleElement` elements
* (representing the `<title>` tag). Instead, this service can be used to set and get the current
* title value.
*
* @experimental
*/
@Injectable()
export class Title {
constructor(@Inject(DOCUMENT) private _doc: any) {}
/**
* Get the title of the current HTML document.
* @returns {string}
*/
getTitle(): string { return getDOM().getTitle(this._doc); }
/**
* Set the title of the current HTML document.
* @param newTitle
*/
setTitle(newTitle: string) { getDOM().setTitle(this._doc, newTitle); }
}

View File

@ -0,0 +1,10 @@
/**
* @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
*/
const win = typeof window !== 'undefined' && window || <any>{};
export {win as window};

View File

@ -0,0 +1,71 @@
/**
* @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 {ApplicationRef, ComponentRef} from '@angular/core';
import {getDOM} from '../../dom/dom_adapter';
import {window} from './browser';
export class ChangeDetectionPerfRecord {
constructor(public msPerTick: number, public numTicks: number) {}
}
/**
* Entry point for all Angular profiling-related debug tools. This object
* corresponds to the `ng.profiler` in the dev console.
*/
export class AngularProfiler {
appRef: ApplicationRef;
constructor(ref: ComponentRef<any>) { this.appRef = ref.injector.get(ApplicationRef); }
// tslint:disable:no-console
/**
* Exercises change detection in a loop and then prints the average amount of
* time in milliseconds how long a single round of change detection takes for
* the current state of the UI. It runs a minimum of 5 rounds for a minimum
* of 500 milliseconds.
*
* Optionally, a user may pass a `config` parameter containing a map of
* options. Supported options are:
*
* `record` (boolean) - causes the profiler to record a CPU profile while
* it exercises the change detector. Example:
*
* ```
* ng.profiler.timeChangeDetection({record: true})
* ```
*/
timeChangeDetection(config: any): ChangeDetectionPerfRecord {
const record = config && config['record'];
const profileName = 'Change Detection';
// Profiler is not available in Android browsers, nor in IE 9 without dev tools opened
const isProfilerAvailable = window.console.profile != null;
if (record && isProfilerAvailable) {
window.console.profile(profileName);
}
const start = getDOM().performanceNow();
let numTicks = 0;
while (numTicks < 5 || (getDOM().performanceNow() - start) < 500) {
this.appRef.tick();
numTicks++;
}
const end = getDOM().performanceNow();
if (record && isProfilerAvailable) {
// need to cast to <any> because type checker thinks there's no argument
// while in fact there is:
//
// https://developer.mozilla.org/en-US/docs/Web/API/Console/profileEnd
(<any>window.console.profileEnd)(profileName);
}
const msPerTick = (end - start) / numTicks;
window.console.log(`ran ${numTicks} change detection cycles`);
window.console.log(`${msPerTick.toFixed(2)} ms per check`);
return new ChangeDetectionPerfRecord(msPerTick, numTicks);
}
}

View File

@ -0,0 +1,41 @@
/**
* @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 {ComponentRef} from '@angular/core';
import {getDOM} from '../../dom/dom_adapter';
import {AngularProfiler} from './common_tools';
const PROFILER_GLOBAL_NAME = 'ng.profiler';
/**
* Enabled Angular debug tools that are accessible via your browser's
* developer console.
*
* Usage:
*
* 1. Open developer console (e.g. in Chrome Ctrl + Shift + j)
* 1. Type `ng.` (usually the console will show auto-complete suggestion)
* 1. Try the change detection profiler `ng.profiler.timeChangeDetection()`
* then hit Enter.
*
* @experimental All debugging apis are currently experimental.
*/
export function enableDebugTools<T>(ref: ComponentRef<T>): ComponentRef<T> {
getDOM().setGlobalVar(PROFILER_GLOBAL_NAME, new AngularProfiler(ref));
return ref;
}
/**
* Disables Angular tools.
*
* @experimental All debugging apis are currently experimental.
*/
export function disableDebugTools(): void {
getDOM().setGlobalVar(PROFILER_GLOBAL_NAME, null);
}