Skip to content

Instantly share code, notes, and snippets.

@stamat
Created December 17, 2013 17:41
Show Gist options
  • Save stamat/8009291 to your computer and use it in GitHub Desktop.
Save stamat/8009291 to your computer and use it in GitHub Desktop.
Supreme Events class from IVARTECH library
/**
* @file IVARTECH Events Class | Mediator pattern
* @author Nikola Stamatovic Stamat <stamat@ivartech.com>
* @copyright IVARTECH http://ivartech.com
* @version 20130313
*
* @namespace ivar.patt
*/
//TODO: write support for ['foo','baz','baz']
/**
* @class
* @classdesc Events class enables you to stack functions under a single name to be executed when that name is called from anywhere in the program. Functions can be passed arguments when firing an event.
*
* @constructor
* @property {object} list Contains the event names paired with arrays of functions
* @property {array} event_names Contains all the event names
*/
Events = function Events(obj) {
this.list = {};
this.event_names = [];
this.multi = {
names: {},
states: {},
separator: ','
};
this.any;
if (isSet(obj)) extend(obj, this);
};
Events.prototype.constructor = Events;
/**
* Binds a single function or an array of functions to an event name
var me = new ivar.patt.Events();
me.bind(['foo','bar','baz'], function(){
print('foo%bar%baz');
});
me.bind(['foo','baz'], function(){
print('foo%baz');
});
me.bind(['baz','bar'], function(){
print('baz%bar');
});
ivar.print(me);
me.fire('foo');
me.unbind(['bar','baz']);
me.fire('baz');
me.fire('bar');
*
* @param {string|number} event_name
* @param {function|array[function,...]} func(...)
*/
Events.prototype.bind = function (event_name, func) {
if (event_name === 'any') {
this.any = func;
return;
}
if (isArray(event_name)) {
var multi_event_name = this.multi_nameGen(event_name);
this.multi_bind(event_name, multi_event_name);
event_name = multi_event_name;
}
if (!isArray(func)) func = [func];
for (var i = 0; i < func.length; i++) {
if (!this.list.hasOwnProperty(event_name)) {
this.list[event_name] = new Array();
this.list[event_name].push(func[i]);
this.event_names.push(event_name);
} else {
this.list[event_name].push(func[i]);
}
}
};
/**
* Removes a certain function or array of functions from an event, thus disabling them to be fired
*
* @param {string|number} event_name
* @param {function|array[function,...]} func(...)
*/
Events.prototype.unbind = function (event_name, func) {
if (event_name === 'any') {
this.any = undefined;
return;
}
if (isArray(event_name)) {
var multi_event_name = this.multi_nameGen(event_name);
this.multi_unbind(event_name, multi_event_name);
event_name = multi_event_name;
}
var arr = this.list[event_name];
if (!ivar.isArray(func)) func = [func];
if (isSet(arr) && isFunction(func)) {
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < func.length; j++) {
if (arr[i] === func[j]) {
this.list[event_name].rm(i);
}
}
}
}
if (ivar.isSet(arr) && (arr.length == 0)) this.clearBound(event_name);
};
Events.prototype.multi_nameGen = function (arr) {
return arr.sort().join(this.multi.separator);
};
Events.prototype.multi_stateReset = function (multi_event_name) {
if (this.multi.states.hasOwnProperty(multi_event_name)) {
for (var j in this.multi.states[multi_event_name]) {
this.multi.states[multi_event_name][j] = false;
}
}
};
Events.prototype.multi_singleEventFired = function (event_name) {
if (this.multi.names.hasOwnProperty(event_name)) {
for (var i = 0; i < this.multi.names[event_name].length; i++) {
var multi_event_name = this.multi.names[event_name][i];
if (this.multi.states.hasOwnProperty(multi_event_name)) {
var multi_event_state = this.multi.states[multi_event_name];
var fire = true;
if (multi_event_state.hasOwnProperty(event_name)) multi_event_state[event_name] = true
for (var j in multi_event_state) {
if (multi_event_state[j] == false) {
fire = false;
break;
}
}
}
if (fire) this.fire(this.multi.names[event_name][i].split(this.multi.separator));
}
}
};
Events.prototype.multi_bind = function (arr, multi_event_name) {
for (var i = 0; i < arr.length; i++) {
if (!this.multi.states.hasOwnProperty(multi_event_name)) this.multi.states[multi_event_name] = {};
this.multi.states[multi_event_name][arr[i]] = false;
if (!this.multi.names.hasOwnProperty(arr[i])) this.multi.names[arr[i]] = [];
this.multi.names[arr[i]].push(multi_event_name);
}
}
Events.prototype.multi_unbind = function (arr, multi_event_name) {
if (this.multi.states.hasOwnProperty(multi_event_name)) delete this.multi.states[multi_event_name];
for (var i = 0; i < arr.length; i++) {
if (this.multi.names.hasOwnProperty(arr[i])) this.multi.names[arr[i]].rm(this.multi.names[arr[i]].find(multi_event_name));
if (this.multi.names[arr[i]].length == 0) delete this.multi.names[arr[i]];
}
}
/**
* Executes all of the functions stored under given event name. Except mandatory event name parameter it can be passed infinite number of parameters that will be passed to functions.
*
* @param {string|number} event_name
*/
Events.prototype.fire = function (event_name) {
if (isArray(event_name)) {
event_name = this.multi_nameGen(event_name);
this.multi_stateReset(event_name);
}
var arr = this.list[event_name];
var args = [];
eachArg(arguments, function (i, elem) {
args.push(elem);
});
if (isSet(this.any) && isFunction(this.any)) this.any.apply(null, args);
args.splice(0, 1);
var arrayOfEventNames = [];
if (isSet(arr)) arr.each(function (i, obj) {
if (isSet(obj)) {
if (isFunction(obj)) obj.apply(null, args);
else if (isString(obj)) arrayOfEventNames.push(obj);
}
});
if (!isArray(event_name)) this.multi_singleEventFired(event_name);
if (arrayOfEventNames.length > 0) this.fireMultipleEvents(arrayOfEventNames);
};
Events.prototype.fireMultipleEvents = function (arrayOfEventNames) {
for (var i = 0; i < arrayOfEventNames.length; i++)
this.fire(arrayOfEventNames[i]);
};
/**
* Returns an array of all of the names of events that can be fired
*
* @param {string|number} event_name
*/
Events.prototype.getBound = function (event_name) {
return this.list[event_name];
};
/**
* Removes an event by given event name with all bound functions
*
* @param {string|number} event_name
*/
Events.prototype.clearBound = function (event_name) {
delete this.list[event_name];
for (var i = 0; i < this.event_names.length; i++) {
if (this.event_names[i] === event_name) {
this.event_names.rm(i);
break;
}
}
};
/**
* Removes all entries and restores the object to null state
*/
Events.prototype.clear = function () {
this.list = {};
this.event_names = [];
this.multi.names = {};
this.multi.states = {};
};
/**
* Returns length|size, that is number of events
*
* @return {number}
*/
Events.prototype.size = function () {
return this.event_names.length();
};
/*======= HELPER FUNCTIONS =========*/
Array.prototype.each = function (fn, reversed) {
var count = 0;
var step = 1;
if (reversed) {
step = -1;
count = this.length - 1;
}
for (var i = 0; i < this.length; i++) {
fn(count, this[count]);
count = count + step;
}
};
String.prototype.each = Array.prototype.each;
//Thanks to perfectionkills.com <http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/>
getClass = function (val) {
return Object.prototype.toString.call(val)
.match(/^\[object\s(.*)\]$/)[1];
};
is = function (obj, type) {
if (type === 'number') return isNumber(obj);
if (whatis(obj) === type) return true;
if (type === 'empty') return isEmpty(obj);
if (type === 'set') return isSet(obj);
return false;
};
isArray = function (val) {
return is(val, 'array');
};
isNumber = function (val) {
if (isNaN(val)) return false;
var type = typeof val;
if (type === 'object') type = getClass(val).toLowerCase();
return type === 'number';
};
isInt = function (val) {
return is(val, 'integer');
};
isFloat = function (val) {
return is(val, 'float');
};
isString = function (val) {
return is(val, 'string');
};
isObject = function (val) {
return is(val, 'object');
};
isFunction = function (val) {
return is(val, 'function');
};
isDate = function (val) {
return is(val, 'date');
};
isBool = function (val) {
return is(val, 'boolean');
};
isRegExp = function (val) {
return is(val, 'regexp');
};
isNull = function (val) {
return val === null;
};
isUndefined = function (val) {
return val === undefined;
};
getClassName = function (val) {
return val.constructor.parseName();
}
whatis = function (val) {
if (val === undefined) return 'undefined';
if (val === null) return 'null';
var type = typeof val;
if (type === 'object') type = getClass(val).toLowerCase();
if (type === 'number') {
if (val.toString().indexOf('.') > 0) return 'float';
else return 'integer';
}
return type;
};
isSet = function (val) {
return (val !== undefined) && (val !== null);
};
isEmpty = function (obj) {
if (obj.length && obj.length > 0) return false;
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
};
extend = function (o1, o2, if_not_exists) {
for (var i in o2) {
if (!(isSet(o1[i]) && if_not_exists)) {
if (whatis(o1[i]) === 'object' && whatis(o2[i]) === 'object') {
extend(o1[i], o2[i], clone, if_not_exists);
} else {
o1[i] = o2[i]
}
}
}
return o1;
};
eachArg = function (args, fn) {
var i = 0;
while (args.hasOwnProperty(i)) {
if (fn !== undefined) fn(i, args[i]);
i++;
}
return i - 1;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment