text: ﻿(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'
  
  });
  
  })();