/*! |
/*! |
* bean.js - copyright Jacob Thornton 2011 |
* bean.js - copyright Jacob Thornton 2011 |
* https://github.com/fat/bean |
* https://github.com/fat/bean |
* MIT License |
* MIT License |
* special thanks to: |
* special thanks to: |
* dean edwards: http://dean.edwards.name/ |
* dean edwards: http://dean.edwards.name/ |
* dperini: https://github.com/dperini/nwevents |
* dperini: https://github.com/dperini/nwevents |
* the entire mootools team: github.com/mootools/mootools-core |
* the entire mootools team: github.com/mootools/mootools-core |
*/ |
*/ |
/*global module:true, define:true*/ |
/*global module:true, define:true*/ |
!function (name, context, definition) { |
!function (name, context, definition) { |
if (typeof module !== 'undefined') module.exports = definition(name, context); |
if (typeof module !== 'undefined') module.exports = definition(name, context); |
else if (typeof define === 'function' && typeof define.amd === 'object') define(definition); |
else if (typeof define === 'function' && typeof define.amd === 'object') define(definition); |
else context[name] = definition(name, context); |
else context[name] = definition(name, context); |
}('bean', this, function (name, context) { |
}('bean', this, function (name, context) { |
var win = window |
var win = window |
, old = context[name] |
, old = context[name] |
, overOut = /over|out/ |
, overOut = /over|out/ |
, namespaceRegex = /[^\.]*(?=\..*)\.|.*/ |
, namespaceRegex = /[^\.]*(?=\..*)\.|.*/ |
, nameRegex = /\..*/ |
, nameRegex = /\..*/ |
, addEvent = 'addEventListener' |
, addEvent = 'addEventListener' |
, attachEvent = 'attachEvent' |
, attachEvent = 'attachEvent' |
, removeEvent = 'removeEventListener' |
, removeEvent = 'removeEventListener' |
, detachEvent = 'detachEvent' |
, detachEvent = 'detachEvent' |
, doc = document || {} |
, doc = document || {} |
, root = doc.documentElement || {} |
, root = doc.documentElement || {} |
, W3C_MODEL = root[addEvent] |
, W3C_MODEL = root[addEvent] |
, eventSupport = W3C_MODEL ? addEvent : attachEvent |
, eventSupport = W3C_MODEL ? addEvent : attachEvent |
, slice = Array.prototype.slice |
, slice = Array.prototype.slice |
, mouseTypeRegex = /click|mouse|menu|drag|drop/i |
, mouseTypeRegex = /click|mouse|menu|drag|drop/i |
, touchTypeRegex = /^touch|^gesture/i |
, touchTypeRegex = /^touch|^gesture/i |
, ONE = { one: 1 } // singleton for quick matching making add() do one() |
, ONE = { one: 1 } // singleton for quick matching making add() do one() |
|
|
, nativeEvents = (function (hash, events, i) { |
, nativeEvents = (function (hash, events, i) { |
for (i = 0; i < events.length; i++) |
for (i = 0; i < events.length; i++) |
hash[events[i]] = 1 |
hash[events[i]] = 1 |
return hash |
return hash |
})({}, ( |
})({}, ( |
'click dblclick mouseup mousedown contextmenu ' + // mouse buttons |
'click dblclick mouseup mousedown contextmenu ' + // mouse buttons |
'mousewheel DOMMouseScroll ' + // mouse wheel |
'mousewheel DOMMouseScroll ' + // mouse wheel |
'mouseover mouseout mousemove selectstart selectend ' + // mouse movement |
'mouseover mouseout mousemove selectstart selectend ' + // mouse movement |
'keydown keypress keyup ' + // keyboard |
'keydown keypress keyup ' + // keyboard |
'orientationchange ' + // mobile |
'orientationchange ' + // mobile |
'focus blur change reset select submit ' + // form elements |
'focus blur change reset select submit ' + // form elements |
'load unload beforeunload resize move DOMContentLoaded readystatechange ' + // window |
'load unload beforeunload resize move DOMContentLoaded readystatechange ' + // window |
'error abort scroll ' + // misc |
'error abort scroll ' + // misc |
(W3C_MODEL ? // element.fireEvent('onXYZ'... is not forgiving if we try to fire an event |
(W3C_MODEL ? // element.fireEvent('onXYZ'... is not forgiving if we try to fire an event |
// that doesn't actually exist, so make sure we only do these on newer browsers |
// that doesn't actually exist, so make sure we only do these on newer browsers |
'show ' + // mouse buttons |
'show ' + // mouse buttons |
'input invalid ' + // form elements |
'input invalid ' + // form elements |
'touchstart touchmove touchend touchcancel ' + // touch |
'touchstart touchmove touchend touchcancel ' + // touch |
'gesturestart gesturechange gestureend ' + // gesture |
'gesturestart gesturechange gestureend ' + // gesture |
'message readystatechange pageshow pagehide popstate ' + // window |
'message readystatechange pageshow pagehide popstate ' + // window |
'hashchange offline online ' + // window |
'hashchange offline online ' + // window |
'afterprint beforeprint ' + // printing |
'afterprint beforeprint ' + // printing |
'dragstart dragenter dragover dragleave drag drop dragend ' + // dnd |
'dragstart dragenter dragover dragleave drag drop dragend ' + // dnd |
'loadstart progress suspend emptied stalled loadmetadata ' + // media |
'loadstart progress suspend emptied stalled loadmetadata ' + // media |
'loadeddata canplay canplaythrough playing waiting seeking ' + // media |
'loadeddata canplay canplaythrough playing waiting seeking ' + // media |
'seeked ended durationchange timeupdate play pause ratechange ' + // media |
'seeked ended durationchange timeupdate play pause ratechange ' + // media |
'volumechange cuechange ' + // media |
'volumechange cuechange ' + // media |
'checking noupdate downloading cached updateready obsolete ' + // appcache |
'checking noupdate downloading cached updateready obsolete ' + // appcache |
'' : '') |
'' : '') |
).split(' ') |
).split(' ') |
) |
) |
|
|
, customEvents = (function () { |
, customEvents = (function () { |
function isDescendant(parent, node) { |
function isDescendant(parent, node) { |
while ((node = node.parentNode) !== null) { |
while ((node = node.parentNode) !== null) { |
if (node === parent) return true |
if (node === parent) return true |
} |
} |
return false |
|
} |
|
|
|
function check(event) { |
|
var related = event.relatedTarget |
|
if (!related) return related === null |
|
return (related !== this && related.prefix !== 'xul' && !/document/.test(this.toString()) && !isDescendant(this, related)) |
|
} |
|
|
|
return { |
|
mouseenter: { base: 'mouseover', condition: check } |
|
, mouseleave: { base: 'mouseout', condition: check } |
|
, mousewheel: { base: /Firefox/.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel' } |
|
} |
|
})() |
|
|
|
, fixEvent = (function () { |
|
var commonProps = 'altKey attrChange attrName bubbles cancelable ctrlKey currentTarget detail eventPhase getModifierState isTrusted metaKey relatedNode relatedTarget shiftKey srcElement target timeStamp type view which'.split(' ') |
|
, mouseProps = commonProps.concat('button buttons clientX clientY dataTransfer fromElement offsetX offsetY pageX pageY screenX screenY toElement'.split(' ')) |
|
, keyProps = commonProps.concat('char charCode key keyCode'.split(' ')) |
|
, touchProps = commonProps.concat('touches targetTouches changedTouches scale rotation'.split(' ')) |
|
, preventDefault = 'preventDefault' |
|
, createPreventDefault = function (event) { |
|
return function () { |
|
if (event[preventDefault]) |
|
event[preventDefault]() |
|
else |
|
event.returnValue = false |
|
} |
|
} |
|
, stopPropagation = 'stopPropagation' |
|
, createStopPropagation = function (event) { |
|
return function () { |
|
if (event[stopPropagation]) |
|
event[stopPropagation]() |
|
else |
|
event.cancelBubble = true |
|
} |
|
} |
|
, createStop = function (synEvent) { |
|
return function () { |
|
synEvent[preventDefault]() |
|
synEvent[stopPropagation]() |
|
synEvent.stopped = true |
|
} |
|
} |
|
, copyProps = function (event, result, props) { |
|
var i, p |
|
for (i = props.length; i--;) { |
|
p = props[i] |
|
if (!(p in result) && p in event) result[p] = event[p] |
|
} |
|
} |
|
|
|
return function (event, isNative) { |
|
var result = { originalEvent: event, isNative: isNative } |
|
if (!event) |
|
return result |
|
|
|
var props |
|
, type = event.type |
|
, target = event.target || event.srcElement |
|
|
|
result[preventDefault] = createPreventDefault(event) |
|
result[stopPropagation] = createStopPropagation(event) |
|
result.stop = createStop(result) |
|
result.target = target && target.nodeType === 3 ? target.parentNode : target |
|
|
|
if (isNative) { // we only need basic augmentation on custom events, the rest is too expensive |
|
if (type.indexOf('key') !== -1) { |
|
props = keyProps |
|
result.keyCode = event.which || event.keyCode |
|
} else if (mouseTypeRegex.test(type)) { |
|
props = mouseProps |
|
result.rightClick = event.which === 3 || event.button === 2 |
|
result.pos = { x: 0, y: 0 } |
|
if (event.pageX || event.pageY) { |
|
result.clientX = event.pageX |
|
result.clientY = event.pageY |
|
} else if (event.clientX || event.clientY) { |
|
result.clientX = event.clientX + doc.body.scrollLeft + root.scrollLeft |
|
result.clientY = event.clientY + doc.body.scrollTop + root.scrollTop |
|
} |
|
if (overOut.test(type)) |
|
result.relatedTarget = event.relatedTarget || event[(type === 'mouseover' ? 'from' : 'to') + 'Element'] |
|
} else if (touchTypeRegex.test(type)) { |
|
props = touchProps |
|
} |
|
copyProps(event, result, props || commonProps) |
|
} |
|
return result |
|
} |
|
})() |
|
|
|
// if we're in old IE we can't do onpropertychange on doc or win so we use doc.documentElement for both |
|
, targetElement = function (element, isNative) { |
|
return !W3C_MODEL && !isNative && (element === doc || element === win) ? root : element |
|
} |
|
|
|
// we use one of these per listener, of any type |
|
, RegEntry = (function () { |
|
function entry(element, type, handler, original, namespaces) { |
|
this.element = element |
|
this.type = type |
|
this.handler = handler |
|
this.original = original |
|
this.namespaces = namespaces |
|
this.custom = customEvents[type] |
|
this.isNative = nativeEvents[type] && element[eventSupport] |
|
this.eventType = W3C_MODEL || this.isNative ? type : 'propertychange' |
|
this.customType = !W3C_MODEL && !this.isNative && type |
|
this.target = targetElement(element, this.isNative) |
|
this.eventSupport = this.target[eventSupport] |
|
} |
|
|
|
entry.prototype = { |
|
// given a list of namespaces, is our entry in any of them? |
|
inNamespaces: function (checkNamespaces) { |
|
var i, j |
|
if (!checkNamespaces) |
|
return true |
|
if (!this.namespaces) |
|
return false |
return false |
for (i = checkNamespaces.length; i--;) { |
} |
for (j = this.namespaces.length; j--;) { |
|
if (checkNamespaces[i] === this.namespaces[j]) |
function check(event) { |
return true |
var related = event.relatedTarget |
} |
if (!related) return related === null |
} |
return (related !== this && related.prefix !== 'xul' && !/document/.test(this.toString()) && !isDescendant(this, related)) |
return false |
} |
} |
|
|
return { |
// match by element, original fn (opt), handler fn (opt) |
mouseenter: { base: 'mouseover', condition: check }, mouseleave: { base: 'mouseout', condition: check }, mousewheel: { base: /Firefox/.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel' } |
, matches: function (checkElement, checkOriginal, checkHandler) { |
} |
return this.element === checkElement && |
})() |
(!checkOriginal || this.original === checkOriginal) && |
|
(!checkHandler || this.handler === checkHandler) |
, fixEvent = (function () { |
} |
var commonProps = 'altKey attrChange attrName bubbles cancelable ctrlKey currentTarget detail eventPhase getModifierState isTrusted metaKey relatedNode relatedTarget shiftKey srcElement target timeStamp type view which'.split(' ') |
} |
, mouseProps = commonProps.concat('button buttons clientX clientY dataTransfer fromElement offsetX offsetY pageX pageY screenX screenY toElement'.split(' ')) |
|
, keyProps = commonProps.concat('char charCode key keyCode'.split(' ')) |
return entry |
, touchProps = commonProps.concat('touches targetTouches changedTouches scale rotation'.split(' ')) |
})() |
, preventDefault = 'preventDefault' |
|
, createPreventDefault = function (event) { |
, registry = (function () { |
return function () { |
// our map stores arrays by event type, just because it's better than storing |
if (event[preventDefault]) |
// everything in a single array. uses '$' as a prefix for the keys for safety |
event[preventDefault]() |
var map = {} |
else |
|
event.returnValue = false |
// generic functional search of our registry for matching listeners, |
} |
// `fn` returns false to break out of the loop |
} |
, forAll = function (element, type, original, handler, fn) { |
, stopPropagation = 'stopPropagation' |
if (!type || type === '*') { |
, createStopPropagation = function (event) { |
// search the whole registry |
return function () { |
for (var t in map) { |
if (event[stopPropagation]) |
if (t.charAt(0) === '$') |
event[stopPropagation]() |
forAll(element, t.substr(1), original, handler, fn) |
else |
} |
event.cancelBubble = true |
} else { |
} |
var i = 0, l, list = map['$' + type], all = element === '*' |
} |
if (!list) |
, createStop = function (synEvent) { |
return |
return function () { |
for (l = list.length; i < l; i++) { |
synEvent[preventDefault]() |
if (all || list[i].matches(element, original, handler)) |
synEvent[stopPropagation]() |
if (!fn(list[i], list, i, type)) |
synEvent.stopped = true |
return |
} |
} |
} |
} |
, copyProps = function (event, result, props) { |
} |
var i, p |
|
for (i = props.length; i--;) { |
, has = function (element, type, original) { |
p = props[i] |
// we're not using forAll here simply because it's a bit slower and this |
if (!(p in result) && p in event) result[p] = event[p] |
// needs to be fast |
} |
var i, list = map['$' + type] |
} |
if (list) { |
|
for (i = list.length; i--;) { |
return function (event, isNative) { |
if (list[i].matches(element, original, null)) |
var result = { originalEvent: event, isNative: isNative } |
return true |
if (!event) |
} |
return result |
} |
|
return false |
var props |
} |
, type = event.type |
|
, target = event.target || event.srcElement |
, get = function (element, type, original) { |
|
var entries = [] |
result[preventDefault] = createPreventDefault(event) |
forAll(element, type, original, null, function (entry) { return entries.push(entry) }) |
result[stopPropagation] = createStopPropagation(event) |
return entries |
result.stop = createStop(result) |
} |
result.target = target && target.nodeType === 3 ? target.parentNode : target |
|
|
, put = function (entry) { |
if (isNative) { // we only need basic augmentation on custom events, the rest is too expensive |
(map['$' + entry.type] || (map['$' + entry.type] = [])).push(entry) |
if (type.indexOf('key') !== -1) { |
return entry |
props = keyProps |
} |
result.keyCode = event.which || event.keyCode |
|
} else if (mouseTypeRegex.test(type)) { |
, del = function (entry) { |
props = mouseProps |
forAll(entry.element, entry.type, null, entry.handler, function (entry, list, i) { |
result.rightClick = event.which === 3 || event.button === 2 |
list.splice(i, 1) |
result.pos = { x: 0, y: 0 } |
if (list.length === 0) |
if (event.pageX || event.pageY) { |
delete map['$' + entry.type] |
result.clientX = event.pageX |
return false |
result.clientY = event.pageY |
}) |
} else if (event.clientX || event.clientY) { |
} |
result.clientX = event.clientX + doc.body.scrollLeft + root.scrollLeft |
|
result.clientY = event.clientY + doc.body.scrollTop + root.scrollTop |
|
} |
|
if (overOut.test(type)) |
|
result.relatedTarget = event.relatedTarget || event[(type === 'mouseover' ? 'from' : 'to') + 'Element'] |
|
} else if (touchTypeRegex.test(type)) { |
|
props = touchProps |
|
} |
|
copyProps(event, result, props || commonProps) |
|
} |
|
return result |
|
} |
|
})() |
|
|
|
// if we're in old IE we can't do onpropertychange on doc or win so we use doc.documentElement for both |
|
, targetElement = function (element, isNative) { |
|
return !W3C_MODEL && !isNative && (element === doc || element === win) ? root : element |
|
} |
|
|
|
// we use one of these per listener, of any type |
|
, RegEntry = (function () { |
|
function entry(element, type, handler, original, namespaces) { |
|
this.element = element |
|
this.type = type |
|
this.handler = handler |
|
this.original = original |
|
this.namespaces = namespaces |
|
this.custom = customEvents[type] |
|
this.isNative = nativeEvents[type] && element[eventSupport] |
|
this.eventType = W3C_MODEL || this.isNative ? type : 'propertychange' |
|
this.custom |