/**
* @copyright 2015 Tridium, Inc. All Rights Reserved.
* @author Gareth Johnson
*/
/**
* Defines {@link baja.event}.
* @module baja/nav/event
*/
define([ "bajaScript/sys" ], function mixin(baja) {
"use strict";
var strictArg = baja.strictArg,
bsguid = 0, // A unique id for assigned to event handlers
event;
//TODO: Could probably factor this into a framework method at some point
function isObjEmpty(obj) {
var q;
for (q in obj) {
if (obj.hasOwnProperty(q)) {
return false;
}
}
return true;
}
event = /** @lends baja.event */ {
/**
* Attach an event handler to listen for events.
*
* @private
*
* @param {String} hName event handler name.
* @param {Function} func the event handler function.
*/
attach: function attach(hName, func) {
var that = this,
p,
i,
names,
name,
handlers,
map;
// If an Object is passed in then scan it for handlers
// (hName can be an Object map)
if (hName && typeof hName === "object") {
for (p in hName) {
if (hName.hasOwnProperty(p)) {
if (typeof hName[p] === "function") {
that.attach(p, hName[p]);
}
}
}
} else {
// Validate and then add a handler
strictArg(hName, String);
strictArg(func, Function);
// Assign a unique id to this function
if (!func.$bsguid) {
func.$bsguid = ++bsguid;
}
// Lazily create handlers map
handlers = that.$handlers;
if (!handlers) {
handlers = that.$handlers = {};
}
// If separated by a space then assign the function to multiple events
names = hName.split(" ");
for (i = 0; i < names.length; ++i) {
name = names[i];
map = handlers[name];
if (!map) {
map = handlers[name] = {};
}
// Assign handler into map
map[func.$bsguid] = func;
}
}
},
/**
* Detach an Event Handler.
*
* If no arguments are used with this method then all events are removed.
*
* @private
*
* @param {String} [hName] the name of the handler to detach.
* @param {Function} [func] the function to remove. It's recommended to supply this just in case
* other scripts have added event handlers.
*/
detach: function detach(hName, func) {
var p,
that = this,
handlers,
names,
name,
map,
guid,
i;
// If there are no arguments then remove all handlers...
if (arguments.length === 0) {
that.$handlers = undefined;
}
if (!that.$handlers) {
return;
}
// If an object is passed in then scan it for handlers
if (hName && typeof hName === "object") {
if (hName.hasOwnProperty(p)) {
if (typeof hName[p] === "function") {
this.detach(p, hName[p]);
}
}
} else {
strictArg(hName, String);
if (func) {
strictArg(func, Function);
}
handlers = that.$handlers;
// If separated by a space then remove from multiple event types...
names = hName.split(" ");
for (i = 0; i < names.length; ++i) {
name = names[i];
if (!func) {
delete handlers[name];
} else {
map = handlers[name];
guid = func.$bsguid;
if (map && guid && map[guid]) {
delete map[guid];
// If there aren't any more handlers then delete the entry
if (isObjEmpty(map)) {
delete handlers[name];
}
}
}
}
// If there are no handlers then set this back to undefined
if (isObjEmpty(handlers)) {
that.$handlers = undefined;
}
}
},
/**
* Fire events for the given handler name.
*
* Unlike `getHandlers` or `hasHandlers` this method can only invoke one
* event handler name at a time.
*
* This method should only be used internally by Tridium developers.
*
* @private
*
* @param {String} hName the name of the handler.
* @param {Function} error called if any of the invoked handlers throw an error.
* @param {*} context the object used as the 'this' parameter in any invoked event handler.
* @param {...*} args Any extra arguments will be used as parameters in any invoked event
* handler functions.
*/
fireHandlers: function fireHandlers(hName, error, context, ...args) {
const handlers = this.$handlers;
// Bail if there are no handlers registered
if (!handlers) {
return;
}
// Iterate through and invoke the event handlers we're after
if (handlers.hasOwnProperty(hName)) {
const handler = handlers[hName];
for (const p in handler) {
if (handler.hasOwnProperty(p)) {
try {
handler[p].apply(context, args);
} catch (err) {
error(err);
}
}
}
}
},
/**
* Return an array of event handlers.
*
* To access multiple handlers, insert a space between the handler names.
*
* @private
*
* @param {String} hName the name of the handler
* @returns {Array}
*/
getHandlers: function getHandlers(hName) {
if (!this.$handlers) {
return [];
}
var names = hName.split(" "),
i,
p,
a = [],
handlers = this.$handlers;
for (i = 0; i < names.length; ++i) {
if (handlers.hasOwnProperty(names[i])) {
for (p in handlers[names[i]]) {
if (handlers[names[i]].hasOwnProperty(p)) {
a.push(handlers[names[i]][p]);
}
}
}
}
return a;
},
/**
* Return true if there any handlers registered for the given handler name.
*
* If no handler name is specified then test to see if there are any handlers registered at all.
*
* Multiple handlers can be tested for by using a space character between the names.
*
* @private
*
* @param {String} [hName] the name of the handler. If undefined, then see if there are any
* handlers registered at all.
* @returns {Boolean}
*/
hasHandlers: function hasHandlers(hName) {
var handlers = this.$handlers,
names,
i;
// If there are no handlers then bail
if (!handlers) {
return false;
}
// If there isn't a handler name defined then at this point we must have some handler
if (hName === undefined) {
return true;
}
names = hName.split(" ");
for (i = 0; i < names.length; ++i) {
if (!handlers.hasOwnProperty(names[i])) {
return false;
}
}
return true;
},
/**
* Mix-in the event handlers onto the given Object.
*
* @private
*
* @param obj
*/
mixin: function (obj) {
obj.attach = event.attach;
obj.detach = event.detach;
obj.fireHandlers = event.fireHandlers;
obj.getHandlers = event.getHandlers;
obj.hasHandlers = event.hasHandlers;
}
};
return event;
});