diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index c4cd5aebf..49e46c608 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -4,27 +4,63 @@ var _; /* 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"); - _ = require("ep_etherpad-lite/static/js/underscore"); + var _ = require("ep_etherpad-lite/static/js/underscore"); } else { var async = require("async"); - _ = require("underscore"); + var _ = require("underscore"); } exports.bubbleExceptions = true var hookCallWrapper = function (hook, hook_name, args, cb) { if (cb === undefined) cb = function (x) { return x; }; + + // Normalize output to list for both sync and async cases + var normalize = function(x) { + if (x == undefined) return []; + return x; + } + var normalizedhook = function () { + return normalize(hook.hook_fn(hook_name, args, function (x) { + return cb(normalize(x)); + })); + } + if (exports.bubbleExceptions) { - return hook.hook_fn(hook_name, args, cb); + return normalizedhook(); } else { try { - return hook.hook_fn(hook_name, args, cb); + return normalizedhook(); } catch (ex) { console.error([hook_name, hook.part.full_name, ex.stack || ex]); } } } +exports.syncMapFirst = function (lst, fn) { + var i; + var result; + for (i = 0; i < lst.length; i++) { + result = fn(lst[i]) + if (result.length) return result; + } + return undefined; +} + +exports.mapFirst = function (lst, fn, cb) { + var i = 0; + + next = function () { + if (i >= lst.length) return cb(undefined); + fn(lst[i++], function (err, result) { + if (err) return cb(err); + if (result.length) return cb(null, result); + next(); + }); + } + next(); +} + /* Don't use Array.concat as it flatterns arrays within the array */ exports.flatten = function (lst) { @@ -44,9 +80,9 @@ exports.flatten = function (lst) { exports.callAll = function (hook_name, args) { if (!args) args = {}; if (plugins.hooks[hook_name] === undefined) return []; - return exports.flatten(_.map(plugins.hooks[hook_name], function (hook) { + return _.flatten(_.map(plugins.hooks[hook_name], function (hook) { return hookCallWrapper(hook, hook_name, args); - })); + }), true); } exports.aCallAll = function (hook_name, args, cb) { @@ -59,7 +95,7 @@ exports.aCallAll = function (hook_name, args, cb) { hookCallWrapper(hook, hook_name, args, function (res) { cb(null, res); }); }, function (err, res) { - cb(null, exports.flatten(res)); + cb(null, _.flatten(res, true)); } ); } @@ -67,14 +103,22 @@ exports.aCallAll = function (hook_name, args, cb) { exports.callFirst = function (hook_name, args) { if (!args) args = {}; if (plugins.hooks[hook_name][0] === undefined) return []; - return exports.flatten(hookCallWrapper(plugins.hooks[hook_name][0], hook_name, args)); + return exports.syncMapFirst(plugins.hooks[hook_name], function (hook) { + return hookCallWrapper(hook, hook_name, args); + }); } exports.aCallFirst = function (hook_name, args, cb) { if (!args) args = {}; if (!cb) cb = function () {}; - if (plugins.hooks[hook_name][0] === undefined) return cb(null, []); - hookCallWrapper(plugins.hooks[hook_name][0], hook_name, args, function (res) { cb(null, exports.flatten(res)); }); + if (plugins.hooks[hook_name] === undefined) return cb(null, []); + exports.mapFirst( + plugins.hooks[hook_name], + function (hook, cb) { + hookCallWrapper(hook, hook_name, args, function (res) { cb(null, res); }); + }, + cb + ); } exports.callAllStr = function(hook_name, args, sep, pre, post) {