// Zepto.js // (c) 2010-2012 Thomas Fuchs // Zepto.js may be freely distributed under the MIT license. ;(function($){ var $$ = $.zepto.qsa, handlers = {}, _zid = 1, specialEvents={}, hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' function zid(element) { return element._zid || (element._zid = _zid++) } function findHandlers(element, event, fn, selector) { event = parse(event) if (event.ns) var matcher = matcherFor(event.ns) return (handlers[zid(element)] || []).filter(function(handler) { return handler && (!event.e || handler.e == event.e) && (!event.ns || matcher.test(handler.ns)) && (!fn || zid(handler.fn) === zid(fn)) && (!selector || handler.sel == selector) }) } function parse(event) { var parts = ('' + event).split('.') return {e: parts[0], ns: parts.slice(1).sort().join(' ')} } function matcherFor(ns) { return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') } function eachEvent(events, fn, iterator){ if ($.isObject(events)) $.each(events, iterator) else events.split(/\s/).forEach(function(type){ iterator(type, fn) }) } function eventCapture(handler, captureSetting) { return handler.del && (handler.e == 'focus' || handler.e == 'blur') || !!captureSetting } function realEvent(type) { return hover[type] || type } function add(element, events, fn, selector, getDelegate, capture){ var id = zid(element), set = (handlers[id] || (handlers[id] = [])) eachEvent(events, fn, function(event, fn){ var handler = parse(event) handler.fn = fn handler.sel = selector // emulate mouseenter, mouseleave if (handler.e in hover) fn = function(e){ var related = e.relatedTarget if (!related || (related !== this && !$.contains(this, related))) return handler.fn.apply(this, arguments) } handler.del = getDelegate && getDelegate(fn, event) var callback = handler.del || fn handler.proxy = function (e) { var result = callback.apply(element, [e].concat(e.data)) if (result === false) e.preventDefault(), e.stopPropagation() return result } handler.i = set.length set.push(handler) element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) }) } function remove(element, events, fn, selector, capture){ var id = zid(element) eachEvent(events || '', fn, function(event, fn){ findHandlers(element, event, fn, selector).forEach(function(handler){ delete handlers[id][handler.i] element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) }) }) } $.event = { add: add, remove: remove } $.proxy = function(fn, context) { if ($.isFunction(fn)) { var proxyFn = function(){ return fn.apply(context, arguments) } proxyFn._zid = zid(fn) return proxyFn } else if (typeof context == 'string') { return $.proxy(fn[context], fn) } else { throw new TypeError("expected function") } } $.fn.bind = function(event, callback){ return this.each(function(){ add(this, event, callback) }) } $.fn.unbind = function(event, callback){ return this.each(function(){ remove(this, event, callback) }) } $.fn.one = function(event, callback){ return this.each(function(i, element){ add(this, event, callback, null, function(fn, type){ return function(){ var result = fn.apply(element, arguments) remove(element, type, fn) return result } }) }) } var returnTrue = function(){return true}, returnFalse = function(){return false}, ignoreProperties = /^([A-Z]|layer[XY]$)/, eventMethods = { preventDefault: 'isDefaultPrevented', stopImmediatePropagation: 'isImmediatePropagationStopped', stopPropagation: 'isPropagationStopped' } function createProxy(event) { var key, proxy = { originalEvent: event } for (key in event) if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] $.each(eventMethods, function(name, predicate) { proxy[name] = function(){ this[predicate] = returnTrue return event[name].apply(event, arguments) } proxy[predicate] = returnFalse }) return proxy } // emulates the 'defaultPrevented' property for browsers that have none function fix(event) { if (!('defaultPrevented' in event)) { event.defaultPrevented = false var prevent = event.preventDefault event.preventDefault = function() { this.defaultPrevented = true prevent.call(this) } } } $.fn.delegate = function(selector, event, callback){ return this.each(function(i, element){ add(element, event, callback, selector, function(fn){ return function(e){ var evt, match = $(e.target).closest(selector, element).get(0) if (match) { evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) return fn.apply(match, [evt].concat([].slice.call(arguments, 1))) } } }) }) } $.fn.undelegate = function(selector, event, callback){ return this.each(function(){ remove(this, event, callback, selector) }) } $.fn.live = function(event, callback){ $(document.body).delegate(this.selector, event, callback) return this } $.fn.die = function(event, callback){ $(document.body).undelegate(this.selector, event, callback) return this } $.fn.on = function(event, selector, callback){ return !selector || $.isFunction(selector) ? this.bind(event, selector || callback) : this.delegate(selector, event, callback) } $.fn.off = function(event, selector, callback){ return !selector || $.isFunction(selector) ? this.unbind(event, selector || callback) : this.undelegate(selector, event, callback) } $.fn.trigger = function(event, data){ if (typeof event == 'string' || $.isPlainObject(event)) event = $.Event(event) fix(event) event.data = data return this.each(function(){ // items in the collection might not be DOM elements // (todo: possibly support events on plain old objects) if('dispatchEvent' in this) this.dispatchEvent(event) }) } // triggers event handlers on current element just as if an event occurred, // doesn't trigger an actual event, doesn't bubble $.fn.triggerHandler = function(event, data){ var e, result this.each(function(i, element){ e = createProxy(typeof event == 'string' ? $.Event(event) : event) e.data = data e.target = element $.each(findHandlers(element, event.type || event), function(i, handler){ result = handler.proxy(e) if (e.isImmediatePropagationStopped()) return false }) }) return result } // shortcut methods for `.bind(event, fn)` for each event type ;('focusin focusout load resize scroll unload click dblclick '+ 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ 'change select keydown keypress keyup error').split(' ').forEach(function(event) { $.fn[event] = function(callback) { return callback ? this.bind(event, callback) : this.trigger(event) } }) ;['focus', 'blur'].forEach(function(name) { $.fn[name] = function(callback) { if (callback) this.bind(name, callback) else this.each(function(){ try { this[name]() } catch(e) {} }) return this } }) $.Event = function(type, props) { if (typeof type != 'string') props = type, type = props.type var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null) event.isDefaultPrevented = function(){ return this.defaultPrevented } return event } })(Zepto)