feat(components): initial implementation of emulated content tag
This commit is contained in:
parent
0f8f4801bd
commit
fbcc59dc67
|
@ -13,6 +13,7 @@ import {createDefaultSteps} from './pipeline/default_steps';
|
||||||
import {TemplateLoader} from './template_loader';
|
import {TemplateLoader} from './template_loader';
|
||||||
import {DirectiveMetadata} from './directive_metadata';
|
import {DirectiveMetadata} from './directive_metadata';
|
||||||
import {Component} from '../annotations/annotations';
|
import {Component} from '../annotations/annotations';
|
||||||
|
import {Content} from './shadow_dom_emulation/content_tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache that stores the ProtoView of the template of a component.
|
* Cache that stores the ProtoView of the template of a component.
|
||||||
|
@ -60,13 +61,8 @@ export class Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
createSteps(component:DirectiveMetadata):List<CompileStep> {
|
createSteps(component:DirectiveMetadata):List<CompileStep> {
|
||||||
var annotation: Component = component.annotation;
|
var dirs = ListWrapper.map(component.componentDirectives, (d) => this._reader.read(d));
|
||||||
var directives = annotation.template.directives;
|
return createDefaultSteps(this._parser, component, dirs);
|
||||||
var annotatedDirectives = ListWrapper.create();
|
|
||||||
for (var i=0; i<directives.length; i++) {
|
|
||||||
ListWrapper.push(annotatedDirectives, this._reader.read(directives[i]));
|
|
||||||
}
|
|
||||||
return createDefaultSteps(this._parser, component, annotatedDirectives);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compile(component:Type, templateRoot:Element = null):Promise<ProtoView> {
|
compile(component:Type, templateRoot:Element = null):Promise<ProtoView> {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {Type, FIELD} from 'facade/lang';
|
import {Type, FIELD} from 'facade/lang';
|
||||||
import {Directive} from '../annotations/annotations'
|
import {Directive} from '../annotations/annotations'
|
||||||
|
import {List} from 'facade/collection'
|
||||||
import {ShadowDomStrategy} from './shadow_dom';
|
import {ShadowDomStrategy} from './shadow_dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,10 +10,13 @@ export class DirectiveMetadata {
|
||||||
type:Type;
|
type:Type;
|
||||||
annotation:Directive;
|
annotation:Directive;
|
||||||
shadowDomStrategy:ShadowDomStrategy;
|
shadowDomStrategy:ShadowDomStrategy;
|
||||||
|
componentDirectives:List<Type>;
|
||||||
|
|
||||||
constructor(type:Type, annotation:Directive, shadowDomStrategy:ShadowDomStrategy) {
|
constructor(type:Type, annotation:Directive, shadowDomStrategy:ShadowDomStrategy,
|
||||||
|
componentDirectives:List<Type>) {
|
||||||
this.annotation = annotation;
|
this.annotation = annotation;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.shadowDomStrategy = shadowDomStrategy;
|
this.shadowDomStrategy = shadowDomStrategy;
|
||||||
|
this.componentDirectives = componentDirectives;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {Type, isPresent, BaseException, stringify} from 'facade/lang';
|
import {Type, isPresent, BaseException, stringify} from 'facade/lang';
|
||||||
|
import {List, ListWrapper} from 'facade/collection';
|
||||||
import {Directive, Component} from '../annotations/annotations';
|
import {Directive, Component} from '../annotations/annotations';
|
||||||
import {DirectiveMetadata} from './directive_metadata';
|
import {DirectiveMetadata} from './directive_metadata';
|
||||||
import {reflector} from 'reflection/reflection';
|
import {reflector} from 'reflection/reflection';
|
||||||
|
@ -12,11 +13,17 @@ export class DirectiveMetadataReader {
|
||||||
var annotation = annotations[i];
|
var annotation = annotations[i];
|
||||||
|
|
||||||
if (annotation instanceof Component) {
|
if (annotation instanceof Component) {
|
||||||
return new DirectiveMetadata(type, annotation, this.parseShadowDomStrategy(annotation));
|
var shadowDomStrategy = this.parseShadowDomStrategy(annotation);
|
||||||
|
return new DirectiveMetadata(
|
||||||
|
type,
|
||||||
|
annotation,
|
||||||
|
shadowDomStrategy,
|
||||||
|
this.componentDirectivesMetadata(annotation, shadowDomStrategy)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (annotation instanceof Directive) {
|
if (annotation instanceof Directive) {
|
||||||
return new DirectiveMetadata(type, annotation, null);
|
return new DirectiveMetadata(type, annotation, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,4 +33,16 @@ export class DirectiveMetadataReader {
|
||||||
parseShadowDomStrategy(annotation:Component):ShadowDomStrategy{
|
parseShadowDomStrategy(annotation:Component):ShadowDomStrategy{
|
||||||
return isPresent(annotation.shadowDom) ? annotation.shadowDom : ShadowDomNative;
|
return isPresent(annotation.shadowDom) ? annotation.shadowDom : ShadowDomNative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDirectivesMetadata(annotation:Component, shadowDomStrategy:ShadowDomStrategy):List<Type> {
|
||||||
|
var polyDirs = shadowDomStrategy.polyfillDirectives();
|
||||||
|
var template = annotation.template;
|
||||||
|
var templateDirs = isPresent(template) && isPresent(template.directives) ? template.directives : [];
|
||||||
|
|
||||||
|
var res = [];
|
||||||
|
res = ListWrapper.concat(res, templateDirs)
|
||||||
|
res = ListWrapper.concat(res, polyDirs)
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {List, ListWrapper} from 'facade/collection';
|
||||||
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'di/di';
|
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'di/di';
|
||||||
import {Parent, Ancestor} from 'core/annotations/visibility';
|
import {Parent, Ancestor} from 'core/annotations/visibility';
|
||||||
import {View} from 'core/compiler/view';
|
import {View} from 'core/compiler/view';
|
||||||
|
import {LightDom, SourceLightDom, DestinationLightDom} from 'core/compiler/shadow_dom_emulation/light_dom';
|
||||||
import {ViewPort} from 'core/compiler/viewport';
|
import {ViewPort} from 'core/compiler/viewport';
|
||||||
import {NgElement} from 'core/dom/element';
|
import {NgElement} from 'core/dom/element';
|
||||||
|
|
||||||
|
@ -19,11 +20,16 @@ class StaticKeys {
|
||||||
viewId:int;
|
viewId:int;
|
||||||
ngElementId:int;
|
ngElementId:int;
|
||||||
viewPortId:int;
|
viewPortId:int;
|
||||||
|
destinationLightDomId:int;
|
||||||
|
sourceLightDomId:int;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
|
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
|
||||||
this.viewId = Key.get(View).id;
|
this.viewId = Key.get(View).id;
|
||||||
this.ngElementId = Key.get(NgElement).id;
|
this.ngElementId = Key.get(NgElement).id;
|
||||||
this.viewPortId = Key.get(ViewPort).id;
|
this.viewPortId = Key.get(ViewPort).id;
|
||||||
|
this.destinationLightDomId = Key.get(DestinationLightDom).id;
|
||||||
|
this.sourceLightDomId = Key.get(SourceLightDom).id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static instance() {
|
static instance() {
|
||||||
|
@ -105,10 +111,12 @@ export class PreBuiltObjects {
|
||||||
view:View;
|
view:View;
|
||||||
element:NgElement;
|
element:NgElement;
|
||||||
viewPort:ViewPort;
|
viewPort:ViewPort;
|
||||||
constructor(view, element:NgElement, viewPort: ViewPort) {
|
lightDom:LightDom;
|
||||||
|
constructor(view, element:NgElement, viewPort:ViewPort, lightDom:LightDom) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.viewPort = viewPort;
|
this.viewPort = viewPort;
|
||||||
|
this.lightDom = lightDom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,6 +314,15 @@ export class ElementInjector extends TreeNode {
|
||||||
return this._getByKey(Key.get(token), 0, null);
|
return this._getByKey(Key.get(token), 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasDirective(type:Type):boolean {
|
||||||
|
return this._getDirectiveByKeyId(Key.get(type).id) !== _undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPreBuiltObject(type:Type):boolean {
|
||||||
|
var pb = this._getPreBuiltObjectByKeyId(Key.get(type).id);
|
||||||
|
return pb !== _undefined && isPresent(pb);
|
||||||
|
}
|
||||||
|
|
||||||
getComponent() {
|
getComponent() {
|
||||||
if (this._proto._binding0IsComponent) {
|
if (this._proto._binding0IsComponent) {
|
||||||
return this._obj0;
|
return this._obj0;
|
||||||
|
@ -421,11 +438,15 @@ export class ElementInjector extends TreeNode {
|
||||||
var staticKeys = StaticKeys.instance();
|
var staticKeys = StaticKeys.instance();
|
||||||
if (keyId === staticKeys.viewId) return this._preBuiltObjects.view;
|
if (keyId === staticKeys.viewId) return this._preBuiltObjects.view;
|
||||||
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
|
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
|
||||||
if (keyId === staticKeys.viewPortId) {
|
if (keyId === staticKeys.viewPortId) return this._preBuiltObjects.viewPort;
|
||||||
if (isBlank(staticKeys.viewPortId)) throw new BaseException(
|
if (keyId === staticKeys.destinationLightDomId) {
|
||||||
'ViewPort is constructed only for @Template directives');
|
var p:ElementInjector = this._parent;
|
||||||
return this._preBuiltObjects.viewPort;
|
return isPresent(p) ? p._preBuiltObjects.lightDom : null;
|
||||||
}
|
}
|
||||||
|
if (keyId === staticKeys.sourceLightDomId) {
|
||||||
|
return this._host._preBuiltObjects.lightDom;
|
||||||
|
}
|
||||||
|
|
||||||
//TODO add other objects as needed
|
//TODO add other objects as needed
|
||||||
return _undefined;
|
return _undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import {Decorator} from '../../annotations/annotations';
|
||||||
|
import {SourceLightDom, DestinationLightDom, LightDom} from './light_dom';
|
||||||
|
import {Inject} from 'di/di';
|
||||||
|
import {Element, Node, DOM} from 'facade/dom';
|
||||||
|
import {List, ListWrapper} from 'facade/collection';
|
||||||
|
import {NgElement} from 'core/dom/element';
|
||||||
|
|
||||||
|
var _scriptTemplate = DOM.createScriptTag('type', 'ng/content')
|
||||||
|
|
||||||
|
@Decorator({
|
||||||
|
selector: 'content'
|
||||||
|
})
|
||||||
|
export class Content {
|
||||||
|
_destinationLightDom:LightDom;
|
||||||
|
|
||||||
|
_beginScript:Element;
|
||||||
|
_endScript:Element;
|
||||||
|
|
||||||
|
select:string;
|
||||||
|
|
||||||
|
constructor(@Inject(DestinationLightDom) destinationLightDom, contentEl:NgElement) {
|
||||||
|
this._destinationLightDom = destinationLightDom;
|
||||||
|
|
||||||
|
this.select = contentEl.getAttribute('select');
|
||||||
|
|
||||||
|
this._replaceContentElementWithScriptTags(contentEl.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(nodes:List<Node>) {
|
||||||
|
DOM.insertAllBefore(this._endScript, nodes);
|
||||||
|
this._removeNodesUntil(ListWrapper.isEmpty(nodes) ? this._endScript : nodes[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_replaceContentElementWithScriptTags(contentEl:Element) {
|
||||||
|
this._beginScript = DOM.clone(_scriptTemplate);
|
||||||
|
this._endScript = DOM.clone(_scriptTemplate);
|
||||||
|
|
||||||
|
DOM.insertBefore(contentEl, this._beginScript);
|
||||||
|
DOM.insertBefore(contentEl, this._endScript);
|
||||||
|
DOM.removeChild(DOM.parentElement(contentEl), contentEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeNodesUntil(node:Node) {
|
||||||
|
var p = DOM.parentElement(this._beginScript);
|
||||||
|
for (var next = DOM.nextSibling(this._beginScript);
|
||||||
|
next !== node;
|
||||||
|
next = DOM.nextSibling(this._beginScript)) {
|
||||||
|
DOM.removeChild(p, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
import {Element, Node, DOM} from 'facade/dom';
|
||||||
|
import {List, ListWrapper} from 'facade/collection';
|
||||||
|
import {isBlank, isPresent} from 'facade/lang';
|
||||||
|
|
||||||
|
import {View} from '../view';
|
||||||
|
import {ElementInjector} from '../element_injector';
|
||||||
|
import {ViewPort} from '../viewport';
|
||||||
|
import {Content} from './content_tag';
|
||||||
|
|
||||||
|
export class SourceLightDom {}
|
||||||
|
export class DestinationLightDom {}
|
||||||
|
|
||||||
|
// TODO: LightDom should implement SourceLightDom and DestinationLightDom
|
||||||
|
// once interfaces are supported
|
||||||
|
export class LightDom {
|
||||||
|
lightDomView:View;
|
||||||
|
shadowDomView:View;
|
||||||
|
roots:List<Node>;
|
||||||
|
|
||||||
|
constructor(lightDomView:View, shadowDomView:View, element:Element) {
|
||||||
|
this.lightDomView = lightDomView;
|
||||||
|
this.shadowDomView = shadowDomView;
|
||||||
|
this.roots = DOM.childNodesAsList(element);
|
||||||
|
DOM.clearNodes(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
redistribute() {
|
||||||
|
redistributeNodes(this.contentTags(), this.expandedDomNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
contentTags(): List<Content> {
|
||||||
|
return this._collectAllContentTags(this.shadowDomView, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
_collectAllContentTags(item, acc:List<Content>):List<Content> {
|
||||||
|
ListWrapper.forEach(item.elementInjectors, (ei) => {
|
||||||
|
if (ei.hasDirective(Content)) {
|
||||||
|
ListWrapper.push(acc, ei.get(Content));
|
||||||
|
|
||||||
|
} else if (ei.hasPreBuiltObject(ViewPort)) {
|
||||||
|
var vp = ei.get(ViewPort);
|
||||||
|
ListWrapper.forEach(vp.contentTagContainers(), (c) => {
|
||||||
|
this._collectAllContentTags(c, acc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
expandedDomNodes():List {
|
||||||
|
var res = [];
|
||||||
|
ListWrapper.forEach(this.roots, (root) => {
|
||||||
|
// TODO: vsavkin calculcate this info statically when creating light dom
|
||||||
|
var viewPort = this.lightDomView.getViewPortByTemplateElement(root);
|
||||||
|
if (isPresent(viewPort)) {
|
||||||
|
res = ListWrapper.concat(res, viewPort.nodes());
|
||||||
|
} else {
|
||||||
|
ListWrapper.push(res, root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function redistributeNodes(contents:List<Content>, nodes:List<Node>) {
|
||||||
|
for (var i = 0; i < contents.length; ++i) {
|
||||||
|
var content = contents[i];
|
||||||
|
var select = content.select;
|
||||||
|
var matchSelector = (n) => DOM.elementMatches(n, select);
|
||||||
|
|
||||||
|
if (isBlank(select)) {
|
||||||
|
content.insert(nodes);
|
||||||
|
ListWrapper.clear(nodes);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var matchingNodes = ListWrapper.filter(nodes, matchSelector);
|
||||||
|
content.insert(matchingNodes);
|
||||||
|
ListWrapper.removeAll(nodes, matchingNodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,15 @@
|
||||||
import {CONST} from 'facade/lang';
|
import {CONST} from 'facade/lang';
|
||||||
import {DOM} from 'facade/dom';
|
import {DOM, Element} from 'facade/dom';
|
||||||
import {Element} from 'facade/dom';
|
import {List} from 'facade/collection';
|
||||||
import {View} from './view';
|
import {View} from './view';
|
||||||
|
import {Content} from './shadow_dom_emulation/content_tag';
|
||||||
|
import {LightDom} from './shadow_dom_emulation/light_dom';
|
||||||
|
|
||||||
export class ShadowDomStrategy {
|
export class ShadowDomStrategy {
|
||||||
@CONST() constructor() {}
|
@CONST() constructor() {}
|
||||||
attachTemplate(el:Element, view:View){}
|
attachTemplate(el:Element, view:View){}
|
||||||
|
constructLightDom(lightDomView:View, shadowDomView:View, el:Element){}
|
||||||
|
polyfillDirectives():List<Type>{ return null; };
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EmulatedShadowDomStrategy extends ShadowDomStrategy {
|
export class EmulatedShadowDomStrategy extends ShadowDomStrategy {
|
||||||
|
@ -14,6 +18,14 @@ export class EmulatedShadowDomStrategy extends ShadowDomStrategy {
|
||||||
DOM.clearNodes(el);
|
DOM.clearNodes(el);
|
||||||
moveViewNodesIntoParent(el, view);
|
moveViewNodesIntoParent(el, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructLightDom(lightDomView:View, shadowDomView:View, el:Element){
|
||||||
|
return new LightDom(lightDomView, shadowDomView, el);
|
||||||
|
}
|
||||||
|
|
||||||
|
polyfillDirectives():List<Type> {
|
||||||
|
return [Content];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
||||||
|
@ -21,6 +33,14 @@ export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
||||||
attachTemplate(el:Element, view:View){
|
attachTemplate(el:Element, view:View){
|
||||||
moveViewNodesIntoParent(el.createShadowRoot(), view);
|
moveViewNodesIntoParent(el.createShadowRoot(), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructLightDom(lightDomView:View, shadowDomView:View, el:Element){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
polyfillDirectives():List<Type> {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveViewNodesIntoParent(parent, view) {
|
function moveViewNodesIntoParent(parent, view) {
|
||||||
|
|
|
@ -12,6 +12,8 @@ import {Injector} from 'di/di';
|
||||||
import {NgElement} from 'core/dom/element';
|
import {NgElement} from 'core/dom/element';
|
||||||
import {ViewPort} from './viewport';
|
import {ViewPort} from './viewport';
|
||||||
import {OnChange} from './interfaces';
|
import {OnChange} from './interfaces';
|
||||||
|
import {Content} from './shadow_dom_emulation/content_tag';
|
||||||
|
import {LightDom, DestinationLightDom} from './shadow_dom_emulation/light_dom';
|
||||||
|
|
||||||
const NG_BINDING_CLASS = 'ng-binding';
|
const NG_BINDING_CLASS = 'ng-binding';
|
||||||
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
||||||
|
@ -38,6 +40,7 @@ export class View {
|
||||||
proto: ProtoView;
|
proto: ProtoView;
|
||||||
context: any;
|
context: any;
|
||||||
contextWithLocals:ContextWithVariableBindings;
|
contextWithLocals:ContextWithVariableBindings;
|
||||||
|
|
||||||
constructor(proto:ProtoView, nodes:List<Node>, protoRecordRange:ProtoRecordRange, protoContextLocals:Map) {
|
constructor(proto:ProtoView, nodes:List<Node>, protoRecordRange:ProtoRecordRange, protoContextLocals:Map) {
|
||||||
this.proto = proto;
|
this.proto = proto;
|
||||||
this.nodes = nodes;
|
this.nodes = nodes;
|
||||||
|
@ -156,7 +159,12 @@ export class View {
|
||||||
// componentChildViews
|
// componentChildViews
|
||||||
if (isPresent(shadowDomAppInjector)) {
|
if (isPresent(shadowDomAppInjector)) {
|
||||||
this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector,
|
this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector,
|
||||||
elementInjector, elementInjector.getComponent());
|
elementInjector, elementInjector.getComponent());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresent(componentDirective)) {
|
||||||
|
var lightDom = this.preBuiltObjects[i].lightDom;
|
||||||
|
if (isPresent(lightDom)) lightDom.redistribute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,6 +199,16 @@ export class View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getViewPortByTemplateElement(node):ViewPort {
|
||||||
|
if (!(node instanceof Element)) return null;
|
||||||
|
|
||||||
|
for (var i = 0; i < this.viewPorts.length; ++i) {
|
||||||
|
if (this.viewPorts[i].templateElement === node) return this.viewPorts[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
_invokeMementoForRecords(records:List<Record>) {
|
_invokeMementoForRecords(records:List<Record>) {
|
||||||
for(var i = 0; i < records.length; ++i) {
|
for(var i = 0; i < records.length; ++i) {
|
||||||
this._invokeMementoFor(records[i]);
|
this._invokeMementoFor(records[i]);
|
||||||
|
@ -267,12 +285,18 @@ export class ProtoView {
|
||||||
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
||||||
instantiate(hostElementInjector: ElementInjector):View {
|
instantiate(hostElementInjector: ElementInjector):View {
|
||||||
var rootElementClone = this.instantiateInPlace ? this.element : DOM.clone(this.element);
|
var rootElementClone = this.instantiateInPlace ? this.element : DOM.clone(this.element);
|
||||||
var elementsWithBindings;
|
var elementsWithBindingsDynamic;
|
||||||
if (this.isTemplateElement) {
|
if (this.isTemplateElement) {
|
||||||
elementsWithBindings = DOM.querySelectorAll(rootElementClone.content, NG_BINDING_CLASS_SELECTOR);
|
elementsWithBindingsDynamic = DOM.querySelectorAll(rootElementClone.content, NG_BINDING_CLASS_SELECTOR);
|
||||||
} else {
|
} else {
|
||||||
elementsWithBindings = DOM.getElementsByClassName(rootElementClone, NG_BINDING_CLASS);
|
elementsWithBindingsDynamic= DOM.getElementsByClassName(rootElementClone, NG_BINDING_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var elementsWithBindings = ListWrapper.createFixedSize(elementsWithBindingsDynamic.length);
|
||||||
|
for (var i = 0; i < elementsWithBindingsDynamic.length; ++i) {
|
||||||
|
elementsWithBindings[i] = elementsWithBindingsDynamic[i];
|
||||||
|
}
|
||||||
|
|
||||||
var viewNodes;
|
var viewNodes;
|
||||||
if (this.isTemplateElement) {
|
if (this.isTemplateElement) {
|
||||||
var childNode = DOM.firstChild(rootElementClone.content);
|
var childNode = DOM.firstChild(rootElementClone.content);
|
||||||
|
@ -319,21 +343,6 @@ export class ProtoView {
|
||||||
}
|
}
|
||||||
elementInjectors[i] = elementInjector;
|
elementInjectors[i] = elementInjector;
|
||||||
|
|
||||||
// viewPorts
|
|
||||||
var viewPort = null;
|
|
||||||
if (isPresent(binder.templateDirective)) {
|
|
||||||
viewPort = new ViewPort(view, element, binder.nestedProtoView, elementInjector);
|
|
||||||
ListWrapper.push(viewPorts, viewPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
// preBuiltObjects
|
|
||||||
var preBuiltObject = null;
|
|
||||||
if (isPresent(elementInjector)) {
|
|
||||||
preBuiltObject = new PreBuiltObjects(view, new NgElement(element), viewPort);
|
|
||||||
}
|
|
||||||
preBuiltObjects[i] = preBuiltObject;
|
|
||||||
|
|
||||||
// elementsWithPropertyBindings
|
|
||||||
if (binder.hasElementPropertyBindings) {
|
if (binder.hasElementPropertyBindings) {
|
||||||
ListWrapper.push(elementsWithPropertyBindings, element);
|
ListWrapper.push(elementsWithPropertyBindings, element);
|
||||||
}
|
}
|
||||||
|
@ -351,13 +360,29 @@ export class ProtoView {
|
||||||
}
|
}
|
||||||
|
|
||||||
// componentChildViews
|
// componentChildViews
|
||||||
|
var lightDom = null;
|
||||||
if (isPresent(binder.componentDirective)) {
|
if (isPresent(binder.componentDirective)) {
|
||||||
var childView = binder.nestedProtoView.instantiate(elementInjector);
|
var childView = binder.nestedProtoView.instantiate(elementInjector);
|
||||||
view.recordRange.addRange(childView.recordRange);
|
view.recordRange.addRange(childView.recordRange);
|
||||||
|
|
||||||
|
lightDom = binder.componentDirective.shadowDomStrategy.constructLightDom(view, childView, element);
|
||||||
binder.componentDirective.shadowDomStrategy.attachTemplate(element, childView);
|
binder.componentDirective.shadowDomStrategy.attachTemplate(element, childView);
|
||||||
|
|
||||||
ListWrapper.push(componentChildViews, childView);
|
ListWrapper.push(componentChildViews, childView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// viewPorts
|
||||||
|
var viewPort = null;
|
||||||
|
if (isPresent(binder.templateDirective)) {
|
||||||
|
var destLightDom = this._parentElementLightDom(protoElementInjector, preBuiltObjects);
|
||||||
|
viewPort = new ViewPort(view, element, binder.nestedProtoView, elementInjector, destLightDom);
|
||||||
|
ListWrapper.push(viewPorts, viewPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
// preBuiltObjects
|
||||||
|
if (isPresent(elementInjector)) {
|
||||||
|
preBuiltObjects[i] = new PreBuiltObjects(view, new NgElement(element), viewPort, lightDom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view.init(elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings,
|
view.init(elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings,
|
||||||
|
@ -366,6 +391,11 @@ export class ProtoView {
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_parentElementLightDom(protoElementInjector:ProtoElementInjector, preBuiltObjects:List):LightDom {
|
||||||
|
var p = protoElementInjector.parent;
|
||||||
|
return isPresent(p) ? preBuiltObjects[p.index].lightDom : null;
|
||||||
|
}
|
||||||
|
|
||||||
bindVariable(contextName:string, templateName:string) {
|
bindVariable(contextName:string, templateName:string) {
|
||||||
MapWrapper.set(this.variableBindings, contextName, templateName);
|
MapWrapper.set(this.variableBindings, contextName, templateName);
|
||||||
MapWrapper.set(this.protoContextLocals, templateName, null);
|
MapWrapper.set(this.protoContextLocals, templateName, null);
|
||||||
|
|
|
@ -12,16 +12,18 @@ export class ViewPort {
|
||||||
defaultProtoView: ProtoView;
|
defaultProtoView: ProtoView;
|
||||||
_views: List<View>;
|
_views: List<View>;
|
||||||
_viewLastNode: List<Node>;
|
_viewLastNode: List<Node>;
|
||||||
|
_lightDom: any;
|
||||||
elementInjector: ElementInjector;
|
elementInjector: ElementInjector;
|
||||||
appInjector: Injector;
|
appInjector: Injector;
|
||||||
hostElementInjector: ElementInjector;
|
hostElementInjector: ElementInjector;
|
||||||
|
|
||||||
constructor(parentView: View, templateElement: Element, defaultProtoView: ProtoView,
|
constructor(parentView: View, templateElement: Element, defaultProtoView: ProtoView,
|
||||||
elementInjector: ElementInjector) {
|
elementInjector: ElementInjector, lightDom = null) {
|
||||||
this.parentView = parentView;
|
this.parentView = parentView;
|
||||||
this.templateElement = templateElement;
|
this.templateElement = templateElement;
|
||||||
this.defaultProtoView = defaultProtoView;
|
this.defaultProtoView = defaultProtoView;
|
||||||
this.elementInjector = elementInjector;
|
this.elementInjector = elementInjector;
|
||||||
|
this._lightDom = lightDom;
|
||||||
|
|
||||||
// The order in this list matches the DOM order.
|
// The order in this list matches the DOM order.
|
||||||
this._views = [];
|
this._views = [];
|
||||||
|
@ -77,7 +79,11 @@ export class ViewPort {
|
||||||
insert(view, atIndex=-1): View {
|
insert(view, atIndex=-1): View {
|
||||||
if (atIndex == -1) atIndex = this._views.length;
|
if (atIndex == -1) atIndex = this._views.length;
|
||||||
ListWrapper.insert(this._views, atIndex, view);
|
ListWrapper.insert(this._views, atIndex, view);
|
||||||
ViewPort.moveViewNodesAfterSibling(this._siblingToInsertAfter(atIndex), view);
|
if (isBlank(this._lightDom)) {
|
||||||
|
ViewPort.moveViewNodesAfterSibling(this._siblingToInsertAfter(atIndex), view);
|
||||||
|
} else {
|
||||||
|
this._lightDom.redistribute();
|
||||||
|
}
|
||||||
this.parentView.recordRange.addRange(view.recordRange);
|
this.parentView.recordRange.addRange(view.recordRange);
|
||||||
this._linkElementInjectors(view);
|
this._linkElementInjectors(view);
|
||||||
return view;
|
return view;
|
||||||
|
@ -87,12 +93,28 @@ export class ViewPort {
|
||||||
if (atIndex == -1) atIndex = this._views.length - 1;
|
if (atIndex == -1) atIndex = this._views.length - 1;
|
||||||
var removedView = this.get(atIndex);
|
var removedView = this.get(atIndex);
|
||||||
ListWrapper.removeAt(this._views, atIndex);
|
ListWrapper.removeAt(this._views, atIndex);
|
||||||
ViewPort.removeViewNodesFromParent(this.templateElement.parentNode, removedView);
|
if (isBlank(this._lightDom)) {
|
||||||
|
ViewPort.removeViewNodesFromParent(this.templateElement.parentNode, removedView);
|
||||||
|
} else {
|
||||||
|
this._lightDom.redistribute();
|
||||||
|
}
|
||||||
removedView.recordRange.remove();
|
removedView.recordRange.remove();
|
||||||
this._unlinkElementInjectors(removedView);
|
this._unlinkElementInjectors(removedView);
|
||||||
return removedView;
|
return removedView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contentTagContainers() {
|
||||||
|
return this._views;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes():List<Node> {
|
||||||
|
var r = [];
|
||||||
|
for (var i = 0; i < this._views.length; ++i) {
|
||||||
|
r = ListWrapper.concat(r, this._views[i].nodes);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
_linkElementInjectors(view) {
|
_linkElementInjectors(view) {
|
||||||
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
|
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
|
||||||
view.rootElementInjectors[i].parent = this.elementInjector;
|
view.rootElementInjectors[i].parent = this.elementInjector;
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import {Element} from 'facade/dom';
|
import {DOM, Element} from 'facade/dom';
|
||||||
|
import {normalizeBlank} from 'facade/lang';
|
||||||
|
|
||||||
export class NgElement {
|
export class NgElement {
|
||||||
domElement:Element;
|
domElement:Element;
|
||||||
constructor(domElement:Element) {
|
constructor(domElement:Element) {
|
||||||
this.domElement = domElement;
|
this.domElement = domElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAttribute(name:string) {
|
||||||
|
return normalizeBlank(DOM.getAttribute(this.domElement, name));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,20 @@
|
||||||
import {ddescribe, describe, it, iit, expect, beforeEach} from 'test_lib/test_lib';
|
import {ddescribe, describe, it, iit, expect, beforeEach} from 'test_lib/test_lib';
|
||||||
import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
|
import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
|
||||||
import {Decorator, Component} from 'core/annotations/annotations';
|
import {Decorator, Component} from 'core/annotations/annotations';
|
||||||
|
import {TemplateConfig} from 'core/annotations/template_config';
|
||||||
import {DirectiveMetadata} from 'core/compiler/directive_metadata';
|
import {DirectiveMetadata} from 'core/compiler/directive_metadata';
|
||||||
import {ShadowDomEmulated, ShadowDomNative} from 'core/compiler/shadow_dom';
|
import {ShadowDomStrategy, ShadowDomNative} from 'core/compiler/shadow_dom';
|
||||||
|
import {CONST} from 'facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
class FakeShadowDomStrategy extends ShadowDomStrategy {
|
||||||
|
@CONST()
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
polyfillDirectives() {
|
||||||
|
return [SomeDirective];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Decorator({
|
@Decorator({
|
||||||
selector: 'someSelector'
|
selector: 'someSelector'
|
||||||
|
@ -17,13 +29,28 @@ class ComponentWithoutExplicitShadowDomStrategy {}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'someSelector',
|
selector: 'someSelector',
|
||||||
shadowDom: ShadowDomEmulated
|
shadowDom: new FakeShadowDomStrategy()
|
||||||
})
|
})
|
||||||
class ComponentWithExplicitShadowDomStrategy {}
|
class ComponentWithExplicitShadowDomStrategy {}
|
||||||
|
|
||||||
class SomeDirectiveWithoutAnnotation {
|
class SomeDirectiveWithoutAnnotation {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'withoutDirectives'
|
||||||
|
})
|
||||||
|
class ComponentWithoutDirectives {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'withDirectives',
|
||||||
|
template: new TemplateConfig({
|
||||||
|
directives: [ComponentWithoutDirectives]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
class ComponentWithDirectives {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe("DirectiveMetadataReader", () => {
|
describe("DirectiveMetadataReader", () => {
|
||||||
var reader;
|
var reader;
|
||||||
|
@ -35,7 +62,7 @@ export function main() {
|
||||||
it('should read out the annotation', () => {
|
it('should read out the annotation', () => {
|
||||||
var directiveMetadata = reader.read(SomeDirective);
|
var directiveMetadata = reader.read(SomeDirective);
|
||||||
expect(directiveMetadata).toEqual(
|
expect(directiveMetadata).toEqual(
|
||||||
new DirectiveMetadata(SomeDirective, new Decorator({selector: 'someSelector'}), null));
|
new DirectiveMetadata(SomeDirective, new Decorator({selector: 'someSelector'}), null, null));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if not matching annotation is found', () => {
|
it('should throw if not matching annotation is found', () => {
|
||||||
|
@ -47,7 +74,7 @@ export function main() {
|
||||||
describe("shadow dom strategy", () => {
|
describe("shadow dom strategy", () => {
|
||||||
it('should return the provided shadow dom strategy when it is present', () => {
|
it('should return the provided shadow dom strategy when it is present', () => {
|
||||||
var directiveMetadata = reader.read(ComponentWithExplicitShadowDomStrategy);
|
var directiveMetadata = reader.read(ComponentWithExplicitShadowDomStrategy);
|
||||||
expect(directiveMetadata.shadowDomStrategy).toEqual(ShadowDomEmulated);
|
expect(directiveMetadata.shadowDomStrategy).toBeAnInstanceOf(FakeShadowDomStrategy);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return Native otherwise', () => {
|
it('should return Native otherwise', () => {
|
||||||
|
@ -55,5 +82,22 @@ export function main() {
|
||||||
expect(directiveMetadata.shadowDomStrategy).toEqual(ShadowDomNative);
|
expect(directiveMetadata.shadowDomStrategy).toEqual(ShadowDomNative);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("componentDirectives", () => {
|
||||||
|
it("should return an empty list when no directives specified", () => {
|
||||||
|
var cmp = reader.read(ComponentWithoutDirectives);
|
||||||
|
expect(cmp.componentDirectives).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return a list of directives specified in the template config", () => {
|
||||||
|
var cmp = reader.read(ComponentWithDirectives);
|
||||||
|
expect(cmp.componentDirectives).toEqual([ComponentWithoutDirectives]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should include directives required by the shadow DOM strategy", () => {
|
||||||
|
var cmp = reader.read(ComponentWithExplicitShadowDomStrategy);
|
||||||
|
expect(cmp.componentDirectives).toEqual([SomeDirective]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -8,11 +8,16 @@ import {View} from 'core/compiler/view';
|
||||||
import {ProtoRecordRange} from 'change_detection/change_detection';
|
import {ProtoRecordRange} from 'change_detection/change_detection';
|
||||||
import {ViewPort} from 'core/compiler/viewport';
|
import {ViewPort} from 'core/compiler/viewport';
|
||||||
import {NgElement} from 'core/dom/element';
|
import {NgElement} from 'core/dom/element';
|
||||||
|
import {LightDom, SourceLightDom, DestinationLightDom} from 'core/compiler/shadow_dom_emulation/light_dom';
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(View)
|
@IMPLEMENTS(View)
|
||||||
class DummyView extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}}
|
class DummyView extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(LightDom)
|
||||||
|
class DummyLightDom extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}}
|
||||||
|
|
||||||
|
|
||||||
class Directive {
|
class Directive {
|
||||||
}
|
}
|
||||||
|
@ -65,7 +70,7 @@ class NeedsView {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null);
|
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null, null);
|
||||||
|
|
||||||
function humanize(tree, names:List) {
|
function humanize(tree, names:List) {
|
||||||
var lookupName = (item) =>
|
var lookupName = (item) =>
|
||||||
|
@ -88,12 +93,15 @@ export function main() {
|
||||||
return inj;
|
return inj;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentChildInjectors(parentBindings, childBindings) {
|
function parentChildInjectors(parentBindings, childBindings, parentPreBuildObjects = null) {
|
||||||
|
if (isBlank(parentPreBuildObjects)) parentPreBuildObjects = defaultPreBuiltObjects;
|
||||||
|
|
||||||
var inj = new Injector([]);
|
var inj = new Injector([]);
|
||||||
|
|
||||||
var protoParent = new ProtoElementInjector(null, 0, parentBindings);
|
var protoParent = new ProtoElementInjector(null, 0, parentBindings);
|
||||||
var parent = protoParent.instantiate(null, null);
|
var parent = protoParent.instantiate(null, null);
|
||||||
parent.instantiateDirectives(inj, null, defaultPreBuiltObjects);
|
|
||||||
|
parent.instantiateDirectives(inj, null, parentPreBuildObjects);
|
||||||
|
|
||||||
var protoChild = new ProtoElementInjector(protoParent, 1, childBindings);
|
var protoChild = new ProtoElementInjector(protoParent, 1, childBindings);
|
||||||
var child = protoChild.instantiate(parent, null);
|
var child = protoChild.instantiate(parent, null);
|
||||||
|
@ -102,13 +110,15 @@ export function main() {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hostShadowInjectors(hostBindings, shadowBindings) {
|
function hostShadowInjectors(hostBindings, shadowBindings, hostPreBuildObjects = null) {
|
||||||
|
if (isBlank(hostPreBuildObjects)) hostPreBuildObjects = defaultPreBuiltObjects;
|
||||||
|
|
||||||
var inj = new Injector([]);
|
var inj = new Injector([]);
|
||||||
var shadowInj = inj.createChild([]);
|
var shadowInj = inj.createChild([]);
|
||||||
|
|
||||||
var protoParent = new ProtoElementInjector(null, 0, hostBindings, true);
|
var protoParent = new ProtoElementInjector(null, 0, hostBindings, true);
|
||||||
var host = protoParent.instantiate(null, null);
|
var host = protoParent.instantiate(null, null);
|
||||||
host.instantiateDirectives(inj, shadowInj, null);
|
host.instantiateDirectives(inj, shadowInj, hostPreBuildObjects);
|
||||||
|
|
||||||
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false);
|
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false);
|
||||||
var shadow = protoChild.instantiate(null, host);
|
var shadow = protoChild.instantiate(null, host);
|
||||||
|
@ -186,7 +196,7 @@ export function main() {
|
||||||
|
|
||||||
it("should instantiate directives that depend on pre built objects", function () {
|
it("should instantiate directives that depend on pre built objects", function () {
|
||||||
var view = new DummyView();
|
var view = new DummyView();
|
||||||
var inj = injector([NeedsView], null, null, new PreBuiltObjects(view, null, null));
|
var inj = injector([NeedsView], null, null, new PreBuiltObjects(view, null, null, null));
|
||||||
|
|
||||||
expect(inj.get(NeedsView).view).toBe(view);
|
expect(inj.get(NeedsView).view).toBe(view);
|
||||||
});
|
});
|
||||||
|
@ -291,24 +301,51 @@ export function main() {
|
||||||
describe("pre built objects", function () {
|
describe("pre built objects", function () {
|
||||||
it("should return view", function () {
|
it("should return view", function () {
|
||||||
var view = new DummyView();
|
var view = new DummyView();
|
||||||
var inj = injector([], null, null, new PreBuiltObjects(view, null, null));
|
var inj = injector([], null, null, new PreBuiltObjects(view, null, null, null));
|
||||||
|
|
||||||
expect(inj.get(View)).toEqual(view);
|
expect(inj.get(View)).toEqual(view);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return element", function () {
|
it("should return element", function () {
|
||||||
var element = new NgElement(null);
|
var element = new NgElement(null);
|
||||||
var inj = injector([], null, null, new PreBuiltObjects(null, element, null));
|
var inj = injector([], null, null, new PreBuiltObjects(null, element, null, null));
|
||||||
|
|
||||||
expect(inj.get(NgElement)).toEqual(element);
|
expect(inj.get(NgElement)).toEqual(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return viewPort', function () {
|
it('should return viewPort', function () {
|
||||||
var viewPort = new ViewPort(null, null, null, null);
|
var viewPort = new ViewPort(null, null, null, null);
|
||||||
var inj = injector([], null, null, new PreBuiltObjects(null, null, viewPort));
|
var inj = injector([], null, null, new PreBuiltObjects(null, null, viewPort, null));
|
||||||
|
|
||||||
expect(inj.get(ViewPort)).toEqual(viewPort);
|
expect(inj.get(ViewPort)).toEqual(viewPort);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("light DOM", () => {
|
||||||
|
var lightDom, parentPreBuiltObjects;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
lightDom = new DummyLightDom();
|
||||||
|
parentPreBuiltObjects = new PreBuiltObjects(null, null, null, lightDom);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return destination light DOM from the parent's injector", function () {
|
||||||
|
var child = parentChildInjectors([], [], parentPreBuiltObjects);
|
||||||
|
|
||||||
|
expect(child.get(DestinationLightDom)).toEqual(lightDom);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return null when parent's injector is a component boundary", function () {
|
||||||
|
var child = hostShadowInjectors([], [], parentPreBuiltObjects);
|
||||||
|
|
||||||
|
expect(child.get(DestinationLightDom)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return source light DOM from the closest component boundary", function () {
|
||||||
|
var child = hostShadowInjectors([], [], parentPreBuiltObjects);
|
||||||
|
|
||||||
|
expect(child.get(SourceLightDom)).toEqual(lightDom);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {Lexer, Parser, ChangeDetector} from 'change_detection/change_detection';
|
||||||
|
|
||||||
import {Compiler, CompilerCache} from 'core/compiler/compiler';
|
import {Compiler, CompilerCache} from 'core/compiler/compiler';
|
||||||
import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
|
import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
|
||||||
|
import {ShadowDomEmulated} from 'core/compiler/shadow_dom';
|
||||||
|
|
||||||
import {Decorator, Component, Template} from 'core/annotations/annotations';
|
import {Decorator, Component, Template} from 'core/annotations/annotations';
|
||||||
import {TemplateConfig} from 'core/annotations/template_config';
|
import {TemplateConfig} from 'core/annotations/template_config';
|
||||||
|
@ -108,9 +109,51 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should emulate content tag', (done) => {
|
||||||
|
var el = `<emulated-shadow-dom-component>` +
|
||||||
|
`<div>Light</div>` +
|
||||||
|
`<div template="trivial-template">DOM</div>` +
|
||||||
|
`</emulated-shadow-dom-component>`;
|
||||||
|
|
||||||
|
function createView(pv) {
|
||||||
|
var view = pv.instantiate(null);
|
||||||
|
view.hydrate(new Injector([]), null, {});
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler.compile(MyComp, createElement(el)).
|
||||||
|
then(createView).
|
||||||
|
then((view) => {
|
||||||
|
expect(DOM.getText(view.nodes[0])).toEqual('Before LightDOM After');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Template({
|
||||||
|
selector: '[trivial-template]'
|
||||||
|
})
|
||||||
|
class TrivialTemplateDirective {
|
||||||
|
constructor(viewPort:ViewPort) {
|
||||||
|
viewPort.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'emulated-shadow-dom-component',
|
||||||
|
template: new TemplateConfig({
|
||||||
|
inline: 'Before <content></content> After',
|
||||||
|
directives: []
|
||||||
|
}),
|
||||||
|
shadowDom: ShadowDomEmulated
|
||||||
|
})
|
||||||
|
class EmulatedShadowDomCmp {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Decorator({
|
@Decorator({
|
||||||
selector: '[my-dir]',
|
selector: '[my-dir]',
|
||||||
bind: {'elprop':'dirProp'}
|
bind: {'elprop':'dirProp'}
|
||||||
|
@ -124,7 +167,7 @@ class MyDir {
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: new TemplateConfig({
|
template: new TemplateConfig({
|
||||||
directives: [MyDir, ChildComp, SomeTemplate]
|
directives: [MyDir, ChildComp, SomeTemplate, EmulatedShadowDomCmp, TrivialTemplateDirective]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
import {describe, beforeEach, it, expect, ddescribe, iit, SpyObject} from 'test_lib/test_lib';
|
||||||
|
import {proxy, IMPLEMENTS} from 'facade/lang';
|
||||||
|
import {DOM} from 'facade/dom';
|
||||||
|
import {Content} from 'core/compiler/shadow_dom_emulation/content_tag';
|
||||||
|
import {NgElement} from 'core/dom/element';
|
||||||
|
import {LightDom} from 'core/compiler/shadow_dom_emulation/light_dom';
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(LightDom)
|
||||||
|
class DummyLightDom extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}}
|
||||||
|
|
||||||
|
var _script = `<script type="ng/content"></script>`;
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('Content', function() {
|
||||||
|
it("should insert the nodes", () => {
|
||||||
|
var lightDom = new DummyLightDom();
|
||||||
|
var parent = createElement("<div><content></content></div>");
|
||||||
|
var content = DOM.firstChild(parent);
|
||||||
|
|
||||||
|
var c = new Content(lightDom, new NgElement(content));
|
||||||
|
c.insert([createElement("<a></a>"), createElement("<b></b>")])
|
||||||
|
|
||||||
|
expect(DOM.getInnerHTML(parent)).toEqual(`${_script}<a></a><b></b>${_script}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should remove the nodes from the previous insertion", () => {
|
||||||
|
var lightDom = new DummyLightDom();
|
||||||
|
var parent = createElement("<div><content></content></div>");
|
||||||
|
var content = DOM.firstChild(parent);
|
||||||
|
|
||||||
|
var c = new Content(lightDom, new NgElement(content));
|
||||||
|
c.insert([createElement("<a></a>")]);
|
||||||
|
c.insert([createElement("<b></b>")]);
|
||||||
|
|
||||||
|
expect(DOM.getInnerHTML(parent)).toEqual(`${_script}<b></b>${_script}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should insert empty list", () => {
|
||||||
|
var lightDom = new DummyLightDom();
|
||||||
|
var parent = createElement("<div><content></content></div>");
|
||||||
|
var content = DOM.firstChild(parent);
|
||||||
|
|
||||||
|
var c = new Content(lightDom, new NgElement(content));
|
||||||
|
c.insert([createElement("<a></a>")]);
|
||||||
|
c.insert([]);
|
||||||
|
|
||||||
|
expect(DOM.getInnerHTML(parent)).toEqual(`${_script}${_script}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createElement(html) {
|
||||||
|
return DOM.createTemplate(html).content.firstChild;
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
import {describe, beforeEach, it, expect, ddescribe, iit, SpyObject} from 'test_lib/test_lib';
|
||||||
|
import {proxy, IMPLEMENTS, isBlank} from 'facade/lang';
|
||||||
|
import {ListWrapper, MapWrapper} from 'facade/collection';
|
||||||
|
import {DOM} from 'facade/dom';
|
||||||
|
import {Content} from 'core/compiler/shadow_dom_emulation/content_tag';
|
||||||
|
import {NgElement} from 'core/dom/element';
|
||||||
|
import {LightDom} from 'core/compiler/shadow_dom_emulation/light_dom';
|
||||||
|
import {View} from 'core/compiler/view';
|
||||||
|
import {ViewPort} from 'core/compiler/viewport';
|
||||||
|
import {ElementInjector} from 'core/compiler/element_injector';
|
||||||
|
import {ProtoRecordRange} from 'change_detection/change_detection';
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(ElementInjector)
|
||||||
|
class FakeElementInjector {
|
||||||
|
content;
|
||||||
|
viewPort;
|
||||||
|
|
||||||
|
constructor(content, viewPort) {
|
||||||
|
this.content = content;
|
||||||
|
this.viewPort = viewPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasDirective(type) {
|
||||||
|
return this.content != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPreBuiltObject(type) {
|
||||||
|
return this.viewPort != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(t) {
|
||||||
|
if (t === Content) return this.content;
|
||||||
|
if (t === ViewPort) return this.viewPort;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
noSuchMethod(i) {
|
||||||
|
super.noSuchMethod(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(View)
|
||||||
|
class FakeView {
|
||||||
|
elementInjectors;
|
||||||
|
ports;
|
||||||
|
|
||||||
|
constructor(elementInjectors = null, ports = null) {
|
||||||
|
this.elementInjectors = elementInjectors;
|
||||||
|
this.ports = ports;
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewPortByTemplateElement(el) {
|
||||||
|
if (isBlank(this.ports)) return null;
|
||||||
|
return MapWrapper.get(this.ports, el);
|
||||||
|
}
|
||||||
|
|
||||||
|
noSuchMethod(i) {
|
||||||
|
super.noSuchMethod(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(ViewPort)
|
||||||
|
class FakeViewPort {
|
||||||
|
_nodes;
|
||||||
|
_contentTagContainers;
|
||||||
|
|
||||||
|
constructor(nodes, views) {
|
||||||
|
this._nodes = nodes;
|
||||||
|
this._contentTagContainers = views;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes(){
|
||||||
|
return this._nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
contentTagContainers(){
|
||||||
|
return this._contentTagContainers;
|
||||||
|
}
|
||||||
|
|
||||||
|
noSuchMethod(i) {
|
||||||
|
super.noSuchMethod(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(Content)
|
||||||
|
class FakeContentTag {
|
||||||
|
select;
|
||||||
|
nodes;
|
||||||
|
|
||||||
|
constructor(select = null) {
|
||||||
|
this.select = select;
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(nodes){
|
||||||
|
this.nodes = ListWrapper.clone(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
noSuchMethod(i) {
|
||||||
|
super.noSuchMethod(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('LightDom', function() {
|
||||||
|
var lightDomView;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
lightDomView = new FakeView([], MapWrapper.create());
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("contentTags", () => {
|
||||||
|
it("should collect content tags from element injectors", () => {
|
||||||
|
var tag = new FakeContentTag();
|
||||||
|
var shadowDomView = new FakeView([new FakeElementInjector(tag, null)]);
|
||||||
|
|
||||||
|
var lightDom = new LightDom(lightDomView, shadowDomView, createElement("<div></div>"));
|
||||||
|
|
||||||
|
expect(lightDom.contentTags()).toEqual([tag]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should collect content tags from view ports", () => {
|
||||||
|
var tag = new FakeContentTag();
|
||||||
|
var vp = new FakeViewPort(null, [
|
||||||
|
new FakeView([new FakeElementInjector(tag, null)])
|
||||||
|
]);
|
||||||
|
|
||||||
|
var shadowDomView = new FakeView([new FakeElementInjector(null, vp)]);
|
||||||
|
|
||||||
|
var lightDom = new LightDom(lightDomView, shadowDomView, createElement("<div></div>"));
|
||||||
|
|
||||||
|
expect(lightDom.contentTags()).toEqual([tag]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("expanded roots", () => {
|
||||||
|
it("should contain root nodes", () => {
|
||||||
|
var lightDomEl = createElement("<div><a></a></div>")
|
||||||
|
var lightDom = new LightDom(lightDomView, new FakeView(), lightDomEl);
|
||||||
|
expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should include view port nodes", () => {
|
||||||
|
var lightDomEl = createElement("<div><template></template></div>")
|
||||||
|
var template = lightDomEl.childNodes[0];
|
||||||
|
|
||||||
|
var lightDomView = new FakeView([],
|
||||||
|
MapWrapper.createFromPairs([
|
||||||
|
[template, new FakeViewPort([createElement("<a></a>")], null)]
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
var lightDom = new LightDom(lightDomView, new FakeView(), lightDomEl);
|
||||||
|
|
||||||
|
expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("redistribute", () => {
|
||||||
|
it("should redistribute nodes between content tags with select property set", () => {
|
||||||
|
var contentA = new FakeContentTag("a");
|
||||||
|
var contentB = new FakeContentTag("b");
|
||||||
|
|
||||||
|
var lightDomEl = createElement("<div><a>1</a><b>2</b><a>3</a></div>")
|
||||||
|
|
||||||
|
var lightDom = new LightDom(lightDomView, new FakeView([
|
||||||
|
new FakeElementInjector(contentA, null),
|
||||||
|
new FakeElementInjector(contentB, null)
|
||||||
|
]), lightDomEl);
|
||||||
|
|
||||||
|
lightDom.redistribute();
|
||||||
|
|
||||||
|
expect(toHtml(contentA.nodes)).toEqual(["<a>1</a>", "<a>3</a>"]);
|
||||||
|
expect(toHtml(contentB.nodes)).toEqual(["<b>2</b>"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should support wildcard content tags", () => {
|
||||||
|
var wildcard = new FakeContentTag(null);
|
||||||
|
var contentB = new FakeContentTag("b");
|
||||||
|
|
||||||
|
var lightDomEl = createElement("<div><a>1</a><b>2</b><a>3</a></div>")
|
||||||
|
|
||||||
|
var lightDom = new LightDom(lightDomView, new FakeView([
|
||||||
|
new FakeElementInjector(wildcard, null),
|
||||||
|
new FakeElementInjector(contentB, null)
|
||||||
|
]), lightDomEl);
|
||||||
|
|
||||||
|
lightDom.redistribute();
|
||||||
|
|
||||||
|
expect(toHtml(wildcard.nodes)).toEqual(["<a>1</a>", "<b>2</b>", "<a>3</a>"]);
|
||||||
|
expect(toHtml(contentB.nodes)).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toHtml(nodes) {
|
||||||
|
if (isBlank(nodes)) return [];
|
||||||
|
return ListWrapper.map(nodes, DOM.getOuterHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createElement(html) {
|
||||||
|
return DOM.createTemplate(html).content.firstChild;
|
||||||
|
}
|
|
@ -7,14 +7,30 @@ import {Component, Decorator, Template} from 'core/annotations/annotations';
|
||||||
import {OnChange} from 'core/core';
|
import {OnChange} from 'core/core';
|
||||||
import {Lexer, Parser, ProtoRecordRange, ChangeDetector} from 'change_detection/change_detection';
|
import {Lexer, Parser, ProtoRecordRange, ChangeDetector} from 'change_detection/change_detection';
|
||||||
import {TemplateConfig} from 'core/annotations/template_config';
|
import {TemplateConfig} from 'core/annotations/template_config';
|
||||||
import {List} from 'facade/collection';
|
import {List, MapWrapper} from 'facade/collection';
|
||||||
import {DOM, Element} from 'facade/dom';
|
import {DOM, Element} from 'facade/dom';
|
||||||
import {int} from 'facade/lang';
|
import {int, proxy, IMPLEMENTS} from 'facade/lang';
|
||||||
import {Injector} from 'di/di';
|
import {Injector} from 'di/di';
|
||||||
import {View} from 'core/compiler/view';
|
import {View} from 'core/compiler/view';
|
||||||
import {ViewPort} from 'core/compiler/viewport';
|
import {ViewPort} from 'core/compiler/viewport';
|
||||||
import {reflector} from 'reflection/reflection';
|
import {reflector} from 'reflection/reflection';
|
||||||
|
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(ViewPort)
|
||||||
|
class FakeViewPort {
|
||||||
|
templateElement;
|
||||||
|
|
||||||
|
constructor(templateElement) {
|
||||||
|
this.templateElement = templateElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
noSuchMethod(i) {
|
||||||
|
super.noSuchMethod(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('view', function() {
|
describe('view', function() {
|
||||||
var parser, someComponentDirective, someTemplateDirective;
|
var parser, someComponentDirective, someTemplateDirective;
|
||||||
|
@ -53,6 +69,25 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getViewPortByTemplateElement", () => {
|
||||||
|
var view, viewPort, templateElement;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
templateElement = createElement("<template></template>");
|
||||||
|
view = new View(null, null, new ProtoRecordRange(), MapWrapper.create());
|
||||||
|
viewPort = new FakeViewPort(templateElement);
|
||||||
|
view.viewPorts = [viewPort];
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return null when the given element is not an element", () => {
|
||||||
|
expect(view.getViewPortByTemplateElement("not an element")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return a view port with the matching template element", () => {
|
||||||
|
expect(view.getViewPortByTemplateElement(templateElement)).toBe(viewPort);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('with locals', function() {
|
describe('with locals', function() {
|
||||||
var view;
|
var view;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -96,8 +96,14 @@ class ListWrapper {
|
||||||
static bool isList(l) => l is List;
|
static bool isList(l) => l is List;
|
||||||
static void insert(List l, int index, value) { l.insert(index, value); }
|
static void insert(List l, int index, value) { l.insert(index, value); }
|
||||||
static void removeAt(List l, int index) { l.removeAt(index); }
|
static void removeAt(List l, int index) { l.removeAt(index); }
|
||||||
|
static void removeAll(List list, List items) {
|
||||||
|
for (var i = 0; i < items.length; ++i) {
|
||||||
|
list.remove(items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
static void clear(List l) { l.clear(); }
|
static void clear(List l) { l.clear(); }
|
||||||
static String join(List l, String s) => l.join(s);
|
static String join(List l, String s) => l.join(s);
|
||||||
|
static bool isEmpty(list) => list.isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isListLikeIterable(obj) => obj is Iterable;
|
bool isListLikeIterable(obj) => obj is Iterable;
|
||||||
|
|
|
@ -143,12 +143,21 @@ export class ListWrapper {
|
||||||
list.splice(index, 1);
|
list.splice(index, 1);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
static removeAll(list, items) {
|
||||||
|
for (var i = 0; i < items.length; ++i) {
|
||||||
|
var index = list.indexOf(items[i]);
|
||||||
|
list.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
static clear(list) {
|
static clear(list) {
|
||||||
list.splice(0, list.length);
|
list.splice(0, list.length);
|
||||||
}
|
}
|
||||||
static join(list, s) {
|
static join(list, s) {
|
||||||
return list.join(s);
|
return list.join(s);
|
||||||
}
|
}
|
||||||
|
static isEmpty(list) {
|
||||||
|
return list.length == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isListLikeIterable(obj):boolean {
|
export function isListLikeIterable(obj):boolean {
|
||||||
|
|
|
@ -47,6 +47,9 @@ class DOM {
|
||||||
static List<Node> childNodes(el) {
|
static List<Node> childNodes(el) {
|
||||||
return el.childNodes;
|
return el.childNodes;
|
||||||
}
|
}
|
||||||
|
static childNodesAsList(el) {
|
||||||
|
return childNodes(el).toList();
|
||||||
|
}
|
||||||
static clearNodes(el) {
|
static clearNodes(el) {
|
||||||
el.nodes = [];
|
el.nodes = [];
|
||||||
}
|
}
|
||||||
|
@ -56,6 +59,12 @@ class DOM {
|
||||||
static removeChild(el, node) {
|
static removeChild(el, node) {
|
||||||
node.remove();
|
node.remove();
|
||||||
}
|
}
|
||||||
|
static insertBefore(el, node) {
|
||||||
|
el.parentNode.insertBefore(node, el);
|
||||||
|
}
|
||||||
|
static insertAllBefore(el, nodes) {
|
||||||
|
el.parentNode.insertAllBefore(nodes, el);
|
||||||
|
}
|
||||||
static insertAfter(el, node) {
|
static insertAfter(el, node) {
|
||||||
el.parentNode.insertBefore(node, el.nextNode);
|
el.parentNode.insertBefore(node, el.nextNode);
|
||||||
}
|
}
|
||||||
|
@ -74,6 +83,12 @@ class DOM {
|
||||||
if (doc == null) doc = document;
|
if (doc == null) doc = document;
|
||||||
return doc.createElement(tagName);
|
return doc.createElement(tagName);
|
||||||
}
|
}
|
||||||
|
static createScriptTag(String attrName, String attrValue, [doc=null]) {
|
||||||
|
if (doc == null) doc = document;
|
||||||
|
var el = doc.createElement("SCRIPT");
|
||||||
|
el.setAttribute(attrName, attrValue);
|
||||||
|
return el;
|
||||||
|
}
|
||||||
static clone(Node node) {
|
static clone(Node node) {
|
||||||
return node.clone(true);
|
return node.clone(true);
|
||||||
}
|
}
|
||||||
|
@ -95,9 +110,15 @@ class DOM {
|
||||||
static hasClass(Element element, classname) {
|
static hasClass(Element element, classname) {
|
||||||
return element.classes.contains(classname);
|
return element.classes.contains(classname);
|
||||||
}
|
}
|
||||||
|
static String tagName(Element element) {
|
||||||
|
return element.tagName;
|
||||||
|
}
|
||||||
static attributeMap(Element element) {
|
static attributeMap(Element element) {
|
||||||
return element.attributes;
|
return element.attributes;
|
||||||
}
|
}
|
||||||
|
static getAttribute(Element element, String attribute) {
|
||||||
|
return element.getAttribute(attribute);
|
||||||
|
}
|
||||||
static Node templateAwareRoot(Element el) {
|
static Node templateAwareRoot(Element el) {
|
||||||
return el is TemplateElement ? el.content : el;
|
return el is TemplateElement ? el.content : el;
|
||||||
}
|
}
|
||||||
|
@ -107,4 +128,7 @@ class DOM {
|
||||||
static HtmlDocument defaultDoc() {
|
static HtmlDocument defaultDoc() {
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
static bool elementMatches(n, String selector) {
|
||||||
|
return n is Element && n.matches(selector);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ export var TemplateElement = window.HTMLTemplateElement;
|
||||||
export var document = window.document;
|
export var document = window.document;
|
||||||
export var location = window.location;
|
export var location = window.location;
|
||||||
|
|
||||||
import {List, MapWrapper} from 'facade/collection';
|
import {List, MapWrapper, ListWrapper} from 'facade/collection';
|
||||||
|
|
||||||
export class DOM {
|
export class DOM {
|
||||||
static query(selector) {
|
static query(selector) {
|
||||||
|
@ -40,6 +40,14 @@ export class DOM {
|
||||||
static childNodes(el):NodeList {
|
static childNodes(el):NodeList {
|
||||||
return el.childNodes;
|
return el.childNodes;
|
||||||
}
|
}
|
||||||
|
static childNodesAsList(el):List {
|
||||||
|
var childNodes = el.childNodes;
|
||||||
|
var res = ListWrapper.createFixedSize(childNodes.length);
|
||||||
|
for (var i=0; i<childNodes.length; i++) {
|
||||||
|
res[i] = childNodes[i];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
static clearNodes(el) {
|
static clearNodes(el) {
|
||||||
el.innerHTML = "";
|
el.innerHTML = "";
|
||||||
}
|
}
|
||||||
|
@ -49,6 +57,14 @@ export class DOM {
|
||||||
static removeChild(el, node) {
|
static removeChild(el, node) {
|
||||||
el.removeChild(node);
|
el.removeChild(node);
|
||||||
}
|
}
|
||||||
|
static insertBefore(el, node) {
|
||||||
|
el.parentNode.insertBefore(node, el);
|
||||||
|
}
|
||||||
|
static insertAllBefore(el, nodes) {
|
||||||
|
ListWrapper.forEach(nodes, (n) => {
|
||||||
|
el.parentNode.insertBefore(n, el);
|
||||||
|
});
|
||||||
|
}
|
||||||
static insertAfter(el, node) {
|
static insertAfter(el, node) {
|
||||||
el.parentNode.insertBefore(node, el.nextSibling);
|
el.parentNode.insertBefore(node, el.nextSibling);
|
||||||
}
|
}
|
||||||
|
@ -69,6 +85,11 @@ export class DOM {
|
||||||
static createElement(tagName, doc=document) {
|
static createElement(tagName, doc=document) {
|
||||||
return doc.createElement(tagName);
|
return doc.createElement(tagName);
|
||||||
}
|
}
|
||||||
|
static createScriptTag(attrName:string, attrValue:string, doc=document) {
|
||||||
|
var el = doc.createElement("SCRIPT");
|
||||||
|
el.setAttribute(attrName, attrValue);
|
||||||
|
return el;
|
||||||
|
}
|
||||||
static clone(node:Node) {
|
static clone(node:Node) {
|
||||||
return node.cloneNode(true);
|
return node.cloneNode(true);
|
||||||
}
|
}
|
||||||
|
@ -90,6 +111,9 @@ export class DOM {
|
||||||
static hasClass(element:Element, classname:string) {
|
static hasClass(element:Element, classname:string) {
|
||||||
return element.classList.contains(classname);
|
return element.classList.contains(classname);
|
||||||
}
|
}
|
||||||
|
static tagName(element:Element):string {
|
||||||
|
return element.tagName;
|
||||||
|
}
|
||||||
static attributeMap(element:Element) {
|
static attributeMap(element:Element) {
|
||||||
var res = MapWrapper.create();
|
var res = MapWrapper.create();
|
||||||
var elAttrs = element.attributes;
|
var elAttrs = element.attributes;
|
||||||
|
@ -99,6 +123,9 @@ export class DOM {
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
static getAttribute(element:Element, attribute:string) {
|
||||||
|
return element.getAttribute(attribute);
|
||||||
|
}
|
||||||
static templateAwareRoot(el:Element):Node {
|
static templateAwareRoot(el:Element):Node {
|
||||||
return el instanceof TemplateElement ? el.content : el;
|
return el instanceof TemplateElement ? el.content : el;
|
||||||
}
|
}
|
||||||
|
@ -108,4 +135,7 @@ export class DOM {
|
||||||
static defaultDoc() {
|
static defaultDoc() {
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
static elementMatches(n, selector:string):boolean {
|
||||||
|
return n instanceof Element && n.matches(selector);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue