feature(ShadowDomTransformer): create a compiler step to transform the shadow DOM

This commit is contained in:
Victor Berchet
2015-02-05 15:07:06 +01:00
parent 7bf5ab8f43
commit 47042bc503
15 changed files with 317 additions and 165 deletions

View File

@ -39,6 +39,8 @@ export class CompileElement {
inheritedElementBinder:ElementBinder;
distanceToParentInjector:number;
compileChildren: boolean;
ignoreBindings: boolean;
constructor(element:Element) {
this.element = element;
this._attrs = null;
@ -64,6 +66,8 @@ export class CompileElement {
this.inheritedElementBinder = null;
this.distanceToParentInjector = 0;
this.compileChildren = true;
// set to true to ignore all the bindings on the element
this.ignoreBindings = false;
}
refreshAttrs() {

View File

@ -1,5 +1,5 @@
import {ChangeDetection, Parser} from 'angular2/change_detection';
import {List} from 'angular2/src/facade/collection';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {PropertyBindingParser} from './property_binding_parser';
import {TextInterpolationParser} from './text_interpolation_parser';
@ -9,9 +9,11 @@ import {ElementBindingMarker} from './element_binding_marker';
import {ProtoViewBuilder} from './proto_view_builder';
import {ProtoElementInjectorBuilder} from './proto_element_injector_builder';
import {ElementBinderBuilder} from './element_binder_builder';
import {ShadowDomTransformer} from './shadow_dom_transformer';
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {stringify} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/facade/dom';
/**
* Default steps used for compiling a template.
@ -27,8 +29,14 @@ export function createDefaultSteps(
var compilationUnit = stringify(compiledComponent.type);
return [
new ViewSplitter(parser, compilationUnit),
var steps = [new ViewSplitter(parser, compilationUnit)];
if (!(shadowDomStrategy instanceof NativeShadowDomStrategy)) {
var step = new ShadowDomTransformer(compiledComponent, shadowDomStrategy, DOM.defaultDoc().head);
ListWrapper.push(steps, step);
}
steps = ListWrapper.concat(steps,[
new PropertyBindingParser(parser, compilationUnit),
new DirectiveParser(directives),
new TextInterpolationParser(parser, compilationUnit),
@ -36,5 +44,7 @@ export function createDefaultSteps(
new ProtoViewBuilder(changeDetection, shadowDomStrategy),
new ProtoElementInjectorBuilder(),
new ElementBinderBuilder(parser, compilationUnit)
];
]);
return steps;
}

View File

@ -26,6 +26,10 @@ const NG_BINDING_CLASS = 'ng-binding';
*/
export class ElementBindingMarker extends CompileStep {
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
if (current.ignoreBindings) {
return;
}
var hasBindings =
(isPresent(current.textNodeBindings) && MapWrapper.size(current.textNodeBindings)>0) ||
(isPresent(current.propertyBindings) && MapWrapper.size(current.propertyBindings)>0) ||

View File

@ -38,6 +38,10 @@ export class PropertyBindingParser extends CompileStep {
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
if (current.ignoreBindings) {
return;
}
var attrs = current.attrs();
MapWrapper.forEach(attrs, (attrValue, attrName) => {
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);

View File

@ -0,0 +1,79 @@
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {shimCssText} from 'angular2/src/core/compiler/shadow_dom_emulation/shim_css';
import {DOM, Element} from 'angular2/src/facade/dom';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {StringMapWrapper} from 'angular2/src/facade/collection';
var _cssCache = StringMapWrapper.create();
export class ShadowDomTransformer extends CompileStep {
_selector: string;
_strategy: ShadowDomStrategy;
_styleHost: Element;
_lastInsertedStyle: Element;
constructor(cmpMetadata: DirectiveMetadata, strategy: ShadowDomStrategy, styleHost: Element) {
super();
this._strategy = strategy;
this._selector = cmpMetadata.annotation.selector;
this._styleHost = styleHost;
this._lastInsertedStyle = null;
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
// May be remove the styles
if (DOM.tagName(current.element) == 'STYLE') {
current.ignoreBindings = true;
if (this._strategy.extractStyles()) {
DOM.remove(current.element);
var css = DOM.getText(current.element);
if (this._strategy.shim()) {
// The css generated here is unique for the component (because of the shim).
// Then we do not need to cache it.
css = shimCssText(css, this._selector);
this._insertStyle(this._styleHost, css);
} else {
var seen = isPresent(StringMapWrapper.get(_cssCache, css));
if (!seen) {
StringMapWrapper.set(_cssCache, css, true);
this._insertStyle(this._styleHost, css);
}
}
}
} else {
if (this._strategy.shim()) {
try {
DOM.setAttribute(current.element, this._selector, '');
} catch(e) {
// TODO(vicb): for now only simple selector (tag name) are supported
}
}
}
}
clearCache() {
_cssCache = StringMapWrapper.create();
}
_insertStyle(el: Element, css: string) {
var style = DOM.createStyleElement(css);
if (isBlank(this._lastInsertedStyle)) {
var firstChild = DOM.firstChild(el);
if (isPresent(firstChild)) {
DOM.insertBefore(firstChild, style);
} else {
DOM.appendChild(el, style);
}
} else {
DOM.insertAfter(this._lastInsertedStyle, style);
}
this._lastInsertedStyle = style;
}
}

View File

@ -23,7 +23,7 @@ export class TextInterpolationParser extends CompileStep {
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
if (!current.compileChildren) {
if (!current.compileChildren || current.ignoreBindings) {
return;
}
var element = current.element;