diff --git a/bean.js b/bean.js index 318fefe..a04561e 100644 --- a/bean.js +++ b/bean.js @@ -28,6 +28,10 @@ W3C_MODEL = root[addEvent], eventSupport = W3C_MODEL ? addEvent : attachEvent, + customNamespace = function(type) { + return ".__beanCustomNamespace_" + type.replace(/\./, "_"); + }, + isDescendant = function (parent, child) { var node = child.parentNode; while (node !== null) { @@ -72,6 +76,10 @@ }; }, + targetElement = function (element, isNative) { + return !W3C_MODEL && !isNative && (element === doc || element === win) ? root : element; + }, + addListener = function (element, orgType, fn, args) { var type = orgType.replace(stripName, ''), events = retrieveEvents(element), @@ -83,8 +91,15 @@ } var custom = customEvents[type]; if (custom) { - fn = custom.condition ? customHandler(element, fn, type, custom.condition) : fn; - type = custom.base || type; + // if custom event has a handler and a base, separately register the handler on the base action + // and continue registering a custom event, allowing the handler to determine when to fire/trigger + if (custom.handler && custom.base) { + addListener(element, custom.base + customNamespace(orgType), customHandler(element, custom.handler, custom.base, custom.condition)) + } else { + // otherwise, this is a traditional custom event + fn = custom.condition ? customHandler(element, fn, type, custom.condition) : fn; + type = custom.base || type; + } } var isNative = nativeEvents[type]; fn = isNative ? nativeHandler(element, fn, args) : customHandler(element, fn, type, false, args); @@ -95,6 +110,7 @@ removeListener(element, type, fn) && org(); }; } + element = targetElement(element, isNative); element[eventSupport] && listener(element, isNative ? type : 'propertychange', fn, true, !isNative && type); handlers[uid] = fn; fn.__uid = uid; @@ -103,10 +119,13 @@ }, removeListener = function (element, orgType, handler) { - var uid, names, uids, i, events = retrieveEvents(element), type = orgType.replace(stripName, ''); + var uid = element.__uid, names, uids, i, events = retrieveEvents(element), type = orgType.replace(stripName, ''); + if (!events || !events[type]) { return element; } + + handler && handler.__one && (handler = handler.__one) names = orgType.replace(namespace, ''); uids = names ? names.split('.') : [handler.__uid]; @@ -117,8 +136,19 @@ } delete events[type][uid]; if (element[eventSupport]) { - type = customEvents[type] ? customEvents[type].base : type; + var custom = customEvents[type]; + // if the custom event has a handler and a base + // separately remove the listener from the base event and + // continue to remove the custom event listener as well + if(custom && custom.handler && custom.base) { + // this version or remove does the proper namespace lookup + remove(element, custom.base + customNamespace(orgType)); + } else { + // otherwise we have a traditional custom event or none at all + type = custom ? custom.base : type; + } var isNative = W3C_MODEL || nativeEvents[type]; + element = targetElement(element, isNative); listener(element, isNative ? type : 'propertychange', handler, false, !isNative && type); } } @@ -126,6 +156,15 @@ destroyHandler(names); //get combos for (i = uids.length; i--; destroyHandler(uids[i])) {} //get singles + if (isEmpty(events[type])) { + delete events[type]; + } + + if (isEmpty(registry[uid])) { + delete registry[uid]; + delete collected[uid]; + } + return element; }, @@ -142,21 +181,36 @@ }; }, - add = function (element, events, fn, delfn, $) { + _add = function (meth, element, events, fn, delfn, $) { if (typeof events == 'object' && !fn) { for (var type in events) { - events.hasOwnProperty(type) && add(element, type, events[type]); + events.hasOwnProperty(type) && _add(meth, element, type, events[type]); } } else { var isDel = typeof fn == 'string', types = (isDel ? fn : events).split(' '); - fn = isDel ? del(events, delfn, $) : fn; + fn = isDel ? del(events, delfn, $) : meth == 'one' ? + function(fn) { + var one = function() { + remove(element, events, one) + fn.apply(this, arguments) + } + return (fn.__one = one) + }(fn) : fn for (var i = types.length; i--;) { - addListener(element, types[i], fn, Array.prototype.slice.call(arguments, isDel ? 4 : 3)); + addListener(element, types[i], fn, Array.prototype.slice.call(arguments, isDel ? 5 : 4)); } } return element; }, + add = function () { + return _add.apply(this, ['add'].concat(Array.prototype.slice.call(arguments, 0))) + }, + + one = function () { + return _add.apply(this, ['one'].concat(Array.prototype.slice.call(arguments, 0))) + }, + remove = function (element, orgEvents, fn) { var k, m, type, events, i, isString = typeof(orgEvents) == 'string', @@ -176,7 +230,9 @@ if (attached.hasOwnProperty(k)) { for (i in attached[k]) { for (m = names.length; m--;) { - attached[k].hasOwnProperty(i) && new RegExp('^' + names[m] + '::\\d*(\\..*)?$').test(i) && rm(element, [k, i].join('.')); + attached[k].hasOwnProperty(i) && + new RegExp('^' + names[m] + '::\\d*(\\..*)?$').test(i) && + rm(element, [k, i].join('.')); } } } @@ -231,6 +287,7 @@ evt[isNative ? 'initEvent' : 'initUIEvent'](type, true, true, win, 1); element.dispatchEvent(evt); } : function (isNative, type, element) { + element = targetElement(element, isNative); isNative ? element.fireEvent('on' + type, document.createEventObject()) : element['_on' + type]++; }, @@ -262,8 +319,8 @@ result.clientX = e.pageX; result.clientY = e.pageY; } else if (e.clientX || e.clientY) { - result.clientX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; - result.clientY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; + result.clientX = e.clientX + doc.body.scrollLeft + root.scrollLeft; + result.clientY = e.clientY + doc.body.scrollTop + root.scrollTop; } overOut.test(type) && (result.relatedTarget = e.relatedTarget || e[(type == 'mouseover' ? 'from' : 'to') + 'Element']); } @@ -273,7 +330,14 @@ } } return result; - }; + }, + + isEmpty = function (obj) { + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) return false; + } + return true; + } fixEvent.preventDefault = function (e) { return function () { @@ -321,7 +385,7 @@ mousewheel: { base: /Firefox/.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel' } }; - var bean = { add: add, remove: remove, clone: clone, fire: fire }; + var bean = { add: add, one: one, remove: remove, clone: clone, fire: fire }; var clean = function (el) { var uid = remove(el).__uid; @@ -340,10 +404,13 @@ }); } + bean.customEvents = customEvents; + bean.noConflict = function () { context.bean = old; return this; }; return bean; -}); \ No newline at end of file + +}); diff --git a/bean.min.js b/bean.min.js index 63aae18..6e3fff8 100644 --- a/bean.min.js +++ b/bean.min.js @@ -7,4 +7,4 @@ * dperini: https://github.com/dperini/nwevents * the entire mootools team: github.com/mootools/mootools-core */ -!function(a,b){typeof module!="undefined"?module.exports=b():typeof define=="function"&&typeof define.amd=="object"?define(b):this[a]=b()}("bean",function(){function F(a){var b=a.relatedTarget;return b?b!=this&&b.prefix!="xul"&&!/document/.test(this.toString())&&!p(this,b):b===null}var a=window,b=1,c={},d={},e=/over|out/,f=/[^\.]*(?=\..*)\.|.*/,g=/\..*/,h="addEventListener",i="attachEvent",j="removeEventListener",k="detachEvent",l=document||{},m=l.documentElement||{},n=m[h],o=n?h:i,p=function(a,b){var c=b.parentNode;while(c!==null){if(c==a)return!0;c=c.parentNode}},q=function(a,c){return a.__uid=c&&c+"::"+b++||a.__uid||b++},r=function(a){var b=q(a);return c[b]=c[b]||{}},s=n?function(a,b,c,d){a[d?h:j](b,c,!1)}:function(a,b,c,d,e){e&&d&&a["_on"+e]===null&&(a["_on"+e]=0),a[d?i:k]("on"+b,c)},t=function(b,c,d){return function(e){e=D(e||((this.ownerDocument||this.document||this).parentWindow||a).event);return c.apply(b,[e].concat(d))}},u=function(b,c,d,e,f){return function(g){if(e?e.apply(this,arguments):n?!0:g&&g.propertyName=="_on"+d||!g)g=g?D(g||((this.ownerDocument||this.document||this).parentWindow||a).event):null,c.apply(b,Array.prototype.slice.call(arguments,g?0:1).concat(f))}},v=function(a,b,c,e){var h=b.replace(g,""),i=r(a),j=i[h]||(i[h]={}),k=c,l=q(c,b.replace(f,""));if(j[l])return a;var m=G[h];m&&(c=m.condition?u(a,c,h,m.condition):c,h=m.base||h);var p=E[h];c=p?t(a,c,e):u(a,c,h,!1,e),p=n||p;if(h=="unload"){var v=c;c=function(){w(a,h,c)&&v()}}a[o]&&s(a,p?h:"propertychange",c,!0,!p&&h),j[l]=c,c.__uid=l,c.__originalFn=k;return h=="unload"?a:d[q(a)]=a},w=function(a,b,c){function l(b){c=j[k][b];if(!!c){delete j[k][b];if(a[o]){k=G[k]?G[k].base:k;var d=n||E[k];s(a,d?k:"propertychange",c,!1,!d&&k)}}}var d,e,h,i,j=r(a),k=b.replace(g,"");if(!j||!j[k])return a;e=b.replace(f,""),h=e?e.split("."):[c.__uid],l(e);for(i=h.length;i--;l(h[i]));return a},x=function(a,b,c){return function(d){var e=typeof a=="string"?c(a,this):a;for(var f=d.target;f&&f!=this;f=f.parentNode)for(var g=e.length;g--;)if(e[g]==f)return b.apply(f,arguments)}},y=function(a,b,c,d,e){if(typeof b=="object"&&!c)for(var f in b)b.hasOwnProperty(f)&&y(a,f,b[f]);else{var g=typeof c=="string",h=(g?c:b).split(" ");c=g?x(b,d,e):c;for(var i=h.length;i--;)v(a,h[i],c,Array.prototype.slice.call(arguments,g?4:3))}return a},z=function(a,b,c){var d,e,h,i,j,k=typeof b=="string",l=k&&b.replace(f,""),m=w,n=r(a);l=l&&l.split(".");if(k&&/\s/.test(b)){b=b.split(" "),j=b.length-1;while(z(a,b[j])&&j--);return a}i=k?b.replace(g,""):b;if(!n||l||k&&!n[i]){for(d in n)if(n.hasOwnProperty(d))for(j in n[d])for(e=l.length;e--;)n[d].hasOwnProperty(j)&&(new RegExp("^"+l[e]+"::\\d*(\\..*)?$")).test(j)&&m(a,[d,j].join("."));return a}if(typeof c=="function")m(a,i,c);else if(l)m(a,b);else{m=i?m:z,h=k&&i,i=i?c||n[i]||i:n;for(d in i)i.hasOwnProperty(d)&&(m(a,h||d,i[d]),delete i[d])}return a},A=function(a,b,c){var d,e,h,i,j=b.split(" ");for(h=j.length;h--;){b=j[h].replace(g,"");var k=E[b],l=j[h].replace(f,""),m=r(a)[b];if(l){l=l.split(".");for(e=l.length;e--;)for(i in m)m.hasOwnProperty(i)&&(new RegExp("^"+l[e]+"::\\d*(\\..*)?$")).test(i)&&m[i].apply(a,[!1].concat(c))}else if(!c&&a[o])B(k,b,a);else for(e in m)m.hasOwnProperty(e)&&m[e].apply(a,[!1].concat(c))}return a},B=n?function(b,c,d){evt=document.createEvent(b?"HTMLEvents":"UIEvents"),evt[b?"initEvent":"initUIEvent"](c,!0,!0,a,1),d.dispatchEvent(evt)}:function(a,b,c){a?c.fireEvent("on"+b,document.createEventObject()):c["_on"+b]++},C=function(a,b,c){var d=r(b),e,f,g=q(a);e=c?d[c]:d;for(f in e)e.hasOwnProperty(f)&&(c?y:C)(a,c||b,c?e[f].__originalFn:f);return a},D=function(a){var b={};if(!a)return b;var c=a.type,d=a.target||a.srcElement;b.preventDefault=D.preventDefault(a),b.stopPropagation=D.stopPropagation(a),b.target=d&&d.nodeType==3?d.parentNode:d;if(~c.indexOf("key"))b.keyCode=a.which||a.keyCode;else if(/click|mouse|menu/i.test(c)){b.rightClick=a.which==3||a.button==2,b.pos={x:0,y:0};if(a.pageX||a.pageY)b.clientX=a.pageX,b.clientY=a.pageY;else if(a.clientX||a.clientY)b.clientX=a.clientX+document.body.scrollLeft+document.documentElement.scrollLeft,b.clientY=a.clientY+document.body.scrollTop+document.documentElement.scrollTop;e.test(c)&&(b.relatedTarget=a.relatedTarget||a[(c=="mouseover"?"from":"to")+"Element"])}for(var f in a)f in b||(b[f]=a[f]);return b};D.preventDefault=function(a){return function(){a.preventDefault?a.preventDefault():a.returnValue=!1}},D.stopPropagation=function(a){return function(){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0}};var E={click:1,dblclick:1,mouseup:1,mousedown:1,contextmenu:1,mousewheel:1,DOMMouseScroll:1,mouseover:1,mouseout:1,mousemove:1,selectstart:1,selectend:1,keydown:1,keypress:1,keyup:1,orientationchange:1,touchstart:1,touchmove:1,touchend:1,touchcancel:1,gesturestart:1,gesturechange:1,gestureend:1,focus:1,blur:1,change:1,reset:1,select:1,submit:1,load:1,unload:1,beforeunload:1,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,error:1,abort:1,scroll:1},G={mouseenter:{base:"mouseover",condition:F},mouseleave:{base:"mouseout",condition:F},mousewheel:{base:/Firefox/.test(navigator.userAgent)?"DOMMouseScroll":"mousewheel"}},H={add:y,remove:z,clone:C,fire:A},I=function(a){var b=z(a).__uid;b&&(delete d[b],delete c[b])};a[i]&&y(a,"unload",function(){for(var b in d)d.hasOwnProperty(b)&&I(d[b]);a.CollectGarbage&&CollectGarbage()}),H.noConflict=function(){context.bean=old;return this};return H}) \ No newline at end of file +!function(a,b){typeof module!="undefined"?module.exports=b():typeof define=="function"&&typeof define.amd=="object"?define(b):this[a]=b()}("bean",function(){function K(a){var b=a.relatedTarget;return b?b!=this&&b.prefix!="xul"&&!/document/.test(this.toString())&&!q(this,b):b===null}var a=window,b=1,c={},d={},e=/over|out/,f=/[^\.]*(?=\..*)\.|.*/,g=/\..*/,h="addEventListener",i="attachEvent",j="removeEventListener",k="detachEvent",l=document||{},m=l.documentElement||{},n=m[h],o=n?h:i,p=function(a){return".__beanCustomNamespace_"+a.replace(/\./,"_")},q=function(a,b){var c=b.parentNode;while(c!==null){if(c==a)return!0;c=c.parentNode}},r=function(a,c){return a.__uid=c&&c+"::"+b++||a.__uid||b++},s=function(a){var b=r(a);return c[b]=c[b]||{}},t=n?function(a,b,c,d){a[d?h:j](b,c,!1)}:function(a,b,c,d,e){e&&d&&a["_on"+e]===null&&(a["_on"+e]=0),a[d?i:k]("on"+b,c)},u=function(b,c,d){return function(e){return e=H(e||((this.ownerDocument||this.document||this).parentWindow||a).event),c.apply(b,[e].concat(d))}},v=function(b,c,d,e,f){return function(g){if(e?e.apply(this,arguments):n?!0:g&&g.propertyName=="_on"+d||!g)g=g?H(g||((this.ownerDocument||this.document||this).parentWindow||a).event):null,c.apply(b,Array.prototype.slice.call(arguments,g?0:1).concat(f))}},w=function(b,c){return!n&&!c&&(b===l||b===a)?m:b},x=function(a,b,c,e){var h=b.replace(g,""),i=s(a),j=i[h]||(i[h]={}),k=c,l=r(c,b.replace(f,""));if(j[l])return a;var m=L[h];m&&(m.handler&&m.base?x(a,m.base+p(b),v(a,m.handler,m.base,m.condition)):(c=m.condition?v(a,c,h,m.condition):c,h=m.base||h));var q=J[h];c=q?u(a,c,e):v(a,c,h,!1,e),q=n||q;if(h=="unload"){var z=c;c=function(){y(a,h,c)&&z()}}return a=w(a,q),a[o]&&t(a,q?h:"propertychange",c,!0,!q&&h),j[l]=c,c.__uid=l,c.__originalFn=k,h=="unload"?a:d[r(a)]=a},y=function(a,b,e){function q(c){e=l[m][c];if(!e)return;delete l[m][c];if(a[o]){var d=L[m];d&&d.handler&&d.base?D(a,d.base+p(b)):m=d?d.base:m;var f=n||J[m];a=w(a,f),t(a,f?m:"propertychange",e,!1,!f&&m)}}var h=a.__uid,i,j,k,l=s(a),m=b.replace(g,"");if(!l||!l[m])return a;e&&e.__one&&(e=e.__one),i=b.replace(f,""),j=i?i.split("."):[e.__uid],q(i);for(k=j.length;k--;q(j[k]));return I(l[m])&&delete l[m],I(c[h])&&(delete c[h],delete d[h]),a},z=function(a,b,c){return function(d){var e=typeof a=="string"?c(a,this):a;for(var f=d.target;f&&f!=this;f=f.parentNode)for(var g=e.length;g--;)if(e[g]==f)return b.apply(f,arguments)}},A=function(a,b,c,d,e,f){if(typeof c=="object"&&!d)for(var g in c)c.hasOwnProperty(g)&&A(a,b,g,c[g]);else{var h=typeof d=="string",i=(h?d:c).split(" ");d=h?z(c,e,f):a=="one"?function(a){var d=function(){D(b,c,d),a.apply(this,arguments)};return a.__one=d}(d):d;for(var j=i.length;j--;)x(b,i[j],d,Array.prototype.slice.call(arguments,h?5:4))}return b},B=function(){return A.apply(this,["add"].concat(Array.prototype.slice.call(arguments,0)))},C=function(){return A.apply(this,["one"].concat(Array.prototype.slice.call(arguments,0)))},D=function(a,b,c){var d,e,h,i,j,k=typeof b=="string",l=k&&b.replace(f,""),m=y,n=s(a);l=l&&l.split(".");if(k&&/\s/.test(b)){b=b.split(" "),j=b.length-1;while(D(a,b[j])&&j--);return a}i=k?b.replace(g,""):b;if(!n||l||k&&!n[i]){for(d in n)if(n.hasOwnProperty(d))for(j in n[d])for(e=l.length;e--;)n[d].hasOwnProperty(j)&&(new RegExp("^"+l[e]+"::\\d*(\\..*)?$")).test(j)&&m(a,[d,j].join("."));return a}if(typeof c=="function")m(a,i,c);else if(l)m(a,b);else{m=i?m:D,h=k&&i,i=i?c||n[i]||i:n;for(d in i)i.hasOwnProperty(d)&&(m(a,h||d,i[d]),delete i[d])}return a},E=function(a,b,c){var d,e,h,i,j=b.split(" ");for(h=j.length;h--;){b=j[h].replace(g,"");var k=J[b],l=j[h].replace(f,""),m=s(a)[b];if(l){l=l.split(".");for(e=l.length;e--;)for(i in m)m.hasOwnProperty(i)&&(new RegExp("^"+l[e]+"::\\d*(\\..*)?$")).test(i)&&m[i].apply(a,[!1].concat(c))}else if(!c&&a[o])F(k,b,a);else for(e in m)m.hasOwnProperty(e)&&m[e].apply(a,[!1].concat(c))}return a},F=n?function(b,c,d){evt=document.createEvent(b?"HTMLEvents":"UIEvents"),evt[b?"initEvent":"initUIEvent"](c,!0,!0,a,1),d.dispatchEvent(evt)}:function(a,b,c){c=w(c,a),a?c.fireEvent("on"+b,document.createEventObject()):c["_on"+b]++},G=function(a,b,c){var d=s(b),e,f,g=r(a);e=c?d[c]:d;for(f in e)e.hasOwnProperty(f)&&(c?B:G)(a,c||b,c?e[f].__originalFn:f);return a},H=function(a){var b={};if(!a)return b;var c=a.type,d=a.target||a.srcElement;b.preventDefault=H.preventDefault(a),b.stopPropagation=H.stopPropagation(a),b.target=d&&d.nodeType==3?d.parentNode:d;if(~c.indexOf("key"))b.keyCode=a.which||a.keyCode;else if(/click|mouse|menu/i.test(c)){b.rightClick=a.which==3||a.button==2,b.pos={x:0,y:0};if(a.pageX||a.pageY)b.clientX=a.pageX,b.clientY=a.pageY;else if(a.clientX||a.clientY)b.clientX=a.clientX+l.body.scrollLeft+m.scrollLeft,b.clientY=a.clientY+l.body.scrollTop+m.scrollTop;e.test(c)&&(b.relatedTarget=a.relatedTarget||a[(c=="mouseover"?"from":"to")+"Element"])}for(var f in a)f in b||(b[f]=a[f]);return b},I=function(a){for(var b in a)if(a.hasOwnProperty(b))return!1;return!0};H.preventDefault=function(a){return function(){a.preventDefault?a.preventDefault():a.returnValue=!1}},H.stopPropagation=function(a){return function(){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0}};var J={click:1,dblclick:1,mouseup:1,mousedown:1,contextmenu:1,mousewheel:1,DOMMouseScroll:1,mouseover:1,mouseout:1,mousemove:1,selectstart:1,selectend:1,keydown:1,keypress:1,keyup:1,orientationchange:1,touchstart:1,touchmove:1,touchend:1,touchcancel:1,gesturestart:1,gesturechange:1,gestureend:1,focus:1,blur:1,change:1,reset:1,select:1,submit:1,load:1,unload:1,beforeunload:1,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,error:1,abort:1,scroll:1},L={mouseenter:{base:"mouseover",condition:K},mouseleave:{base:"mouseout",condition:K},mousewheel:{base:/Firefox/.test(navigator.userAgent)?"DOMMouseScroll":"mousewheel"}},M={add:B,one:C,remove:D,clone:G,fire:E},N=function(a){var b=D(a).__uid;b&&(delete d[b],delete c[b])};return a[i]&&B(a,"unload",function(){for(var b in d)d.hasOwnProperty(b)&&N(d[b]);a.CollectGarbage&&CollectGarbage()}),M.customEvents=L,M.noConflict=function(){return context.bean=old,this},M}) \ No newline at end of file diff --git a/src/bean.js b/src/bean.js index b8620fa..8404175 100644 --- a/src/bean.js +++ b/src/bean.js @@ -19,6 +19,10 @@ W3C_MODEL = root[addEvent], eventSupport = W3C_MODEL ? addEvent : attachEvent, + customNamespace = function(type) { + return ".__beanCustomNamespace_" + type.replace(/\./, "_"); + }, + isDescendant = function (parent, child) { var node = child.parentNode; while (node !== null) { @@ -78,8 +82,15 @@ } var custom = customEvents[type]; if (custom) { - fn = custom.condition ? customHandler(element, fn, type, custom.condition) : fn; - type = custom.base || type; + // if custom event has a handler and a base, separately register the handler on the base action + // and continue registering a custom event, allowing the handler to determine when to fire/trigger + if (custom.handler && custom.base) { + addListener(element, custom.base + customNamespace(orgType), customHandler(element, custom.handler, custom.base, custom.condition)) + } else { + // otherwise, this is a traditional custom event + fn = custom.condition ? customHandler(element, fn, type, custom.condition) : fn; + type = custom.base || type; + } } var isNative = nativeEvents[type]; fn = isNative ? nativeHandler(element, fn, args) : customHandler(element, fn, type, false, args); @@ -116,7 +127,17 @@ } delete events[type][uid]; if (element[eventSupport]) { - type = customEvents[type] ? customEvents[type].base : type; + var custom = customEvents[type]; + // if the custom event has a handler and a base + // separately remove the listener from the base event and + // continue to remove the custom event listener as well + if(custom && custom.handler && custom.base) { + // this version or remove does the proper namespace lookup + remove(element, custom.base + customNamespace(orgType)); + } else { + // otherwise we have a traditional custom event or none at all + type = custom ? custom.base : type; + } var isNative = W3C_MODEL || nativeEvents[type]; element = targetElement(element, isNative); listener(element, isNative ? type : 'propertychange', handler, false, !isNative && type); @@ -374,6 +395,8 @@ }); } + bean.customEvents = customEvents; + bean.noConflict = function () { context.bean = old; return this; diff --git a/tests/tests.js b/tests/tests.js index 6856c96..29147a0 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -508,6 +508,79 @@ sink('namespaces', function (test, ok) { bean.remove(el1, 'click.ded.fat'); Syn.click(el1); }); +}); + +sink('custom extended events', function (test, ok) { + test('custom extended events: testing adding custom events with handlers', 1, function () { + var el = document.getElementById('foo'); + bean.customEvents.threeclicks = { + base: 'click', + handler: function(event) + { + var elem = this, clicks = this.__threeclicks__ || 0; + clicks += 1; + if ( clicks === 3) { + clicks = 0; + + // set event type to "tripleclick" + event.type = "threeclicks"; + + //FIRE AWAY + bean.fire(this, "threeclicks", event); + } + this.__threeclicks__ = clicks; + } + } + bean.remove(el); + bean.add(el, 'threeclicks', function () { + ok(true, 'custom events: threeclick worked') + bean.remove(el, 'threeclicks'); + delete bean.customEvents.threeclicks; + }); + Syn.click(el); + Syn.click(el); + Syn.click(el); + }); + + test('custom extended events: testing removing custom events with handlers', 3, function () { + var test = 0; + var el = document.getElementById('foo'); + bean.customEvents.threeclicks = { + base: 'click', + handler: function(event) + { + var elem = this, clicks = this.__threeclicks__ || 0; + clicks += 1; + if ( clicks === 3) { + ok(test++ == 0, 'custom extended events: triggering custom handler') + clicks = 0; + + // set event type to "tripleclick" + event.type = "threeclicks"; + + //FIRE AWAY + bean.fire(this, "threeclicks", event); + } + this.__threeclicks__ = clicks; + } + } + bean.remove(el); + var handler = function() { + ok(test++ == 1, 'custom extended events: removing handler') + bean.remove(el, 'threeclicks', handler); + delete bean.customEvents.threeclicks; + Syn.click(el); + Syn.click(el); + Syn.click(el); + }; + bean.add(el, 'threeclicks', handler); + // by timeout, we shouldn't run the custom and native handlers more than one time each! + setTimeout(function() { ok(test++ == 2, 'custom extended events: no extra calls made')}, 500); + Syn.click(el); + Syn.click(el); + Syn.click(el); + }); + });