(function(){ [Element, Window, Document].invoke('implement', {hasEvent: function(event){ var events = this.retrieve('events'), list = (events && events[event]) ? events[event].values : null; if (list){ for (var i = list.length; i--;) if (i in list){ return true; } } return false; }}); var wrap = function(custom, method, extended, name){ method = custom[method]; extended = custom[extended]; return function(fn, customName){ if (!customName) customName = name; if (extended && !this.hasEvent(customName)) extended.call(this, fn, customName); if (method) method.call(this, fn, customName); }; }; var inherit = function(custom, base, method, name){ return function(fn, customName){ base[method].call(this, fn, customName || name); custom[method].call(this, fn, customName || name); }; }; var events = Element.Events; Element.defineCustomEvent = function(name, custom){ var base = events[custom.base]; custom.onAdd = wrap(custom, 'onAdd', 'onSetup', name); custom.onRemove = wrap(custom, 'onRemove', 'onTeardown', name); events[name] = base ? Object.append({}, custom, { base: base.base, condition: function(event){ return (!base.condition || base.condition.call(this, event)) && (!custom.condition || custom.condition.call(this, event)); }, onAdd: inherit(custom, base, 'onAdd', name), onRemove: inherit(custom, base, 'onRemove', name) }) : custom; return this; }; var loop = function(name){ var method = 'on' + name.capitalize(); Element[name + 'CustomEvents'] = function(){ Object.each(events, function(event, name){ if (event[method]) event[method].call(event, name); }); }; return loop; }; loop('enable')('disable'); })(); (function(){ Browser.Device = { name: 'other' }; if (Browser.Platform.ios){ var device = navigator.userAgent.toLowerCase().match(/(ip(ad|od|hone))/)[0]; Browser.Device[device] = true; Browser.Device.name = device; } if (this.devicePixelRatio == 2) Browser.hasHighResolution = true; Browser.isMobile = !['mac', 'linux', 'win'].contains(Browser.Platform.name); }).call(this); Browser.Features.Touch = (function(){ try { document.createEvent('TouchEvent').initTouchEvent('touchstart'); return true; } catch (exception){} return false; })(); // Android doesn't have a touch delay and dispatchEvent does not fire the handler Browser.Features.iOSTouch = (function(){ var name = 'cantouch', // Name does not matter html = document.html, hasTouch = false; if (!html.addEventListener) return false; var handler = function(){ html.removeEventListener(name, handler, true); hasTouch = true; }; try { html.addEventListener(name, handler, true); var event = document.createEvent('TouchEvent'); event.initTouchEvent(name); html.dispatchEvent(event); return hasTouch; } catch (exception){} handler(); // Remove listener return false; })(); (function(){ var preventDefault = function(event){ if (!event.target || event.target.tagName.toLowerCase() != 'select') event.preventDefault(); }; var disabled; Element.defineCustomEvent('touch', { base: 'touchend', condition: function(event){ if (disabled || event.targetTouches.length != 0) return false; var touch = event.changedTouches[0], target = document.elementFromPoint(touch.clientX, touch.clientY); do { if (target == this) return true; } while (target && (target = target.parentNode)); return false; }, onSetup: function(){ this.addEvent('touchstart', preventDefault); }, onTeardown: function(){ this.removeEvent('touchstart', preventDefault); }, onEnable: function(){ disabled = false; }, onDisable: function(){ disabled = true; } }); })(); if (Browser.Features.iOSTouch) (function(){ var name = 'click'; delete Element.NativeEvents[name]; Element.defineCustomEvent(name, { base: 'touch' }); })();