Skip to content
Browse files

implemented new on() method

  • Loading branch information...
1 parent b043043 commit 4a850d87a86617c7cf415c5efc6b13a39ae7e02a @rvagg rvagg committed Jul 28, 2012
Showing with 204 additions and 126 deletions.
  1. +103 −60 bean.js
  2. +1 −1 bean.min.js
  3. +73 −50 src/bean.js
  4. +2 −0 tests/clone-test.js
  5. +2 −3 tests/common.js
  6. +4 −4 tests/custom-test.js
  7. +2 −0 tests/custom-types-test.js
  8. +13 −6 tests/delegate-test.js
  9. +4 −2 tests/event-object-test.js
View
163 bean.js
@@ -56,7 +56,9 @@
'seeked ended durationchange timeupdate play pause ratechange ' + // media
'volumechange cuechange ' + // media
'checking noupdate downloading cached updateready obsolete ' // appcache
- , str2arr = function (s, d) { return s.split(d || ' ') }
+ , str2arr = function (s, d) { return s.split(d || ' ') }
+ , isString = function (s) { return typeof s == 'string' }
+ , isArray = function (a) { return Object.prototype.toString.call(a) === '[object Array]' }
, nativeEvents = (function (hash, events, i) {
for (i = 0; i < events.length; i++) events[i] && (hash[events[i]] = 1)
@@ -233,18 +235,35 @@
// we use one of these per listener, of any type
, RegEntry = (function () {
- function entry(element, type, handler, original, namespaces) {
- var isNative = this.isNative = nativeEvents[type] && !!element[eventSupport]
+ function entry(element, type, handler, original, namespaces, args) {
+ var customType = customEvents[type]
+ , isNative
+
+ if (type == 'unload') {
+ // self clean-up
+ handler = once(removeListener, element, type, handler, original)
+ }
+
+ if (customType) {
+ if (customType.condition) {
+ handler = customHandler(element, handler, type, customType.condition, args, true)
+ }
+ type = customType.base || type
+ }
+
+ this.isNative = isNative = nativeEvents[type] && !!element[eventSupport]
+ this.customType = !W3C_MODEL && !isNative && type
this.element = element
this.type = type
- this.handler = handler
this.original = original
this.namespaces = namespaces
- this.custom = customEvents[type]
this.eventType = W3C_MODEL || isNative ? type : 'propertychange'
- this.customType = !W3C_MODEL && !isNative && type
this[targetS] = targetElement(element, isNative)
this[eventSupport] = !!this[targetS][eventSupport]
+
+ this.handler = isNative
+ ? nativeHandler(element, handler, args)
+ : customHandler(element, handler, type, false, args, false)
}
entry.prototype = {
@@ -283,7 +302,9 @@
if (!type || type == '*') {
// search the whole registry
for (var t in map) {
- if (t.charAt(0) == '$') forAll(element, t.substr(1), original, handler, fn)
+ if (t.charAt(0) == '$') {
+ forAll(element, t.substr(1), original, handler, fn)
+ }
}
} else {
var i = 0, l, list = map['$' + type], all = element == '*'
@@ -308,7 +329,9 @@
, get = function (element, type, original) {
var entries = []
- forAll(element, type, original, null, function (entry) { return entries.push(entry) })
+ forAll(element, type, original, null, function (entry) {
+ return entries.push(entry)
+ })
return entries
}
@@ -337,16 +360,19 @@
return { has: has, get: get, put: put, del: del, entries: entries }
}())
- , selectorEngine = doc[qSA]
- ? function (s, r) {
- return r[qSA](s)
- }
- : function () {
- throw new Error('Bean: No selector engine installed') // eeek
- }
-
+ , selectorEngine
, setSelectorEngine = function (e) {
- selectorEngine = e
+ if (!arguments.length) {
+ selectorEngine = doc[qSA]
+ ? function (s, r) {
+ return r[qSA](s)
+ }
+ : function () {
+ throw new Error('Bean: No selector engine installed') // eeek
+ }
+ } else {
+ selectorEngine = e
+ }
}
// add and remove listeners to DOM elements
@@ -416,32 +442,11 @@
}
}
- , addListener = function (element, orgType, fn, originalFn, args) {
- var type = orgType.replace(nameRegex, '')
- , namespaces = str2arr(orgType.replace(namespaceRegex, ''), '.')
- , entry
-
- if (type == 'unload') fn = once(removeListener, element, type, fn, originalFn) // self clean-up
- if (customEvents[type]) {
- if (customEvents[type].condition) {
- fn = customHandler(element, fn, type, customEvents[type].condition, args, true)
- }
- type = customEvents[type].base || type
- }
- entry = registry.put(new RegEntry(element, type, fn, originalFn, namespaces[0] && namespaces))
- entry.handler = entry.isNative
- ? nativeHandler(element, entry.handler, args)
- : customHandler(element, entry.handler, type, false, args, false)
- if (entry[eventSupport]) {
- listener(entry[targetS], entry.eventType, entry.handler, true, entry.customType)
- }
- }
-
- , del = function (selector, fn, $) {
+ , delegate = function (selector, fn) {
//TODO: findTarget (therefore $) is called twice, once for match and once for
// setting e.currentTarget, fix this so it's only needed once
var findTarget = function (target, root) {
- var i, array = typeof selector == 'string' ? $(selector, root) : selector
+ var i, array = isString(selector) ? selectorEngine(selector, root) : selector
for (; target && target !== root; target = target.parentNode) {
for (i = array.length; i--;) {
if (array[i] === target) return target
@@ -456,28 +461,29 @@
handler.__beanDel = {
ft : findTarget // attach it here for customEvents to use too
, selector : selector
- , $ : $
}
return handler
}
, remove = function (element, typeSpec, fn) {
var rm = removeListener
- , isString = typeSpec && typeof typeSpec == 'string'
+ , isTypeStr = isString(typeSpec)
, k, type, namespaces, i
- if (isString && typeSpec.indexOf(' ') > 0) {
+ if (isTypeStr && typeSpec.indexOf(' ') > 0) {
// remove(el, 't1 t2 t3', fn) or remove(el, 't1 t2 t3')
typeSpec = str2arr(typeSpec)
for (i = typeSpec.length; i--;)
remove(element, typeSpec[i], fn)
return element
}
- type = isString && typeSpec.replace(nameRegex, '')
+
+ type = isTypeStr && typeSpec.replace(nameRegex, '')
if (type && customEvents[type]) type = customEvents[type].type
- if (!typeSpec || isString) {
+
+ if (!typeSpec || isTypeStr) {
// remove(el) or remove(el, t1.ns) or remove(el, .ns) or remove(el, .ns1.ns2.ns3)
- if (namespaces = isString && typeSpec.replace(namespaceRegex, '')) namespaces = str2arr(namespaces, '.')
+ if (namespaces = isTypeStr && typeSpec.replace(namespaceRegex, '')) namespaces = str2arr(namespaces, '.')
rm(element, type, fn, namespaces)
} else if (typeof typeSpec == 'function') {
// remove(el, fn)
@@ -488,30 +494,65 @@
if (typeSpec.hasOwnProperty(k)) remove(element, k, typeSpec[k])
}
}
+
return element
}
- // 5th argument, $=selector engine, is deprecated and will be removed
- , add = function (element, events, fn, delfn, $) {
- var originalFn = fn
- , isDel = fn && typeof fn == 'string'
- , type, types, i, args
+ , on = function(element, events, selector, fn) {
+ var originalFn, type, types, i, args, entry
- if (events && !fn && typeof events == 'object') {
+ if (selector === undefined && typeof events == 'object') {
+ //TODO: this can't handle delegated events
for (type in events) {
- if (events.hasOwnProperty(type)) add.apply(this, [ element, type, events[type] ])
+ if (events.hasOwnProperty(type)) {
+ on.call(this, element, type, events[type])
+ }
}
+ return
+ }
+
+ if (isString(selector) || isArray(selector)) {
+ originalFn = fn
+ args = slice.call(arguments, 4)
+ fn = delegate(selector, originalFn, selectorEngine)
} else {
- args = arguments.length > 3 ? slice.call(arguments, 3) : []
- types = str2arr(isDel ? fn : events)
- isDel && (fn = del(events, (originalFn = delfn), $ || selectorEngine)) && (args = slice.call(args, 1))
- // special case for one()
- this === ONE && (fn = once(remove, element, events, fn, originalFn))
- for (i = types.length; i--;) addListener(element, types[i], fn, originalFn, args)
+ args = slice.call(arguments, 3)
+ fn = originalFn = selector
+ }
+
+ types = str2arr(events)
+
+ // special case for one()
+ if (this === ONE) {
+ fn = once(remove, element, events, fn, originalFn)
+ }
+
+ for (i = types.length; i--;) {
+ entry = registry.put(new RegEntry(
+ element
+ , types[i].replace(nameRegex, '')
+ , fn
+ , originalFn
+ , str2arr(types[i].replace(namespaceRegex, ''), '.') // namespaces
+ , args
+ ))
+ if (entry[eventSupport]) {
+ listener(entry[targetS], entry.eventType, entry.handler, true, entry.customType)
+ }
}
+
return element
}
+ , add = function (element, events, fn, delfn) {
+ return on.apply(
+ this
+ , !isString(fn)
+ ? slice.call(arguments)
+ : [ element, fn, events, delfn ].concat(arguments.length > 3 ? slice.call(arguments, 5) : [])
+ )
+ }
+
, one = function () {
return add.apply(ONE, arguments)
}
@@ -556,7 +597,7 @@
if (handlers[i].original) {
beanDel = handlers[i].handler.__beanDel
if (beanDel) {
- args = [ element, beanDel.selector, handlers[i].type, handlers[i].original, beanDel.$]
+ args = [ element, beanDel.selector, handlers[i].type, handlers[i].original ]
} else
args = [ element, handlers[i].type, handlers[i].original ]
add.apply(null, args)
@@ -591,5 +632,7 @@
win[attachEvent]('onunload', cleanup)
}
+ setSelectorEngine()
+
return bean
}));
View
2 bean.min.js
@@ -7,4 +7,4 @@
* dperini: https://github.com/dperini/nwevents
* the entire mootools team: github.com/mootools/mootools-core
*/
-!function(e,t,n){typeof module!="undefined"?module.exports=n(e,t):typeof define=="function"&&typeof define.amd=="object"?define(n):t[e]=n(e,t)}("bean",this,function(e,t){var n=window,r=t[e],i=/[^\.]*(?=\..*)\.|.*/,s=/\..*/,o="addEventListener",u="attachEvent",a="removeEventListener",f="detachEvent",l="ownerDocument",c="target",h="querySelectorAll",p=document||{},d=p.documentElement||{},v=d[o],m=v?o:u,g=Array.prototype.slice,y={},b="click dblclick mouseup mousedown contextmenu mousewheel mousemultiwheel DOMMouseScroll mouseover mouseout mousemove selectstart selectend keydown keypress keyup orientationchange focus blur change reset select submit load unload beforeunload resize move DOMContentLoaded readystatechange message error abort scroll ",w="show input invalid touchstart touchmove touchend touchcancel gesturestart gesturechange gestureend textinputreadystatechange pageshow pagehide popstate hashchange offline online afterprint beforeprint dragstart dragenter dragover dragleave drag drop dragend loadstart progress suspend emptied stalled loadmetadata loadeddata canplay canplaythrough playing waiting seeking seeked ended durationchange timeupdate play pause ratechange volumechange cuechange checking noupdate downloading cached updateready obsolete ",E=function(e,t){return e.split(t||" ")},S=function(e,t,n){for(n=0;n<t.length;n++)t[n]&&(e[t[n]]=1);return e}({},E(b+(v?w:""))),x=function(){var e="compareDocumentPosition",t=e in d?function(t,n){return n[e]&&(n[e](t)&16)===16}:"contains"in d?function(e,t){return t=t.nodeType===9||t===window?d:t,t!==e&&t.contains(e)}:function(e,t){while(e=e.parentNode)if(e===t)return 1;return 0},n=function(e){var n=e.relatedTarget;return n?n!==this&&n.prefix!=="xul"&&!/document/.test(this.toString())&&!t(n,this):n==null};return{mouseenter:{base:"mouseover",condition:n},mouseleave:{base:"mouseout",condition:n},mousewheel:{base:/Firefox/.test(navigator.userAgent)?"DOMMouseScroll":"mousewheel"}}}(),T=function(){var e=E("altKey attrChange attrName bubbles cancelable ctrlKey currentTarget detail eventPhase getModifierState isTrusted metaKey relatedNode relatedTarget shiftKey srcElement target timeStamp type view which"),t=e.concat(E("button buttons clientX clientY dataTransfer fromElement offsetX offsetY pageX pageY screenX screenY toElement")),n=t.concat(E("wheelDelta wheelDeltaX wheelDeltaY wheelDeltaZ axis")),r=e.concat(E("char charCode key keyCode keyIdentifier keyLocation location")),i=e.concat(E("data")),s=e.concat(E("touches targetTouches changedTouches scale rotation")),o=e.concat(E("data origin source")),u=e.concat(E("state")),a=/over|out/,f=[{reg:/key/i,fix:function(e,t){return t.keyCode=e.which||e.keyCode,r}},{reg:/click|mouse(?!(.*wheel|scroll))|menu|drag|drop/i,fix:function(e,n,r){n.rightClick=e.which===3||e.button===2,n.pos={x:0,y:0};if(e.pageX||e.pageY)n.clientX=e.pageX,n.clientY=e.pageY;else if(e.clientX||e.clientY)n.clientX=e.clientX+p.body.scrollLeft+d.scrollLeft,n.clientY=e.clientY+p.body.scrollTop+d.scrollTop;return a.test(r)&&(n.relatedTarget=e.relatedTarget||e[(r=="mouseover"?"from":"to")+"Element"]),t}},{reg:/mouse.*(wheel|scroll)/i,fix:function(){return n}},{reg:/^text/i,fix:function(){return i}},{reg:/^touch|^gesture/i,fix:function(){return s}},{reg:/^message$/i,fix:function(){return o}},{reg:/^popstate$/i,fix:function(){return u}},{reg:/.*/,fix:function(){return e}}],l={},h="preventDefault",v=function(e){return function(){e[h]?e[h]():e.returnValue=!1}},m="stopPropagation",g=function(e){return function(){e[m]?e[m]():e.cancelBubble=!0}},y="stopImmediatePropagation",b=function(e){return function(){e[y]&&e[y]()}},w=function(e){return function(){e[h](),e[m](),e.stopped=!0}},S=function(e,t,n){var r,i;for(r=n.length;r--;)!((i=n[r])in t)&&i in e&&(t[i]=e[i])};return function(e,t){var n={originalEvent:e,isNative:t,isBean:!0};if(!e)return n;var r,i,s,o=e.type,u=e[c]||e.srcElement;n[h]=v(e),n[m]=g(e),n[y]=b(e),n.stop=w(n),n[c]=u&&u.nodeType===3?u.parentNode:u;if(t){s=l[o];if(!s)for(r=0,i=f.length;r<i;r++)if(f[r].reg.test(o)){l[o]=s=f[r].fix;break}S(e,n,s(e,n,o))}return n}}(),N=function(e,t){return!v&&!t&&(e===p||e===n)?d:e},C=function(){function e(e,t,n,r,i){var s=this.isNative=S[t]&&!!e[m];this.element=e,this.type=t,this.handler=n,this.original=r,this.namespaces=i,this.custom=x[t],this.eventType=v||s?t:"propertychange",this.customType=!v&&!s&&t,this[c]=N(e,s),this[m]=!!this[c][m]}return e.prototype={inNamespaces:function(e){var t,n,r=0;if(!e)return!0;if(!this.namespaces)return!1;for(t=e.length;t--;)for(n=this.namespaces.length;n--;)e[t]==this.namespaces[n]&&r++;return e.length===r},matches:function(e,t,n){return this.element===e&&(!t||this.original===t)&&(!n||this.handler===n)}},e}(),k=function(){var e={},t=function(n,r,i,s,o){if(!r||r=="*")for(var u in e)u.charAt(0)=="$"&&t(n,u.substr(1),i,s,o);else{var a=0,f,l=e["$"+r],c=n=="*";if(!l)return;for(f=l.length;a<f;a++)if((c||l[a].matches(n,i,s))&&!o(l[a],l,a,r))return}},n=function(t,n,r){var i,s=e["$"+n];if(s)for(i=s.length;i--;)if(s[i].matches(t,r,null))return!0;return!1},r=function(e,n,r){var i=[];return t(e,n,r,null,function(e){return i.push(e)}),i},i=function(t){return(e["$"+t.type]||(e["$"+t.type]=[])).push(t),t},s=function(n){t(n.element,n.type,null,n.handler,function(t,n,r){return n.splice(r,1),n.length===0&&delete e["$"+t.type],!1})},o=function(){var t,n=[];for(t in e)t.charAt(0)=="$"&&(n=n.concat(e[t]));return n};return{has:n,get:r,put:i,del:s,entries:o}}(),L=p[h]?function(e,t){return t[h](e)}:function(){throw new Error("Bean: No selector engine installed")},A=function(e){L=e},O=v?function(e,t,n,r){e[r?o:a](t,n,!1)}:function(e,t,n,r,i){i&&r&&e["_on"+i]==null&&(e["_on"+i]=0),e[r?u:f]("on"+t,n)},M=function(e,t,r){var i=t.__beanDel,s=function(s){return s=T(s||((this[l]||this.document||this).parentWindow||n).event,!0),i&&(s.currentTarget=i.ft(s[c],e)),t.apply(e,[s].concat(r))};return s.__beanDel=i,s},_=function(e,t,r,i,s,o){var u=t.__beanDel,a=function(a){var f=u?u.ft(a[c],e):this,h=i?i.apply(f,arguments):v?!0:a&&a.propertyName=="_on"+r||!a;h&&(a&&(a=T(a||((this[l]||this.document||this).parentWindow||n).event,o),a.currentTarget=f),t.apply(e,a&&(!s||s.length===0)?arguments:g.call(arguments,a?0:1).concat(s)))};return a.__beanDel=u,a},D=function(e,t,n,r,i){return function(){e(t,n,i),r.apply(this,arguments)}},P=function(e,t,n,r){var i=t&&t.replace(s,""),o=k.get(e,i,n),u,a,f;for(u=0,a=o.length;u<a;u++)o[u].inNamespaces(r)&&((f=o[u])[m]&&O(f[c],f.eventType,f.handler,!1,f.type),k.del(f))},H=function(e,t,n,r,o){var u=t.replace(s,""),a=E(t.replace(i,""),"."),f;u=="unload"&&(n=D(P,e,u,n,r)),x[u]&&(x[u].condition&&(n=_(e,n,u,x[u].condition,o,!0)),u=x[u].base||u),f=k.put(new C(e,u,n,r,a[0]&&a)),f.handler=f.isNative?M(e,f.handler,o):_(e,f.handler,u,!1,o,!1),f[m]&&O(f[c],f.eventType,f.handler,!0,f.customType)},B=function(e,t,n){var r=function(t,r){var i,s=typeof e=="string"?n(e,r):e;for(;t&&t!==r;t=t.parentNode)for(i=s.length;i--;)if(s[i]===t)return t},i=function(e){var n=r(e[c],this);n&&t.apply(n,arguments)};return i.__beanDel={ft:r,selector:e,$:n},i},j=function(e,t,n){var r=P,o=t&&typeof t=="string",u,a,f,l;if(o&&t.indexOf(" ")>0){t=E(t);for(l=t.length;l--;)j(e,t[l],n);return e}a=o&&t.replace(s,""),a&&x[a]&&(a=x[a].type);if(!t||o){if(f=o&&t.replace(i,""))f=E(f,".");r(e,a,n,f)}else if(typeof t=="function")r(e,null,t);else for(u in t)t.hasOwnProperty(u)&&j(e,u,t[u]);return e},F=function(e,t,n,r,i){var s=n,o=n&&typeof n=="string",u,a,f,l;if(t&&!n&&typeof t=="object")for(u in t)t.hasOwnProperty(u)&&F.apply(this,[e,u,t[u]]);else{l=arguments.length>3?g.call(arguments,3):[],a=E(o?n:t),o&&(n=B(t,s=r,i||L))&&(l=g.call(l,1)),this===y&&(n=D(j,e,t,n,s));for(f=a.length;f--;)H(e,a[f],n,s,l)}return e},I=function(){return F.apply(y,arguments)},q=v?function(e,t,r){var i=p.createEvent(e?"HTMLEvents":"UIEvents");i[e?"initEvent":"initUIEvent"](t,!0,!0,n,1),r.dispatchEvent(i)}:function(e,t,n){n=N(n,e),e?n.fireEvent("on"+t,p.createEventObject()):n["_on"+t]++},R=function(e,t,n){var r=E(t),o,u,a,f,l;for(o=r.length;o--;){t=r[o].replace(s,"");if(f=r[o].replace(i,""))f=E(f,".");if(!f&&!n&&e[m])q(S[t],t,e);else{l=k.get(e,t),n=[!1].concat(n);for(u=0,a=l.length;u<a;u++)l[u].inNamespaces(f)&&l[u].handler.apply(e,n)}}return e},U=function(e,t,n){var r=k.get(t,n),i,s,o,u;for(i=0,s=r.length;i<s;i++)r[i].original&&(u=r[i].handler.__beanDel,u?o=[e,u.selector,r[i].type,r[i].original,u.$]:o=[e,r[i].type,r[i].original],F.apply(null,o));return e},z={add:F,one:I,remove:j,clone:U,fire:R,setSelectorEngine:A,noConflict:function(){return t[e]=r,this}};if(n[u]){var W=function(){var e,t=k.entries();for(e in t)t[e].type&&t[e].type!=="unload"&&j(t[e].element,t[e].type);n[f]("onunload",W),n.CollectGarbage&&n.CollectGarbage()};n[u]("onunload",W)}return z})
+!function(e,t,n){typeof module!="undefined"?module.exports=n(e,t):typeof define=="function"&&typeof define.amd=="object"?define(n):t[e]=n(e,t)}("bean",this,function(e,t){var n=window,r=t[e],i=/[^\.]*(?=\..*)\.|.*/,s=/\..*/,o="addEventListener",u="attachEvent",a="removeEventListener",f="detachEvent",l="ownerDocument",c="target",h="querySelectorAll",p=document||{},d=p.documentElement||{},v=d[o],m=v?o:u,g=Array.prototype.slice,y={},b="click dblclick mouseup mousedown contextmenu mousewheel mousemultiwheel DOMMouseScroll mouseover mouseout mousemove selectstart selectend keydown keypress keyup orientationchange focus blur change reset select submit load unload beforeunload resize move DOMContentLoaded readystatechange message error abort scroll ",w="show input invalid touchstart touchmove touchend touchcancel gesturestart gesturechange gestureend textinputreadystatechange pageshow pagehide popstate hashchange offline online afterprint beforeprint dragstart dragenter dragover dragleave drag drop dragend loadstart progress suspend emptied stalled loadmetadata loadeddata canplay canplaythrough playing waiting seeking seeked ended durationchange timeupdate play pause ratechange volumechange cuechange checking noupdate downloading cached updateready obsolete ",E=function(e,t){return e.split(t||" ")},S=function(e){return typeof e=="string"},x=function(e){return Object.prototype.toString.call(e)==="[object Array]"},T=function(e,t,n){for(n=0;n<t.length;n++)t[n]&&(e[t[n]]=1);return e}({},E(b+(v?w:""))),N=function(){var e="compareDocumentPosition",t=e in d?function(t,n){return n[e]&&(n[e](t)&16)===16}:"contains"in d?function(e,t){return t=t.nodeType===9||t===window?d:t,t!==e&&t.contains(e)}:function(e,t){while(e=e.parentNode)if(e===t)return 1;return 0},n=function(e){var n=e.relatedTarget;return n?n!==this&&n.prefix!=="xul"&&!/document/.test(this.toString())&&!t(n,this):n==null};return{mouseenter:{base:"mouseover",condition:n},mouseleave:{base:"mouseout",condition:n},mousewheel:{base:/Firefox/.test(navigator.userAgent)?"DOMMouseScroll":"mousewheel"}}}(),C=function(){var e=E("altKey attrChange attrName bubbles cancelable ctrlKey currentTarget detail eventPhase getModifierState isTrusted metaKey relatedNode relatedTarget shiftKey srcElement target timeStamp type view which"),t=e.concat(E("button buttons clientX clientY dataTransfer fromElement offsetX offsetY pageX pageY screenX screenY toElement")),n=t.concat(E("wheelDelta wheelDeltaX wheelDeltaY wheelDeltaZ axis")),r=e.concat(E("char charCode key keyCode keyIdentifier keyLocation location")),i=e.concat(E("data")),s=e.concat(E("touches targetTouches changedTouches scale rotation")),o=e.concat(E("data origin source")),u=e.concat(E("state")),a=/over|out/,f=[{reg:/key/i,fix:function(e,t){return t.keyCode=e.which||e.keyCode,r}},{reg:/click|mouse(?!(.*wheel|scroll))|menu|drag|drop/i,fix:function(e,n,r){n.rightClick=e.which===3||e.button===2,n.pos={x:0,y:0};if(e.pageX||e.pageY)n.clientX=e.pageX,n.clientY=e.pageY;else if(e.clientX||e.clientY)n.clientX=e.clientX+p.body.scrollLeft+d.scrollLeft,n.clientY=e.clientY+p.body.scrollTop+d.scrollTop;return a.test(r)&&(n.relatedTarget=e.relatedTarget||e[(r=="mouseover"?"from":"to")+"Element"]),t}},{reg:/mouse.*(wheel|scroll)/i,fix:function(){return n}},{reg:/^text/i,fix:function(){return i}},{reg:/^touch|^gesture/i,fix:function(){return s}},{reg:/^message$/i,fix:function(){return o}},{reg:/^popstate$/i,fix:function(){return u}},{reg:/.*/,fix:function(){return e}}],l={},h="preventDefault",v=function(e){return function(){e[h]?e[h]():e.returnValue=!1}},m="stopPropagation",g=function(e){return function(){e[m]?e[m]():e.cancelBubble=!0}},y="stopImmediatePropagation",b=function(e){return function(){e[y]&&e[y]()}},w=function(e){return function(){e[h](),e[m](),e.stopped=!0}},S=function(e,t,n){var r,i;for(r=n.length;r--;)!((i=n[r])in t)&&i in e&&(t[i]=e[i])};return function(e,t){var n={originalEvent:e,isNative:t,isBean:!0};if(!e)return n;var r,i,s,o=e.type,u=e[c]||e.srcElement;n[h]=v(e),n[m]=g(e),n[y]=b(e),n.stop=w(n),n[c]=u&&u.nodeType===3?u.parentNode:u;if(t){s=l[o];if(!s)for(r=0,i=f.length;r<i;r++)if(f[r].reg.test(o)){l[o]=s=f[r].fix;break}S(e,n,s(e,n,o))}return n}}(),k=function(e,t){return!v&&!t&&(e===p||e===n)?d:e},L=function(){function e(e,t,n,r,i,s){var o=N[t],u;t=="unload"&&(n=H(B,e,t,n,r)),o&&(o.condition&&(n=P(e,n,t,o.condition,s,!0)),t=o.base||t),this.isNative=u=T[t]&&!!e[m],this.customType=!v&&!u&&t,this.element=e,this.type=t,this.original=r,this.namespaces=i,this.eventType=v||u?t:"propertychange",this[c]=k(e,u),this[m]=!!this[c][m],this.handler=u?D(e,n,s):P(e,n,t,!1,s,!1)}return e.prototype={inNamespaces:function(e){var t,n,r=0;if(!e)return!0;if(!this.namespaces)return!1;for(t=e.length;t--;)for(n=this.namespaces.length;n--;)e[t]==this.namespaces[n]&&r++;return e.length===r},matches:function(e,t,n){return this.element===e&&(!t||this.original===t)&&(!n||this.handler===n)}},e}(),A=function(){var e={},t=function(n,r,i,s,o){if(!r||r=="*")for(var u in e)u.charAt(0)=="$"&&t(n,u.substr(1),i,s,o);else{var a=0,f,l=e["$"+r],c=n=="*";if(!l)return;for(f=l.length;a<f;a++)if((c||l[a].matches(n,i,s))&&!o(l[a],l,a,r))return}},n=function(t,n,r){var i,s=e["$"+n];if(s)for(i=s.length;i--;)if(s[i].matches(t,r,null))return!0;return!1},r=function(e,n,r){var i=[];return t(e,n,r,null,function(e){return i.push(e)}),i},i=function(t){return(e["$"+t.type]||(e["$"+t.type]=[])).push(t),t},s=function(n){t(n.element,n.type,null,n.handler,function(t,n,r){return n.splice(r,1),n.length===0&&delete e["$"+t.type],!1})},o=function(){var t,n=[];for(t in e)t.charAt(0)=="$"&&(n=n.concat(e[t]));return n};return{has:n,get:r,put:i,del:s,entries:o}}(),O,M=function(e){arguments.length?O=e:O=p[h]?function(e,t){return t[h](e)}:function(){throw new Error("Bean: No selector engine installed")}},_=v?function(e,t,n,r){e[r?o:a](t,n,!1)}:function(e,t,n,r,i){i&&r&&e["_on"+i]==null&&(e["_on"+i]=0),e[r?u:f]("on"+t,n)},D=function(e,t,r){var i=t.__beanDel,s=function(s){return s=C(s||((this[l]||this.document||this).parentWindow||n).event,!0),i&&(s.currentTarget=i.ft(s[c],e)),t.apply(e,[s].concat(r))};return s.__beanDel=i,s},P=function(e,t,r,i,s,o){var u=t.__beanDel,a=function(a){var f=u?u.ft(a[c],e):this,h=i?i.apply(f,arguments):v?!0:a&&a.propertyName=="_on"+r||!a;h&&(a&&(a=C(a||((this[l]||this.document||this).parentWindow||n).event,o),a.currentTarget=f),t.apply(e,a&&(!s||s.length===0)?arguments:g.call(arguments,a?0:1).concat(s)))};return a.__beanDel=u,a},H=function(e,t,n,r,i){return function(){e(t,n,i),r.apply(this,arguments)}},B=function(e,t,n,r){var i=t&&t.replace(s,""),o=A.get(e,i,n),u,a,f;for(u=0,a=o.length;u<a;u++)o[u].inNamespaces(r)&&((f=o[u])[m]&&_(f[c],f.eventType,f.handler,!1,f.type),A.del(f))},j=function(e,t){var n=function(t,n){var r,i=S(e)?O(e,n):e;for(;t&&t!==n;t=t.parentNode)for(r=i.length;r--;)if(i[r]===t)return t},r=function(e){var r=n(e[c],this);r&&t.apply(r,arguments)};return r.__beanDel={ft:n,selector:e},r},F=function(e,t,n){var r=B,o=S(t),u,a,f,l;if(o&&t.indexOf(" ")>0){t=E(t);for(l=t.length;l--;)F(e,t[l],n);return e}a=o&&t.replace(s,""),a&&N[a]&&(a=N[a].type);if(!t||o){if(f=o&&t.replace(i,""))f=E(f,".");r(e,a,n,f)}else if(typeof t=="function")r(e,null,t);else for(u in t)t.hasOwnProperty(u)&&F(e,u,t[u]);return e},I=function(e,t,n,r){var o,u,a,f,l,h;if(n===undefined&&typeof t=="object"){for(u in t)t.hasOwnProperty(u)&&I.call(this,e,u,t[u]);return}S(n)||x(n)?(o=r,l=g.call(arguments,4),r=j(n,o,O)):(l=g.call(arguments,3),r=o=n),a=E(t),this===y&&(r=H(F,e,t,r,o));for(f=a.length;f--;)h=A.put(new L(e,a[f].replace(s,""),r,o,E(a[f].replace(i,""),"."),l)),h[m]&&_(h[c],h.eventType,h.handler,!0,h.customType);return e},q=function(e,t,n,r){return I.apply(this,S(n)?[e,n,t,r].concat(arguments.length>3?g.call(arguments,5):[]):g.call(arguments))},R=function(){return q.apply(y,arguments)},U=v?function(e,t,r){var i=p.createEvent(e?"HTMLEvents":"UIEvents");i[e?"initEvent":"initUIEvent"](t,!0,!0,n,1),r.dispatchEvent(i)}:function(e,t,n){n=k(n,e),e?n.fireEvent("on"+t,p.createEventObject()):n["_on"+t]++},z=function(e,t,n){var r=E(t),o,u,a,f,l;for(o=r.length;o--;){t=r[o].replace(s,"");if(f=r[o].replace(i,""))f=E(f,".");if(!f&&!n&&e[m])U(T[t],t,e);else{l=A.get(e,t),n=[!1].concat(n);for(u=0,a=l.length;u<a;u++)l[u].inNamespaces(f)&&l[u].handler.apply(e,n)}}return e},W=function(e,t,n){var r=A.get(t,n),i,s,o,u;for(i=0,s=r.length;i<s;i++)r[i].original&&(u=r[i].handler.__beanDel,u?o=[e,u.selector,r[i].type,r[i].original]:o=[e,r[i].type,r[i].original],q.apply(null,o));return e},X={add:q,one:R,remove:F,clone:W,fire:z,setSelectorEngine:M,noConflict:function(){return t[e]=r,this}};if(n[u]){var V=function(){var e,t=A.entries();for(e in t)t[e].type&&t[e].type!=="unload"&&F(t[e].element,t[e].type);n[f]("onunload",V),n.CollectGarbage&&n.CollectGarbage()};n[u]("onunload",V)}return M(),X})
View
123 src/bean.js
@@ -47,7 +47,9 @@
'seeked ended durationchange timeupdate play pause ratechange ' + // media
'volumechange cuechange ' + // media
'checking noupdate downloading cached updateready obsolete ' // appcache
- , str2arr = function (s, d) { return s.split(d || ' ') }
+ , str2arr = function (s, d) { return s.split(d || ' ') }
+ , isString = function (s) { return typeof s == 'string' }
+ , isArray = function (a) { return Object.prototype.toString.call(a) === '[object Array]' }
, nativeEvents = (function (hash, events, i) {
for (i = 0; i < events.length; i++) events[i] && (hash[events[i]] = 1)
@@ -291,7 +293,9 @@
if (!type || type == '*') {
// search the whole registry
for (var t in map) {
- if (t.charAt(0) == '$') forAll(element, t.substr(1), original, handler, fn)
+ if (t.charAt(0) == '$') {
+ forAll(element, t.substr(1), original, handler, fn)
+ }
}
} else {
var i = 0, l, list = map['$' + type], all = element == '*'
@@ -316,7 +320,9 @@
, get = function (element, type, original) {
var entries = []
- forAll(element, type, original, null, function (entry) { return entries.push(entry) })
+ forAll(element, type, original, null, function (entry) {
+ return entries.push(entry)
+ })
return entries
}
@@ -345,16 +351,19 @@
return { has: has, get: get, put: put, del: del, entries: entries }
}())
- , selectorEngine = doc[qSA]
- ? function (s, r) {
- return r[qSA](s)
- }
- : function () {
- throw new Error('Bean: No selector engine installed') // eeek
- }
-
+ , selectorEngine
, setSelectorEngine = function (e) {
- selectorEngine = e
+ if (!arguments.length) {
+ selectorEngine = doc[qSA]
+ ? function (s, r) {
+ return r[qSA](s)
+ }
+ : function () {
+ throw new Error('Bean: No selector engine installed') // eeek
+ }
+ } else {
+ selectorEngine = e
+ }
}
// add and remove listeners to DOM elements
@@ -424,11 +433,11 @@
}
}
- , delegate = function (selector, fn, $) {
+ , delegate = function (selector, fn) {
//TODO: findTarget (therefore $) is called twice, once for match and once for
// setting e.currentTarget, fix this so it's only needed once
var findTarget = function (target, root) {
- var i, array = typeof selector == 'string' ? $(selector, root) : selector
+ var i, array = isString(selector) ? selectorEngine(selector, root) : selector
for (; target && target !== root; target = target.parentNode) {
for (i = array.length; i--;) {
if (array[i] === target) return target
@@ -443,30 +452,29 @@
handler.__beanDel = {
ft : findTarget // attach it here for customEvents to use too
, selector : selector
- , $ : $
}
return handler
}
, remove = function (element, typeSpec, fn) {
var rm = removeListener
- , isString = typeSpec && typeof typeSpec == 'string'
+ , isTypeStr = isString(typeSpec)
, k, type, namespaces, i
- if (isString && typeSpec.indexOf(' ') > 0) {
+ if (isTypeStr && typeSpec.indexOf(' ') > 0) {
// remove(el, 't1 t2 t3', fn) or remove(el, 't1 t2 t3')
typeSpec = str2arr(typeSpec)
for (i = typeSpec.length; i--;)
remove(element, typeSpec[i], fn)
return element
}
- type = isString && typeSpec.replace(nameRegex, '')
+ type = isTypeStr && typeSpec.replace(nameRegex, '')
if (type && customEvents[type]) type = customEvents[type].type
- if (!typeSpec || isString) {
+ if (!typeSpec || isTypeStr) {
// remove(el) or remove(el, t1.ns) or remove(el, .ns) or remove(el, .ns1.ns2.ns3)
- if (namespaces = isString && typeSpec.replace(namespaceRegex, '')) namespaces = str2arr(namespaces, '.')
+ if (namespaces = isTypeStr && typeSpec.replace(namespaceRegex, '')) namespaces = str2arr(namespaces, '.')
rm(element, type, fn, namespaces)
} else if (typeof typeSpec == 'function') {
// remove(el, fn)
@@ -481,49 +489,61 @@
return element
}
- // 5th argument, $=selector engine, is deprecated and will be removed
- , add = function (element, events, fn, delfn, $) {
- var originalFn = fn
- , isDelegated = fn && typeof fn == 'string'
- , type, types, i, args, entry
+ , on = function(element, events, selector, fn) {
+ var originalFn, type, types, i, args, entry
- if (events && !fn && typeof events == 'object') {
+ if (selector === undefined && typeof events == 'object') {
//TODO: this can't handle delegated events
for (type in events) {
- if (events.hasOwnProperty(type)) add.apply(this, [ element, type, events[type] ])
+ if (events.hasOwnProperty(type)) {
+ on.call(this, element, type, events[type])
+ }
}
+ return
+ }
+
+ if (isString(selector) || isArray(selector)) {
+ originalFn = fn
+ args = slice.call(arguments, 4)
+ fn = delegate(selector, originalFn, selectorEngine)
} else {
- args = arguments.length > 3 ? slice.call(arguments, 3) : []
- types = str2arr(isDelegated ? fn : events)
+ args = slice.call(arguments, 3)
+ fn = originalFn = selector
+ }
- if (isDelegated) {
- fn = delegate(events, (originalFn = delfn), $ || selectorEngine)
- args = slice.call(args, 1)
- }
+ types = str2arr(events)
- // special case for one()
- if (this === ONE) {
- fn = once(remove, element, events, fn, originalFn)
- }
+ // special case for one()
+ if (this === ONE) {
+ fn = once(remove, element, events, fn, originalFn)
+ }
- for (i = types.length; i--;) {
- entry = registry.put(new RegEntry(
- element
- , types[i].replace(nameRegex, '')
- , fn
- , originalFn
- , str2arr(types[i].replace(namespaceRegex, ''), '.') // namespaces
- , args
- ))
- if (entry[eventSupport]) {
- listener(entry[targetS], entry.eventType, entry.handler, true, entry.customType)
- }
+ for (i = types.length; i--;) {
+ entry = registry.put(new RegEntry(
+ element
+ , types[i].replace(nameRegex, '')
+ , fn
+ , originalFn
+ , str2arr(types[i].replace(namespaceRegex, ''), '.') // namespaces
+ , args
+ ))
+ if (entry[eventSupport]) {
+ listener(entry[targetS], entry.eventType, entry.handler, true, entry.customType)
}
}
return element
}
+ , add = function (element, events, fn, delfn) {
+ return on.apply(
+ this
+ , !isString(fn)
+ ? slice.call(arguments)
+ : [ element, fn, events, delfn ].concat(arguments.length > 3 ? slice.call(arguments, 5) : [])
+ )
+ }
+
, one = function () {
return add.apply(ONE, arguments)
}
@@ -568,7 +588,7 @@
if (handlers[i].original) {
beanDel = handlers[i].handler.__beanDel
if (beanDel) {
- args = [ element, beanDel.selector, handlers[i].type, handlers[i].original, beanDel.$]
+ args = [ element, beanDel.selector, handlers[i].type, handlers[i].original ]
} else
args = [ element, handlers[i].type, handlers[i].original ]
add.apply(null, args)
@@ -579,6 +599,7 @@
, bean = {
add : add
+ , on : on
, one : one
, remove : remove
, clone : clone
@@ -603,5 +624,7 @@
win[attachEvent]('onunload', cleanup)
}
+ setSelectorEngine()
+
return bean
}));
View
2 tests/clone-test.js
@@ -89,6 +89,8 @@ buster.testCase('clone', {
done()
})
+ bean.setSelectorEngine(qwery)
+
bean.add(foo, '.bang', 'click', trigger.wrap(spy1), qwery)
bean.add(foo, '.baz' , 'click', trigger.wrap(spy2), qwery)
View
5 tests/common.js
@@ -1,4 +1,4 @@
-/*global bean:true*/
+/*global bean:true, qwery:true*/
var fixturesHTML =
'<input id="input" type="text" />\n' +
@@ -13,7 +13,6 @@ var fixturesHTML =
' <input type="text" id="txt" value="">\n' +
'</div>\n'
-
var features = {
w3c: !!window.addEventListener
, qSA: !!document.querySelectorAll
@@ -51,6 +50,7 @@ var features = {
, SpyTrigger = function () {}
, globalSetUp = function () {
var removables = this.removables = []
+ this.timeout = 1000
this.byId = function (id) {
var el = document.getElementById(id)
@@ -115,5 +115,4 @@ SpyTrigger.prototype.reset = function () {
}
if (!window.console) window.console = { log: function () {}}
-
insertFixtures()
View
8 tests/custom-test.js
@@ -4,7 +4,7 @@ buster.testCase('custom', {
'setUp': globalSetUp
, 'tearDown': globalTearDown
- , 'custom: should be able to add single custom events': function (done) {
+ , 'should be able to add single custom events': function (done) {
var el = this.byId('input')
, trigger = this.trigger()
, spy = this.spy()
@@ -18,7 +18,7 @@ buster.testCase('custom', {
bean.fire(el, 'partytime')
}
- , 'custom: should bubble up dom like traditional events': function (done) {
+ , 'should bubble up dom like traditional events': function (done) {
if (features.w3c) {
//dean edwards' onpropertychange hack doesn't bubble unfortunately :(
var el1 = this.byId('foo')
@@ -39,7 +39,7 @@ buster.testCase('custom', {
}
}
- , 'custom: should be able to add, fire and remove custom events to document': function (done) {
+ , 'should be able to add, fire and remove custom events to document': function (done) {
var calls = 0
, trigger = this.trigger()
@@ -59,7 +59,7 @@ buster.testCase('custom', {
bean.fire(document, 'justlookatthat')
}
- , 'custom: should be able to add, fire and remove custom events to window': function (done) {
+ , 'should be able to add, fire and remove custom events to window': function (done) {
var calls = 0
, trigger = this.trigger()
View
2 tests/custom-types-test.js
@@ -104,6 +104,8 @@ buster.testCase('custom types', {
done()
}, 50)
+ bean.setSelectorEngine(qwery)
+
bean.add(foo, '.bang', 'mouseenter', trigger.wrap(meSpy), qwery)
bean.add(foo, '.bang', 'mouseleave', trigger.wrap(mlSpy), qwery)
View
19 tests/delegate-test.js
@@ -4,6 +4,8 @@ buster.testCase('delegate', {
'setUp': function () {
globalSetUp.call(this)
+ bean.setSelectorEngine(qwery)
+
this.verifySimpleDelegateSpy = function (spy, target) {
assert.equals(spy.callCount, 2, 'delegated on selector')
assert.same(spy.thisValues[0], target, 'context (this) was set to delegated element')
@@ -15,7 +17,10 @@ buster.testCase('delegate', {
}
}
- , 'tearDown': globalTearDown
+ , 'tearDown': function () {
+ globalTearDown.call(this)
+ bean.setSelectorEngine() // reset to default
+ }
, 'should be able to delegate on selectors': function (done) {
var el1 = this.byId('foo')
@@ -31,7 +36,7 @@ buster.testCase('delegate', {
done()
})
- bean.add(el1, '.bar', 'click', trigger.wrap(spy), qwery)
+ bean.add(el1, '.bar', 'click', trigger.wrap(spy))
Syn.click(el2)
Syn.click(el3)
@@ -51,13 +56,13 @@ buster.testCase('delegate', {
done()
}, 50)
- bean.add(el1, '.bar', 'mouseup mousedown', trigger.wrap(spy), qwery)
+ bean.add(el1, '.bar', 'mouseup mousedown', trigger.wrap(spy))
Syn.click(el2)
Syn.click(el3)
}
- , 'should be able to delegate on arary': function (done) {
+ , 'should be able to delegate on array': function (done) {
var el1 = this.byId('foo')
, el2 = this.byId('bar')
, el3 = this.byId('baz')
@@ -71,7 +76,7 @@ buster.testCase('delegate', {
done()
})
- bean.add(el1, [el2], 'click', trigger.wrap(spy), qwery)
+ bean.add(el1, [el2], 'click', trigger.wrap(spy))
Syn.click(el2)
Syn.click(el3)
@@ -93,7 +98,7 @@ buster.testCase('delegate', {
done()
})
- bean.add(el1, '.bar', 'click', trigger.wrap(fn), qwery)
+ bean.add(el1, '.bar', 'click', trigger.wrap(fn))
Syn.click(el2)
Syn.click(el2)
@@ -118,6 +123,7 @@ buster.testCase('delegate', {
done()
})
+ bean.setSelectorEngine() // reset to default
bean.add(el1, '.bar', 'click', trigger.wrap(spy))
Syn.click(el2)
@@ -135,6 +141,7 @@ buster.testCase('delegate', {
, el2 = this.byId('bar')
, spy = this.spy()
+ bean.setSelectorEngine() // reset to default
bean.add(el1, '.bar', 'click', spy)
window.onerror = function (e) {
View
6 tests/event-object-test.js
@@ -95,10 +95,12 @@ buster.testCase('event object', {
done()
})
+ bean.setSelectorEngine(qwery)
+
txt.value = ''
if (delegate) {
- bean.add(parent , '*', 'keypress', trigger.wrap(txtHandler), qwery)
- bean.add(fixture , 'keypress', trigger.wrap(parentSpy), qwery)
+ bean.add(parent , '*', 'keypress', trigger.wrap(txtHandler))
+ bean.add(fixture , 'keypress', trigger.wrap(parentSpy))
} else {
bean.add(txt , 'keypress', trigger.wrap(txtHandler))
bean.add(parent, 'keypress', trigger.wrap(parentSpy))

0 comments on commit 4a850d8

Please sign in to comment.
Something went wrong with that request. Please try again.