refactor(Compiler): inline styles before compiling the template
This commit is contained in:
parent
3875f02a52
commit
3ea655918e
|
@ -25,6 +25,8 @@ import {
|
|||
} from 'angular2/change_detection';
|
||||
import {ExceptionHandler} from './exception_handler';
|
||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
|
||||
import {TemplateResolver} from './compiler/template_resolver';
|
||||
import {DirectiveResolver} from './compiler/directive_resolver';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
@ -42,8 +44,6 @@ import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
|
|||
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
|
||||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||
import {
|
||||
ComponentRef,
|
||||
|
@ -105,9 +105,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
|||
},
|
||||
[NgZone]),
|
||||
bind(ShadowDomStrategy)
|
||||
.toFactory((styleInliner, styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(
|
||||
styleInliner, styleUrlResolver, doc.head),
|
||||
[StyleInliner, StyleUrlResolver, DOCUMENT_TOKEN]),
|
||||
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
|
||||
DomRenderer,
|
||||
DefaultDomCompiler,
|
||||
bind(Renderer).toAlias(DomRenderer),
|
||||
|
|
|
@ -22,7 +22,7 @@ export class TemplateResolver {
|
|||
return view;
|
||||
}
|
||||
|
||||
_resolve(component: Type) {
|
||||
_resolve(component: Type): View {
|
||||
var annotations = reflector.annotations(component);
|
||||
for (var i = 0; i < annotations.length; i++) {
|
||||
var annotation = annotations[i];
|
||||
|
|
|
@ -67,10 +67,10 @@ class Html5LibDomAdapter implements DomAdapter {
|
|||
throw 'not implemented';
|
||||
}
|
||||
querySelector(el, String selector) {
|
||||
throw 'not implemented';
|
||||
return el.querySelector(selector);
|
||||
}
|
||||
List querySelectorAll(el, String selector) {
|
||||
throw 'not implemented';
|
||||
return el.querySelectorAll(selector);
|
||||
}
|
||||
on(el, evt, listener) {
|
||||
throw 'not implemented';
|
||||
|
@ -94,7 +94,7 @@ class Html5LibDomAdapter implements DomAdapter {
|
|||
return el.innerHtml;
|
||||
}
|
||||
getOuterHTML(el) {
|
||||
throw 'not implemented';
|
||||
return el.outerHtml;
|
||||
}
|
||||
String nodeName(node) {
|
||||
switch (node.nodeType) {
|
||||
|
@ -130,12 +130,12 @@ class Html5LibDomAdapter implements DomAdapter {
|
|||
}
|
||||
|
||||
parentElement(el) {
|
||||
throw 'not implemented';
|
||||
return el.parent;
|
||||
}
|
||||
List childNodes(el) => el.nodes;
|
||||
List childNodesAsList(el) => el.nodes;
|
||||
clearNodes(el) {
|
||||
throw 'not implemented';
|
||||
el.nodes.forEach((e) => e.remove());
|
||||
}
|
||||
appendChild(el, node) => el.append(node.remove());
|
||||
removeChild(el, node) {
|
||||
|
@ -153,7 +153,7 @@ class Html5LibDomAdapter implements DomAdapter {
|
|||
throw 'not implemented';
|
||||
}
|
||||
setInnerHTML(el, value) {
|
||||
throw 'not implemented';
|
||||
el.innerHtml = value;
|
||||
}
|
||||
getText(el) {
|
||||
return el.text;
|
||||
|
|
|
@ -303,7 +303,7 @@ export class RenderCompiler {
|
|||
* we don't need to serialize all possible components over the wire,
|
||||
* but only the needed ones based on previous calls.
|
||||
*/
|
||||
compile(template: ViewDefinition): Promise<ProtoViewDto> { return null; }
|
||||
compile(view: ViewDefinition): Promise<ProtoViewDto> { return null; }
|
||||
}
|
||||
|
||||
export interface RenderElementRef {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import {List} from 'angular2/src/facade/collection';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {Parser} from 'angular2/change_detection';
|
||||
import {ViewDefinition} from '../../api';
|
||||
import {CompileStep} from './compile_step';
|
||||
|
@ -12,21 +10,19 @@ import {ShadowDomCompileStep} from '../shadow_dom/shadow_dom_compile_step';
|
|||
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||
|
||||
export class CompileStepFactory {
|
||||
createSteps(template: ViewDefinition, subTaskPromises: List<Promise<any>>): List<CompileStep> {
|
||||
return null;
|
||||
}
|
||||
createSteps(template: ViewDefinition): List<CompileStep> { return null; }
|
||||
}
|
||||
|
||||
export class DefaultStepFactory extends CompileStepFactory {
|
||||
constructor(public _parser: Parser, public _shadowDomStrategy: ShadowDomStrategy) { super(); }
|
||||
|
||||
createSteps(template: ViewDefinition, subTaskPromises: List<Promise<any>>) {
|
||||
createSteps(template: ViewDefinition): List<CompileStep> {
|
||||
return [
|
||||
new ViewSplitter(this._parser),
|
||||
new PropertyBindingParser(this._parser),
|
||||
new DirectiveParser(this._parser, template.directives),
|
||||
new TextInterpolationParser(this._parser),
|
||||
new ShadowDomCompileStep(this._shadowDomStrategy, template, subTaskPromises)
|
||||
new ShadowDomCompileStep(this._shadowDomStrategy, template)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,11 @@ export class DomCompiler extends RenderCompiler {
|
|||
super();
|
||||
}
|
||||
|
||||
compile(template: ViewDefinition): Promise<ProtoViewDto> {
|
||||
var tplPromise = this._templateLoader.load(template);
|
||||
compile(view: ViewDefinition): Promise<ProtoViewDto> {
|
||||
var tplPromise = this._templateLoader.load(view);
|
||||
return PromiseWrapper.then(
|
||||
tplPromise, (el) => this._compileTemplate(template, el, ViewType.COMPONENT), (e) => {
|
||||
throw new BaseException(
|
||||
`Failed to load the template for "${template.componentId}" : ${e}`);
|
||||
tplPromise, (el) => this._compileTemplate(view, el, ViewType.COMPONENT), (e) => {
|
||||
throw new BaseException(`Failed to load the template for "${view.componentId}" : ${e}`);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -51,17 +50,10 @@ export class DomCompiler extends RenderCompiler {
|
|||
|
||||
_compileTemplate(viewDef: ViewDefinition, tplElement,
|
||||
protoViewType: ViewType): Promise<ProtoViewDto> {
|
||||
var subTaskPromises = [];
|
||||
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef, subTaskPromises));
|
||||
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef));
|
||||
var compileElements = pipeline.process(tplElement, protoViewType, viewDef.componentId);
|
||||
|
||||
var protoView = compileElements[0].inheritedProtoView.build();
|
||||
|
||||
if (subTaskPromises.length > 0) {
|
||||
return PromiseWrapper.all(subTaskPromises).then((_) => protoView);
|
||||
} else {
|
||||
return PromiseWrapper.resolve(protoView);
|
||||
}
|
||||
return PromiseWrapper.resolve(compileElements[0].inheritedProtoView.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
import {isBlank, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
||||
import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isBlank, isPresent, BaseException, stringify, isPromise} from 'angular2/src/facade/lang';
|
||||
import {Map, MapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
|
||||
import {ViewDefinition} from '../../api';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
|
||||
import {StyleInliner} from './style_inliner';
|
||||
import {StyleUrlResolver} from './style_url_resolver';
|
||||
|
||||
/**
|
||||
* Strategy to load component templates.
|
||||
|
@ -17,37 +19,36 @@ import {UrlResolver} from 'angular2/src/services/url_resolver';
|
|||
export class TemplateLoader {
|
||||
_cache: Map<string, Promise<string>> = new Map();
|
||||
|
||||
constructor(private _xhr: XHR, urlResolver: UrlResolver) {}
|
||||
constructor(private _xhr: XHR, private _styleInliner: StyleInliner,
|
||||
private _styleUrlResolver: StyleUrlResolver) {}
|
||||
|
||||
load(view: ViewDefinition): Promise</*element*/ any> {
|
||||
let html;
|
||||
let fetchedStyles;
|
||||
let tplElAndStyles: List<string | Promise<string>> = [this._loadHtml(view)];
|
||||
|
||||
// Load the HTML
|
||||
if (isPresent(view.template)) {
|
||||
html = PromiseWrapper.resolve(view.template);
|
||||
} else if (isPresent(view.templateAbsUrl)) {
|
||||
html = this._loadText(view.templateAbsUrl);
|
||||
} else {
|
||||
throw new BaseException('View should have either the templateUrl or template property set');
|
||||
if (isPresent(view.styles)) {
|
||||
view.styles.forEach((cssText: string) => {
|
||||
let textOrPromise = this._resolveAndInlineCssText(cssText, view.templateAbsUrl);
|
||||
tplElAndStyles.push(textOrPromise);
|
||||
});
|
||||
}
|
||||
|
||||
// Load the styles
|
||||
if (isPresent(view.styleAbsUrls) && view.styleAbsUrls.length > 0) {
|
||||
fetchedStyles = ListWrapper.map(view.styleAbsUrls, url => this._loadText(url));
|
||||
} else {
|
||||
fetchedStyles = [];
|
||||
if (isPresent(view.styleAbsUrls)) {
|
||||
view.styleAbsUrls.forEach(url => {
|
||||
let promise = this._loadText(url).then(
|
||||
cssText => this._resolveAndInlineCssText(cssText, view.templateAbsUrl));
|
||||
tplElAndStyles.push(promise);
|
||||
});
|
||||
}
|
||||
|
||||
// Inline the styles and return a template element
|
||||
return PromiseWrapper.all(ListWrapper.concat([html], fetchedStyles))
|
||||
// Inline the styles from the @View annotation and return a template element
|
||||
return PromiseWrapper.all(tplElAndStyles)
|
||||
.then((res: List<string>) => {
|
||||
let html = res[0];
|
||||
let fetchedStyles = ListWrapper.slice(res, 1);
|
||||
let tplEl = res[0];
|
||||
let cssTexts = ListWrapper.slice(res, 1);
|
||||
|
||||
html = _createStyleTags(view.styles) + _createStyleTags(fetchedStyles) + html;
|
||||
_insertCssTexts(DOM.content(tplEl), cssTexts);
|
||||
|
||||
return DOM.createTemplate(html);
|
||||
return tplEl;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -67,10 +68,74 @@ export class TemplateLoader {
|
|||
|
||||
return response;
|
||||
}
|
||||
|
||||
// Load the html and inline any style tags
|
||||
private _loadHtml(view: ViewDefinition): Promise<any /* element */> {
|
||||
let html;
|
||||
|
||||
// Load the HTML
|
||||
if (isPresent(view.template)) {
|
||||
html = PromiseWrapper.resolve(view.template);
|
||||
} else if (isPresent(view.templateAbsUrl)) {
|
||||
html = this._loadText(view.templateAbsUrl);
|
||||
} else {
|
||||
throw new BaseException('View should have either the templateUrl or template property set');
|
||||
}
|
||||
|
||||
// Inline the style tags from the html
|
||||
return html.then(html => {
|
||||
var tplEl = DOM.createTemplate(html);
|
||||
let styleEls = DOM.querySelectorAll(DOM.content(tplEl), 'STYLE');
|
||||
|
||||
let promises: List<Promise<string>> = [];
|
||||
for (let i = 0; i < styleEls.length; i++) {
|
||||
let promise = this._resolveAndInlineElement(styleEls[i], view.templateAbsUrl);
|
||||
if (isPromise(promise)) {
|
||||
promises.push(promise);
|
||||
}
|
||||
}
|
||||
|
||||
return promises.length > 0 ? PromiseWrapper.all(promises).then(_ => tplEl) : tplEl;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inlines a style element.
|
||||
*
|
||||
* @param styleEl The style element
|
||||
* @param baseUrl The base url
|
||||
* @returns {Promise<any>} null when no @import rule exist in the css or a Promise
|
||||
* @private
|
||||
*/
|
||||
private _resolveAndInlineElement(styleEl, baseUrl: string): Promise<any> {
|
||||
let textOrPromise = this._resolveAndInlineCssText(DOM.getText(styleEl), baseUrl);
|
||||
|
||||
if (isPromise(textOrPromise)) {
|
||||
return (<Promise<string>>textOrPromise).then(css => { DOM.setText(styleEl, css); });
|
||||
} else {
|
||||
DOM.setText(styleEl, <string>textOrPromise);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private _resolveAndInlineCssText(cssText: string, baseUrl: string): string | Promise<string> {
|
||||
cssText = this._styleUrlResolver.resolveUrls(cssText, baseUrl);
|
||||
return this._styleInliner.inlineImports(cssText, baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
function _createStyleTags(styles?: List<string>): string {
|
||||
return isBlank(styles) ?
|
||||
'' :
|
||||
ListWrapper.map(styles, css => `<style type='text/css'>${css}</style>`).join('');
|
||||
function _insertCssTexts(element, cssTexts: List<string>): void {
|
||||
if (cssTexts.length == 0) return;
|
||||
|
||||
let insertBefore = DOM.firstChild(element);
|
||||
|
||||
for (let i = cssTexts.length - 1; i >= 0; i--) {
|
||||
let styleEl = DOM.createStyleElement(cssTexts[i]);
|
||||
if (isPresent(insertBefore)) {
|
||||
DOM.insertBefore(insertBefore, styleEl);
|
||||
} else {
|
||||
DOM.appendChild(element, styleEl);
|
||||
}
|
||||
insertBefore = styleEl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import {isBlank, isPresent, isPromise} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {EmulatedUnscopedShadowDomStrategy} from './emulated_unscoped_shadow_dom_strategy';
|
||||
import {
|
||||
getContentAttribute,
|
||||
|
@ -27,39 +24,21 @@ import {
|
|||
* - see `ShadowCss` for more information and limitations.
|
||||
*/
|
||||
export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomStrategy {
|
||||
constructor(styleInliner: StyleInliner, styleUrlResolver: StyleUrlResolver, styleHost) {
|
||||
super(styleInliner, styleUrlResolver, styleHost);
|
||||
}
|
||||
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleEl): Promise<any> {
|
||||
var cssText = DOM.getText(styleEl);
|
||||
|
||||
cssText = this.styleUrlResolver.resolveUrls(cssText, templateUrl);
|
||||
var inlinedCss = this.styleInliner.inlineImports(cssText, templateUrl);
|
||||
|
||||
var ret = null;
|
||||
if (isPromise(inlinedCss)) {
|
||||
DOM.setText(styleEl, '');
|
||||
ret = (<Promise<string>>inlinedCss)
|
||||
.then((css) => {
|
||||
css = shimCssForComponent(css, hostComponentId);
|
||||
DOM.setText(styleEl, css);
|
||||
});
|
||||
} else {
|
||||
var css = shimCssForComponent(<string>inlinedCss, hostComponentId);
|
||||
DOM.setText(styleEl, css);
|
||||
}
|
||||
constructor(styleHost) { super(styleHost); }
|
||||
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleEl): void {
|
||||
let cssText = DOM.getText(styleEl);
|
||||
cssText = shimCssForComponent(cssText, hostComponentId);
|
||||
DOM.setText(styleEl, cssText);
|
||||
this._moveToStyleHost(styleEl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
_moveToStyleHost(styleEl) {
|
||||
_moveToStyleHost(styleEl): void {
|
||||
DOM.remove(styleEl);
|
||||
insertStyleElement(this.styleHost, styleEl);
|
||||
}
|
||||
|
||||
processElement(hostComponentId: string, elementComponentId: string, element) {
|
||||
processElement(hostComponentId: string, elementComponentId: string, element): void {
|
||||
// Shim the element as a child of the compiled component
|
||||
if (isPresent(hostComponentId)) {
|
||||
var contentAttribute = getContentAttribute(getComponentId(hostComponentId));
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import {isPromise} from 'angular2/src/facade/lang';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import * as viewModule from '../view/view';
|
||||
|
||||
import {LightDom} from './light_dom';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {StyleUrlResolver} from './style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
import {insertSharedStyleText} from './util';
|
||||
|
||||
/**
|
||||
|
@ -21,10 +16,7 @@ import {insertSharedStyleText} from './util';
|
|||
* - you can **not** use shadow DOM specific selectors in the styles
|
||||
*/
|
||||
export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
||||
constructor(public styleInliner: StyleInliner, public styleUrlResolver: StyleUrlResolver,
|
||||
public styleHost) {
|
||||
super();
|
||||
}
|
||||
constructor(public styleHost) { super(); }
|
||||
|
||||
hasNativeContentElement(): boolean { return false; }
|
||||
|
||||
|
@ -34,21 +26,8 @@ export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
|||
return new LightDom(lightDomView, el);
|
||||
}
|
||||
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleEl): Promise<any> {
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleEl): void {
|
||||
var cssText = DOM.getText(styleEl);
|
||||
|
||||
cssText = this.styleUrlResolver.resolveUrls(cssText, templateUrl);
|
||||
var inlinedCss = this.styleInliner.inlineImports(cssText, templateUrl);
|
||||
|
||||
var ret = null;
|
||||
if (isPromise(inlinedCss)) {
|
||||
DOM.setText(styleEl, '');
|
||||
ret = (<Promise<string>>inlinedCss).then(css => { DOM.setText(styleEl, css); });
|
||||
} else {
|
||||
DOM.setText(styleEl, <string>inlinedCss);
|
||||
}
|
||||
|
||||
insertSharedStyleText(cssText, this.styleHost, styleEl);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
import {isPromise} from 'angular2/src/facade/lang';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {Injectable} from 'angular2/di';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {StyleUrlResolver} from './style_url_resolver';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
|
||||
/**
|
||||
* This strategies uses the native Shadow DOM support.
|
||||
|
@ -16,23 +10,5 @@ import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
|||
*/
|
||||
@Injectable()
|
||||
export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
||||
constructor(public styleInliner: StyleInliner, public styleUrlResolver: StyleUrlResolver) {
|
||||
super();
|
||||
}
|
||||
|
||||
prepareShadowRoot(el) { return DOM.createShadowRoot(el); }
|
||||
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleEl): Promise<any> {
|
||||
var cssText = DOM.getText(styleEl);
|
||||
|
||||
cssText = this.styleUrlResolver.resolveUrls(cssText, templateUrl);
|
||||
var inlinedCss = this.styleInliner.inlineImports(cssText, templateUrl);
|
||||
|
||||
if (isPromise(inlinedCss)) {
|
||||
return (<Promise<string>>inlinedCss).then(css => { DOM.setText(styleEl, css); });
|
||||
} else {
|
||||
DOM.setText(styleEl, <string>inlinedCss);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* This file is a port of shadowCSS from webcomponents.js to AtScript.
|
||||
* This file is a port of shadowCSS from webcomponents.js to TypeScript.
|
||||
*
|
||||
* Please make sure to keep to edits in sync with the source file.
|
||||
*
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import {isBlank, isPresent, assertionsEnabled, isPromise} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
|
@ -11,8 +9,7 @@ import {ViewDefinition} from '../../api';
|
|||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
|
||||
export class ShadowDomCompileStep implements CompileStep {
|
||||
constructor(public _shadowDomStrategy: ShadowDomStrategy, public _template: ViewDefinition,
|
||||
public _subTaskPromises: List<Promise<any>>) {}
|
||||
constructor(public _shadowDomStrategy: ShadowDomStrategy, public _view: ViewDefinition) {}
|
||||
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var tagName = DOM.tagName(current.element).toUpperCase();
|
||||
|
@ -22,17 +19,13 @@ export class ShadowDomCompileStep implements CompileStep {
|
|||
this._processContentElement(current);
|
||||
} else {
|
||||
var componentId = current.isBound() ? current.inheritedElementBinder.componentId : null;
|
||||
this._shadowDomStrategy.processElement(this._template.componentId, componentId,
|
||||
current.element);
|
||||
this._shadowDomStrategy.processElement(this._view.componentId, componentId, current.element);
|
||||
}
|
||||
}
|
||||
|
||||
_processStyleElement(current: CompileElement, control: CompileControl) {
|
||||
var stylePromise = this._shadowDomStrategy.processStyleElement(
|
||||
this._template.componentId, this._template.templateAbsUrl, current.element);
|
||||
if (isPresent(stylePromise) && isPromise(stylePromise)) {
|
||||
this._subTaskPromises.push(stylePromise);
|
||||
}
|
||||
this._shadowDomStrategy.processStyleElement(this._view.componentId, this._view.templateAbsUrl,
|
||||
current.element);
|
||||
|
||||
// Style elements should not be further processed by the compiler, as they can not contain
|
||||
// bindings. Skipping further compiler steps allow speeding up the compilation process.
|
||||
|
|
|
@ -1,28 +1,20 @@
|
|||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import * as viewModule from '../view/view';
|
||||
import {LightDom} from './light_dom';
|
||||
|
||||
export class ShadowDomStrategy {
|
||||
// Whether the strategy understands the native <content> tag
|
||||
hasNativeContentElement(): boolean { return true; }
|
||||
|
||||
/**
|
||||
* Prepares and returns the shadow root for the given element.
|
||||
*/
|
||||
// Prepares and returns the (emulated) shadow root for the given element.
|
||||
prepareShadowRoot(el): any { return null; }
|
||||
|
||||
constructLightDom(lightDomView: viewModule.DomView, el): LightDom { return null; }
|
||||
|
||||
/**
|
||||
* An optional step that can modify the template style elements.
|
||||
*/
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleElement): Promise<any> {
|
||||
return null;
|
||||
}
|
||||
// An optional step that can modify the template style elements.
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleElement): void {}
|
||||
|
||||
/**
|
||||
* An optional step that can modify the template elements (style elements exlcuded).
|
||||
*/
|
||||
processElement(hostComponentId: string, elementComponentId: string, element) {}
|
||||
// An optional step that can modify the template elements (style elements exlcuded).
|
||||
processElement(hostComponentId: string, elementComponentId: string, element): void {}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ import {XHR} from 'angular2/src/render/xhr';
|
|||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
@ -88,9 +88,7 @@ function _getAppBindings() {
|
|||
bind(DOCUMENT_TOKEN)
|
||||
.toValue(appDoc),
|
||||
bind(ShadowDomStrategy)
|
||||
.toFactory((styleInliner, styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(
|
||||
styleInliner, styleUrlResolver, doc.head),
|
||||
[StyleInliner, StyleUrlResolver, DOCUMENT_TOKEN]),
|
||||
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
|
||||
DomRenderer,
|
||||
DefaultDomCompiler,
|
||||
bind(Renderer).toAlias(DomRenderer),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
library angular2.transform.template_compiler.compile_step_factory;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:angular2/src/change_detection/parser/parser.dart' as ng;
|
||||
import 'package:angular2/src/render/api.dart';
|
||||
import 'package:angular2/src/render/dom/compiler/compile_step.dart';
|
||||
|
@ -15,8 +14,7 @@ class CompileStepFactory implements base.CompileStepFactory {
|
|||
final ng.Parser _parser;
|
||||
CompileStepFactory(this._parser);
|
||||
|
||||
List<CompileStep> createSteps(
|
||||
ViewDefinition template, List<Future> subTaskPromises) {
|
||||
List<CompileStep> createSteps(ViewDefinition template) {
|
||||
return [
|
||||
new ViewSplitter(_parser),
|
||||
new PropertyBindingParser(_parser),
|
||||
|
|
|
@ -7,6 +7,8 @@ import 'package:angular2/src/change_detection/parser/parser.dart' as ng;
|
|||
import 'package:angular2/src/core/compiler/proto_view_factory.dart';
|
||||
import 'package:angular2/src/render/api.dart';
|
||||
import 'package:angular2/src/render/dom/compiler/compile_pipeline.dart';
|
||||
import 'package:angular2/src/render/dom/compiler/style_inliner.dart';
|
||||
import 'package:angular2/src/render/dom/compiler/style_url_resolver.dart';
|
||||
import 'package:angular2/src/render/dom/compiler/template_loader.dart';
|
||||
import 'package:angular2/src/render/xhr.dart' show XHR;
|
||||
import 'package:angular2/src/reflection/reflection.dart';
|
||||
|
@ -79,11 +81,17 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
|
|||
/// reflectively accessed from that template.
|
||||
class _TemplateExtractor {
|
||||
final CompileStepFactory _factory;
|
||||
final TemplateLoader _loader;
|
||||
TemplateLoader _loader;
|
||||
|
||||
_TemplateExtractor(XHR xhr)
|
||||
: _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())),
|
||||
_loader = new TemplateLoader(xhr, new UrlResolver());
|
||||
: _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())) {
|
||||
|
||||
var urlResolver = new UrlResolver();
|
||||
var styleUrlResolver = new StyleUrlResolver(urlResolver);
|
||||
var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
|
||||
_loader = new TemplateLoader(xhr, styleInliner, styleUrlResolver);
|
||||
}
|
||||
|
||||
Future<_ExtractResult> extractTemplates(ViewDefinition viewDef) async {
|
||||
// Check for "imperative views".
|
||||
|
@ -98,9 +106,7 @@ class _TemplateExtractor {
|
|||
var recordingCapabilities = new RecordingReflectionCapabilities();
|
||||
reflector.reflectionCapabilities = recordingCapabilities;
|
||||
|
||||
var subtaskPromises = [];
|
||||
var pipeline =
|
||||
new CompilePipeline(_factory.createSteps(viewDef, subtaskPromises));
|
||||
var pipeline = new CompilePipeline(_factory.createSteps(viewDef));
|
||||
|
||||
var compileElements =
|
||||
pipeline.process(templateEl, ViewType.COMPONENT, viewDef.componentId);
|
||||
|
@ -109,9 +115,7 @@ class _TemplateExtractor {
|
|||
|
||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||
|
||||
return Future
|
||||
.wait(subtaskPromises)
|
||||
.then((_) => new _ExtractResult(recordingCapabilities, protoViewDto));
|
||||
return new _ExtractResult(recordingCapabilities, protoViewDto);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,8 +62,8 @@ export function main() {
|
|||
protoViewFactoryResults: List<List<AppProtoView>>) {
|
||||
var urlResolver = new FakeUrlResolver();
|
||||
renderCompileRequests = [];
|
||||
renderCompiler.spy('compile').andCallFake((template) => {
|
||||
renderCompileRequests.push(template);
|
||||
renderCompiler.spy('compile').andCallFake((view) => {
|
||||
renderCompileRequests.push(view);
|
||||
return PromiseWrapper.resolve(ListWrapper.removeAt(renderCompileResults, 0));
|
||||
});
|
||||
|
||||
|
@ -467,12 +467,12 @@ export function main() {
|
|||
});
|
||||
}
|
||||
|
||||
function createDirectiveBinding(directiveResolver, type) {
|
||||
function createDirectiveBinding(directiveResolver, type): DirectiveBinding {
|
||||
var annotation = directiveResolver.resolve(type);
|
||||
return DirectiveBinding.createFromType(type, annotation);
|
||||
}
|
||||
|
||||
function createProtoView(elementBinders = null) {
|
||||
function createProtoView(elementBinders = null): AppProtoView {
|
||||
var pv = new AppProtoView(null, null, new Map(), null);
|
||||
if (isBlank(elementBinders)) {
|
||||
elementBinders = [];
|
||||
|
@ -481,18 +481,19 @@ function createProtoView(elementBinders = null) {
|
|||
return pv;
|
||||
}
|
||||
|
||||
function createComponentElementBinder(directiveResolver, type) {
|
||||
function createComponentElementBinder(directiveResolver, type): ElementBinder {
|
||||
var binding = createDirectiveBinding(directiveResolver, type);
|
||||
return new ElementBinder(0, null, 0, null, binding);
|
||||
}
|
||||
|
||||
function createViewportElementBinder(nestedProtoView) {
|
||||
function createViewportElementBinder(nestedProtoView): ElementBinder {
|
||||
var elBinder = new ElementBinder(0, null, 0, null, null);
|
||||
elBinder.nestedProtoView = nestedProtoView;
|
||||
return elBinder;
|
||||
}
|
||||
|
||||
function createRenderProtoView(elementBinders = null, type: renderApi.ViewType = null) {
|
||||
function createRenderProtoView(elementBinders = null,
|
||||
type: renderApi.ViewType = null): renderApi.ProtoViewDto {
|
||||
if (isBlank(type)) {
|
||||
type = renderApi.ViewType.COMPONENT;
|
||||
}
|
||||
|
@ -502,16 +503,16 @@ function createRenderProtoView(elementBinders = null, type: renderApi.ViewType =
|
|||
return new renderApi.ProtoViewDto({elementBinders: elementBinders, type: type});
|
||||
}
|
||||
|
||||
function createRenderComponentElementBinder(directiveIndex) {
|
||||
function createRenderComponentElementBinder(directiveIndex): renderApi.ElementBinder {
|
||||
return new renderApi.ElementBinder(
|
||||
{directives: [new renderApi.DirectiveBinder({directiveIndex: directiveIndex})]});
|
||||
}
|
||||
|
||||
function createRenderViewportElementBinder(nestedProtoView) {
|
||||
function createRenderViewportElementBinder(nestedProtoView): renderApi.ElementBinder {
|
||||
return new renderApi.ElementBinder({nestedProtoView: nestedProtoView});
|
||||
}
|
||||
|
||||
function createRootProtoView(directiveResolver, type) {
|
||||
function createRootProtoView(directiveResolver, type): AppProtoView {
|
||||
return createProtoView([createComponentElementBinder(directiveResolver, type)]);
|
||||
}
|
||||
|
||||
|
@ -577,23 +578,18 @@ class FakeAppRootUrl extends AppRootUrl {
|
|||
|
||||
|
||||
class FakeTemplateResolver extends TemplateResolver {
|
||||
_cmpTemplates: Map<Type, viewAnn.View>;
|
||||
_cmpViews: Map<Type, viewAnn.View> = new Map();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._cmpTemplates = new Map();
|
||||
}
|
||||
constructor() { super(); }
|
||||
|
||||
resolve(component: Type): viewAnn.View {
|
||||
var template = this._cmpTemplates.get(component);
|
||||
if (isBlank(template)) {
|
||||
// dynamic component
|
||||
return null;
|
||||
}
|
||||
return template;
|
||||
// returns null for dynamic components
|
||||
return this._cmpViews.has(component) ? this._cmpViews.get(component) : null;
|
||||
}
|
||||
|
||||
setView(component: Type, template: viewAnn.View) { this._cmpTemplates.set(component, template); }
|
||||
setView(component: Type, template: viewAnn.View): void {
|
||||
this._cmpViews.set(component, template);
|
||||
}
|
||||
}
|
||||
|
||||
class FakeProtoViewFactory extends ProtoViewFactory {
|
||||
|
|
|
@ -21,7 +21,7 @@ import {Component, View} from 'angular2/angular2';
|
|||
import {NgIf} from 'angular2/src/directives/ng_if';
|
||||
|
||||
export function main() {
|
||||
describe('if directive', () => {
|
||||
describe('ng-if directive', () => {
|
||||
it('should work in a template attribute',
|
||||
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
|
||||
var html = '<div><copy-me template="ng-if booleanCondition">hello</copy-me></div>';
|
||||
|
|
|
@ -24,8 +24,6 @@ import {CompileStepFactory} from 'angular2/src/render/dom/compiler/compile_step_
|
|||
import {CompileControl} from 'angular2/src/render/dom/compiler/compile_control';
|
||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
|
||||
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
|
||||
export function runCompilerCommonTests() {
|
||||
|
@ -111,30 +109,6 @@ export function runCompilerCommonTests() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should wait for async subtasks to be resolved', inject([AsyncTestCompleter], (async) => {
|
||||
var subTasksCompleted = false;
|
||||
|
||||
var completer = PromiseWrapper.completer();
|
||||
|
||||
var compiler = createCompiler((parent, current, control) => {
|
||||
mockStepFactory.subTaskPromises.push(
|
||||
completer.promise.then((_) => { subTasksCompleted = true; }));
|
||||
});
|
||||
|
||||
// It should always return a Promise because the subtask is async
|
||||
var pvPromise = compiler.compile(
|
||||
new ViewDefinition({componentId: 'someId', template: 'some component'}));
|
||||
expect(pvPromise).toBePromise();
|
||||
expect(subTasksCompleted).toEqual(false);
|
||||
|
||||
// The Promise should resolve after the subtask is ready
|
||||
completer.resolve(null);
|
||||
pvPromise.then((protoView) => {
|
||||
expect(subTasksCompleted).toEqual(true);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return ProtoViews of type COMPONENT_VIEW_TYPE',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var compiler = createCompiler(EMPTY_STEP);
|
||||
|
@ -174,10 +148,8 @@ class MockStepFactory extends CompileStepFactory {
|
|||
super();
|
||||
this.steps = steps;
|
||||
}
|
||||
createSteps(viewDef, subTaskPromises) {
|
||||
createSteps(viewDef): List<CompileStep> {
|
||||
this.viewDef = viewDef;
|
||||
this.subTaskPromises = subTaskPromises;
|
||||
ListWrapper.forEach(this.subTaskPromises, (p) => this.subTaskPromises.push(p));
|
||||
return this.steps;
|
||||
}
|
||||
}
|
||||
|
@ -199,20 +171,20 @@ var EMPTY_STEP = (parent, current, control) => {
|
|||
class FakeTemplateLoader extends TemplateLoader {
|
||||
_urlData: Map<string, string>;
|
||||
constructor(urlData) {
|
||||
super(null, new UrlResolver());
|
||||
super(null, null, null);
|
||||
this._urlData = urlData;
|
||||
}
|
||||
|
||||
load(template: ViewDefinition) {
|
||||
if (isPresent(template.template)) {
|
||||
return PromiseWrapper.resolve(DOM.createTemplate(template.template));
|
||||
load(view: ViewDefinition): Promise<any> {
|
||||
if (isPresent(view.template)) {
|
||||
return PromiseWrapper.resolve(DOM.createTemplate(view.template));
|
||||
}
|
||||
|
||||
if (isPresent(template.templateAbsUrl)) {
|
||||
var content = this._urlData.get(template.templateAbsUrl);
|
||||
if (isPresent(view.templateAbsUrl)) {
|
||||
var content = this._urlData.get(view.templateAbsUrl);
|
||||
return isPresent(content) ?
|
||||
PromiseWrapper.resolve(DOM.createTemplate(content)) :
|
||||
PromiseWrapper.reject(`Failed to fetch url "${template.templateAbsUrl}"`, null);
|
||||
PromiseWrapper.reject(`Failed to fetch url "${view.templateAbsUrl}"`, null);
|
||||
}
|
||||
|
||||
throw new BaseException('View should have either the templateUrl or template property set');
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
|
||||
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
|
@ -1,5 +1,5 @@
|
|||
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/test_lib';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
|
||||
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
|
|
@ -12,19 +12,27 @@ import {
|
|||
} from 'angular2/test_lib';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
|
||||
import {ViewDefinition} from 'angular2/src/render/api';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {StringWrapper, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
import {MockXHR} from 'angular2/src/render/xhr_mock';
|
||||
|
||||
export function main() {
|
||||
describe('TemplateLoader', () => {
|
||||
var loader, xhr;
|
||||
var loader, xhr, styleUrlResolver, urlResolver;
|
||||
|
||||
beforeEach(() => {
|
||||
xhr = new MockXHR();
|
||||
loader = new TemplateLoader(xhr, new FakeUrlResolver());
|
||||
urlResolver = new FakeUrlResolver();
|
||||
styleUrlResolver = new StyleUrlResolver(urlResolver);
|
||||
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
loader = new TemplateLoader(xhr, styleInliner, styleUrlResolver);
|
||||
});
|
||||
|
||||
describe('html', () => {
|
||||
|
@ -46,6 +54,33 @@ export function main() {
|
|||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should resolve urls in styles', inject([AsyncTestCompleter], (async) => {
|
||||
xhr.expect('base/foo.html',
|
||||
'<style>.foo { background-image: url("double.jpg"); }</style>');
|
||||
var template = new ViewDefinition({templateAbsUrl: 'base/foo.html'});
|
||||
loader.load(template).then((el) => {
|
||||
expect(DOM.content(el))
|
||||
.toHaveText(".foo { background-image: url('/base/double.jpg'); }");
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should inline styles', inject([AsyncTestCompleter], (async) => {
|
||||
let xhr = new FakeXHR();
|
||||
xhr.reply('base/foo.html', '<style>@import "foo.css";</style>');
|
||||
xhr.reply('/base/foo.css', '/* foo.css */');
|
||||
|
||||
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
let loader = new TemplateLoader(xhr, styleInliner, styleUrlResolver);
|
||||
|
||||
var template = new ViewDefinition({templateAbsUrl: 'base/foo.html'});
|
||||
loader.load(template).then((el) => {
|
||||
expect(DOM.getInnerHTML(el)).toEqual("<style>/* foo.css */\n</style>");
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return a new template element on each call',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var firstEl;
|
||||
|
@ -91,8 +126,17 @@ export function main() {
|
|||
var template = new ViewDefinition({template: 'html', styles: ['style 1', 'style 2']});
|
||||
loader.load(template).then((el) => {
|
||||
expect(DOM.getInnerHTML(el))
|
||||
.toEqual(
|
||||
'<style type="text/css">style 1</style><style type="text/css">style 2</style>html');
|
||||
.toEqual('<style>style 1</style><style>style 2</style>html');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should resolve urls in inline styles', inject([AsyncTestCompleter], (async) => {
|
||||
var template = new ViewDefinition(
|
||||
{template: 'html', styles: ['.foo { background-image: url("double.jpg"); }']});
|
||||
loader.load(template).then((el) => {
|
||||
expect(DOM.getInnerHTML(el))
|
||||
.toEqual("<style>.foo { background-image: url('/double.jpg'); }</style>html");
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
@ -108,13 +152,29 @@ export function main() {
|
|||
});
|
||||
loader.load(template).then((el) => {
|
||||
expect(DOM.getInnerHTML(el))
|
||||
.toEqual(
|
||||
'<style type="text/css">i1</style><style type="text/css">1</style><style type="text/css">2</style>xhr template');
|
||||
.toEqual('<style>i1</style><style>1</style><style>2</style>xhr template');
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should inline styles', inject([AsyncTestCompleter], (async) => {
|
||||
let xhr = new FakeXHR();
|
||||
xhr.reply('base/foo.html', '<p>template</p>');
|
||||
xhr.reply('/base/foo.css', '/* foo.css */');
|
||||
|
||||
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
let loader = new TemplateLoader(xhr, styleInliner, styleUrlResolver);
|
||||
|
||||
var template = new ViewDefinition(
|
||||
{templateAbsUrl: 'base/foo.html', styles: ['@import "foo.css";']});
|
||||
loader.load(template).then((el) => {
|
||||
expect(DOM.getInnerHTML(el)).toEqual("<style>/* foo.css */\n</style><p>template</p>");
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should return a rejected Promise when XHR loading fails',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
xhr.expect('base/foo.css', null);
|
||||
|
@ -136,5 +196,27 @@ class SomeComponent {}
|
|||
class FakeUrlResolver extends UrlResolver {
|
||||
constructor() { super(); }
|
||||
|
||||
resolve(baseUrl: string, url: string): string { return baseUrl + url; }
|
||||
resolve(baseUrl: string, url: string): string {
|
||||
if (url.length > 0 && url[0] == '/') return url;
|
||||
if (!isPresent(baseUrl)) return `/${url}`;
|
||||
var parts: List<string> = baseUrl.split('/');
|
||||
if (parts.length > 1) {
|
||||
ListWrapper.removeLast(parts);
|
||||
}
|
||||
parts.push(url);
|
||||
return '/' + parts.join('/');
|
||||
}
|
||||
}
|
||||
|
||||
class FakeXHR extends XHR {
|
||||
_responses: Map<string, string> = new Map();
|
||||
|
||||
constructor() { super(); }
|
||||
|
||||
get(url: string): Promise<string> {
|
||||
return this._responses.has(url) ? PromiseWrapper.resolve(this._responses.get(url)) :
|
||||
PromiseWrapper.reject('xhr error', null);
|
||||
}
|
||||
|
||||
reply(url: string, response: string): void { this._responses.set(url, response); }
|
||||
}
|
||||
|
|
|
@ -13,12 +13,7 @@ import {
|
|||
normalizeCSS
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
|
||||
import {
|
||||
EmulatedScopedShadowDomStrategy,
|
||||
|
@ -26,21 +21,14 @@ import {
|
|||
import {
|
||||
resetShadowDomCache,
|
||||
} from 'angular2/src/render/dom/shadow_dom/util';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
|
||||
export function main() {
|
||||
describe('EmulatedScopedShadowDomStrategy', () => {
|
||||
var xhr, styleHost, strategy;
|
||||
var styleHost, strategy;
|
||||
|
||||
beforeEach(() => {
|
||||
var urlResolver = new UrlResolver();
|
||||
var styleUrlResolver = new StyleUrlResolver(urlResolver);
|
||||
xhr = new FakeXHR();
|
||||
var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
styleHost = el('<div></div>');
|
||||
strategy = new EmulatedScopedShadowDomStrategy(styleInliner, styleUrlResolver, styleHost);
|
||||
strategy = new EmulatedScopedShadowDomStrategy(styleHost);
|
||||
resetShadowDomCache();
|
||||
});
|
||||
|
||||
|
@ -49,34 +37,12 @@ export function main() {
|
|||
expect(strategy.prepareShadowRoot(host)).toBe(host);
|
||||
});
|
||||
|
||||
it('should rewrite style urls', () => {
|
||||
var styleElement = el('<style>.foo {background-image: url("img.jpg");}</style>');
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
expect(normalizeCSS(DOM.getText(styleElement)))
|
||||
.toEqual(".foo[_ngcontent-0] { background-image:url(http://base/img.jpg); }");
|
||||
});
|
||||
|
||||
it('should scope styles', () => {
|
||||
var styleElement = el('<style>.foo {} :host {}</style>');
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
expect(styleElement).toHaveText(".foo[_ngcontent-0] {\n\n}\n\n[_nghost-0] {\n\n}");
|
||||
});
|
||||
|
||||
it('should inline @import rules', inject([AsyncTestCompleter], (async) => {
|
||||
xhr.reply('http://base/one.css', '.one {}');
|
||||
|
||||
var styleElement = el('<style>@import "one.css";</style>');
|
||||
var stylePromise =
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
expect(stylePromise).toBePromise();
|
||||
expect(styleElement).toHaveText('');
|
||||
|
||||
stylePromise.then((_) => {
|
||||
expect(styleElement).toHaveText('.one[_ngcontent-0] {\n\n}');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return the same style given the same component', () => {
|
||||
var styleElement = el('<style>.foo {} :host {}</style>');
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
|
@ -97,22 +63,6 @@ export function main() {
|
|||
expect(DOM.getText(styleElement)).not.toEqual(DOM.getText(styleElement2));
|
||||
});
|
||||
|
||||
it('should move the style element to the style host when @imports are present',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
xhr.reply('http://base/one.css', '.one {}');
|
||||
|
||||
var compileElement = el('<div><style>@import "one.css";</style></div>');
|
||||
var styleElement = DOM.firstChild(compileElement);
|
||||
var stylePromise =
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
|
||||
stylePromise.then((_) => {
|
||||
expect(compileElement).toHaveText('');
|
||||
expect(styleHost).toHaveText('.one[_ngcontent-0] {\n\n}');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should move the style element to the style host', () => {
|
||||
var compileElement = el('<div><style>.one {}</style></div>');
|
||||
var styleElement = DOM.firstChild(compileElement);
|
||||
|
@ -136,23 +86,3 @@ export function main() {
|
|||
|
||||
});
|
||||
}
|
||||
|
||||
class FakeXHR extends XHR {
|
||||
_responses: Map<string, string>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._responses = new Map();
|
||||
}
|
||||
|
||||
get(url: string): Promise<string> {
|
||||
var response = this._responses.get(url);
|
||||
if (isBlank(response)) {
|
||||
return PromiseWrapper.reject('xhr error', null);
|
||||
}
|
||||
|
||||
return PromiseWrapper.resolve(response);
|
||||
}
|
||||
|
||||
reply(url: string, response: string) { this._responses.set(url, response); }
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {Map, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {
|
||||
EmulatedUnscopedShadowDomStrategy,
|
||||
|
@ -21,28 +21,16 @@ import {
|
|||
import {
|
||||
resetShadowDomCache,
|
||||
} from 'angular2/src/render/dom/shadow_dom/util';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
|
||||
export function main() {
|
||||
var strategy;
|
||||
|
||||
describe('EmulatedUnscopedShadowDomStrategy', () => {
|
||||
var xhr, styleHost;
|
||||
var styleHost;
|
||||
|
||||
beforeEach(() => {
|
||||
var urlResolver = new UrlResolver();
|
||||
var styleUrlResolver = new StyleUrlResolver(urlResolver);
|
||||
xhr = new FakeXHR();
|
||||
var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
styleHost = el('<div></div>');
|
||||
strategy = new EmulatedUnscopedShadowDomStrategy(styleInliner, styleUrlResolver, styleHost);
|
||||
strategy = new EmulatedUnscopedShadowDomStrategy(styleHost);
|
||||
resetShadowDomCache();
|
||||
});
|
||||
|
||||
|
@ -51,27 +39,6 @@ export function main() {
|
|||
expect(strategy.prepareShadowRoot(host)).toBe(host);
|
||||
});
|
||||
|
||||
it('should rewrite style urls', () => {
|
||||
var styleElement = el('<style>.foo {background-image: url("img.jpg");}</style>');
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
expect(styleElement).toHaveText(".foo {background-image: url('http://base/img.jpg');}");
|
||||
});
|
||||
|
||||
it('should inline @import rules', inject([AsyncTestCompleter], (async) => {
|
||||
xhr.reply('http://base/one.css', '.one {}');
|
||||
|
||||
var styleElement = el('<style>@import "one.css";</style>');
|
||||
var stylePromise =
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
expect(stylePromise).toBePromise();
|
||||
expect(styleElement).toHaveText('');
|
||||
|
||||
stylePromise.then((_) => {
|
||||
expect(styleElement).toHaveText('.one {}\n');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should move the style element to the style host', () => {
|
||||
var compileElement = el('<div><style>.one {}</style></div>');
|
||||
var styleElement = DOM.firstChild(compileElement);
|
||||
|
@ -96,23 +63,3 @@ export function main() {
|
|||
|
||||
});
|
||||
}
|
||||
|
||||
class FakeXHR extends XHR {
|
||||
_responses: Map<string, string>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._responses = <Map<string, string>>{};
|
||||
}
|
||||
|
||||
get(url: string): Promise<string> {
|
||||
var response = this._responses[url];
|
||||
if (isBlank(response)) {
|
||||
return PromiseWrapper.reject('xhr error', null);
|
||||
}
|
||||
|
||||
return PromiseWrapper.resolve(response);
|
||||
}
|
||||
|
||||
reply(url: string, response: string) { this._responses[url] = response; }
|
||||
}
|
||||
|
|
|
@ -15,15 +15,6 @@ import {
|
|||
import {
|
||||
NativeShadowDomStrategy
|
||||
} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {Map} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
|
@ -31,14 +22,7 @@ export function main() {
|
|||
var strategy;
|
||||
|
||||
describe('NativeShadowDomStrategy', () => {
|
||||
var xhr;
|
||||
beforeEach(() => {
|
||||
var urlResolver = new UrlResolver();
|
||||
var styleUrlResolver = new StyleUrlResolver(urlResolver);
|
||||
xhr = new FakeXHR();
|
||||
var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
strategy = new NativeShadowDomStrategy(styleInliner, styleUrlResolver);
|
||||
});
|
||||
beforeEach(() => { strategy = new NativeShadowDomStrategy(); });
|
||||
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
it('should use the native shadow root', () => {
|
||||
|
@ -46,47 +30,5 @@ export function main() {
|
|||
expect(strategy.prepareShadowRoot(host)).toBe(DOM.getShadowRoot(host));
|
||||
});
|
||||
}
|
||||
|
||||
it('should rewrite style urls', () => {
|
||||
var styleElement = el('<style>.foo {background-image: url("img.jpg");}</style>');
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
expect(styleElement)
|
||||
.toHaveText(".foo {" + "background-image: url('http://base/img.jpg');" + "}");
|
||||
});
|
||||
|
||||
it('should inline @import rules', inject([AsyncTestCompleter], (async) => {
|
||||
xhr.reply('http://base/one.css', '.one {}');
|
||||
|
||||
var styleElement = el('<style>@import "one.css";</style>');
|
||||
var stylePromise =
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
expect(stylePromise).toBePromise();
|
||||
|
||||
stylePromise.then((_) => {
|
||||
expect(styleElement).toHaveText('.one {}\n');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class FakeXHR extends XHR {
|
||||
_responses: Map<string, string>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._responses = <Map<string, string>>{};
|
||||
}
|
||||
|
||||
get(url: string): Promise<string> {
|
||||
var response = this._responses[url];
|
||||
if (isBlank(response)) {
|
||||
return PromiseWrapper.reject('xhr error', null);
|
||||
}
|
||||
|
||||
return PromiseWrapper.resolve(response);
|
||||
}
|
||||
|
||||
reply(url: string, response: string) { this._responses[url] = response; }
|
||||
}
|
||||
|
|
|
@ -29,35 +29,21 @@ import {
|
|||
import {
|
||||
NativeShadowDomStrategy
|
||||
} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
|
||||
import {DomTestbed, elRef} from './dom_testbed';
|
||||
|
||||
export function main() {
|
||||
describe('ShadowDom integration tests', function() {
|
||||
var styleHost;
|
||||
var styleHost = DOM.createElement('div');
|
||||
|
||||
var strategies = {
|
||||
"scoped":
|
||||
bind(ShadowDomStrategy)
|
||||
.toFactory((styleInliner, styleUrlResolver) => new EmulatedScopedShadowDomStrategy(
|
||||
styleInliner, styleUrlResolver, styleHost),
|
||||
[StyleInliner, StyleUrlResolver]),
|
||||
"unscoped":
|
||||
bind(ShadowDomStrategy)
|
||||
.toFactory((styleInliner, styleUrlResolver) => new EmulatedUnscopedShadowDomStrategy(
|
||||
styleInliner, styleUrlResolver, null),
|
||||
[StyleInliner, StyleUrlResolver])
|
||||
"scoped": bind(ShadowDomStrategy).toValue(new EmulatedScopedShadowDomStrategy(styleHost)),
|
||||
"unscoped": bind(ShadowDomStrategy).toValue(new EmulatedUnscopedShadowDomStrategy(styleHost))
|
||||
};
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
StringMapWrapper.set(
|
||||
strategies, "native",
|
||||
bind(ShadowDomStrategy)
|
||||
.toFactory((styleInliner, styleUrlResolver) =>
|
||||
new NativeShadowDomStrategy(styleInliner, styleUrlResolver),
|
||||
[StyleInliner, StyleUrlResolver]));
|
||||
StringMapWrapper.set(strategies, "native",
|
||||
bind(ShadowDomStrategy).toValue(new NativeShadowDomStrategy()));
|
||||
}
|
||||
beforeEach(() => { styleHost = el('<div></div>'); });
|
||||
|
||||
StringMapWrapper.forEach(strategies, (strategyBinding, name) => {
|
||||
describe(`${name} shadow dom strategy`, () => {
|
||||
|
|
|
@ -16,10 +16,7 @@ import {Component, Directive, View} from 'angular2/angular2';
|
|||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||
|
||||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
@ -39,11 +36,9 @@ export function main() {
|
|||
var templateResolver = new MultipleTemplateResolver(
|
||||
count, [BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]);
|
||||
var urlResolver = new UrlResolver();
|
||||
var styleUrlResolver = new StyleUrlResolver(urlResolver);
|
||||
var styleInliner = new StyleInliner(null, styleUrlResolver, urlResolver);
|
||||
var shadowDomStrategy = new NativeShadowDomStrategy(styleInliner, styleUrlResolver);
|
||||
var shadowDomStrategy = new NativeShadowDomStrategy();
|
||||
var renderCompiler = new rc.DefaultDomCompiler(new Parser(new Lexer()), shadowDomStrategy,
|
||||
new TemplateLoader(null, urlResolver));
|
||||
new TemplateLoader(null, null, null));
|
||||
var compiler = new Compiler(
|
||||
reader, cache, templateResolver, new ComponentUrlMapper(), urlResolver, renderCompiler,
|
||||
new ProtoViewFactory(new DynamicChangeDetection(null)), new FakeAppRootUrl());
|
||||
|
|
|
@ -27,16 +27,16 @@ export class DemoUrlResolver extends UrlResolver {
|
|||
|
||||
constructor() {
|
||||
super();
|
||||
if (isBlank(UrlResolver.a)) {
|
||||
UrlResolver.a = DOM.createElement('a');
|
||||
if (isBlank(DemoUrlResolver.a)) {
|
||||
DemoUrlResolver.a = DOM.createElement('a');
|
||||
}
|
||||
this.isInPubServe = _isInPubServe();
|
||||
}
|
||||
|
||||
resolve(baseUrl: string, url: string): string {
|
||||
if (isBlank(baseUrl)) {
|
||||
DOM.resolveAndSetHref(UrlResolver.a, url, null);
|
||||
return DOM.getHref(UrlResolver.a);
|
||||
DOM.resolveAndSetHref(DemoUrlResolver.a, url, null);
|
||||
return DOM.getHref(DemoUrlResolver.a);
|
||||
}
|
||||
|
||||
if (isBlank(url) || url == '') {
|
||||
|
|
Loading…
Reference in New Issue