/* I'm not thrilled with this, but it should be enough to demonstrate ... something ... */ function Breadcrumb(el) { this.path = el.parentElement ? new Breadcrumb(el.parentElement) : null; this.left = el.previousElementSibling; this.right = el.nextElementSibling; } function Zipper(tree, path) { this.tree = tree; this.crumb = new Breadcrumb(tree); } function hierarchyPanic(msg) { throw new DOMError("HierarchyRequestError", msg); } Zipper.prototype.goLeft = function () { if (!this.crumb.path) hierarchyPanic("cannot go left of top"); if (!this.crumb.left) hierarchyPanic("cannot go left of first"); return new Zipper(this.crumb.left); } Zipper.prototype.goRight = function () { if (!this.crumb.path) hierarchyPanic("cannot go right of top"); if (!this.crumb.right) hierarchyPanic("cannot go left of last"); return new Zipper(this.crumb.right); } Zipper.prototype.goUp = function () { if (!this.crumb.path) hierarchyPanic("cannot go up of top"); return new Zipper(this.tree.parentElement); } Zipper.prototype.goDown = function () { var child = this.tree.firstElementChild if (!child) hierarchyPanic("cannot go down from leaf"); return new Zipper(child); } Zipper.prototype.change = function (el) { this.tree.parentElement.replaceChild(el, this.tree); return new Zipper(el); } Zipper.prototype.insertRight = function (el) { this.tree.parentElement.insertBefore(el, this.crumb.right); return new Zipper(this.tree); } Zipper.prototype.insertLeft = function (el) { this.tree.parentElement.insertBefore(el, this.tree); return new Zipper(this.tree); } Zipper.prototype.insertDown = function (el) { this.tree.appendChild(el); return new Zipper(el); }