UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

205 lines (167 loc) 5.5 kB
/* --- name: Element.Delegation description: Extends the Element native object to include the delegate method for more efficient event management. license: MIT-style license. requires: [Element.Event] provides: [Element.Delegation] ... */ (function(){ var eventListenerSupport = !!window.addEventListener; Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2; var bubbleUp = function(self, match, fn, event, target){ while (target && target != self){ if (match(target, event)) return fn.call(target, event, target); target = document.id(target.parentNode); } }; var map = { mouseenter: { base: 'mouseover', condition: Element.MouseenterCheck }, mouseleave: { base: 'mouseout', condition: Element.MouseenterCheck }, focus: { base: 'focus' + (eventListenerSupport ? '' : 'in'), capture: true }, blur: { base: eventListenerSupport ? 'blur' : 'focusout', capture: true } }; /*<ltIE9>*/ var _key = '$delegation:'; var formObserver = function(type){ return { base: 'focusin', remove: function(self, uid){ var list = self.retrieve(_key + type + 'listeners', {})[uid]; if (list && list.forms) for (var i = list.forms.length; i--;){ // the form may have been destroyed, so it won't have the // removeEvent method anymore. In that case the event was // removed as well. if (list.forms[i].removeEvent) list.forms[i].removeEvent(type, list.fns[i]); } }, listen: function(self, match, fn, event, target, uid){ var form = (target.get('tag') == 'form') ? target : event.target.getParent('form'); if (!form) return; var listeners = self.retrieve(_key + type + 'listeners', {}), listener = listeners[uid] || {forms: [], fns: []}, forms = listener.forms, fns = listener.fns; if (forms.indexOf(form) != -1) return; forms.push(form); var _fn = function(event){ bubbleUp(self, match, fn, event, target); }; form.addEvent(type, _fn); fns.push(_fn); listeners[uid] = listener; self.store(_key + type + 'listeners', listeners); } }; }; var inputObserver = function(type){ return { base: 'focusin', listen: function(self, match, fn, event, target){ var events = {blur: function(){ this.removeEvents(events); }}; events[type] = function(event){ bubbleUp(self, match, fn, event, target); }; event.target.addEvents(events); } }; }; if (!eventListenerSupport) Object.append(map, { submit: formObserver('submit'), reset: formObserver('reset'), change: inputObserver('change'), select: inputObserver('select') }); /*</ltIE9>*/ var proto = Element.prototype, addEvent = proto.addEvent, removeEvent = proto.removeEvent; var relay = function(old, method){ return function(type, fn, useCapture){ if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture); var parsed = Slick.parse(type).expressions[0][0]; if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture); var newType = parsed.tag; parsed.pseudos.slice(1).each(function(pseudo){ newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : ''); }); old.call(this, type, fn); return method.call(this, newType, parsed.pseudos[0].value, fn); }; }; var delegation = { addEvent: function(type, match, fn){ var storage = this.retrieve('$delegates', {}), stored = storage[type]; if (stored) for (var _uid in stored){ if (stored[_uid].fn == fn && stored[_uid].match == match) return this; } var _type = type, _match = match, _fn = fn, _map = map[type] || {}; type = _map.base || _type; match = function(target){ return Slick.match(target, _match); }; var elementEvent = Element.Events[_type]; if (_map.condition || elementEvent && elementEvent.condition){ var __match = match, condition = _map.condition || elementEvent.condition; match = function(target, event){ return __match(target, event) && condition.call(target, event, type); }; } var self = this, uid = String.uniqueID(); var delegator = _map.listen ? function(event, target){ if (!target && event && event.target) target = event.target; if (target) _map.listen(self, match, fn, event, target, uid); } : function(event, target){ if (!target && event && event.target) target = event.target; if (target) bubbleUp(self, match, fn, event, target); }; if (!stored) stored = {}; stored[uid] = { match: _match, fn: _fn, delegator: delegator }; storage[_type] = stored; return addEvent.call(this, type, delegator, _map.capture); }, removeEvent: function(type, match, fn, _uid){ var storage = this.retrieve('$delegates', {}), stored = storage[type]; if (!stored) return this; if (_uid){ var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {}; type = _map.base || _type; if (_map.remove) _map.remove(this, _uid); delete stored[_uid]; storage[_type] = stored; return removeEvent.call(this, type, delegator, _map.capture); } var __uid, s; if (fn) for (__uid in stored){ s = stored[__uid]; if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid); } else for (__uid in stored){ s = stored[__uid]; if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid); } return this; } }; [Element, Window, Document].invoke('implement', { addEvent: relay(addEvent, delegation.addEvent), removeEvent: relay(removeEvent, delegation.removeEvent) }); })();