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,127 @@
/**
* @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 xhr2: any = require('xhr2');
import {Injectable, Provider} from '@angular/core';
import {BrowserXhr, Connection, ConnectionBackend, Http, ReadyState, Request, RequestOptions, Response, XHRBackend, XSRFStrategy} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import {Subscription} from 'rxjs/Subscription';
@Injectable()
export class ServerXhr implements BrowserXhr {
build(): XMLHttpRequest { return new xhr2.XMLHttpRequest(); }
}
@Injectable()
export class ServerXsrfStrategy implements XSRFStrategy {
configureRequest(req: Request): void {}
}
export class ZoneMacroTaskConnection implements Connection {
response: Observable<Response>;
lastConnection: Connection;
constructor(public request: Request, backend: XHRBackend) {
this.response = new Observable((observer: Observer<Response>) => {
let task: Task = null;
let scheduled: boolean = false;
let sub: Subscription = null;
let savedResult: any = null;
let savedError: any = null;
const scheduleTask = (_task: Task) => {
task = _task;
scheduled = true;
this.lastConnection = backend.createConnection(request);
sub = (this.lastConnection.response as Observable<Response>)
.subscribe(
res => savedResult = res,
err => {
if (!scheduled) {
throw new Error('invoke twice');
}
savedError = err;
scheduled = false;
task.invoke();
},
() => {
if (!scheduled) {
throw new Error('invoke twice');
}
scheduled = false;
task.invoke();
});
};
const cancelTask = (_task: Task) => {
if (!scheduled) {
return;
}
scheduled = false;
if (sub) {
sub.unsubscribe();
sub = null;
}
};
const onComplete = () => {
if (savedError !== null) {
observer.error(savedError);
} else {
observer.next(savedResult);
observer.complete();
}
};
// MockBackend is currently synchronous, which means that if scheduleTask is by
// scheduleMacroTask, the request will hit MockBackend and the response will be
// sent, causing task.invoke() to be called.
const _task = Zone.current.scheduleMacroTask(
'ZoneMacroTaskConnection.subscribe', onComplete, {}, () => null, cancelTask);
scheduleTask(_task);
return () => {
if (scheduled && task) {
task.zone.cancelTask(task);
scheduled = false;
}
if (sub) {
sub.unsubscribe();
sub = null;
}
};
});
}
get readyState(): ReadyState {
return !!this.lastConnection ? this.lastConnection.readyState : ReadyState.Unsent;
}
}
export class ZoneMacroTaskBackend implements ConnectionBackend {
constructor(private backend: XHRBackend) {}
createConnection(request: any): ZoneMacroTaskConnection {
return new ZoneMacroTaskConnection(request, this.backend);
}
}
export function httpFactory(xhrBackend: XHRBackend, options: RequestOptions) {
const macroBackend = new ZoneMacroTaskBackend(xhrBackend);
return new Http(macroBackend, options);
}
export const SERVER_HTTP_PROVIDERS: Provider[] = [
{provide: Http, useFactory: httpFactory, deps: [XHRBackend, RequestOptions]},
{provide: BrowserXhr, useClass: ServerXhr},
{provide: XSRFStrategy, useClass: ServerXsrfStrategy},
];

View File

@ -0,0 +1,93 @@
/**
* @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 {LocationChangeEvent, LocationChangeListener, PlatformLocation} from '@angular/common';
import {Inject, Injectable, Optional} from '@angular/core';
import {DOCUMENT, ɵgetDOM as getDOM} from '@angular/platform-browser';
import {Subject} from 'rxjs/Subject';
import * as url from 'url';
import {INITIAL_CONFIG, PlatformConfig} from './tokens';
function parseUrl(urlStr: string): {pathname: string, search: string, hash: string} {
const parsedUrl = url.parse(urlStr);
return {
pathname: parsedUrl.pathname || '',
search: parsedUrl.search || '',
hash: parsedUrl.hash || '',
};
}
/**
* Server-side implementation of URL state. Implements `pathname`, `search`, and `hash`
* but not the state stack.
*/
@Injectable()
export class ServerPlatformLocation implements PlatformLocation {
private _path: string = '/';
private _search: string = '';
private _hash: string = '';
private _hashUpdate = new Subject<LocationChangeEvent>();
constructor(
@Inject(DOCUMENT) private _doc: any, @Optional() @Inject(INITIAL_CONFIG) _config: any) {
const config = _config as PlatformConfig | null;
if (!!config && !!config.url) {
const parsedUrl = parseUrl(config.url);
this._path = parsedUrl.pathname;
this._search = parsedUrl.search;
this._hash = parsedUrl.hash;
}
}
getBaseHrefFromDOM(): string { return getDOM().getBaseHref(this._doc); }
onPopState(fn: LocationChangeListener): void {
// No-op: a state stack is not implemented, so
// no events will ever come.
}
onHashChange(fn: LocationChangeListener): void { this._hashUpdate.subscribe(fn); }
get pathname(): string { return this._path; }
get search(): string { return this._search; }
get hash(): string { return this._hash; }
get url(): string { return `${this.pathname}${this.search}${this.hash}`; }
private setHash(value: string, oldUrl: string) {
if (this._hash === value) {
// Don't fire events if the hash has not changed.
return;
}
this._hash = value;
const newUrl = this.url;
scheduleMicroTask(
() => this._hashUpdate.next({ type: 'hashchange', oldUrl, newUrl } as LocationChangeEvent));
}
replaceState(state: any, title: string, newUrl: string): void {
const oldUrl = this.url;
const parsedUrl = parseUrl(newUrl);
this._path = parsedUrl.pathname;
this._search = parsedUrl.search;
this.setHash(parsedUrl.hash, oldUrl);
}
pushState(state: any, title: string, newUrl: string): void {
this.replaceState(state, title, newUrl);
}
forward(): void { throw new Error('Not implemented'); }
back(): void { throw new Error('Not implemented'); }
}
export function scheduleMicroTask(fn: Function) {
Zone.current.scheduleMicroTask('scheduleMicrotask', fn);
}

