bem
Version:
201 lines (167 loc) • 6.52 kB
JavaScript
/**
* Observable plugin
*
* Copyright (c) 2010 Filatov Dmitry (alpha@zforms.ru)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* @version 1.0.0
* @requires $.identify
* @requires $.inherit
*/
(function($) {
var storageExpando = '__' + (+new Date) + 'storage',
getFnId = function(fn, ctx) {
return $.identify(fn) + (ctx? $.identify(ctx) : '');
},
Observable = /** @lends $.observable.prototype */{
/**
* Builds full event name
* @protected
* @param {String} e Event type
* @returns {String}
*/
buildEventName : function(e) {
return e;
},
/**
* Adding event handler
* @param {String} e Event type
* @param {Object} [data] Additional data that the handler gets as e.data
* @param {Function} fn Handler
* @param {Object} [ctx] Handler context
* @returns {$.observable}
*/
on : function(e, data, fn, ctx, _special) {
if(typeof e == 'string') {
if($.isFunction(data)) {
ctx = fn;
fn = data;
data = undefined;
}
var id = getFnId(fn, ctx),
storage = this[storageExpando] || (this[storageExpando] = {}),
eList = e.split(' '),
i = 0,
eStorage;
while(e = eList[i++]) {
e = this.buildEventName(e);
eStorage = storage[e] || (storage[e] = { ids : {}, list : {} });
if(!(id in eStorage.ids)) {
var list = eStorage.list,
item = { fn : fn, data : data, ctx : ctx, special : _special };
if(list.last) {
list.last.next = item;
item.prev = list.last;
} else {
list.first = item;
}
eStorage.ids[id] = list.last = item;
}
}
} else {
var _this = this;
$.each(e, function(e, fn) {
_this.on(e, fn, data, _special);
});
}
return this;
},
onFirst : function(e, data, fn, ctx) {
return this.on(e, data, fn, ctx, { one : true });
},
/**
* Removing event handler(s)
* @param {String} [e] Event type
* @param {Function} [fn] Handler
* @param {Object} [ctx] Handler context
* @returns {$.observable}
*/
un : function(e, fn, ctx) {
if(typeof e == 'string' || typeof e == 'undefined') {
var storage = this[storageExpando];
if(storage) {
if(e) { // if event type was passed
var eList = e.split(' '),
i = 0,
eStorage;
while(e = eList[i++]) {
e = this.buildEventName(e);
if(eStorage = storage[e]) {
if(fn) { // if specific handler was passed
var id = getFnId(fn, ctx),
ids = eStorage.ids;
if(id in ids) {
var list = eStorage.list,
item = ids[id],
prev = item.prev,
next = item.next;
if(prev) {
prev.next = next;
}
else if(item === list.first) {
list.first = next;
}
if(next) {
next.prev = prev;
}
else if(item === list.last) {
list.last = prev;
}
delete ids[id];
}
} else {
delete this[storageExpando][e];
}
}
}
} else {
delete this[storageExpando];
}
}
} else {
var _this = this;
$.each(e, function(e, fn) {
_this.un(e, fn, ctx);
});
}
return this;
},
/**
* Fires event handlers
* @param {String|$.Event} e Event
* @param {Object} [data] Additional data
* @returns {$.observable}
*/
trigger : function(e, data) {
var _this = this,
storage = _this[storageExpando],
rawType;
typeof e === 'string'?
e = $.Event(_this.buildEventName(rawType = e)) :
e.type = _this.buildEventName(rawType = e.type);
e.target || (e.target = _this);
if(storage && (storage = storage[e.type])) {
var item = storage.list.first,
ret;
while(item) {
e.data = item.data;
ret = item.fn.call(item.ctx || _this, e, data);
if(typeof ret !== 'undefined') {
e.result = ret;
if(ret === false) {
e.preventDefault();
e.stopPropagation();
}
}
item.special && item.special.one &&
_this.un(rawType, item.fn, item.ctx);
item = item.next;
}
}
return this;
}
};
$.observable = $.inherit(Observable, Observable);
})(jQuery);