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.
		
		
		
		
		
			
		
			
				
					
					
						
							146 lines
						
					
					
						
							3.6 KiB
						
					
					
				
			
		
		
	
	
							146 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();
 | |
|     };
 | |
| };
 |