145 lines
4.3 KiB
TypeScript
Raw Normal View History

import {DOM} from 'angular2/src/dom/dom_adapter';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {isBlank, isPresent} from 'angular2/src/facade/lang';
import * as viewModule from '../view/view';
import {Content} from './content_tag';
export class DestinationLightDom {}
class _Root {
node;
2015-05-18 11:57:20 -07:00
boundElementIndex: number;
constructor(node, boundElementIndex) {
this.node = node;
this.boundElementIndex = boundElementIndex;
}
}
// TODO: LightDom should implement DestinationLightDom
// once interfaces are supported
export class LightDom {
// The light DOM of the element is enclosed inside the lightDomView
2015-05-18 11:57:20 -07:00
lightDomView: viewModule.DomView;
// The shadow DOM
2015-05-18 11:57:20 -07:00
shadowDomView: viewModule.DomView;
// The nodes of the light DOM
2015-05-18 11:57:20 -07:00
nodes: List</*node*/ any>;
private _roots: List<_Root>;
2015-05-18 11:57:20 -07:00
constructor(lightDomView: viewModule.DomView, element) {
this.lightDomView = lightDomView;
this.nodes = DOM.childNodesAsList(element);
2015-05-18 11:57:20 -07:00
this._roots = null;
this.shadowDomView = null;
}
2015-05-18 11:57:20 -07:00
attachShadowDomView(shadowDomView: viewModule.DomView) { this.shadowDomView = shadowDomView; }
2015-05-18 11:57:20 -07:00
detachShadowDomView() { this.shadowDomView = null; }
2015-05-18 11:57:20 -07:00
redistribute() { redistributeNodes(this.contentTags(), this.expandedDomNodes()); }
contentTags(): List<Content> {
if (isPresent(this.shadowDomView)) {
return this._collectAllContentTags(this.shadowDomView, []);
} else {
return [];
}
}
// Collects the Content directives from the view and all its child views
2015-05-18 11:57:20 -07:00
private _collectAllContentTags(view: viewModule.DomView, acc: List<Content>): List<Content> {
var contentTags = view.contentTags;
var vcs = view.viewContainers;
2015-05-18 11:57:20 -07:00
for (var i = 0; i < vcs.length; i++) {
var vc = vcs[i];
var contentTag = contentTags[i];
if (isPresent(contentTag)) {
ListWrapper.push(acc, contentTag);
}
if (isPresent(vc)) {
2015-05-18 11:57:20 -07:00
ListWrapper.forEach(vc.contentTagContainers(),
(view) => { this._collectAllContentTags(view, acc); });
}
}
return acc;
}
// Collects the nodes of the light DOM by merging:
// - nodes from enclosed ViewContainers,
// - nodes from enclosed content tags,
// - plain DOM nodes
2015-05-18 11:57:20 -07:00
expandedDomNodes(): List</*node*/ any> {
var res = [];
2015-05-18 11:57:20 -07:00
var roots = this._findRoots();
for (var i = 0; i < roots.length; ++i) {
var root = roots[i];
if (isPresent(root.boundElementIndex)) {
var vc = this.lightDomView.viewContainers[root.boundElementIndex];
var content = this.lightDomView.contentTags[root.boundElementIndex];
if (isPresent(vc)) {
res = ListWrapper.concat(res, vc.nodes());
} else if (isPresent(content)) {
res = ListWrapper.concat(res, content.nodes());
} else {
ListWrapper.push(res, root.node);
}
} else {
ListWrapper.push(res, root.node);
}
}
return res;
}
// Returns a list of Roots for all the nodes of the light DOM.
// The Root object contains the DOM node and its corresponding boundElementIndex
2015-05-18 11:57:20 -07:00
private _findRoots() {
if (isPresent(this._roots)) return this._roots;
var boundElements = this.lightDomView.boundElements;
2015-05-18 11:57:20 -07:00
this._roots = ListWrapper.map(this.nodes, (n) => {
var boundElementIndex = null;
2015-05-18 11:57:20 -07:00
for (var i = 0; i < boundElements.length; i++) {
var boundEl = boundElements[i];
if (isPresent(boundEl) && boundEl === n) {
boundElementIndex = i;
break;
}
}
return new _Root(n, boundElementIndex);
});
2015-05-18 11:57:20 -07:00
return this._roots;
}
}
// Projects the light DOM into the shadow DOM
2015-05-18 11:57:20 -07:00
function redistributeNodes(contents: List<Content>, nodes: List</*node*/ any>) {
for (var i = 0; i < contents.length; ++i) {
var content = contents[i];
var select = content.select;
// Empty selector is identical to <content/>
if (select.length === 0) {
content.insert(ListWrapper.clone(nodes));
ListWrapper.clear(nodes);
} else {
var matchSelector = (n) => DOM.elementMatches(n, select);
var matchingNodes = ListWrapper.filter(nodes, matchSelector);
content.insert(matchingNodes);
ListWrapper.removeAll(nodes, matchingNodes);
}
}
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (isPresent(node.parentNode)) {
DOM.remove(nodes[i]);
}
}
}