| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- var Traverse = require('traverse');
- var EventEmitter = require('events').EventEmitter;
- module.exports = Chainsaw;
- function Chainsaw (builder) {
- var saw = Chainsaw.saw(builder, {});
- var r = builder.call(saw.handlers, saw);
- if (r !== undefined) saw.handlers = r;
- saw.record();
- return saw.chain();
- };
- Chainsaw.light = function ChainsawLight (builder) {
- var saw = Chainsaw.saw(builder, {});
- var r = builder.call(saw.handlers, saw);
- if (r !== undefined) saw.handlers = r;
- return saw.chain();
- };
- Chainsaw.saw = function (builder, handlers) {
- var saw = new EventEmitter;
- saw.handlers = handlers;
- saw.actions = [];
- saw.chain = function () {
- var ch = Traverse(saw.handlers).map(function (node) {
- if (this.isRoot) return node;
- var ps = this.path;
- if (typeof node === 'function') {
- this.update(function () {
- saw.actions.push({
- path : ps,
- args : [].slice.call(arguments)
- });
- return ch;
- });
- }
- });
- process.nextTick(function () {
- saw.emit('begin');
- saw.next();
- });
- return ch;
- };
- saw.pop = function () {
- return saw.actions.shift();
- };
- saw.next = function () {
- var action = saw.pop();
- if (!action) {
- saw.emit('end');
- }
- else if (!action.trap) {
- var node = saw.handlers;
- action.path.forEach(function (key) { node = node[key] });
- node.apply(saw.handlers, action.args);
- }
- };
- saw.nest = function (cb) {
- var args = [].slice.call(arguments, 1);
- var autonext = true;
- if (typeof cb === 'boolean') {
- var autonext = cb;
- cb = args.shift();
- }
- var s = Chainsaw.saw(builder, {});
- var r = builder.call(s.handlers, s);
- if (r !== undefined) s.handlers = r;
- // If we are recording...
- if ("undefined" !== typeof saw.step) {
- // ... our children should, too
- s.record();
- }
- cb.apply(s.chain(), args);
- if (autonext !== false) s.on('end', saw.next);
- };
- saw.record = function () {
- upgradeChainsaw(saw);
- };
- ['trap', 'down', 'jump'].forEach(function (method) {
- saw[method] = function () {
- throw new Error("To use the trap, down and jump features, please "+
- "call record() first to start recording actions.");
- };
- });
- return saw;
- };
- function upgradeChainsaw(saw) {
- saw.step = 0;
- // override pop
- saw.pop = function () {
- return saw.actions[saw.step++];
- };
- saw.trap = function (name, cb) {
- var ps = Array.isArray(name) ? name : [name];
- saw.actions.push({
- path : ps,
- step : saw.step,
- cb : cb,
- trap : true
- });
- };
- saw.down = function (name) {
- var ps = (Array.isArray(name) ? name : [name]).join('/');
- var i = saw.actions.slice(saw.step).map(function (x) {
- if (x.trap && x.step <= saw.step) return false;
- return x.path.join('/') == ps;
- }).indexOf(true);
- if (i >= 0) saw.step += i;
- else saw.step = saw.actions.length;
- var act = saw.actions[saw.step - 1];
- if (act && act.trap) {
- // It's a trap!
- saw.step = act.step;
- act.cb();
- }
- else saw.next();
- };
- saw.jump = function (step) {
- saw.step = step;
- saw.next();
- };
- };
|