161 lines
4.3 KiB
TypeScript
Raw Normal View History

/**
* @license
* Copyright Google LLC 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 {ɵparseCookieValue as parseCookieValue, ɵsetRootDomAdapter as setRootDomAdapter} from '@angular/common';
import {ɵglobal as global} from '@angular/core';
import {GenericBrowserDomAdapter} from './generic_browser_adapter';
const nodeContains: (this: Node, other: Node) => boolean = (() => {
if (global['Node']) {
return global['Node'].prototype.contains || function(this: Node, node: any) {
return !!(this.compareDocumentPosition(node) & 16);
};
}
return undefined as any;
})();
/**
* 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 {
static makeCurrent() {
setRootDomAdapter(new BrowserDomAdapter());
}
getProperty(el: Node, name: string): any {
return (<any>el)[name];
}
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();
}
}
2016-09-27 17:30:26 -07:00
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);
}
2016-09-27 17:30:26 -07:00
remove(node: Node): Node {
if (node.parentNode) {
node.parentNode.removeChild(node);
}
feat(compiler): attach components and project light dom during compilation. Closes #2529 BREAKING CHANGES: - shadow dom emulation no longer supports the `<content>` tag. Use the new `<ng-content>` instead (works with all shadow dom strategies). - removed `DomRenderer.setViewRootNodes` and `AppViewManager.getComponentView` -> use `DomRenderer.getNativeElementSync(elementRef)` and change shadow dom directly - the `Renderer` interface has changed: * `createView` now also has to support sub views * the notion of a container has been removed. Instead, the renderer has to implement methods to attach views next to elements or other views. * a RenderView now contains multiple RenderFragments. Fragments are used to move DOM nodes around. Internal changes / design changes: - Introduce notion of view fragments on render side - DomProtoViews and DomViews on render side are merged, AppProtoViews are not merged, AppViews are partially merged (they share arrays with the other merged AppViews but we keep individual AppView instances for now). - DomProtoViews always have a `<template>` element as root * needed for storing subviews * we have less chunks of DOM to clone now - remove fake ElementBinder / Bound element for root text bindings and model them explicitly. This removes a lot of special cases we had! - AppView shares data with nested component views - some methods in AppViewManager (create, hydrate, dehydrate) are iterative now * now possible as we have all child AppViews / ElementRefs already in an array!
2015-06-24 13:46:39 -07:00
return node;
}
getValue(el: any): string {
return el.value;
}
createElement(tagName: string, doc?: Document): HTMLElement {
doc = doc || this.getDefaultDocument();
return doc.createElement(tagName);
}
createHtmlDocument(): HTMLDocument {
return document.implementation.createHTMLDocument('fakeTitle');
}
getDefaultDocument(): Document {
return document;
}
isElementNode(node: Node): boolean {
return node.nodeType === Node.ELEMENT_NODE;
}
isShadowRoot(node: any): boolean {
return node instanceof DocumentFragment;
}
getGlobalEventTarget(doc: Document, target: string): EventTarget|null {
2016-09-27 17:30:26 -07:00
if (target === 'window') {
return window;
2016-09-27 17:30:26 -07:00
}
if (target === 'document') {
return doc;
2016-09-27 17:30:26 -07:00
}
if (target === 'body') {
return doc.body;
}
return null;
}
getHistory(): History {
return window.history;
}
getLocation(): Location {
return window.location;
}
getBaseHref(doc: Document): string|null {
2016-09-27 17:30:26 -07:00
const href = getBaseElementHref();
2017-03-02 09:37:01 -08:00
return href == null ? null : relativePath(href);
}
resetBaseElement(): void {
baseElement = null;
}
getUserAgent(): string {
return window.navigator.userAgent;
}
performanceNow(): number {
// performance.now() is not available in all browsers, see
// http://caniuse.com/#search=performance.now
2016-09-27 17:30:26 -07:00
return window.performance && window.performance.now ? window.performance.now() :
new Date().getTime();
}
supportsCookies(): boolean {
return true;
}
getCookie(name: string): string|null {
return parseCookieValue(document.cookie, name);
}
}
let baseElement: HTMLElement|null = null;
function getBaseElementHref(): string|null {
2016-09-27 17:30:26 -07:00
if (!baseElement) {
baseElement = document.querySelector('base')!;
2016-09-27 17:30:26 -07:00
if (!baseElement) {
return null;
}
}
return baseElement.getAttribute('href');
}
// based on urlUtils.js in AngularJS 1
2016-09-27 17:30:26 -07:00
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;
}