Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
feat(compiler): Tagging compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
jbdeboer committed Mar 11, 2014
1 parent eb4422a commit 59516af
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 76 deletions.
17 changes: 17 additions & 0 deletions lib/core_dom/element_binder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,27 @@ class ElementBinderTree {
}


class TaggedTextBinder {
ElementBinder binder;
final int offsetIndex;

TaggedTextBinder(this.binder, this.offsetIndex);
}

// Used for the tagging compiler
class TaggedElementBinder {
ElementBinder binder;
int parentBinderOffset;
var injector;

List<TaggedTextBinder> textBinders;

TaggedElementBinder(this.binder, this.parentBinderOffset);

void addText(TaggedTextBinder tagged) {
if (textBinders == null) {
textBinders = [];
}
textBinders.add(tagged);
}
}
111 changes: 74 additions & 37 deletions lib/core_dom/tagging_compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,85 @@ class TaggingCompiler implements Compiler {

NodeCursor domCursor, NodeCursor templateCursor,
ElementBinder useExistingElementBinder,
DirectiveMap directives) {
DirectiveMap directives,
int parentElementBinderOffset,
TaggedElementBinder directParentElementBinder) {
List<TaggedElementBinder> elementBinders = [];
if (domCursor.nodeList().length == 0) return null;
if (domCursor.current == null) return null;


do {
var subtrees, binder;
var node = domCursor.current;

var node = domCursor.nodeList()[0];
ElementBinder elementBinder;

// If nodetype is a element, call selector matchElement. If text, call selector.matchText
if (node.nodeType == 1) {

// TODO: selector will return null for non-useful bindings.
ElementBinder elementBinder = useExistingElementBinder == null
? directives.selector.match(node)
: useExistingElementBinder;
// If nodetype is a element, call selector matchElement. If text, call selector.matchText

if (elementBinder.hasTemplate) {
elementBinder.templateViewFactory = _compileTransclusion(elementBinders,
domCursor, templateCursor,
elementBinder.template, elementBinder.templateBinder, directives);
// TODO: selector will return null for non-useful bindings.
elementBinder = useExistingElementBinder == null
? directives.selector.matchElement(node)
: useExistingElementBinder;

if (elementBinder.hasTemplate) {
elementBinder.templateViewFactory = _compileTransclusion(elementBinders,
domCursor, templateCursor,
elementBinder.template, elementBinder.templateBinder, directives, parentElementBinderOffset);
}
}

if (elementBinder.shouldCompileChildren) {
if (domCursor.descend()) {
templateCursor.descend();
node = domCursor.current;
if (node.nodeType == 1) {

elementBinders.addAll(
_compileView(domCursor, templateCursor, null, directives /*current element list length*/));
var taggedElementBinder = null;
if (elementBinder.hasDirectives || elementBinder.hasTemplate) {
taggedElementBinder = new TaggedElementBinder(elementBinder, parentElementBinderOffset);
elementBinders.add(taggedElementBinder);
parentElementBinderOffset = elementBinders.length - 1;

domCursor.ascend();
templateCursor.ascend();
// TODO(deboer): Hack, this sucks.
(templateCursor.current as dom.Element).classes.add('ng-binding');
node.classes.add('ng-binding');
}
}

// move this up
if (elementBinder.hasDirectives) {
elementBinders.add(new TaggedElementBinder(elementBinder, -1));
node.classes.add('ng-binding');
binder = elementBinder;
if (elementBinder.shouldCompileChildren) {
if (domCursor.descend()) {
templateCursor.descend();

elementBinders.addAll(
_compileView(domCursor, templateCursor, null, directives, parentElementBinderOffset,
taggedElementBinder));

domCursor.ascend();
templateCursor.ascend();
}
}
} else if (node.nodeType == 3 || node.nodeType == 8) {
elementBinder = node.nodeType == 3 ? directives.selector.matchText(node) : elementBinder;

if (elementBinder.hasDirectives && (node.parentNode != null && templateCursor.current.parentNode != null)) {
if (directParentElementBinder == null) {

directParentElementBinder = new TaggedElementBinder(null, parentElementBinderOffset);
elementBinders.add(directParentElementBinder);

assert(templateCursor.current.parentNode is dom.Element);
assert(node.parentNode is dom.Element);

(node.parentNode as dom.Element).classes.add('ng-binding');
(templateCursor.current.parentNode as dom.Element).classes.add('ng-binding');
}
directParentElementBinder.addText(new TaggedTextBinder(elementBinder, 0 /* TODO */));
} else if(!(node.parentNode != null && templateCursor.current.parentNode != null)) { // Always add an elementBinder for top-level text.
elementBinders.add(new TaggedElementBinder(elementBinder, parentElementBinderOffset));
}
// } else if (node.nodeType == 8) { // comment

} else {
throw "wtf";
}
} while (templateCursor.microNext() && domCursor.microNext());
} while (templateCursor.moveNext() && domCursor.moveNext());

return elementBinders;
}
Expand All @@ -61,28 +98,28 @@ class TaggingCompiler implements Compiler {
NodeCursor domCursor, NodeCursor templateCursor,
DirectiveRef directiveRef,
ElementBinder transcludedElementBinder,
DirectiveMap directives) {
DirectiveMap directives,
int parentElementBinderOffset) {
var anchorName = directiveRef.annotation.selector + (directiveRef.value != null ? '=' + directiveRef.value : '');
var viewFactory;
var views;

var transcludeCursor = templateCursor.replaceWithAnchor(anchorName);
var domCursorIndex = domCursor.index;
var elementBinders =
_compileView(domCursor, transcludeCursor, transcludedElementBinder, directives);
_compileView(domCursor, transcludeCursor, transcludedElementBinder, directives, parentElementBinderOffset, null);
if (elementBinders == null) elementBinders = [];

viewFactory = new TaggingViewFactory(transcludeCursor.elements, elementBinders, _perf, _expando);
domCursor.index = domCursorIndex;

if (domCursor.isInstance()) {
if (domCursor.isInstance) {
domCursor.insertAnchorBefore(anchorName);
views = [viewFactory(domCursor.nodeList())];
domCursor.macroNext();
templateCursor.macroNext();
while (domCursor.isValid() && domCursor.isInstance()) {
views.add(viewFactory(domCursor.nodeList()));
domCursor.macroNext();
views = [viewFactory([domCursor.current])];
domCursor.moveNext();
templateCursor.moveNext();
while (domCursor.moveNext() && domCursor.isInstance) {
views.add(viewFactory([domCursor.current]));
templateCursor.remove();
}
} else {
Expand All @@ -99,7 +136,7 @@ class TaggingCompiler implements Compiler {
List<dom.Node> templateElements = cloneElements(domElements);
List<TaggedElementBinder> elementBinders = _compileView(
new NodeCursor(domElements), new NodeCursor(templateElements),
null, directives);
null, directives, -1, null);

var viewFactory = new TaggingViewFactory(templateElements,
elementBinders == null ? [] : elementBinders, _perf, _expando);
Expand Down
72 changes: 35 additions & 37 deletions lib/core_dom/tagging_view_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,53 @@ part of angular.core.dom;

class TaggingViewFactory implements ViewFactory {
final List<TaggedElementBinder> elementBinders;
final List<dom.Node> templateElements;
final List<dom.Node> templateNodes;
final Profiler _perf;
final Expando _expando;

TaggingViewFactory(this.templateElements, this.elementBinders, this._perf, this._expando);
TaggingViewFactory(this.templateNodes, this.elementBinders, this._perf, this._expando);

BoundViewFactory bind(Injector injector) =>
new BoundViewFactory(this, injector);

View call(Injector injector, [List<dom.Node> elements /* TODO: document fragment */]) {
if (elements == null) {
elements = cloneElements(templateElements);
View call(Injector injector, [List<dom.Node> nodes /* TODO: document fragment */]) {
if (nodes == null) {
nodes = cloneElements(templateNodes);
}
var timerId;
try {
assert((timerId = _perf.startTimer('ng.view')) != false);
var view = new View(elements, injector.get(NgAnimate));
_link(view, elements, elementBinders, injector);
var view = new View(nodes);
_link(view, nodes, elementBinders, injector);
return view;
} finally {
assert(_perf.stopTimer(timerId) != false);
}
}

View _link(View view, List<dom.Node> nodeList, List elementBinders, Injector parentInjector) {
_bindTagged(TaggedElementBinder tagged, rootInjector, elementBinders, View view, boundNode) {
var binder = tagged.binder;
var parentInjector = tagged.parentBinderOffset == -1 ? rootInjector : elementBinders[tagged.parentBinderOffset].injector;
assert(parentInjector != null);

tagged.injector = binder != null ? binder.bind(view, parentInjector, boundNode) : parentInjector;

if (tagged.textBinders != null) {
for (var k = 0, kk = tagged.textBinders.length; k < kk; k++) {
TaggedTextBinder taggedText = tagged.textBinders[k];
taggedText.binder.bind(view, tagged.injector, boundNode.childNodes[taggedText.offsetIndex]);
}
}
}

View _link(View view, List<dom.Node> nodeList, List elementBinders, Injector rootInjector) {


var directiveDefsByName = {};

var elementBinderIndex = 0;
for (int i = 0, ii = nodeList.length; i < ii; i++) {
var node = nodeList[i];
print("node: $node ${node.outerHtml}}");

// if node isn't attached to the DOM, create a parent for it.
var parentNode = node.parentNode;
Expand All @@ -45,47 +59,31 @@ class TaggingViewFactory implements ViewFactory {
parentNode.append(node);
}

if (node is dom.Element) {
if (node.nodeType == 1) {
var elts = node.querySelectorAll('.ng-binding');
// HACK: querySelectorAll doesn't return the node.
var startIndex = node.classes.contains('ng-binding') ? -1 : 0;
print("starting at: $startIndex");
for (int j = startIndex, jj = elts.length; j < jj; j++, elementBinderIndex++) {
if (j >= 0) print("elt: ${elts[j]} ${elts[j].outerHtml}");
TaggedElementBinder tagged = elementBinders[elementBinderIndex];
var boundNode = j == -1 ? node : elts[j];

var binder = tagged.binder;

var childInjector = binder != null ? binder.bind(view, parentInjector, j == -1 ? node : elts[j]) : parentInjector;
_bindTagged(tagged, rootInjector, elementBinders, view, boundNode);
}
} else if (node.nodeType == 3 || node.nodeType == 8) {
TaggedElementBinder tagged = elementBinders[elementBinderIndex];
assert(tagged.binder != null);

_bindTagged(tagged, rootInjector, elementBinders, view, node);

elementBinderIndex++;
} else {
throw "nodeType sadness ${node.nodeType}}";
}

if (fakeParent) {
// extract the node from the parentNode.
nodeList[i] = parentNode.nodes[0];
}

// querySelectorAll('.ng-binding') should return a list of nodes in the same order as the elementBinders list.

// keep a injector array --

/*var eb = elementBinders[i];
int index = i;
var binder = eb.binder;
var timerId;
try {
assert((timerId = _perf.startTimer('ng.view.link', _html(node))) != false);
} finally {
assert(_perf.stopTimer(timerId) != false);
}*/
}
return view;
}
Expand Down
3 changes: 1 addition & 2 deletions test/core_dom/compiler_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ forBothCompilers(fn) {
fn();
});

xdescribe('tagging compiler', () {
describe('tagging compiler', () {
beforeEach(module((Module m) {
m.type(Compiler, implementedBy: TaggingCompiler);
return m;
Expand Down Expand Up @@ -591,7 +591,6 @@ void main() {
})));

it('should expose a parent controller to the scope of its children', inject((TestBed _) {

var element = _.compile('<div my-parent-controller>'
' <div my-child-controller>{{ my_parent.data() }}</div>'
'</div>');
Expand Down

0 comments on commit 59516af

Please sign in to comment.