View File

@ -0,0 +1,787 @@
/**
* @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 parse5 = require('parse5');
import {ɵglobal as global} from '@angular/core';
import {ɵDomAdapter as DomAdapter, ɵsetRootDomAdapter as setRootDomAdapter, ɵsetValueOnPath as setValueOnPath} from '@angular/platform-browser';
import {SelectorMatcher, CssSelector} from '@angular/compiler';
let treeAdapter: any;
const _attrToPropMap: {[key: string]: string} = {
'class': 'className',
'innerHtml': 'innerHTML',
'readonly': 'readOnly',
'tabindex': 'tabIndex',
};
const mapProps = ['attribs', 'x-attribsNamespace', 'x-attribsPrefix'];
function _notImplemented(methodName: string) {
return new Error('This method is not implemented in Parse5DomAdapter: ' + methodName);
}
/**
* Parses a document string to a Document object.
*/
export function parseDocument(html: string) {
return parse5.parse(html, {treeAdapter: parse5.treeAdapters.htmlparser2});
}
/* tslint:disable:requireParameterType */
/**
* A `DomAdapter` powered by the `parse5` NodeJS module.
*
* @security Tread carefully! Interacting with the DOM directly is dangerous and
* can introduce XSS risks.
*/
export class Parse5DomAdapter extends DomAdapter {
static makeCurrent() {
treeAdapter = parse5.treeAdapters.htmlparser2;
setRootDomAdapter(new Parse5DomAdapter());
}
hasProperty(element: any, name: string): boolean {
return _HTMLElementPropertyList.indexOf(name) > -1;
}
// TODO(tbosch): don't even call this method when we run the tests on server side
// by not using the DomRenderer in tests. Keeping this for now to make tests happy...
setProperty(el: any, name: string, value: any) {
if (name === 'innerHTML') {
this.setInnerHTML(el, value);
} else if (name === 'className') {
el.attribs['class'] = el.className = value;
} else {
el[name] = value;
}
}
// TODO(tbosch): don't even call this method when we run the tests on server side
// by not using the DomRenderer in tests. Keeping this for now to make tests happy...
getProperty(el: any, name: string): any { return el[name]; }
logError(error: string) { console.error(error); }
// tslint:disable-next-line:no-console
log(error: string) { console.log(error); }
logGroup(error: string) { console.error(error); }
logGroupEnd() {}
get attrToPropMap() { return _attrToPropMap; }
querySelector(el: any, selector: string): any {
return this.querySelectorAll(el, selector)[0] || null;
}
querySelectorAll(el: any, selector: string): any[] {
const res: any[] = [];
const _recursive = (result: any, node: any, selector: any, matcher: any) => {
const cNodes = node.childNodes;
if (cNodes && cNodes.length > 0) {
for (let i = 0; i < cNodes.length; i++) {
const childNode = cNodes[i];
if (this.elementMatches(childNode, selector, matcher)) {
result.push(childNode);
}
_recursive(result, childNode, selector, matcher);
}
}
};
const matcher = new SelectorMatcher();
matcher.addSelectables(CssSelector.parse(selector));
_recursive(res, el, selector, matcher);
return res;
}
elementMatches(node: any, selector: string, matcher: any = null): boolean {
if (this.isElementNode(node) && selector === '*') {
return true;
}
let result = false;
if (selector && selector.charAt(0) == '#') {
result = this.getAttribute(node, 'id') == selector.substring(1);
} else if (selector) {
if (!matcher) {
matcher = new SelectorMatcher();
matcher.addSelectables(CssSelector.parse(selector));
}
const cssSelector = new CssSelector();
cssSelector.setElement(this.tagName(node));
if (node.attribs) {
for (const attrName in node.attribs) {
cssSelector.addAttribute(attrName, node.attribs[attrName]);
}
}
const classList = this.classList(node);
for (let i = 0; i < classList.length; i++) {
cssSelector.addClassName(classList[i]);
}
matcher.match(cssSelector, function(selector: any, cb: any) { result = true; });
}
return result;
}
on(el: any, evt: any, listener: any) {
let listenersMap: {[k: string]: any} = el._eventListenersMap;
if (!listenersMap) {
listenersMap = {};
el._eventListenersMap = listenersMap;
}
const listeners = listenersMap[evt] || [];
listenersMap[evt] = [...listeners, listener];
}
onAndCancel(el: any, evt: any, listener: any): Function {
this.on(el, evt, listener);
return () => { remove(<any[]>(el._eventListenersMap[evt]), listener); };
}
dispatchEvent(el: any, evt: any) {
if (!evt.target) {
evt.target = el;
}
if (el._eventListenersMap) {
const listeners: any = el._eventListenersMap[evt.type];
if (listeners) {
for (let i = 0; i < listeners.length; i++) {
listeners[i](evt);
}
}
}
if (el.parent) {
this.dispatchEvent(el.parent, evt);
}
if (el._window) {
this.dispatchEvent(el._window, evt);
}
}
createMouseEvent(eventType: any): Event { return this.createEvent(eventType); }
createEvent(eventType: string): Event {
const event = <Event>{
type: eventType,
defaultPrevented: false,
preventDefault: () => { (<any>event).defaultPrevented = true; }
};
return event;
}
preventDefault(event: any) { event.returnValue = false; }
isPrevented(event: any): boolean { return event.returnValue != null && !event.returnValue; }
getInnerHTML(el: any): string {
return parse5.serialize(this.templateAwareRoot(el), {treeAdapter});
}
getTemplateContent(el: any): Node { return null; }
getOuterHTML(el: any): string {
const fragment = treeAdapter.createDocumentFragment();
this.appendChild(fragment, el);
return parse5.serialize(fragment, {treeAdapter});
}
nodeName(node: any): string { return node.tagName; }
nodeValue(node: any): string { return node.nodeValue; }
type(node: any): string { throw _notImplemented('type'); }
content(node: any): string { return node.childNodes[0]; }
firstChild(el: any): Node { return el.firstChild; }
nextSibling(el: any): Node { return el.nextSibling; }
parentElement(el: any): Node { return el.parent; }
childNodes(el: any): Node[] { return el.childNodes; }
childNodesAsList(el: any): 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: any) {
while (el.childNodes.length > 0) {
this.remove(el.childNodes[0]);
}
}
appendChild(el: any, node: any) {
this.remove(node);
treeAdapter.appendChild(this.templateAwareRoot(el), node);
}
removeChild(el: any, node: any) {
if (el.childNodes.indexOf(node) > -1) {
this.remove(node);
}
}
remove(el: any): HTMLElement {
const parent = el.parent;
if (parent) {
const index = parent.childNodes.indexOf(el);
parent.childNodes.splice(index, 1);
}
const prev = el.previousSibling;
const next = el.nextSibling;
if (prev) {
prev.next = next;
}
if (next) {
next.prev = prev;
}
el.prev = null;
el.next = null;
el.parent = null;
return el;
}
insertBefore(parent: any, ref: any, newNode: any) {
this.remove(newNode);
if (ref) {
treeAdapter.insertBefore(parent, newNode, ref);
} else {
this.appendChild(parent, newNode);
}
}
insertAllBefore(parent: any, ref: any, nodes: any) {
nodes.forEach((n: any) => this.insertBefore(parent, ref, n));
}
insertAfter(parent: any, ref: any, node: any) {
if (ref.nextSibling) {
this.insertBefore(parent, ref.nextSibling, node);
} else {
this.appendChild(parent, node);
}
}
setInnerHTML(el: any, value: any) {
this.clearNodes(el);
const content = parse5.parseFragment(value, {treeAdapter});
for (let i = 0; i < content.childNodes.length; i++) {
treeAdapter.appendChild(el, content.childNodes[i]);
}
}
getText(el: any, isRecursive?: boolean): string {
if (this.isTextNode(el)) {
return el.data;
}
if (this.isCommentNode(el)) {
// In the DOM, comments within an element return an empty string for textContent
// However, comment node instances return the comment content for textContent getter
return isRecursive ? '' : el.data;
}
if (!el.childNodes || el.childNodes.length == 0) {
return '';
}
let textContent = '';
for (let i = 0; i < el.childNodes.length; i++) {
textContent += this.getText(el.childNodes[i], true);
}
return textContent;
}
setText(el: any, value: string) {
if (this.isTextNode(el) || this.isCommentNode(el)) {
el.data = value;
} else {
this.clearNodes(el);
if (value !== '') treeAdapter.insertText(el, 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 treeAdapter.createCommentNode(text); }
createTemplate(html: any): HTMLElement {
const template = treeAdapter.createElement('template', 'http://www.w3.org/1999/xhtml', []);
const content = parse5.parseFragment(html, {treeAdapter});
treeAdapter.setTemplateContent(template, content);
return template;
}
createElement(tagName: any): HTMLElement {
return treeAdapter.createElement(tagName, 'http://www.w3.org/1999/xhtml', []);
}
createElementNS(ns: any, tagName: any): HTMLElement {
return treeAdapter.createElement(tagName, ns, []);
}
createTextNode(text: string): Text {
const t = <any>this.createComment(text);
t.type = 'text';
return t;
}
createScriptTag(attrName: string, attrValue: string): HTMLElement {
return treeAdapter.createElement(
'script', 'http://www.w3.org/1999/xhtml', [{name: attrName, value: attrValue}]);
}
createStyleElement(css: string): HTMLStyleElement {
const style = this.createElement('style');
this.setText(style, css);
return <HTMLStyleElement>style;
}
createShadowRoot(el: any): HTMLElement {
el.shadowRoot = treeAdapter.createDocumentFragment();
el.shadowRoot.parent = el;
return el.shadowRoot;
}
getShadowRoot(el: any): Element { return el.shadowRoot; }
getHost(el: any): string { return el.host; }
getDistributedNodes(el: any): Node[] { throw _notImplemented('getDistributedNodes'); }
clone(node: Node): Node {
const _recursive = (node: any) => {
const nodeClone = Object.create(Object.getPrototypeOf(node));
for (const prop in node) {
const desc = Object.getOwnPropertyDescriptor(node, prop);
if (desc && 'value' in desc && typeof desc.value !== 'object') {
nodeClone[prop] = node[prop];
}
}
nodeClone.parent = null;
nodeClone.prev = null;
nodeClone.next = null;
nodeClone.children = null;
mapProps.forEach(mapName => {
if (node[mapName] != null) {
nodeClone[mapName] = {};
for (const prop in node[mapName]) {
nodeClone[mapName][prop] = node[mapName][prop];
}
}
});
const cNodes = node.children;
if (cNodes) {
const cNodesClone = new Array(cNodes.length);
for (let i = 0; i < cNodes.length; i++) {
const childNode = cNodes[i];
const childNodeClone = _recursive(childNode);
cNodesClone[i] = childNodeClone;
if (i > 0) {
childNodeClone.prev = cNodesClone[i - 1];
cNodesClone[i - 1].next = childNodeClone;
}
childNodeClone.parent = nodeClone;
}
nodeClone.children = cNodesClone;
}
return nodeClone;
};
return _recursive(node);
}
getElementsByClassName(element: any, name: string): HTMLElement[] {
return this.querySelectorAll(element, '.' + name);
}
getElementsByTagName(element: any, name: string): HTMLElement[] {
return this.querySelectorAll(element, name);
}
classList(element: any): string[] {
let classAttrValue: any = null;
const attributes = element.attribs;
if (attributes && attributes['class'] != null) {
classAttrValue = attributes['class'];
}
return classAttrValue ? classAttrValue.trim().split(/\s+/g) : [];
}
addClass(element: any, className: string) {
const classList = this.classList(element);
const index = classList.indexOf(className);
if (index == -1) {
classList.push(className);
element.attribs['class'] = element.className = classList.join(' ');
}
}
removeClass(element: any, className: string) {
const classList = this.classList(element);
const index = classList.indexOf(className);
if (index > -1) {
classList.splice(index, 1);
element.attribs['class'] = element.className = classList.join(' ');
}
}
hasClass(element: any, className: string): boolean {
return this.classList(element).indexOf(className) > -1;
}
hasStyle(element: any, styleName: string, styleValue: string = null): boolean {
const value = this.getStyle(element, styleName) || '';
return styleValue ? value == styleValue : value.length > 0;
}
/** @internal */
_readStyleAttribute(element: any) {
const styleMap = {};
const attributes = element.attribs;
if (attributes && attributes['style'] != null) {
const styleAttrValue = attributes['style'];
const styleList = styleAttrValue.split(/;+/g);
for (let i = 0; i < styleList.length; i++) {
if (styleList[i].length > 0) {
const elems = styleList[i].split(/:+/g);
(styleMap as any)[elems[0].trim()] = elems[1].trim();
}
}
}
return styleMap;
}
/** @internal */
_writeStyleAttribute(element: any, styleMap: any) {
let styleAttrValue = '';
for (const key in styleMap) {
const newValue = styleMap[key];
if (newValue) {
styleAttrValue += key + ':' + styleMap[key] + ';';
}
}
element.attribs['style'] = styleAttrValue;
}
setStyle(element: any, styleName: string, styleValue: string) {
const styleMap = this._readStyleAttribute(element);
(styleMap as any)[styleName] = styleValue;
this._writeStyleAttribute(element, styleMap);
}
removeStyle(element: any, styleName: string) { this.setStyle(element, styleName, null); }
getStyle(element: any, styleName: string): string {
const styleMap = this._readStyleAttribute(element);
return styleMap.hasOwnProperty(styleName) ? (styleMap as any)[styleName] : '';
}
tagName(element: any): string { return element.tagName == 'style' ? 'STYLE' : element.tagName; }
attributeMap(element: any): Map<string, string> {
const res = new Map<string, string>();
const elAttrs = treeAdapter.getAttrList(element);
for (let i = 0; i < elAttrs.length; i++) {
const attrib = elAttrs[i];
res.set(attrib.name, attrib.value);
}
return res;
}
hasAttribute(element: any, attribute: string): boolean {
return element.attribs && element.attribs[attribute] != null;
}
hasAttributeNS(element: any, ns: string, attribute: string): boolean { throw 'not implemented'; }
getAttribute(element: any, attribute: string): string {
return this.hasAttribute(element, attribute) ? element.attribs[attribute] : null;
}
getAttributeNS(element: any, ns: string, attribute: string): string { throw 'not implemented'; }
setAttribute(element: any, attribute: string, value: string) {
if (attribute) {
element.attribs[attribute] = value;
if (attribute === 'class') {
element.className = value;
}
}
}
setAttributeNS(element: any, ns: string, attribute: string, value: string) {
throw 'not implemented';
}
removeAttribute(element: any, attribute: string) {
if (attribute) {
delete element.attribs[attribute];
}
}
removeAttributeNS(element: any, ns: string, name: string) { throw 'not implemented'; }
templateAwareRoot(el: any): any {
return this.isTemplateElement(el) ? treeAdapter.getTemplateContent(el) : el;
}
createHtmlDocument(): Document {
const newDoc = treeAdapter.createDocument();
newDoc.title = 'fakeTitle';
const head = treeAdapter.createElement('head', null, []);
const body = treeAdapter.createElement('body', 'http://www.w3.org/1999/xhtml', []);
this.appendChild(newDoc, head);
this.appendChild(newDoc, body);
newDoc['head'] = head;
newDoc['body'] = body;
newDoc['_window'] = {};
return newDoc;
}
getBoundingClientRect(el: any): any { return {left: 0, top: 0, width: 0, height: 0}; }
getTitle(doc: Document): string { return doc.title || ''; }
setTitle(doc: Document, newTitle: string) { doc.title = newTitle; }
isTemplateElement(el: any): boolean {
return this.isElementNode(el) && this.tagName(el) === 'template';
}
isTextNode(node: any): boolean { return treeAdapter.isTextNode(node); }
isCommentNode(node: any): boolean { return treeAdapter.isCommentNode(node); }
isElementNode(node: any): boolean { return node ? treeAdapter.isElementNode(node) : false; }
hasShadowRoot(node: any): boolean { return node.shadowRoot != null; }
isShadowRoot(node: any): boolean { return this.getShadowRoot(node) == node; }
importIntoDoc(node: any): any { return this.clone(node); }
adoptNode(node: any): any { return node; }
getHref(el: any): string { return el.href; }
resolveAndSetHref(el: any, baseUrl: string, href: string) {
if (href == null) {
el.href = baseUrl;
} else {
el.href = baseUrl + '/../' + href;
}
}
/** @internal */
_buildRules(parsedRules: any, css?: any) {
const rules: any[] = [];
for (let i = 0; i < parsedRules.length; i++) {
const parsedRule = parsedRules[i];
const rule: {[key: string]: any} = {};
rule['cssText'] = css;
rule['style'] = {content: '', cssText: ''};
if (parsedRule.type == 'rule') {
rule['type'] = 1;
rule['selectorText'] =
parsedRule.selectors.join(', '.replace(/\s{2,}/g, ' ')
.replace(/\s*~\s*/g, ' ~ ')
.replace(/\s*\+\s*/g, ' + ')
.replace(/\s*>\s*/g, ' > ')
.replace(/\[(\w+)=(\w+)\]/g, '[$1="$2"]'));
if (parsedRule.declarations == null) {
continue;
}
for (let j = 0; j < parsedRule.declarations.length; j++) {
const declaration = parsedRule.declarations[j];
rule['style'] = declaration.property[declaration.value];
rule['style'].cssText += declaration.property + ': ' + declaration.value + ';';
}
} else if (parsedRule.type == 'media') {
rule['type'] = 4;
rule['media'] = {mediaText: parsedRule.media};
if (parsedRule.rules) {
rule['cssRules'] = this._buildRules(parsedRule.rules);
}
}
rules.push(rule);
}
return rules;
}
supportsDOMEvents(): boolean { return false; }
supportsNativeShadowDOM(): boolean { return false; }
getGlobalEventTarget(doc: Document, target: string): any {
if (target == 'window') {
return (<any>doc)._window;
} else if (target == 'document') {
return doc;
} else if (target == 'body') {
return doc.body;
}
}
getBaseHref(doc: Document): string {
const base = this.querySelector(doc, 'base');
let href = '';
if (base) {
href = this.getHref(base);
}
// TODO(alxhub): Need relative path logic from BrowserDomAdapter here?
return href == null ? null : href;
}
resetBaseElement(): void { throw 'not implemented'; }
getHistory(): History { throw 'not implemented'; }
getLocation(): Location { throw 'not implemented'; }
getUserAgent(): string { return 'Fake user agent'; }
getData(el: any, name: string): string { return this.getAttribute(el, 'data-' + name); }
getComputedStyle(el: any): any { throw 'not implemented'; }
setData(el: any, name: string, value: string) { this.setAttribute(el, 'data-' + name, value); }
// 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 false; }
performanceNow(): number { return Date.now(); }
getAnimationPrefix(): string { return ''; }
getTransitionEnd(): string { return 'transitionend'; }
supportsAnimation(): boolean { return true; }
replaceChild(el: any, newNode: any, oldNode: any) { throw new Error('not implemented'); }
parse(templateHtml: string) { throw new Error('not implemented'); }
invoke(el: Element, methodName: string, args: any[]): any { throw new Error('not implemented'); }
getEventKey(event: any): string { throw new Error('not implemented'); }
supportsCookies(): boolean { return false; }
getCookie(name: string): string { throw new Error('not implemented'); }
setCookie(name: string, value: string) { throw new Error('not implemented'); }
animate(element: any, keyframes: any[], options: any): any { throw new Error('not implemented'); }
}
// TODO: build a proper list, this one is all the keys of a HTMLInputElement
const _HTMLElementPropertyList = [
'webkitEntries',
'incremental',
'webkitdirectory',
'selectionDirection',
'selectionEnd',
'selectionStart',
'labels',
'validationMessage',
'validity',
'willValidate',
'width',
'valueAsNumber',
'valueAsDate',
'value',
'useMap',
'defaultValue',
'type',
'step',
'src',
'size',
'required',
'readOnly',
'placeholder',
'pattern',
'name',
'multiple',
'min',
'minLength',
'maxLength',
'max',
'list',
'indeterminate',
'height',
'formTarget',
'formNoValidate',
'formMethod',
'formEnctype',
'formAction',
'files',
'form',
'disabled',
'dirName',
'checked',
'defaultChecked',
'autofocus',
'autocomplete',
'alt',
'align',
'accept',
'onautocompleteerror',
'onautocomplete',
'onwaiting',
'onvolumechange',
'ontoggle',
'ontimeupdate',
'onsuspend',
'onsubmit',
'onstalled',
'onshow',
'onselect',
'onseeking',
'onseeked',
'onscroll',
'onresize',
'onreset',
'onratechange',
'onprogress',
'onplaying',
'onplay',
'onpause',
'onmousewheel',
'onmouseup',
'onmouseover',
'onmouseout',
'onmousemove',
'onmouseleave',
'onmouseenter',
'onmousedown',
'onloadstart',
'onloadedmetadata',
'onloadeddata',
'onload',
'onkeyup',
'onkeypress',
'onkeydown',
'oninvalid',
'oninput',
'onfocus',
'onerror',
'onended',
'onemptied',
'ondurationchange',
'ondrop',
'ondragstart',
'ondragover',
'ondragleave',
'ondragenter',
'ondragend',
'ondrag',
'ondblclick',
'oncuechange',
'oncontextmenu',
'onclose',
'onclick',
'onchange',
'oncanplaythrough',
'oncanplay',
'oncancel',
'onblur',
'onabort',
'spellcheck',
'isContentEditable',
'contentEditable',
'outerText',
'innerText',
'accessKey',
'hidden',
'webkitdropzone',
'draggable',
'tabIndex',
'dir',
'translate',
'lang',
'title',
'childElementCount',
'lastElementChild',
'firstElementChild',
'children',
'onwebkitfullscreenerror',
'onwebkitfullscreenchange',
'nextElementSibling',
'previousElementSibling',
'onwheel',
'onselectstart',
'onsearch',
'onpaste',
'oncut',
'oncopy',
'onbeforepaste',
'onbeforecut',
'onbeforecopy',
'shadowRoot',
'dataset',
'classList',
'className',
'outerHTML',
'innerHTML',
'scrollHeight',
'scrollWidth',
'scrollTop',
'scrollLeft',
'clientHeight',
'clientWidth',
'clientTop',
'clientLeft',
'offsetParent',
'offsetHeight',
'offsetWidth',
'offsetTop',
'offsetLeft',
'localName',
'prefix',
'namespaceURI',
'id',
'style',
'attributes',
'tagName',
'parentElement',
'textContent',
'baseURI',
'ownerDocument',
'nextSibling',
'previousSibling',
'lastChild',
'firstChild',
'childNodes',
'parentNode',
'nodeType',
'nodeValue',
'nodeName',
'closure_lm_714617',
'__jsaction',
];
function remove<T>(list: T[], el: T): void {
const index = list.indexOf(el);
if (index > -1) {
list.splice(index, 1);
}
}

View File

@ -0,0 +1,15 @@
/**
* @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 {PlatformState} from './platform_state';
export {ServerModule, platformDynamicServer, platformServer} from './server';
export {INITIAL_CONFIG, PlatformConfig} from './tokens';
export {renderModule, renderModuleFactory} from './utils';
export * from './private_export';
export {VERSION} from './version';

View File

@ -0,0 +1,32 @@
/**
* @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 parse5 = require('parse5');
import {Injectable, Inject} from '@angular/core';
import {DOCUMENT, ɵgetDOM as getDOM} from '@angular/platform-browser';
/**
* Representation of the current platform state.
*
* @experimental
*/
@Injectable()
export class PlatformState {
constructor(@Inject(DOCUMENT) private _doc: any) {}
/**
* Renders the current state of the platform to string.
*/
renderToString(): string { return getDOM().getInnerHTML(this._doc); }
/**
* Returns the current DOM state.
*/
getDocument(): any { return this._doc; }
}

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 {INTERNAL_SERVER_PLATFORM_PROVIDERS as ɵINTERNAL_SERVER_PLATFORM_PROVIDERS, SERVER_RENDER_PROVIDERS as ɵSERVER_RENDER_PROVIDERS} from './server';
export {ServerRendererFactory2 as ɵServerRendererFactory2} from './server_renderer';

View File

@ -0,0 +1,85 @@
/**
* @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 {PlatformLocation, ɵPLATFORM_SERVER_ID as PLATFORM_SERVER_ID} from '@angular/common';
import {platformCoreDynamic} from '@angular/compiler';
import {Injectable, InjectionToken, Injector, NgModule, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, Provider, RendererFactory2, RootRenderer, Testability, createPlatformFactory, isDevMode, platformCore, ɵALLOW_MULTIPLE_PLATFORMS as ALLOW_MULTIPLE_PLATFORMS} from '@angular/core';
import {HttpModule} from '@angular/http';
import {BrowserModule, DOCUMENT, ɵSharedStylesHost as SharedStylesHost, ɵgetDOM as getDOM} from '@angular/platform-browser';
import {SERVER_HTTP_PROVIDERS} from './http';
import {ServerPlatformLocation} from './location';
import {Parse5DomAdapter, parseDocument} from './parse5_adapter';
import {PlatformState} from './platform_state';
import {ServerRendererFactory2} from './server_renderer';
import {ServerStylesHost} from './styles_host';
import {INITIAL_CONFIG, PlatformConfig} from './tokens';
function notSupported(feature: string): Error {
throw new Error(`platform-server does not support '${feature}'.`);
}
export const INTERNAL_SERVER_PLATFORM_PROVIDERS: Array<any /*Type | Provider | any[]*/> = [
{provide: DOCUMENT, useFactory: _document, deps: [Injector]},
{provide: PLATFORM_ID, useValue: PLATFORM_SERVER_ID},
{provide: PLATFORM_INITIALIZER, useFactory: initParse5Adapter, multi: true, deps: [Injector]},
{provide: PlatformLocation, useClass: ServerPlatformLocation}, PlatformState,
// Add special provider that allows multiple instances of platformServer* to be created.
{provide: ALLOW_MULTIPLE_PLATFORMS, useValue: true}
];
function initParse5Adapter(injector: Injector) {
return () => { Parse5DomAdapter.makeCurrent(); };
}
export const SERVER_RENDER_PROVIDERS: Provider[] = [
ServerRendererFactory2,
{provide: RendererFactory2, useExisting: ServerRendererFactory2},
ServerStylesHost,
{provide: SharedStylesHost, useExisting: ServerStylesHost},
];
/**
* The ng module for the server.
*
* @experimental
*/
@NgModule({
exports: [BrowserModule],
imports: [HttpModule],
providers: [
SERVER_RENDER_PROVIDERS,
SERVER_HTTP_PROVIDERS,
{provide: Testability, useValue: null},
],
})
export class ServerModule {
}
function _document(injector: Injector) {
let config: PlatformConfig|null = injector.get(INITIAL_CONFIG, null);
if (config && config.document) {
return parseDocument(config.document);
} else {
return getDOM().createHtmlDocument();
}
}
/**
* @experimental
*/
export const platformServer =
createPlatformFactory(platformCore, 'server', INTERNAL_SERVER_PLATFORM_PROVIDERS);
/**
* The server platform that supports the runtime compiler.
*
* @experimental
*/
export const platformDynamicServer =
createPlatformFactory(platformCoreDynamic, 'serverDynamic', INTERNAL_SERVER_PLATFORM_PROVIDERS);

View File

@ -0,0 +1,206 @@
/**
* @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 {DomElementSchemaRegistry} from '@angular/compiler';
import {APP_ID, Inject, Injectable, NgZone, RenderComponentType, Renderer, Renderer2, RendererFactory2, RendererType2, RootRenderer, ViewEncapsulation, ɵstringify as stringify} from '@angular/core';
import {DOCUMENT, ɵNAMESPACE_URIS as NAMESPACE_URIS, ɵSharedStylesHost as SharedStylesHost, ɵflattenStyles as flattenStyles, ɵgetDOM as getDOM, ɵshimContentAttribute as shimContentAttribute, ɵshimHostAttribute as shimHostAttribute} from '@angular/platform-browser';
const EMPTY_ARRAY: any[] = [];
@Injectable()
export class ServerRendererFactory2 implements RendererFactory2 {
private rendererByCompId = new Map<string, Renderer2>();
private defaultRenderer: Renderer2;
private schema = new DomElementSchemaRegistry();
constructor(
private ngZone: NgZone, @Inject(DOCUMENT) private document: any,
private sharedStylesHost: SharedStylesHost) {
this.defaultRenderer = new DefaultServerRenderer2(document, ngZone, this.schema);
};
createRenderer(element: any, type: RendererType2): Renderer2 {
if (!element || !type) {
return this.defaultRenderer;
}
switch (type.encapsulation) {
case ViewEncapsulation.Emulated: {
let renderer = this.rendererByCompId.get(type.id);
if (!renderer) {
renderer = new EmulatedEncapsulationServerRenderer2(
this.document, this.ngZone, this.sharedStylesHost, this.schema, type);
this.rendererByCompId.set(type.id, renderer);
}
(<EmulatedEncapsulationServerRenderer2>renderer).applyToHost(element);
return renderer;
}
case ViewEncapsulation.Native:
throw new Error('Native encapsulation is not supported on the server!');
default: {
if (!this.rendererByCompId.has(type.id)) {
const styles = flattenStyles(type.id, type.styles, []);
this.sharedStylesHost.addStyles(styles);
this.rendererByCompId.set(type.id, this.defaultRenderer);
}
return this.defaultRenderer;
}
}
}
}
class DefaultServerRenderer2 implements Renderer2 {
data: {[key: string]: any} = Object.create(null);
constructor(
private document: any, private ngZone: NgZone, private schema: DomElementSchemaRegistry) {}
destroy(): void {}
destroyNode: null;
createElement(name: string, namespace?: string, debugInfo?: any): any {
if (namespace) {
return getDOM().createElementNS(NAMESPACE_URIS[namespace], name);
}
return getDOM().createElement(name);
}
createComment(value: string, debugInfo?: any): any { return getDOM().createComment(value); }
createText(value: string, debugInfo?: any): any { return getDOM().createTextNode(value); }
appendChild(parent: any, newChild: any): void { getDOM().appendChild(parent, newChild); }
insertBefore(parent: any, newChild: any, refChild: any): void {
if (parent) {
getDOM().insertBefore(parent, refChild, newChild);
}
}
removeChild(parent: any, oldChild: any): void {
if (parent) {
getDOM().removeChild(parent, oldChild);
}
}
selectRootElement(selectorOrNode: string|any, debugInfo?: any): any {
let el: any;
if (typeof selectorOrNode === 'string') {
el = getDOM().querySelector(this.document, selectorOrNode);
if (!el) {
throw new Error(`The selector "${selectorOrNode}" did not match any elements`);
}
} else {
el = selectorOrNode;
}
getDOM().clearNodes(el);
return el;
}
parentNode(node: any): any { return getDOM().parentElement(node); }
nextSibling(node: any): any { return getDOM().nextSibling(node); }
setAttribute(el: any, name: string, value: string, namespace?: string): void {
if (namespace) {
getDOM().setAttributeNS(el, NAMESPACE_URIS[namespace], namespace + ':' + name, value);
} else {
getDOM().setAttribute(el, name, value);
}
}
removeAttribute(el: any, name: string, namespace?: string): void {
if (namespace) {
getDOM().removeAttributeNS(el, NAMESPACE_URIS[namespace], name);
} else {
getDOM().removeAttribute(el, name);
}
}
addClass(el: any, name: string): void { getDOM().addClass(el, name); }
removeClass(el: any, name: string): void { getDOM().removeClass(el, name); }
setStyle(el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean):
void {
getDOM().setStyle(el, style, value);
}
removeStyle(el: any, style: string, hasVendorPrefix: boolean): void {
getDOM().removeStyle(el, style);
}
// The value was validated already as a property binding, against the property name.
// To know this value is safe to use as an attribute, the security context of the
// attribute with the given name is checked against that security context of the
// property.
private _isSafeToReflectProperty(tagName: string, propertyName: string): boolean {
return this.schema.securityContext(tagName, propertyName, true) ===
this.schema.securityContext(tagName, propertyName, false);
}
setProperty(el: any, name: string, value: any): void {
checkNoSyntheticProp(name, 'property');
getDOM().setProperty(el, name, value);
// Mirror property values for known HTML element properties in the attributes.
const tagName = (el.tagName as string).toLowerCase();
if (value != null && (typeof value === 'number' || typeof value == 'string') &&
this.schema.hasElement(tagName, EMPTY_ARRAY) &&
this.schema.hasProperty(tagName, name, EMPTY_ARRAY) &&
this._isSafeToReflectProperty(tagName, name)) {
this.setAttribute(el, name, value.toString());
}
}
setValue(node: any, value: string): void { getDOM().setText(node, value); }
listen(
target: 'document'|'window'|'body'|any, eventName: string,
callback: (event: any) => boolean): () => void {
// Note: We are not using the EventsPlugin here as this is not needed
// to run our tests.
checkNoSyntheticProp(eventName, 'listener');
const el =
typeof target === 'string' ? getDOM().getGlobalEventTarget(this.document, target) : target;
const outsideHandler = (event: any) => this.ngZone.runGuarded(() => callback(event));
return this.ngZone.runOutsideAngular(() => getDOM().onAndCancel(el, eventName, outsideHandler));
}
}
const AT_CHARCODE = '@'.charCodeAt(0);
function checkNoSyntheticProp(name: string, nameKind: string) {
if (name.charCodeAt(0) === AT_CHARCODE) {
throw new Error(
`Found the synthetic ${nameKind} ${name}. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.`);
}
}
class EmulatedEncapsulationServerRenderer2 extends DefaultServerRenderer2 {
private contentAttr: string;
private hostAttr: string;
constructor(
document: any, ngZone: NgZone, sharedStylesHost: SharedStylesHost,
schema: DomElementSchemaRegistry, private component: RendererType2) {
super(document, ngZone, schema);
const styles = flattenStyles(component.id, component.styles, []);
sharedStylesHost.addStyles(styles);
this.contentAttr = shimContentAttribute(component.id);
this.hostAttr = shimHostAttribute(component.id);
}
applyToHost(element: any) { super.setAttribute(element, this.hostAttr, ''); }
createElement(parent: any, name: string): Element {
const el = super.createElement(parent, name);
super.setAttribute(el, this.contentAttr, '');
return el;
}
}

View File

@ -0,0 +1,36 @@
/**
* @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, Inject, Injectable, Optional} from '@angular/core';
import {DOCUMENT, ɵSharedStylesHost as SharedStylesHost, ɵTRANSITION_ID, ɵgetDOM as getDOM} from '@angular/platform-browser';
import {Parse5DomAdapter} from './parse5_adapter';
@Injectable()
export class ServerStylesHost extends SharedStylesHost {
private head: any = null;
constructor(
@Inject(DOCUMENT) private doc: any,
@Optional() @Inject(ɵTRANSITION_ID) private transitionId: string) {
super();
this.head = getDOM().getElementsByTagName(doc, 'head')[0];
}
private _addStyle(style: string): void {
let adapter: Parse5DomAdapter = getDOM() as Parse5DomAdapter;
const el = adapter.createElement('style');
adapter.setText(el, style);
if (!!this.transitionId) {
adapter.setAttribute(el, 'ng-transition', this.transitionId);
}
adapter.appendChild(this.head, el);
}
onStylesAdded(additions: Set<string>) { additions.forEach(style => this._addStyle(style)); }
}

View File

@ -0,0 +1,26 @@
/**
* @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 {InjectionToken} from '@angular/core';
/**
* Config object passed to initialize the platform.
*
* @experimental
*/
export interface PlatformConfig {
document?: string;
url?: string;
}
/**
* The DI token for setting the initial config for the platform.
*
* @experimental
*/
export const INITIAL_CONFIG = new InjectionToken<PlatformConfig>('Server.INITIAL_CONFIG');

View File

@ -0,0 +1,79 @@
/**
* @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, NgModuleFactory, NgModuleRef, PlatformRef, Provider, Type} from '@angular/core';
import {ɵTRANSITION_ID} from '@angular/platform-browser';
import {filter} from 'rxjs/operator/filter';
import {first} from 'rxjs/operator/first';
import {toPromise} from 'rxjs/operator/toPromise';
import {PlatformState} from './platform_state';
import {platformDynamicServer, platformServer} from './server';
import {INITIAL_CONFIG} from './tokens';
const parse5 = require('parse5');
export interface PlatformOptions {
document?: string;
url?: string;
extraProviders?: Provider[];
}
function _getPlatform(
platformFactory: (extraProviders: Provider[]) => PlatformRef,
options: PlatformOptions): PlatformRef {
const extraProviders = options.extraProviders ? options.extraProviders : [];
return platformFactory([
{provide: INITIAL_CONFIG, useValue: {document: options.document, url: options.url}},
extraProviders
]);
}
function _render<T>(
platform: PlatformRef, moduleRefPromise: Promise<NgModuleRef<T>>): Promise<string> {
return moduleRefPromise.then((moduleRef) => {
const transitionId = moduleRef.injector.get(ɵTRANSITION_ID, null);
if (!transitionId) {
throw new Error(
`renderModule[Factory]() requires the use of BrowserModule.withServerTransition() to ensure
the server-rendered app can be properly bootstrapped into a client app.`);
}
const applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
return toPromise
.call(first.call(filter.call(applicationRef.isStable, (isStable: boolean) => isStable)))
.then(() => {
const output = platform.injector.get(PlatformState).renderToString();
platform.destroy();
return output;
});
});
}
/**
* Renders a Module to string.
*
* Do not use this in a production server environment. Use pre-compiled {@link NgModuleFactory} with
* {link renderModuleFactory} instead.
*
* @experimental
*/
export function renderModule<T>(module: Type<T>, options: PlatformOptions): Promise<string> {
const platform = _getPlatform(platformDynamicServer, options);
return _render(platform, platform.bootstrapModule(module));
}
/**
* Renders a {@link NgModuleFactory} to string.
*
* @experimental
*/
export function renderModuleFactory<T>(
moduleFactory: NgModuleFactory<T>, options: PlatformOptions): Promise<string> {
const platform = _getPlatform(platformServer, options);
return _render(platform, platform.bootstrapModuleFactory(moduleFactory));
}

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');