mirror of
				https://github.com/ether/etherpad-lite.git
				synced 2025-10-31 00:01:16 +01:00 
			
		
		
		
	Client side hooks are now loaded and works
This commit is contained in:
		
							parent
							
								
									35626df77b
								
							
						
					
					
						commit
						73aa0687b8
					
				| @ -26,6 +26,9 @@ | |||||||
| 	"somehookname": "ep_fintest/otherpart:somehook", | 	"somehookname": "ep_fintest/otherpart:somehook", | ||||||
| 	"morehook": "ep_fintest/otherpart:morehook", | 	"morehook": "ep_fintest/otherpart:morehook", | ||||||
| 	"expressCreateServer": "ep_fintest/otherpart:expressServer" | 	"expressCreateServer": "ep_fintest/otherpart:expressServer" | ||||||
|  |       }, | ||||||
|  |       "client_hooks": { | ||||||
|  |         "somehookname": "ep_fintest/static/js/test:bar" | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   ] |   ] | ||||||
|  | |||||||
| @ -1 +1,5 @@ | |||||||
| exports.foo = 42; | exports.foo = 42; | ||||||
|  | 
 | ||||||
|  | exports.bar = function (hook_name, args, cb) { | ||||||
|  |  return cb(["FOOOO"]); | ||||||
|  | } | ||||||
| @ -10,6 +10,15 @@ exports.expressCreateServer = function (hook_name, args, cb) { | |||||||
|     res.end(); |     res.end(); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   // serve plugin definitions
 | ||||||
|  |   // not very static, but served here so that client can do require("pluginfw/static/js/plugin-definitions.js");
 | ||||||
|  |   args.app.get('/pluginfw/plugin-definitions.json', function (req, res, next) { | ||||||
|  |     res.header("Content-Type","application/json; charset: utf-8"); | ||||||
|  |     res.write(JSON.stringify({"plugins": plugins.plugins, "parts": plugins.parts})); | ||||||
|  |     res.end(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|   /* Handle static files for plugins: |   /* Handle static files for plugins: | ||||||
|      paths like "/static/plugins/ep_myplugin/js/test.js" |      paths like "/static/plugins/ep_myplugin/js/test.js" | ||||||
|      are rewritten into ROOT_PATH_OF_MYPLUGIN/static/js/test.js, |      are rewritten into ROOT_PATH_OF_MYPLUGIN/static/js/test.js, | ||||||
|  | |||||||
							
								
								
									
										690
									
								
								src/static/js/pluginfw/async.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										690
									
								
								src/static/js/pluginfw/async.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,690 @@ | |||||||
|  | /*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); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | }()); | ||||||
							
								
								
									
										63
									
								
								src/static/js/pluginfw/hooks.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/static/js/pluginfw/hooks.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | |||||||
|  | var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins"); | ||||||
|  | 
 | ||||||
|  | /* FIXME: Ugly hack, in the future, use same code for server & client */ | ||||||
|  | if (plugins.isClient) { | ||||||
|  |   var async = require("ep_etherpad-lite/static/js/pluginfw/async"); | ||||||
|  | } else { | ||||||
|  |   var async = require("async"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var hookCallWrapper = function (hook, hook_name, args, cb) { | ||||||
|  |   if (cb === undefined) cb = function (x) { return x; }; | ||||||
|  |   try { | ||||||
|  |     return hook.hook_fn(hook_name, args, cb); | ||||||
|  |   } catch (ex) { | ||||||
|  |     console.error([hook_name, hook.part.full_name, ex]); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Don't use Array.concat as it flatterns arrays within the array */ | ||||||
|  | exports.flatten = function (lst) { | ||||||
|  |   var res = []; | ||||||
|  |   if (lst != undefined && lst != null) { | ||||||
|  |     for (var i = 0; i < lst.length; i++) { | ||||||
|  |       if (lst[i] != undefined && lst[i] != null) { | ||||||
|  |         for (var j = 0; j < lst[i].length; j++) { | ||||||
|  |           res.push(lst[i][j]); | ||||||
|  | 	} | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.callAll = function (hook_name, args) { | ||||||
|  |   if (plugins.hooks[hook_name] === undefined) return []; | ||||||
|  |   return exports.flatten(plugins.hooks[hook_name].map(function (hook) { | ||||||
|  |     return hookCallWrapper(hook, hook_name, args); | ||||||
|  |   })); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.aCallAll = function (hook_name, args, cb) { | ||||||
|  |   if (plugins.hooks[hook_name] === undefined) cb([]); | ||||||
|  |   async.map( | ||||||
|  |     plugins.hooks[hook_name], | ||||||
|  |     function (hook, cb) { | ||||||
|  |       hookCallWrapper(hook, hook_name, args, function (res) { cb(null, res); }); | ||||||
|  |     }, | ||||||
|  |     function (err, res) { | ||||||
|  |       cb(exports.flatten(res)); | ||||||
|  |     } | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.callFirst = function (hook_name, args) { | ||||||
|  |   if (plugins.hooks[hook_name][0] === undefined) return []; | ||||||
|  |   return exports.flatten(hookCallWrapper(plugins.hooks[hook_name][0], hook_name, args)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.aCallFirst = function (hook_name, args, cb) { | ||||||
|  |   if (plugins.hooks[hook_name][0] === undefined) cb([]); | ||||||
|  |   hookCallWrapper(plugins.hooks[hook_name][0], hook_name, args, function (res) { cb(exports.flatten(res)); }); | ||||||
|  | } | ||||||
							
								
								
									
										179
									
								
								src/static/js/pluginfw/plugins.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								src/static/js/pluginfw/plugins.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,179 @@ | |||||||
|  | exports.isClient = typeof global != "object"; | ||||||
|  | 
 | ||||||
|  | if (!exports.isClient) { | ||||||
|  |   var npm = require("npm/lib/npm.js"); | ||||||
|  |   var readInstalled = require("npm/lib/utils/read-installed.js"); | ||||||
|  |   var relativize = require("npm/lib/utils/relativize.js"); | ||||||
|  |   var readJson = require("npm/lib/utils/read-json.js"); | ||||||
|  |   var path = require("path"); | ||||||
|  |   var async = require("async"); | ||||||
|  |   var fs = require("fs"); | ||||||
|  |   var tsort = require("./tsort"); | ||||||
|  |   var util = require("util"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.prefix = 'ep_'; | ||||||
|  | exports.loaded = false; | ||||||
|  | exports.plugins = {}; | ||||||
|  | exports.parts = []; | ||||||
|  | exports.hooks = {}; | ||||||
|  | 
 | ||||||
|  | exports.ensure = function (cb) { | ||||||
|  |   if (!exports.loaded) | ||||||
|  |     exports.update(cb); | ||||||
|  |   else | ||||||
|  |     cb(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.formatPlugins = function () { | ||||||
|  |   return Object.keys(exports.plugins).join(", "); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.formatParts = function () { | ||||||
|  |   return exports.parts.map(function (part) { return part.full_name; }).join("\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.formatHooks = function () { | ||||||
|  |   var res = []; | ||||||
|  |   Object.keys(exports.hooks).forEach(function (hook_name) { | ||||||
|  |     exports.hooks[hook_name].forEach(function (hook) { | ||||||
|  |       res.push(hook.hook_name + ": " + hook.hook_fn_name + " from " + hook.part.full_name); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |   return res.join("\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.loadFn = function (path) { | ||||||
|  |   var x = path.split(":"); | ||||||
|  |   var fn = require(x[0]); | ||||||
|  |   x[1].split(".").forEach(function (name) { | ||||||
|  |     fn = fn[name]; | ||||||
|  |   }); | ||||||
|  |   return fn; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.extractHooks = function (parts, hook_set_name) { | ||||||
|  |   var hooks = {}; | ||||||
|  |   parts.forEach(function (part) { | ||||||
|  |     Object.keys(part[hook_set_name] || {}).forEach(function (hook_name) { | ||||||
|  |       if (hooks[hook_name] === undefined) hooks[hook_name] = []; | ||||||
|  |       var hook_fn_name = part[hook_set_name][hook_name]; | ||||||
|  |       var hook_fn = exports.loadFn(part[hook_set_name][hook_name]); | ||||||
|  |       if (hook_fn) { | ||||||
|  |         hooks[hook_name].push({"hook_name": hook_name, "hook_fn": hook_fn, "hook_fn_name": hook_fn_name, "part": part}); | ||||||
|  |       } else { | ||||||
|  | 	console.error("Unable to load hook function for " + part.full_name + " for hook " + hook_name + ": " + part.hooks[hook_name]); | ||||||
|  |       }	 | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |   return hooks; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if (exports.isClient) { | ||||||
|  |   exports.update = function (cb) { | ||||||
|  |     jQuery.getJSON('/pluginfw/plugin-definitions.json', function(data) { | ||||||
|  |       exports.plugins = data.plugins; | ||||||
|  |       exports.parts = data.parts; | ||||||
|  |       exports.hooks = exports.extractHooks(exports.parts, "client_hooks"); | ||||||
|  |       exports.loaded = true; | ||||||
|  |       cb(); | ||||||
|  |      }); | ||||||
|  |   } | ||||||
|  | } else { | ||||||
|  | 
 | ||||||
|  | exports.update = function (cb) { | ||||||
|  |   exports.getPackages(function (er, packages) { | ||||||
|  |     var parts = []; | ||||||
|  |     var plugins = {}; | ||||||
|  |     // Load plugin metadata ep.json
 | ||||||
|  |     async.forEach( | ||||||
|  |       Object.keys(packages), | ||||||
|  |       function (plugin_name, cb) { | ||||||
|  |         exports.loadPlugin(packages, plugin_name, plugins, parts, cb); | ||||||
|  |       }, | ||||||
|  |       function (err) { | ||||||
|  | 	exports.plugins = plugins; | ||||||
|  |         exports.parts = exports.sortParts(parts); | ||||||
|  |         exports.hooks = exports.extractHooks(exports.parts, "hooks"); | ||||||
|  | 	exports.loaded = true; | ||||||
|  |         cb(err); | ||||||
|  |       } | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.getPackages = function (cb) { | ||||||
|  |   // Load list of installed NPM packages, flatten it to a list, and filter out only packages with names that
 | ||||||
|  |   var dir = path.resolve(npm.dir, '..'); | ||||||
|  |   readInstalled(dir, function (er, data) { | ||||||
|  |     if (er) cb(er, null); | ||||||
|  |     var packages = {}; | ||||||
|  |     function flatten(deps) { | ||||||
|  |       Object.keys(deps).forEach(function (name) { | ||||||
|  |         if (name.indexOf(exports.prefix) == 0) { | ||||||
|  |           packages[name] = deps[name]; | ||||||
|  | 	} | ||||||
|  | 	if (deps[name].dependencies !== undefined) | ||||||
|  | 	  flatten(deps[name].dependencies); | ||||||
|  | 	  delete deps[name].dependencies; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |     flatten([data]); | ||||||
|  |     cb(null, packages); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.loadPlugin = function (packages, plugin_name, plugins, parts, cb) { | ||||||
|  |   var plugin_path = path.resolve(packages[plugin_name].path, "ep.json"); | ||||||
|  |   fs.readFile( | ||||||
|  |     plugin_path, | ||||||
|  |     function (er, data) { | ||||||
|  |       if (er) { | ||||||
|  | 	console.error("Unable to load plugin definition file " + plugin_path); | ||||||
|  |         return cb(); | ||||||
|  |       } | ||||||
|  |       try { | ||||||
|  |         var plugin = JSON.parse(data); | ||||||
|  | 	plugin.package = packages[plugin_name]; | ||||||
|  | 	plugins[plugin_name] = plugin; | ||||||
|  | 	plugin.parts.forEach(function (part) { | ||||||
|  | 	  part.plugin = plugin_name; | ||||||
|  | 	  part.full_name = plugin_name + "/" + part.name; | ||||||
|  | 	  parts[part.full_name] = part; | ||||||
|  | 	}); | ||||||
|  |       } catch (ex) { | ||||||
|  | 	console.error("Unable to parse plugin definition file " + plugin_path + ": " + ex.toString()); | ||||||
|  |       } | ||||||
|  |       cb(); | ||||||
|  |     } | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.partsToParentChildList = function (parts) { | ||||||
|  |   var res = []; | ||||||
|  |   Object.keys(parts).forEach(function (name) { | ||||||
|  |     (parts[name].post || []).forEach(function (child_name)  { | ||||||
|  |       res.push([name, child_name]); | ||||||
|  |     }); | ||||||
|  |     (parts[name].pre || []).forEach(function (parent_name)  { | ||||||
|  |       res.push([parent_name, name]); | ||||||
|  |     }); | ||||||
|  |     if (!parts[name].pre && !parts[name].post) { | ||||||
|  |       res.push([name, ":" + name]); // Include apps with no dependency info
 | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |   return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | exports.sortParts = function(parts) { | ||||||
|  |   return tsort( | ||||||
|  |     exports.partsToParentChildList(parts) | ||||||
|  |   ).filter( | ||||||
|  |     function (name) { return parts[name] !== undefined; } | ||||||
|  |   ).map( | ||||||
|  |     function (name) { return parts[name]; } | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -290,22 +290,26 @@ | |||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <script type="text/javascript" src="../static/js/require-kernel.js"></script> |         <script type="text/javascript" src="../static/js/require-kernel.js"></script> | ||||||
|  |         <script type="text/javascript" src="../static/js/jquery.js"></script> | ||||||
|         <script type="text/javascript" src="../socket.io/socket.io.js"></script> |         <script type="text/javascript" src="../socket.io/socket.io.js"></script> | ||||||
|         <script type="text/javascript" src="../minified/pad.js"></script> |         <script type="text/javascript" src="../minified/pad.js"></script> | ||||||
|         <script type="text/javascript" src="../static/custom/pad.js"></script> |  | ||||||
|         <script type="text/javascript"> |         <script type="text/javascript"> | ||||||
|             var clientVars = {}; |             var clientVars = {}; | ||||||
|             (function () { |             (function () { | ||||||
|               require.setRootURI("../minified/"); |               require.setRootURI("../minified/"); | ||||||
|               require.setLibraryURI("../minified/plugins/"); |               require.setLibraryURI("../minified/plugins/"); | ||||||
|               require.setGlobalKeyPath("require"); |               require.setGlobalKeyPath("require"); | ||||||
|               require('ep_etherpad-lite/static/js/pad').init(); |  | ||||||
| 
 | 
 | ||||||
|               /* TODO: These globals shouldn't exist. */ |               var plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins'); | ||||||
|               pad = require('ep_etherpad-lite/static/js/pad').pad; | 	      plugins.update(function () { | ||||||
|               chat = require('ep_etherpad-lite/static/js/chat').chat; | 		require('ep_etherpad-lite/static/js/pad').init(); | ||||||
|               padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar; | 
 | ||||||
|               padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp; | 		/* TODO: These globals shouldn't exist. */ | ||||||
|  | 		pad = require('ep_etherpad-lite/static/js/pad').pad; | ||||||
|  | 		chat = require('ep_etherpad-lite/static/js/chat').chat; | ||||||
|  | 		padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar; | ||||||
|  | 		padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp; | ||||||
|  | 	      }); | ||||||
|             }()); |             }()); | ||||||
|         </script> |         </script> | ||||||
|          |          | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user