You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
3.6 KiB
145 lines
3.6 KiB
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(); |
|
}; |
|
};
|
|
|