mirror of
				https://github.com/ether/etherpad-lite.git
				synced 2025-10-31 08:11:10 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			691 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			691 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*global setTimeout: false, console: false */
 | |
| (function () {
 | |
| 
 | |
|     var async = {};
 | |
| 
 | |
|     // global on the server, window in the browser
 | |
|     var root = this,
 | |
|         previous_async = root.async;
 | |
| 
 | |
|     if (typeof module !== 'undefined' && module.exports) {
 | |
|         module.exports = async;
 | |
|     }
 | |
|     else {
 | |
|         root.async = async;
 | |
|     }
 | |
| 
 | |
|     async.noConflict = function () {
 | |
|         root.async = previous_async;
 | |
|         return async;
 | |
|     };
 | |
| 
 | |
|     //// cross-browser compatiblity functions ////
 | |
| 
 | |
|     var _forEach = function (arr, iterator) {
 | |
|         if (arr.forEach) {
 | |
|             return arr.forEach(iterator);
 | |
|         }
 | |
|         for (var i = 0; i < arr.length; i += 1) {
 | |
|             iterator(arr[i], i, arr);
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     var _map = function (arr, iterator) {
 | |
|         if (arr.map) {
 | |
|             return arr.map(iterator);
 | |
|         }
 | |
|         var results = [];
 | |
|         _forEach(arr, function (x, i, a) {
 | |
|             results.push(iterator(x, i, a));
 | |
|         });
 | |
|         return results;
 | |
|     };
 | |
| 
 | |
|     var _reduce = function (arr, iterator, memo) {
 | |
|         if (arr.reduce) {
 | |
|             return arr.reduce(iterator, memo);
 | |
|         }
 | |
|         _forEach(arr, function (x, i, a) {
 | |
|             memo = iterator(memo, x, i, a);
 | |
|         });
 | |
|         return memo;
 | |
|     };
 | |
| 
 | |
|     var _keys = function (obj) {
 | |
|         if (Object.keys) {
 | |
|             return Object.keys(obj);
 | |
|         }
 | |
|         var keys = [];
 | |
|         for (var k in obj) {
 | |
|             if (obj.hasOwnProperty(k)) {
 | |
|                 keys.push(k);
 | |
|             }
 | |
|         }
 | |
|         return keys;
 | |
|     };
 | |
| 
 | |
|     var _indexOf = function (arr, item) {
 | |
|         if (arr.indexOf) {
 | |
|             return arr.indexOf(item);
 | |
|         }
 | |
|         for (var i = 0; i < arr.length; i += 1) {
 | |
|             if (arr[i] === item) {
 | |
|                 return i;
 | |
|             }
 | |
|         }
 | |
|         return -1;
 | |
|     };
 | |
| 
 | |
|     //// exported async module functions ////
 | |
| 
 | |
|     //// nextTick implementation with browser-compatible fallback ////
 | |
|     if (typeof process === 'undefined' || !(process.nextTick)) {
 | |
|         async.nextTick = function (fn) {
 | |
|             setTimeout(fn, 0);
 | |
|         };
 | |
|     }
 | |
|     else {
 | |
|         async.nextTick = process.nextTick;
 | |
|     }
 | |
| 
 | |
|     async.forEach = function (arr, iterator, callback) {
 | |
|         if (!arr.length) {
 | |
|             return callback();
 | |
|         }
 | |
|         var completed = 0;
 | |
|         _forEach(arr, function (x) {
 | |
|             iterator(x, function (err) {
 | |
|                 if (err) {
 | |
|                     callback(err);
 | |
|                     callback = function () {};
 | |
|                 }
 | |
|                 else {
 | |
|                     completed += 1;
 | |
|                     if (completed === arr.length) {
 | |
|                         callback();
 | |
|                     }
 | |
|                 }
 | |
|             });
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     async.forEachSeries = function (arr, iterator, callback) {
 | |
|         if (!arr.length) {
 | |
|             return callback();
 | |
|         }
 | |
|         var completed = 0;
 | |
|         var iterate = function () {
 | |
|             iterator(arr[completed], function (err) {
 | |
|                 if (err) {
 | |
|                     callback(err);
 | |
|                     callback = function () {};
 | |
|                 }
 | |
|                 else {
 | |
|                     completed += 1;
 | |
|                     if (completed === arr.length) {
 | |
|                         callback();
 | |
|                     }
 | |
|                     else {
 | |
|                         iterate();
 | |
|                     }
 | |
|                 }
 | |
|             });
 | |
|         };
 | |
|         iterate();
 | |
|     };
 | |
|     
 | |
|     async.forEachLimit = function (arr, limit, iterator, callback) {
 | |
|         if (!arr.length || limit <= 0) {
 | |
|             return callback(); 
 | |
|         }
 | |
|         var completed = 0;
 | |
|         var started = 0;
 | |
|         var running = 0;
 | |
|         
 | |
|         (function replenish () {
 | |
|           if (completed === arr.length) {
 | |
|               return callback();
 | |
|           }
 | |
|           
 | |
|           while (running < limit && started < arr.length) {
 | |
|             iterator(arr[started], function (err) {
 | |
|               if (err) {
 | |
|                   callback(err);
 | |
|                   callback = function () {};
 | |
|               }
 | |
|               else {
 | |
|                   completed += 1;
 | |
|                   running -= 1;
 | |
|                   if (completed === arr.length) {
 | |
|                       callback();
 | |
|                   }
 | |
|                   else {
 | |
|                       replenish();
 | |
|                   }
 | |
|               }
 | |
|             });
 | |
|             started += 1;
 | |
|             running += 1;
 | |
|           }
 | |
|         })();
 | |
|     };
 | |
| 
 | |
| 
 | |
|     var doParallel = function (fn) {
 | |
|         return function () {
 | |
|             var args = Array.prototype.slice.call(arguments);
 | |
|             return fn.apply(null, [async.forEach].concat(args));
 | |
|         };
 | |
|     };
 | |
|     var doSeries = function (fn) {
 | |
|         return function () {
 | |
|             var args = Array.prototype.slice.call(arguments);
 | |
|             return fn.apply(null, [async.forEachSeries].concat(args));
 | |
|         };
 | |
|     };
 | |
| 
 | |
| 
 | |
|     var _asyncMap = function (eachfn, arr, iterator, callback) {
 | |
|         var results = [];
 | |
|         arr = _map(arr, function (x, i) {
 | |
|             return {index: i, value: x};
 | |
|         });
 | |
|         eachfn(arr, function (x, callback) {
 | |
|             iterator(x.value, function (err, v) {
 | |
|                 results[x.index] = v;
 | |
|                 callback(err);
 | |
|             });
 | |
|         }, function (err) {
 | |
|             callback(err, results);
 | |
|         });
 | |
|     };
 | |
|     async.map = doParallel(_asyncMap);
 | |
|     async.mapSeries = doSeries(_asyncMap);
 | |
| 
 | |
| 
 | |
|     // reduce only has a series version, as doing reduce in parallel won't
 | |
|     // work in many situations.
 | |
|     async.reduce = function (arr, memo, iterator, callback) {
 | |
|         async.forEachSeries(arr, function (x, callback) {
 | |
|             iterator(memo, x, function (err, v) {
 | |
|                 memo = v;
 | |
|                 callback(err);
 | |
|             });
 | |
|         }, function (err) {
 | |
|             callback(err, memo);
 | |
|         });
 | |
|     };
 | |
|     // inject alias
 | |
|     async.inject = async.reduce;
 | |
|     // foldl alias
 | |
|     async.foldl = async.reduce;
 | |
| 
 | |
|     async.reduceRight = function (arr, memo, iterator, callback) {
 | |
|         var reversed = _map(arr, function (x) {
 | |
|             return x;
 | |
|         }).reverse();
 | |
|         async.reduce(reversed, memo, iterator, callback);
 | |
|     };
 | |
|     // foldr alias
 | |
|     async.foldr = async.reduceRight;
 | |
| 
 | |
|     var _filter = function (eachfn, arr, iterator, callback) {
 | |
|         var results = [];
 | |
|         arr = _map(arr, function (x, i) {
 | |
|             return {index: i, value: x};
 | |
|         });
 | |
|         eachfn(arr, function (x, callback) {
 | |
|             iterator(x.value, function (v) {
 | |
|                 if (v) {
 | |
|                     results.push(x);
 | |
|                 }
 | |
|                 callback();
 | |
|             });
 | |
|         }, function (err) {
 | |
|             callback(_map(results.sort(function (a, b) {
 | |
|                 return a.index - b.index;
 | |
|             }), function (x) {
 | |
|                 return x.value;
 | |
|             }));
 | |
|         });
 | |
|     };
 | |
|     async.filter = doParallel(_filter);
 | |
|     async.filterSeries = doSeries(_filter);
 | |
|     // select alias
 | |
|     async.select = async.filter;
 | |
|     async.selectSeries = async.filterSeries;
 | |
| 
 | |
|     var _reject = function (eachfn, arr, iterator, callback) {
 | |
|         var results = [];
 | |
|         arr = _map(arr, function (x, i) {
 | |
|             return {index: i, value: x};
 | |
|         });
 | |
|         eachfn(arr, function (x, callback) {
 | |
|             iterator(x.value, function (v) {
 | |
|                 if (!v) {
 | |
|                     results.push(x);
 | |
|                 }
 | |
|                 callback();
 | |
|             });
 | |
|         }, function (err) {
 | |
|             callback(_map(results.sort(function (a, b) {
 | |
|                 return a.index - b.index;
 | |
|             }), function (x) {
 | |
|                 return x.value;
 | |
|             }));
 | |
|         });
 | |
|     };
 | |
|     async.reject = doParallel(_reject);
 | |
|     async.rejectSeries = doSeries(_reject);
 | |
| 
 | |
|     var _detect = function (eachfn, arr, iterator, main_callback) {
 | |
|         eachfn(arr, function (x, callback) {
 | |
|             iterator(x, function (result) {
 | |
|                 if (result) {
 | |
|                     main_callback(x);
 | |
|                     main_callback = function () {};
 | |
|                 }
 | |
|                 else {
 | |
|                     callback();
 | |
|                 }
 | |
|             });
 | |
|         }, function (err) {
 | |
|             main_callback();
 | |
|         });
 | |
|     };
 | |
|     async.detect = doParallel(_detect);
 | |
|     async.detectSeries = doSeries(_detect);
 | |
| 
 | |
|     async.some = function (arr, iterator, main_callback) {
 | |
|         async.forEach(arr, function (x, callback) {
 | |
|             iterator(x, function (v) {
 | |
|                 if (v) {
 | |
|                     main_callback(true);
 | |
|                     main_callback = function () {};
 | |
|                 }
 | |
|                 callback();
 | |
|             });
 | |
|         }, function (err) {
 | |
|             main_callback(false);
 | |
|         });
 | |
|     };
 | |
|     // any alias
 | |
|     async.any = async.some;
 | |
| 
 | |
|     async.every = function (arr, iterator, main_callback) {
 | |
|         async.forEach(arr, function (x, callback) {
 | |
|             iterator(x, function (v) {
 | |
|                 if (!v) {
 | |
|                     main_callback(false);
 | |
|                     main_callback = function () {};
 | |
|                 }
 | |
|                 callback();
 | |
|             });
 | |
|         }, function (err) {
 | |
|             main_callback(true);
 | |
|         });
 | |
|     };
 | |
|     // all alias
 | |
|     async.all = async.every;
 | |
| 
 | |
|     async.sortBy = function (arr, iterator, callback) {
 | |
|         async.map(arr, function (x, callback) {
 | |
|             iterator(x, function (err, criteria) {
 | |
|                 if (err) {
 | |
|                     callback(err);
 | |
|                 }
 | |
|                 else {
 | |
|                     callback(null, {value: x, criteria: criteria});
 | |
|                 }
 | |
|             });
 | |
|         }, function (err, results) {
 | |
|             if (err) {
 | |
|                 return callback(err);
 | |
|             }
 | |
|             else {
 | |
|                 var fn = function (left, right) {
 | |
|                     var a = left.criteria, b = right.criteria;
 | |
|                     return a < b ? -1 : a > b ? 1 : 0;
 | |
|                 };
 | |
|                 callback(null, _map(results.sort(fn), function (x) {
 | |
|                     return x.value;
 | |
|                 }));
 | |
|             }
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     async.auto = function (tasks, callback) {
 | |
|         callback = callback || function () {};
 | |
|         var keys = _keys(tasks);
 | |
|         if (!keys.length) {
 | |
|             return callback(null);
 | |
|         }
 | |
| 
 | |
|         var results = {};
 | |
| 
 | |
|         var listeners = [];
 | |
|         var addListener = function (fn) {
 | |
|             listeners.unshift(fn);
 | |
|         };
 | |
|         var removeListener = function (fn) {
 | |
|             for (var i = 0; i < listeners.length; i += 1) {
 | |
|                 if (listeners[i] === fn) {
 | |
|                     listeners.splice(i, 1);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
|         var taskComplete = function () {
 | |
|             _forEach(listeners, function (fn) {
 | |
|                 fn();
 | |
|             });
 | |
|         };
 | |
| 
 | |
|         addListener(function () {
 | |
|             if (_keys(results).length === keys.length) {
 | |
|                 callback(null, results);
 | |
|             }
 | |
|         });
 | |
| 
 | |
|         _forEach(keys, function (k) {
 | |
|             var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
 | |
|             var taskCallback = function (err) {
 | |
|                 if (err) {
 | |
|                     callback(err);
 | |
|                     // stop subsequent errors hitting callback multiple times
 | |
|                     callback = function () {};
 | |
|                 }
 | |
|                 else {
 | |
|                     var args = Array.prototype.slice.call(arguments, 1);
 | |
|                     if (args.length <= 1) {
 | |
|                         args = args[0];
 | |
|                     }
 | |
|                     results[k] = args;
 | |
|                     taskComplete();
 | |
|                 }
 | |
|             };
 | |
|             var requires = task.slice(0, Math.abs(task.length - 1)) || [];
 | |
|             var ready = function () {
 | |
|                 return _reduce(requires, function (a, x) {
 | |
|                     return (a && results.hasOwnProperty(x));
 | |
|                 }, true);
 | |
|             };
 | |
|             if (ready()) {
 | |
|                 task[task.length - 1](taskCallback, results);
 | |
|             }
 | |
|             else {
 | |
|                 var listener = function () {
 | |
|                     if (ready()) {
 | |
|                         removeListener(listener);
 | |
|                         task[task.length - 1](taskCallback, results);
 | |
|                     }
 | |
|                 };
 | |
|                 addListener(listener);
 | |
|             }
 | |
|         });
 | |
|     };
 | |
| 
 | |
|     async.waterfall = function (tasks, callback) {
 | |
|         if (!tasks.length) {
 | |
|             return callback();
 | |
|         }
 | |
|         callback = callback || function () {};
 | |
|         var wrapIterator = function (iterator) {
 | |
|             return function (err) {
 | |
|                 if (err) {
 | |
|                     callback(err);
 | |
|                     callback = function () {};
 | |
|                 }
 | |
|                 else {
 | |
|                     var args = Array.prototype.slice.call(arguments, 1);
 | |
|                     var next = iterator.next();
 | |
|                     if (next) {
 | |
|                         args.push(wrapIterator(next));
 | |
|                     }
 | |
|                     else {
 | |
|                         args.push(callback);
 | |
|                     }
 | |
|                     async.nextTick(function () {
 | |
|                         iterator.apply(null, args);
 | |
|                     });
 | |
|                 }
 | |
|             };
 | |
|         };
 | |
|         wrapIterator(async.iterator(tasks))();
 | |
|     };
 | |
| 
 | |
|     async.parallel = function (tasks, callback) {
 | |
|         callback = callback || function () {};
 | |
|         if (tasks.constructor === Array) {
 | |
|             async.map(tasks, function (fn, callback) {
 | |
|                 if (fn) {
 | |
|                     fn(function (err) {
 | |
|                         var args = Array.prototype.slice.call(arguments, 1);
 | |
|                         if (args.length <= 1) {
 | |
|                             args = args[0];
 | |
|                         }
 | |
|                         callback.call(null, err, args);
 | |
|                     });
 | |
|                 }
 | |
|             }, callback);
 | |
|         }
 | |
|         else {
 | |
|             var results = {};
 | |
|             async.forEach(_keys(tasks), function (k, callback) {
 | |
|                 tasks[k](function (err) {
 | |
|                     var args = Array.prototype.slice.call(arguments, 1);
 | |
|                     if (args.length <= 1) {
 | |
|                         args = args[0];
 | |
|                     }
 | |
|                     results[k] = args;
 | |
|                     callback(err);
 | |
|                 });
 | |
|             }, function (err) {
 | |
|                 callback(err, results);
 | |
|             });
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     async.series = function (tasks, callback) {
 | |
|         callback = callback || function () {};
 | |
|         if (tasks.constructor === Array) {
 | |
|             async.mapSeries(tasks, function (fn, callback) {
 | |
|                 if (fn) {
 | |
|                     fn(function (err) {
 | |
|                         var args = Array.prototype.slice.call(arguments, 1);
 | |
|                         if (args.length <= 1) {
 | |
|                             args = args[0];
 | |
|                         }
 | |
|                         callback.call(null, err, args);
 | |
|                     });
 | |
|                 }
 | |
|             }, callback);
 | |
|         }
 | |
|         else {
 | |
|             var results = {};
 | |
|             async.forEachSeries(_keys(tasks), function (k, callback) {
 | |
|                 tasks[k](function (err) {
 | |
|                     var args = Array.prototype.slice.call(arguments, 1);
 | |
|                     if (args.length <= 1) {
 | |
|                         args = args[0];
 | |
|                     }
 | |
|                     results[k] = args;
 | |
|                     callback(err);
 | |
|                 });
 | |
|             }, function (err) {
 | |
|                 callback(err, results);
 | |
|             });
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     async.iterator = function (tasks) {
 | |
|         var makeCallback = function (index) {
 | |
|             var fn = function () {
 | |
|                 if (tasks.length) {
 | |
|                     tasks[index].apply(null, arguments);
 | |
|                 }
 | |
|                 return fn.next();
 | |
|             };
 | |
|             fn.next = function () {
 | |
|                 return (index < tasks.length - 1) ? makeCallback(index + 1): null;
 | |
|             };
 | |
|             return fn;
 | |
|         };
 | |
|         return makeCallback(0);
 | |
|     };
 | |
| 
 | |
|     async.apply = function (fn) {
 | |
|         var args = Array.prototype.slice.call(arguments, 1);
 | |
|         return function () {
 | |
|             return fn.apply(
 | |
|                 null, args.concat(Array.prototype.slice.call(arguments))
 | |
|             );
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     var _concat = function (eachfn, arr, fn, callback) {
 | |
|         var r = [];
 | |
|         eachfn(arr, function (x, cb) {
 | |
|             fn(x, function (err, y) {
 | |
|                 r = r.concat(y || []);
 | |
|                 cb(err);
 | |
|             });
 | |
|         }, function (err) {
 | |
|             callback(err, r);
 | |
|         });
 | |
|     };
 | |
|     async.concat = doParallel(_concat);
 | |
|     async.concatSeries = doSeries(_concat);
 | |
| 
 | |
|     async.whilst = function (test, iterator, callback) {
 | |
|         if (test()) {
 | |
|             iterator(function (err) {
 | |
|                 if (err) {
 | |
|                     return callback(err);
 | |
|                 }
 | |
|                 async.whilst(test, iterator, callback);
 | |
|             });
 | |
|         }
 | |
|         else {
 | |
|             callback();
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     async.until = function (test, iterator, callback) {
 | |
|         if (!test()) {
 | |
|             iterator(function (err) {
 | |
|                 if (err) {
 | |
|                     return callback(err);
 | |
|                 }
 | |
|                 async.until(test, iterator, callback);
 | |
|             });
 | |
|         }
 | |
|         else {
 | |
|             callback();
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     async.queue = function (worker, concurrency) {
 | |
|         var workers = 0;
 | |
|         var q = {
 | |
|             tasks: [],
 | |
|             concurrency: concurrency,
 | |
|             saturated: null,
 | |
|             empty: null,
 | |
|             drain: null,
 | |
|             push: function (data, callback) {
 | |
|                 q.tasks.push({data: data, callback: callback});
 | |
|                 if(q.saturated && q.tasks.length == concurrency) q.saturated();
 | |
|                 async.nextTick(q.process);
 | |
|             },
 | |
|             process: function () {
 | |
|                 if (workers < q.concurrency && q.tasks.length) {
 | |
|                     var task = q.tasks.shift();
 | |
|                     if(q.empty && q.tasks.length == 0) q.empty();
 | |
|                     workers += 1;
 | |
|                     worker(task.data, function () {
 | |
|                         workers -= 1;
 | |
|                         if (task.callback) {
 | |
|                             task.callback.apply(task, arguments);
 | |
|                         }
 | |
|                         if(q.drain && q.tasks.length + workers == 0) q.drain();
 | |
|                         q.process();
 | |
|                     });
 | |
|                 }
 | |
|             },
 | |
|             length: function () {
 | |
|                 return q.tasks.length;
 | |
|             },
 | |
|             running: function () {
 | |
|                 return workers;
 | |
|             }
 | |
|         };
 | |
|         return q;
 | |
|     };
 | |
| 
 | |
|     var _console_fn = function (name) {
 | |
|         return function (fn) {
 | |
|             var args = Array.prototype.slice.call(arguments, 1);
 | |
|             fn.apply(null, args.concat([function (err) {
 | |
|                 var args = Array.prototype.slice.call(arguments, 1);
 | |
|                 if (typeof console !== 'undefined') {
 | |
|                     if (err) {
 | |
|                         if (console.error) {
 | |
|                             console.error(err);
 | |
|                         }
 | |
|                     }
 | |
|                     else if (console[name]) {
 | |
|                         _forEach(args, function (x) {
 | |
|                             console[name](x);
 | |
|                         });
 | |
|                     }
 | |
|                 }
 | |
|             }]));
 | |
|         };
 | |
|     };
 | |
|     async.log = _console_fn('log');
 | |
|     async.dir = _console_fn('dir');
 | |
|     /*async.info = _console_fn('info');
 | |
|     async.warn = _console_fn('warn');
 | |
|     async.error = _console_fn('error');*/
 | |
| 
 | |
|     async.memoize = function (fn, hasher) {
 | |
|         var memo = {};
 | |
|         var queues = {};
 | |
|         hasher = hasher || function (x) {
 | |
|             return x;
 | |
|         };
 | |
|         var memoized = function () {
 | |
|             var args = Array.prototype.slice.call(arguments);
 | |
|             var callback = args.pop();
 | |
|             var key = hasher.apply(null, args);
 | |
|             if (key in memo) {
 | |
|                 callback.apply(null, memo[key]);
 | |
|             }
 | |
|             else if (key in queues) {
 | |
|                 queues[key].push(callback);
 | |
|             }
 | |
|             else {
 | |
|                 queues[key] = [callback];
 | |
|                 fn.apply(null, args.concat([function () {
 | |
|                     memo[key] = arguments;
 | |
|                     var q = queues[key];
 | |
|                     delete queues[key];
 | |
|                     for (var i = 0, l = q.length; i < l; i++) {
 | |
|                       q[i].apply(null, arguments);
 | |
|                     }
 | |
|                 }]));
 | |
|             }
 | |
|         };
 | |
|         memoized.unmemoized = fn;
 | |
|         return memoized;
 | |
|     };
 | |
| 
 | |
|     async.unmemoize = function (fn) {
 | |
|       return function () {
 | |
|         return (fn.unmemoized || fn).apply(null, arguments);
 | |
|       }
 | |
|     };
 | |
| 
 | |
| }());
 |