marko/compiler/Walker.js
Patrick Steele-Idem e76c7fa6d6 vdom support
2016-09-19 16:50:44 -06:00

153 lines
3.4 KiB
JavaScript

'use strict';
var isArray = Array.isArray;
var Container = require('./ast/Container');
function noop() {}
class Walker {
constructor(options) {
this._enter = options.enter || noop;
this._exit = options.exit || noop;
this._enterArray = options.enterArray || noop;
this._exitArray = options.exitArray || noop;
this._stopped = false;
this._reset();
this._stack = [];
}
_reset() {
this._skipped = false;
this._replaced = undefined;
this._removed = false;
}
skip() {
this._skipped = true;
}
stop() {
this._stopped = true;
}
replace(newNode) {
this._replaced = newNode;
}
remove() {
this._removed = true;
}
_walkArray(array) {
var hasRemoval = false;
array = this._enterArray(array) || array;
array.forEach((node, i) => {
var transformed = this.walk(node);
if (transformed == null) {
array[i] = null;
hasRemoval = true;
} else if (transformed !== node) {
array[i] = transformed;
}
});
if (hasRemoval) {
for (let i=array.length-1; i>=0; i--) {
if (array[i] == null) {
array.splice(i, 1);
}
}
}
array = this._exitArray(array) || array;
return array;
}
_walkContainer(nodes) {
nodes.forEach((node) => {
var transformed = this.walk(node);
if (!transformed) {
node.container.removeChild(node);
} else if (transformed !== node) {
node.container.replaceChild(transformed, node);
}
});
}
walk(node) {
if (!node || this._stopped || typeof node === 'string') {
return node;
}
this._reset();
var parent = this._stack.length ? this._stack[this._stack.length - 1] : undefined;
this._stack.push(node);
var replaced = this._enter(node, parent);
if (replaced === undefined) {
replaced = this._replaced;
}
if (this._removed) {
replaced = null;
}
if (replaced !== undefined) {
this._stack.pop();
return replaced;
}
if (this._skipped || this._stopped) {
this._stack.pop();
return node;
}
if (isArray(node)) {
let array = node;
let newArray = this._walkArray(array);
this._stack.pop();
return newArray;
} else if (node instanceof Container) {
let container = node;
this._walkContainer(container);
this._stack.pop();
return container;
} else {
if (node.walk) {
node.walk(this);
}
}
if (this._stopped) {
this._stack.pop();
return node;
}
this._reset();
replaced = this._exit(node, parent);
if (replaced === undefined) {
replaced = this._replaced;
}
if (this._removed) {
replaced = null;
}
if (replaced !== undefined) {
this._stack.pop();
return replaced;
}
this._stack.pop();
return node;
}
}
module.exports = Walker;