Created
December 17, 2013 17:41
-
-
Save stamat/8009291 to your computer and use it in GitHub Desktop.
Supreme Events class from IVARTECH library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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