| var RetryOperation = require('./retry_operation'); | 
|   | 
| exports.operation = function(options) { | 
|   var timeouts = exports.timeouts(options); | 
|   return new RetryOperation(timeouts, { | 
|       forever: options && options.forever, | 
|       unref: options && options.unref, | 
|       maxRetryTime: options && options.maxRetryTime | 
|   }); | 
| }; | 
|   | 
| exports.timeouts = function(options) { | 
|   if (options instanceof Array) { | 
|     return [].concat(options); | 
|   } | 
|   | 
|   var opts = { | 
|     retries: 10, | 
|     factor: 2, | 
|     minTimeout: 1 * 1000, | 
|     maxTimeout: Infinity, | 
|     randomize: false | 
|   }; | 
|   for (var key in options) { | 
|     opts[key] = options[key]; | 
|   } | 
|   | 
|   if (opts.minTimeout > opts.maxTimeout) { | 
|     throw new Error('minTimeout is greater than maxTimeout'); | 
|   } | 
|   | 
|   var timeouts = []; | 
|   for (var i = 0; i < opts.retries; i++) { | 
|     timeouts.push(this.createTimeout(i, opts)); | 
|   } | 
|   | 
|   if (options && options.forever && !timeouts.length) { | 
|     timeouts.push(this.createTimeout(i, opts)); | 
|   } | 
|   | 
|   // sort the array numerically ascending | 
|   timeouts.sort(function(a,b) { | 
|     return a - b; | 
|   }); | 
|   | 
|   return timeouts; | 
| }; | 
|   | 
| exports.createTimeout = function(attempt, opts) { | 
|   var random = (opts.randomize) | 
|     ? (Math.random() + 1) | 
|     : 1; | 
|   | 
|   var timeout = Math.round(random * opts.minTimeout * Math.pow(opts.factor, attempt)); | 
|   timeout = Math.min(timeout, opts.maxTimeout); | 
|   | 
|   return timeout; | 
| }; | 
|   | 
| exports.wrap = function(obj, options, methods) { | 
|   if (options instanceof Array) { | 
|     methods = options; | 
|     options = null; | 
|   } | 
|   | 
|   if (!methods) { | 
|     methods = []; | 
|     for (var key in obj) { | 
|       if (typeof obj[key] === 'function') { | 
|         methods.push(key); | 
|       } | 
|     } | 
|   } | 
|   | 
|   for (var i = 0; i < methods.length; i++) { | 
|     var method   = methods[i]; | 
|     var original = obj[method]; | 
|   | 
|     obj[method] = function retryWrapper(original) { | 
|       var op       = exports.operation(options); | 
|       var args     = Array.prototype.slice.call(arguments, 1); | 
|       var callback = args.pop(); | 
|   | 
|       args.push(function(err) { | 
|         if (op.retry(err)) { | 
|           return; | 
|         } | 
|         if (err) { | 
|           arguments[0] = op.mainError(); | 
|         } | 
|         callback.apply(this, arguments); | 
|       }); | 
|   | 
|       op.attempt(function() { | 
|         original.apply(obj, args); | 
|       }); | 
|     }.bind(obj, original); | 
|     obj[method].options = options; | 
|   } | 
| }; |