Difference between revisions of "Team:CityU HK/Template/js"

(Created page with "<html> <script> /* * jQuery 1.2.6 - New Wave Javascript * * Copyright (c) 2008 John Resig (jquery.com) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENS...")
 
 
(13 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<html>
 
<script>
 
  
/*
+
/****************************
* jQuery 1.2.6 - New Wave Javascript
+
    custom js
 +
*****************************/
 +
jQuery(function($) {
 +
 
 +
  // Mobile sidebars
 +
  $.fn.expandableSidebar = function(expandedClass) {
 +
    var $me = this;
 +
 
 +
    $me.on('click', function() {
 +
      if(!$me.hasClass(expandedClass)) {
 +
        $me.addClass(expandedClass);
 +
      } else {
 +
        $me.removeClass(expandedClass);
 +
      }
 +
    });
 +
  }
 +
 
 +
  // Interval loop
 +
  $.fn.intervalLoop = function(condition, action, duration, limit) {
 +
    var counter = 0;
 +
    var looper = setInterval(function(){
 +
      if (counter >= limit || $.fn.checkIfElementExists(condition)) {
 +
        clearInterval(looper);
 +
      } else {
 +
        action();
 +
        counter++;
 +
      }
 +
    }, duration);
 +
  }
 +
 
 +
  // Check if element exists
 +
  $.fn.checkIfElementExists = function(selector) {
 +
    return $(selector).length;
 +
  }
 +
 
 +
  var parisController = {
 +
 
 +
    init: function(opts) {
 +
      var base = this;
 +
 
 +
      // Add classes to elements
 +
      base._addClasses();
 +
 
 +
      setTimeout(function(){
 +
        base._checkCartItems();
 +
        base._attachEvents();
 +
      }, 1000);
 +
    },
 +
 
 +
    _addClasses: function() {
 +
      var base = this;
 +
 
 +
      // Add fade in class to nav + logo
 +
      $('.landing-page').addClass('fade-in');
 +
 
 +
      // Add class to nav items with subnav
 +
      $('.wsite-menu-default').find('li.wsite-menu-item-wrap').each(function(){
 +
        var $me = $(this);
 +
 
 +
        if($me.children('.wsite-menu-wrap').length > 0) {
 +
 
 +
          $me.addClass('has-submenu');
 +
          $('<span class="icon-caret"></span>').insertAfter($me.children('a.wsite-menu-item'));
 +
        }
 +
      });
 +
 
 +
      // ADd class to subnav items with subnav
 +
      $('.wsite-menu').find('li.wsite-menu-subitem-wrap').each(function(){
 +
        var $me = $(this);
 +
 
 +
        if($me.children('.wsite-menu-wrap').length > 0) {
 +
 
 +
          $me.addClass('has-submenu');
 +
          $('<span class="icon-caret"></span>').insertAfter($me.children('a.wsite-menu-subitem'));
 +
        }
 +
      });
 +
 
 +
      // Keep subnav open if submenu item is active
 +
      $('.wsite-menu-wrap').find('li.wsite-menu-subitem-wrap').each(function(){
 +
        var $me = $(this);
 +
 
 +
        if($me.hasClass('wsite-nav-current')) {
 +
          $me.parents().addClass('open');
 +
        }
 +
      });
 +
 
 +
      // Add placeholder text to inputs
 +
      $('.wsite-form-sublabel').each(function(){
 +
        var sublabel = $(this).text();
 +
        $(this).prev('.wsite-form-input').attr('placeholder', sublabel);
 +
      });
 +
 
 +
      // Add fullwidth class to gallery thumbs if less than 6
 +
      $('.imageGallery').each(function(){
 +
        if ($(this).children('div').length <= 6) {
 +
          $(this).children('div').addClass('fullwidth-mobile');
 +
        }
 +
      });
 +
    },
 +
 
 +
    _checkCartItems: function() {
 +
      var base = this;
 +
     
 +
      if($('#wsite-mini-cart').find('li.wsite-product-item').length > 0) {
 +
        $('body').addClass('cart-full');
 +
      } else {
 +
        $('body').removeClass('cart-full');
 +
      }
 +
    },
 +
 
 +
    _moveLogin: function() {
 +
      var loginDetach = $('#member-login').detach();
 +
      $('.mobile-nav .wsite-menu-default li:last-child').after(loginDetach);
 +
    },
 +
 
 +
    _attachEvents: function() {
 +
      var base = this;
 +
 
 +
      // Move cart + login
 +
      if ($(window).width() <= 992) {
 +
        $.fn.intervalLoop('.mobile-nav #member-login', base._moveLogin, 800, 5);
 +
      }
 +
 
 +
      // Subnav toggle
 +
      $('li.has-submenu').each(function(){
 +
        var $me = $(this);
 +
        var caret = $me.children('span.icon-caret');
 +
 
 +
        caret.on('click', function(){         
 +
          if($me.children('.wsite-menu-wrap.open').length > 0) {
 +
            caret.siblings('.wsite-menu-wrap').removeClass('open');
 +
          } else {
 +
            caret.siblings('.wsite-menu-wrap').addClass('open');
 +
          }
 +
        });
 +
      });
 +
 
 +
      // Scroll on landing page
 +
      $('#contentArrow').on('click', function() {
 +
        $('html, body').animate({
 +
          scrollTop: $('.main-wrap').offset().top - $('.paris-header').outerHeight()
 +
        }, 500);
 +
      });
 +
 
 +
      // Store category dropdown
 +
      $('.wsite-com-sidebar').expandableSidebar('sidebar-expanded');
 +
 
 +
      // Search filters dropdown
 +
      $('#wsite-search-sidebar').expandableSidebar('sidebar-expanded');
 +
 
 +
      // Init fancybox swipe on mobile
 +
      if ('ontouchstart' in window) {
 +
        $('body').on('click', 'a.w-fancybox', function() {
 +
          base._initSwipeGallery();
 +
        });
 +
      }
 +
    },
 +
 
 +
    _initSwipeGallery: function() {
 +
      var base = this;
 +
 
 +
      setTimeout(function(){
 +
        var touchGallery = document.getElementsByClassName('fancybox-wrap')[0];
 +
        var mc = new Hammer(touchGallery);
 +
        mc.on("panleft panright", function(ev) {
 +
          if (ev.type == "panleft") {
 +
            $("a.fancybox-next").trigger("click");
 +
          } else if (ev.type == "panright") {
 +
            $("a.fancybox-prev").trigger("click");
 +
          }
 +
          base._initSwipeGallery();
 +
        });
 +
      }, 500);
 +
    }
 +
  }
 +
 
 +
 
 +
  $(document).ready(function(){
 +
    parisController.init();
 +
  });
 +
});
 +
 
 +
/***********************************
 +
    Plugin js
 +
***********************************/
 +
 
 +
/*! Hammer.JS - v2.0.4 - 2014-09-28
 +
* http://hammerjs.github.io/
 
  *
 
  *
  * Copyright (c) 2008 John Resig (jquery.com)
+
  * Copyright (c) 2014 Jorik Tangelder;
  * Dual licensed under the MIT (MIT-LICENSE.txt)
+
* Licensed under the MIT license */
  * and GPL (GPL-LICENSE.txt) licenses.
+
(function(window, document, exportName, undefined) {
 +
  'use strict';
 +
 
 +
var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o'];
 +
var TEST_ELEMENT = document.createElement('div');
 +
 
 +
var TYPE_FUNCTION = 'function';
 +
 
 +
var round = Math.round;
 +
var abs = Math.abs;
 +
var now = Date.now;
 +
 
 +
/**
 +
  * set a timeout with a given scope
 +
* @param {Function} fn
 +
* @param {Number} timeout
 +
* @param {Object} context
 +
* @returns {number}
 +
*/
 +
function setTimeoutContext(fn, timeout, context) {
 +
    return setTimeout(bindFn(fn, context), timeout);
 +
}
 +
 
 +
/**
 +
* if the argument is an array, we want to execute the fn on each entry
 +
* if it aint an array we don't want to do a thing.
 +
* this is used by all the methods that accept a single and array argument.
 +
* @param {*|Array} arg
 +
* @param {String} fn
 +
* @param {Object} [context]
 +
* @returns {Boolean}
 +
*/
 +
function invokeArrayArg(arg, fn, context) {
 +
    if (Array.isArray(arg)) {
 +
        each(arg, context[fn], context);
 +
        return true;
 +
    }
 +
    return false;
 +
}
 +
 
 +
/**
 +
* walk objects and arrays
 +
* @param {Object} obj
 +
* @param {Function} iterator
 +
* @param {Object} context
 +
*/
 +
function each(obj, iterator, context) {
 +
    var i;
 +
 
 +
    if (!obj) {
 +
        return;
 +
    }
 +
 
 +
    if (obj.forEach) {
 +
        obj.forEach(iterator, context);
 +
    } else if (obj.length !== undefined) {
 +
        i = 0;
 +
        while (i < obj.length) {
 +
            iterator.call(context, obj[i], i, obj);
 +
            i++;
 +
        }
 +
    } else {
 +
        for (i in obj) {
 +
            obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
 +
        }
 +
    }
 +
}
 +
 
 +
/**
 +
* extend object.
 +
* means that properties in dest will be overwritten by the ones in src.
 +
* @param {Object} dest
 +
* @param {Object} src
 +
* @param {Boolean} [merge]
 +
* @returns {Object} dest
 +
*/
 +
function extend(dest, src, merge) {
 +
    var keys = Object.keys(src);
 +
    var i = 0;
 +
    while (i < keys.length) {
 +
        if (!merge || (merge && dest[keys[i]] === undefined)) {
 +
            dest[keys[i]] = src[keys[i]];
 +
        }
 +
        i++;
 +
    }
 +
    return dest;
 +
}
 +
 
 +
/**
 +
* merge the values from src in the dest.
 +
* means that properties that exist in dest will not be overwritten by src
 +
* @param {Object} dest
 +
* @param {Object} src
 +
* @returns {Object} dest
 +
*/
 +
function merge(dest, src) {
 +
    return extend(dest, src, true);
 +
}
 +
 
 +
/**
 +
* simple class inheritance
 +
* @param {Function} child
 +
* @param {Function} base
 +
* @param {Object} [properties]
 +
*/
 +
function inherit(child, base, properties) {
 +
    var baseP = base.prototype,
 +
        childP;
 +
 
 +
    childP = child.prototype = Object.create(baseP);
 +
    childP.constructor = child;
 +
    childP._super = baseP;
 +
 
 +
    if (properties) {
 +
        extend(childP, properties);
 +
    }
 +
}
 +
 
 +
/**
 +
* simple function bind
 +
* @param {Function} fn
 +
* @param {Object} context
 +
* @returns {Function}
 +
*/
 +
function bindFn(fn, context) {
 +
    return function boundFn() {
 +
        return fn.apply(context, arguments);
 +
    };
 +
}
 +
 
 +
/**
 +
* let a boolean value also be a function that must return a boolean
 +
* this first item in args will be used as the context
 +
* @param {Boolean|Function} val
 +
* @param {Array} [args]
 +
* @returns {Boolean}
 +
*/
 +
function boolOrFn(val, args) {
 +
    if (typeof val == TYPE_FUNCTION) {
 +
        return val.apply(args ? args[0] || undefined : undefined, args);
 +
    }
 +
    return val;
 +
}
 +
 
 +
/**
 +
* use the val2 when val1 is undefined
 +
* @param {*} val1
 +
* @param {*} val2
 +
* @returns {*}
 +
*/
 +
function ifUndefined(val1, val2) {
 +
    return (val1 === undefined) ? val2 : val1;
 +
}
 +
 
 +
/**
 +
* addEventListener with multiple events at once
 +
* @param {EventTarget} target
 +
* @param {String} types
 +
* @param {Function} handler
 +
*/
 +
function addEventListeners(target, types, handler) {
 +
    each(splitStr(types), function(type) {
 +
        target.addEventListener(type, handler, false);
 +
    });
 +
}
 +
 
 +
/**
 +
* removeEventListener with multiple events at once
 +
* @param {EventTarget} target
 +
* @param {String} types
 +
* @param {Function} handler
 +
*/
 +
function removeEventListeners(target, types, handler) {
 +
    each(splitStr(types), function(type) {
 +
        target.removeEventListener(type, handler, false);
 +
    });
 +
}
 +
 
 +
/**
 +
* find if a node is in the given parent
 +
* @method hasParent
 +
* @param {HTMLElement} node
 +
* @param {HTMLElement} parent
 +
* @return {Boolean} found
 +
*/
 +
function hasParent(node, parent) {
 +
    while (node) {
 +
        if (node == parent) {
 +
            return true;
 +
        }
 +
        node = node.parentNode;
 +
    }
 +
    return false;
 +
}
 +
 
 +
/**
 +
* small indexOf wrapper
 +
* @param {String} str
 +
* @param {String} find
 +
* @returns {Boolean} found
 +
*/
 +
function inStr(str, find) {
 +
    return str.indexOf(find) > -1;
 +
}
 +
 
 +
/**
 +
* split string on whitespace
 +
* @param {String} str
 +
* @returns {Array} words
 +
*/
 +
function splitStr(str) {
 +
    return str.trim().split(/\s+/g);
 +
}
 +
 
 +
/**
 +
  * find if a array contains the object using indexOf or a simple polyFill
 +
* @param {Array} src
 +
* @param {String} find
 +
* @param {String} [findByKey]
 +
* @return {Boolean|Number} false when not found, or the index
 +
*/
 +
function inArray(src, find, findByKey) {
 +
    if (src.indexOf && !findByKey) {
 +
        return src.indexOf(find);
 +
    } else {
 +
        var i = 0;
 +
        while (i < src.length) {
 +
            if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
 +
                return i;
 +
            }
 +
            i++;
 +
        }
 +
        return -1;
 +
    }
 +
}
 +
 
 +
/**
 +
* convert array-like objects to real arrays
 +
* @param {Object} obj
 +
* @returns {Array}
 +
*/
 +
function toArray(obj) {
 +
    return Array.prototype.slice.call(obj, 0);
 +
}
 +
 
 +
/**
 +
* unique array with objects based on a key (like 'id') or just by the array's value
 +
* @param {Array} src [{id:1},{id:2},{id:1}]
 +
* @param {String} [key]
 +
* @param {Boolean} [sort=False]
 +
* @returns {Array} [{id:1},{id:2}]
 +
*/
 +
function uniqueArray(src, key, sort) {
 +
    var results = [];
 +
    var values = [];
 +
    var i = 0;
 +
 
 +
    while (i < src.length) {
 +
        var val = key ? src[i][key] : src[i];
 +
        if (inArray(values, val) < 0) {
 +
            results.push(src[i]);
 +
        }
 +
        values[i] = val;
 +
        i++;
 +
    }
 +
 
 +
    if (sort) {
 +
        if (!key) {
 +
            results = results.sort();
 +
        } else {
 +
            results = results.sort(function sortUniqueArray(a, b) {
 +
                return a[key] > b[key];
 +
            });
 +
        }
 +
    }
 +
 
 +
    return results;
 +
}
 +
 
 +
/**
 +
* get the prefixed property
 +
* @param {Object} obj
 +
* @param {String} property
 +
* @returns {String|Undefined} prefixed
 +
*/
 +
function prefixed(obj, property) {
 +
    var prefix, prop;
 +
    var camelProp = property[0].toUpperCase() + property.slice(1);
 +
 
 +
    var i = 0;
 +
    while (i < VENDOR_PREFIXES.length) {
 +
        prefix = VENDOR_PREFIXES[i];
 +
        prop = (prefix) ? prefix + camelProp : property;
 +
 
 +
        if (prop in obj) {
 +
            return prop;
 +
        }
 +
        i++;
 +
    }
 +
    return undefined;
 +
}
 +
 
 +
/**
 +
* get a unique id
 +
* @returns {number} uniqueId
 +
*/
 +
var _uniqueId = 1;
 +
function uniqueId() {
 +
    return _uniqueId++;
 +
}
 +
 
 +
/**
 +
* get the window object of an element
 +
* @param {HTMLElement} element
 +
* @returns {DocumentView|Window}
 +
*/
 +
function getWindowForElement(element) {
 +
    var doc = element.ownerDocument;
 +
    return (doc.defaultView || doc.parentWindow);
 +
}
 +
 
 +
var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
 +
 
 +
var SUPPORT_TOUCH = ('ontouchstart' in window);
 +
var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
 +
var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
 +
 
 +
var INPUT_TYPE_TOUCH = 'touch';
 +
var INPUT_TYPE_PEN = 'pen';
 +
var INPUT_TYPE_MOUSE = 'mouse';
 +
var INPUT_TYPE_KINECT = 'kinect';
 +
 
 +
var COMPUTE_INTERVAL = 25;
 +
 
 +
var INPUT_START = 1;
 +
var INPUT_MOVE = 2;
 +
var INPUT_END = 4;
 +
var INPUT_CANCEL = 8;
 +
 
 +
var DIRECTION_NONE = 1;
 +
var DIRECTION_LEFT = 2;
 +
var DIRECTION_RIGHT = 4;
 +
var DIRECTION_UP = 8;
 +
var DIRECTION_DOWN = 16;
 +
 
 +
var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
 +
var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
 +
var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
 +
 
 +
var PROPS_XY = ['x', 'y'];
 +
var PROPS_CLIENT_XY = ['clientX', 'clientY'];
 +
 
 +
/**
 +
* create new input type manager
 +
* @param {Manager} manager
 +
* @param {Function} callback
 +
* @returns {Input}
 +
* @constructor
 +
*/
 +
function Input(manager, callback) {
 +
    var self = this;
 +
    this.manager = manager;
 +
    this.callback = callback;
 +
    this.element = manager.element;
 +
    this.target = manager.options.inputTarget;
 +
 
 +
    // smaller wrapper around the handler, for the scope and the enabled state of the manager,
 +
    // so when disabled the input events are completely bypassed.
 +
    this.domHandler = function(ev) {
 +
        if (boolOrFn(manager.options.enable, [manager])) {
 +
            self.handler(ev);
 +
        }
 +
    };
 +
 
 +
    this.init();
 +
 
 +
}
 +
 
 +
Input.prototype = {
 +
    /**
 +
    * should handle the inputEvent data and trigger the callback
 +
    * @virtual
 +
    */
 +
    handler: function() { },
 +
 
 +
    /**
 +
    * bind the events
 +
    */
 +
    init: function() {
 +
        this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
 +
        this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
 +
        this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
 +
    },
 +
 
 +
    /**
 +
    * unbind the events
 +
    */
 +
    destroy: function() {
 +
        this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
 +
        this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
 +
        this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
 +
    }
 +
};
 +
 
 +
/**
 +
* create new input type manager
 +
* called by the Manager constructor
 +
* @param {Hammer} manager
 +
* @returns {Input}
 +
*/
 +
function createInputInstance(manager) {
 +
    var Type;
 +
    var inputClass = manager.options.inputClass;
 +
 
 +
    if (inputClass) {
 +
        Type = inputClass;
 +
    } else if (SUPPORT_POINTER_EVENTS) {
 +
        Type = PointerEventInput;
 +
    } else if (SUPPORT_ONLY_TOUCH) {
 +
        Type = TouchInput;
 +
    } else if (!SUPPORT_TOUCH) {
 +
        Type = MouseInput;
 +
    } else {
 +
        Type = TouchMouseInput;
 +
    }
 +
    return new (Type)(manager, inputHandler);
 +
}
 +
 
 +
/**
 +
* handle input events
 +
* @param {Manager} manager
 +
* @param {String} eventType
 +
* @param {Object} input
 +
*/
 +
function inputHandler(manager, eventType, input) {
 +
    var pointersLen = input.pointers.length;
 +
    var changedPointersLen = input.changedPointers.length;
 +
    var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
 +
    var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
 +
 
 +
    input.isFirst = !!isFirst;
 +
    input.isFinal = !!isFinal;
 +
 
 +
    if (isFirst) {
 +
        manager.session = {};
 +
    }
 +
 
 +
    // source event is the normalized value of the domEvents
 +
    // like 'touchstart, mouseup, pointerdown'
 +
    input.eventType = eventType;
 +
 
 +
    // compute scale, rotation etc
 +
    computeInputData(manager, input);
 +
 
 +
    // emit secret event
 +
    manager.emit('hammer.input', input);
 +
 
 +
    manager.recognize(input);
 +
    manager.session.prevInput = input;
 +
}
 +
 
 +
/**
 +
* extend the data with some usable properties like scale, rotate, velocity etc
 +
* @param {Object} manager
 +
* @param {Object} input
 +
*/
 +
function computeInputData(manager, input) {
 +
    var session = manager.session;
 +
    var pointers = input.pointers;
 +
    var pointersLength = pointers.length;
 +
 
 +
    // store the first input to calculate the distance and direction
 +
    if (!session.firstInput) {
 +
        session.firstInput = simpleCloneInputData(input);
 +
    }
 +
 
 +
    // to compute scale and rotation we need to store the multiple touches
 +
    if (pointersLength > 1 && !session.firstMultiple) {
 +
        session.firstMultiple = simpleCloneInputData(input);
 +
    } else if (pointersLength === 1) {
 +
        session.firstMultiple = false;
 +
    }
 +
 
 +
    var firstInput = session.firstInput;
 +
    var firstMultiple = session.firstMultiple;
 +
    var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
 +
 
 +
    var center = input.center = getCenter(pointers);
 +
    input.timeStamp = now();
 +
    input.deltaTime = input.timeStamp - firstInput.timeStamp;
 +
 
 +
    input.angle = getAngle(offsetCenter, center);
 +
    input.distance = getDistance(offsetCenter, center);
 +
 
 +
    computeDeltaXY(session, input);
 +
    input.offsetDirection = getDirection(input.deltaX, input.deltaY);
 +
 
 +
    input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
 +
    input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
 +
 
 +
    computeIntervalInputData(session, input);
 +
 
 +
    // find the correct target
 +
    var target = manager.element;
 +
    if (hasParent(input.srcEvent.target, target)) {
 +
        target = input.srcEvent.target;
 +
    }
 +
    input.target = target;
 +
}
 +
 
 +
function computeDeltaXY(session, input) {
 +
    var center = input.center;
 +
    var offset = session.offsetDelta || {};
 +
    var prevDelta = session.prevDelta || {};
 +
    var prevInput = session.prevInput || {};
 +
 
 +
    if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
 +
        prevDelta = session.prevDelta = {
 +
            x: prevInput.deltaX || 0,
 +
            y: prevInput.deltaY || 0
 +
        };
 +
 
 +
        offset = session.offsetDelta = {
 +
            x: center.x,
 +
            y: center.y
 +
        };
 +
    }
 +
 
 +
    input.deltaX = prevDelta.x + (center.x - offset.x);
 +
    input.deltaY = prevDelta.y + (center.y - offset.y);
 +
}
 +
 
 +
/**
 +
* velocity is calculated every x ms
 +
* @param {Object} session
 +
* @param {Object} input
 +
*/
 +
function computeIntervalInputData(session, input) {
 +
    var last = session.lastInterval || input,
 +
        deltaTime = input.timeStamp - last.timeStamp,
 +
        velocity, velocityX, velocityY, direction;
 +
 
 +
    if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
 +
        var deltaX = last.deltaX - input.deltaX;
 +
        var deltaY = last.deltaY - input.deltaY;
 +
 
 +
        var v = getVelocity(deltaTime, deltaX, deltaY);
 +
        velocityX = v.x;
 +
        velocityY = v.y;
 +
        velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
 +
        direction = getDirection(deltaX, deltaY);
 +
 
 +
        session.lastInterval = input;
 +
    } else {
 +
        // use latest velocity info if it doesn't overtake a minimum period
 +
        velocity = last.velocity;
 +
        velocityX = last.velocityX;
 +
        velocityY = last.velocityY;
 +
        direction = last.direction;
 +
    }
 +
 
 +
    input.velocity = velocity;
 +
    input.velocityX = velocityX;
 +
    input.velocityY = velocityY;
 +
    input.direction = direction;
 +
}
 +
 
 +
/**
 +
* create a simple clone from the input used for storage of firstInput and firstMultiple
 +
* @param {Object} input
 +
* @returns {Object} clonedInputData
 +
*/
 +
function simpleCloneInputData(input) {
 +
    // make a simple copy of the pointers because we will get a reference if we don't
 +
    // we only need clientXY for the calculations
 +
    var pointers = [];
 +
    var i = 0;
 +
    while (i < input.pointers.length) {
 +
        pointers[i] = {
 +
            clientX: round(input.pointers[i].clientX),
 +
            clientY: round(input.pointers[i].clientY)
 +
        };
 +
        i++;
 +
    }
 +
 
 +
    return {
 +
        timeStamp: now(),
 +
        pointers: pointers,
 +
        center: getCenter(pointers),
 +
        deltaX: input.deltaX,
 +
        deltaY: input.deltaY
 +
    };
 +
}
 +
 
 +
/**
 +
* get the center of all the pointers
 +
* @param {Array} pointers
 +
* @return {Object} center contains `x` and `y` properties
 +
*/
 +
function getCenter(pointers) {
 +
    var pointersLength = pointers.length;
 +
 
 +
    // no need to loop when only one touch
 +
    if (pointersLength === 1) {
 +
        return {
 +
            x: round(pointers[0].clientX),
 +
            y: round(pointers[0].clientY)
 +
        };
 +
    }
 +
 
 +
    var x = 0, y = 0, i = 0;
 +
    while (i < pointersLength) {
 +
        x += pointers[i].clientX;
 +
        y += pointers[i].clientY;
 +
        i++;
 +
    }
 +
 
 +
    return {
 +
        x: round(x / pointersLength),
 +
        y: round(y / pointersLength)
 +
    };
 +
}
 +
 
 +
/**
 +
* calculate the velocity between two points. unit is in px per ms.
 +
* @param {Number} deltaTime
 +
* @param {Number} x
 +
* @param {Number} y
 +
* @return {Object} velocity `x` and `y`
 +
*/
 +
function getVelocity(deltaTime, x, y) {
 +
    return {
 +
        x: x / deltaTime || 0,
 +
        y: y / deltaTime || 0
 +
    };
 +
}
 +
 
 +
/**
 +
* get the direction between two points
 +
* @param {Number} x
 +
* @param {Number} y
 +
* @return {Number} direction
 +
*/
 +
function getDirection(x, y) {
 +
    if (x === y) {
 +
        return DIRECTION_NONE;
 +
    }
 +
 
 +
    if (abs(x) >= abs(y)) {
 +
        return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
 +
    }
 +
    return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;
 +
}
 +
 
 +
/**
 +
* calculate the absolute distance between two points
 +
* @param {Object} p1 {x, y}
 +
* @param {Object} p2 {x, y}
 +
* @param {Array} [props] containing x and y keys
 +
* @return {Number} distance
 +
*/
 +
function getDistance(p1, p2, props) {
 +
    if (!props) {
 +
        props = PROPS_XY;
 +
    }
 +
    var x = p2[props[0]] - p1[props[0]],
 +
        y = p2[props[1]] - p1[props[1]];
 +
 
 +
    return Math.sqrt((x * x) + (y * y));
 +
}
 +
 
 +
/**
 +
* calculate the angle between two coordinates
 +
* @param {Object} p1
 +
* @param {Object} p2
 +
* @param {Array} [props] containing x and y keys
 +
* @return {Number} angle
 +
*/
 +
function getAngle(p1, p2, props) {
 +
    if (!props) {
 +
        props = PROPS_XY;
 +
    }
 +
    var x = p2[props[0]] - p1[props[0]],
 +
        y = p2[props[1]] - p1[props[1]];
 +
    return Math.atan2(y, x) * 180 / Math.PI;
 +
}
 +
 
 +
/**
 +
* calculate the rotation degrees between two pointersets
 +
* @param {Array} start array of pointers
 +
* @param {Array} end array of pointers
 +
* @return {Number} rotation
 +
*/
 +
function getRotation(start, end) {
 +
    return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);
 +
}
 +
 
 +
/**
 +
* calculate the scale factor between two pointersets
 +
* no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
 +
* @param {Array} start array of pointers
 +
* @param {Array} end array of pointers
 +
* @return {Number} scale
 +
*/
 +
function getScale(start, end) {
 +
    return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
 +
}
 +
 
 +
var MOUSE_INPUT_MAP = {
 +
    mousedown: INPUT_START,
 +
    mousemove: INPUT_MOVE,
 +
    mouseup: INPUT_END
 +
};
 +
 
 +
var MOUSE_ELEMENT_EVENTS = 'mousedown';
 +
var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
 +
 
 +
/**
 +
* Mouse events input
 +
* @constructor
 +
* @extends Input
 +
*/
 +
function MouseInput() {
 +
    this.evEl = MOUSE_ELEMENT_EVENTS;
 +
    this.evWin = MOUSE_WINDOW_EVENTS;
 +
 
 +
    this.allow = true; // used by Input.TouchMouse to disable mouse events
 +
    this.pressed = false; // mousedown state
 +
 
 +
    Input.apply(this, arguments);
 +
}
 +
 
 +
inherit(MouseInput, Input, {
 +
    /**
 +
    * handle mouse events
 +
    * @param {Object} ev
 +
    */
 +
    handler: function MEhandler(ev) {
 +
        var eventType = MOUSE_INPUT_MAP[ev.type];
 +
 
 +
        // on start we want to have the left mouse button down
 +
        if (eventType & INPUT_START && ev.button === 0) {
 +
            this.pressed = true;
 +
        }
 +
 
 +
        if (eventType & INPUT_MOVE && ev.which !== 1) {
 +
            eventType = INPUT_END;
 +
        }
 +
 
 +
        // mouse must be down, and mouse events are allowed (see the TouchMouse input)
 +
        if (!this.pressed || !this.allow) {
 +
            return;
 +
        }
 +
 
 +
        if (eventType & INPUT_END) {
 +
            this.pressed = false;
 +
        }
 +
 
 +
        this.callback(this.manager, eventType, {
 +
            pointers: [ev],
 +
            changedPointers: [ev],
 +
            pointerType: INPUT_TYPE_MOUSE,
 +
            srcEvent: ev
 +
        });
 +
    }
 +
});
 +
 
 +
var POINTER_INPUT_MAP = {
 +
    pointerdown: INPUT_START,
 +
    pointermove: INPUT_MOVE,
 +
    pointerup: INPUT_END,
 +
    pointercancel: INPUT_CANCEL,
 +
    pointerout: INPUT_CANCEL
 +
};
 +
 
 +
// in IE10 the pointer types is defined as an enum
 +
var IE10_POINTER_TYPE_ENUM = {
 +
    2: INPUT_TYPE_TOUCH,
 +
    3: INPUT_TYPE_PEN,
 +
    4: INPUT_TYPE_MOUSE,
 +
    5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
 +
};
 +
 
 +
var POINTER_ELEMENT_EVENTS = 'pointerdown';
 +
var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
 +
 
 +
// IE10 has prefixed support, and case-sensitive
 +
if (window.MSPointerEvent) {
 +
    POINTER_ELEMENT_EVENTS = 'MSPointerDown';
 +
    POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
 +
}
 +
 
 +
/**
 +
* Pointer events input
 +
* @constructor
 +
* @extends Input
 +
*/
 +
function PointerEventInput() {
 +
    this.evEl = POINTER_ELEMENT_EVENTS;
 +
    this.evWin = POINTER_WINDOW_EVENTS;
 +
 
 +
    Input.apply(this, arguments);
 +
 
 +
    this.store = (this.manager.session.pointerEvents = []);
 +
}
 +
 
 +
inherit(PointerEventInput, Input, {
 +
    /**
 +
    * handle mouse events
 +
    * @param {Object} ev
 +
    */
 +
    handler: function PEhandler(ev) {
 +
        var store = this.store;
 +
        var removePointer = false;
 +
 
 +
        var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
 +
        var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
 +
        var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
 +
 
 +
        var isTouch = (pointerType == INPUT_TYPE_TOUCH);
 +
 
 +
        // get index of the event in the store
 +
        var storeIndex = inArray(store, ev.pointerId, 'pointerId');
 +
 
 +
        // start and mouse must be down
 +
        if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
 +
            if (storeIndex < 0) {
 +
                store.push(ev);
 +
                storeIndex = store.length - 1;
 +
            }
 +
        } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
 +
            removePointer = true;
 +
        }
 +
 
 +
        // it not found, so the pointer hasn't been down (so it's probably a hover)
 +
        if (storeIndex < 0) {
 +
            return;
 +
        }
 +
 
 +
        // update the event in the store
 +
        store[storeIndex] = ev;
 +
 
 +
        this.callback(this.manager, eventType, {
 +
            pointers: store,
 +
            changedPointers: [ev],
 +
            pointerType: pointerType,
 +
            srcEvent: ev
 +
        });
 +
 
 +
        if (removePointer) {
 +
            // remove from the store
 +
            store.splice(storeIndex, 1);
 +
        }
 +
    }
 +
});
 +
 
 +
var SINGLE_TOUCH_INPUT_MAP = {
 +
    touchstart: INPUT_START,
 +
    touchmove: INPUT_MOVE,
 +
    touchend: INPUT_END,
 +
    touchcancel: INPUT_CANCEL
 +
};
 +
 
 +
var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
 +
var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
 +
 
 +
/**
 +
* Touch events input
 +
* @constructor
 +
* @extends Input
 +
*/
 +
function SingleTouchInput() {
 +
    this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
 +
    this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
 +
    this.started = false;
 +
 
 +
    Input.apply(this, arguments);
 +
}
 +
 
 +
inherit(SingleTouchInput, Input, {
 +
    handler: function TEhandler(ev) {
 +
        var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
 +
 
 +
        // should we handle the touch events?
 +
        if (type === INPUT_START) {
 +
            this.started = true;
 +
        }
 +
 
 +
        if (!this.started) {
 +
            return;
 +
        }
 +
 
 +
        var touches = normalizeSingleTouches.call(this, ev, type);
 +
 
 +
        // when done, reset the started state
 +
        if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
 +
            this.started = false;
 +
        }
 +
 
 +
        this.callback(this.manager, type, {
 +
            pointers: touches[0],
 +
            changedPointers: touches[1],
 +
            pointerType: INPUT_TYPE_TOUCH,
 +
            srcEvent: ev
 +
        });
 +
    }
 +
});
 +
 
 +
/**
 +
* @this {TouchInput}
 +
* @param {Object} ev
 +
* @param {Number} type flag
 +
* @returns {undefined|Array} [all, changed]
 +
*/
 +
function normalizeSingleTouches(ev, type) {
 +
    var all = toArray(ev.touches);
 +
    var changed = toArray(ev.changedTouches);
 +
 
 +
    if (type & (INPUT_END | INPUT_CANCEL)) {
 +
        all = uniqueArray(all.concat(changed), 'identifier', true);
 +
    }
 +
 
 +
    return [all, changed];
 +
}
 +
 
 +
var TOUCH_INPUT_MAP = {
 +
    touchstart: INPUT_START,
 +
    touchmove: INPUT_MOVE,
 +
    touchend: INPUT_END,
 +
    touchcancel: INPUT_CANCEL
 +
};
 +
 
 +
var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
 +
 
 +
/**
 +
* Multi-user touch events input
 +
* @constructor
 +
* @extends Input
 +
*/
 +
function TouchInput() {
 +
    this.evTarget = TOUCH_TARGET_EVENTS;
 +
    this.targetIds = {};
 +
 
 +
    Input.apply(this, arguments);
 +
}
 +
 
 +
inherit(TouchInput, Input, {
 +
    handler: function MTEhandler(ev) {
 +
        var type = TOUCH_INPUT_MAP[ev.type];
 +
        var touches = getTouches.call(this, ev, type);
 +
        if (!touches) {
 +
            return;
 +
        }
 +
 
 +
        this.callback(this.manager, type, {
 +
            pointers: touches[0],
 +
            changedPointers: touches[1],
 +
            pointerType: INPUT_TYPE_TOUCH,
 +
            srcEvent: ev
 +
        });
 +
    }
 +
});
 +
 
 +
/**
 +
* @this {TouchInput}
 +
* @param {Object} ev
 +
* @param {Number} type flag
 +
* @returns {undefined|Array} [all, changed]
 +
*/
 +
function getTouches(ev, type) {
 +
    var allTouches = toArray(ev.touches);
 +
    var targetIds = this.targetIds;
 +
 
 +
    // when there is only one touch, the process can be simplified
 +
    if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
 +
        targetIds[allTouches[0].identifier] = true;
 +
        return [allTouches, allTouches];
 +
    }
 +
 
 +
    var i,
 +
        targetTouches,
 +
        changedTouches = toArray(ev.changedTouches),
 +
        changedTargetTouches = [],
 +
        target = this.target;
 +
 
 +
    // get target touches from touches
 +
    targetTouches = allTouches.filter(function(touch) {
 +
        return hasParent(touch.target, target);
 +
    });
 +
 
 +
    // collect touches
 +
    if (type === INPUT_START) {
 +
        i = 0;
 +
        while (i < targetTouches.length) {
 +
            targetIds[targetTouches[i].identifier] = true;
 +
            i++;
 +
        }
 +
    }
 +
 
 +
    // filter changed touches to only contain touches that exist in the collected target ids
 +
    i = 0;
 +
    while (i < changedTouches.length) {
 +
        if (targetIds[changedTouches[i].identifier]) {
 +
            changedTargetTouches.push(changedTouches[i]);
 +
        }
 +
 
 +
        // cleanup removed touches
 +
        if (type & (INPUT_END | INPUT_CANCEL)) {
 +
            delete targetIds[changedTouches[i].identifier];
 +
        }
 +
        i++;
 +
    }
 +
 
 +
    if (!changedTargetTouches.length) {
 +
        return;
 +
    }
 +
 
 +
    return [
 +
        // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
 +
        uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
 +
        changedTargetTouches
 +
    ];
 +
}
 +
 
 +
/**
 +
* Combined touch and mouse input
 
  *
 
  *
  * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
+
  * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
  * $Rev: 5685 $
+
  * This because touch devices also emit mouse events while doing a touch.
 +
*
 +
* @constructor
 +
* @extends Input
 
  */
 
  */
(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else
+
function TouchMouseInput() {
return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else
+
    Input.apply(this, arguments);
return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else
+
selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else
+
return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else
+
this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else
+
return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else
+
jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&&copy&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else
+
script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else
+
for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else
+
for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else
+
jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else
+
ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&&notxml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&&notxml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&&notxml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else
+
while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else
+
while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else
+
for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else
+
jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else
+
xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
+
jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else
+
for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else
+
s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else
+
e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();
+
  
</script>
+
    var handler = bindFn(this.handler, this);
</html>
+
    this.touch = new TouchInput(this.manager, handler);
 +
    this.mouse = new MouseInput(this.manager, handler);
 +
}
  
 +
inherit(TouchMouseInput, Input, {
 +
    /**
 +
    * handle mouse and touch events
 +
    * @param {Hammer} manager
 +
    * @param {String} inputEvent
 +
    * @param {Object} inputData
 +
    */
 +
    handler: function TMEhandler(manager, inputEvent, inputData) {
 +
        var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
 +
            isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
  
<html>
+
        // when we're in a touch event, so  block all upcoming mouse events
<script>
+
        // most mobile browser also emit mouseevents, right after touchstart
 +
        if (isTouch) {
 +
            this.mouse.allow = false;
 +
        } else if (isMouse && !this.mouse.allow) {
 +
            return;
 +
        }
  
/*
+
        // reset the allowMouse when we're done
 +
        if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
 +
            this.mouse.allow = true;
 +
        }
 +
 
 +
        this.callback(manager, inputEvent, inputData);
 +
    },
 +
 
 +
    /**
 +
    * remove the event listeners
 +
    */
 +
    destroy: function destroy() {
 +
        this.touch.destroy();
 +
        this.mouse.destroy();
 +
    }
 +
});
 +
 
 +
var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
 +
var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
 +
 
 +
// magical touchAction value
 +
var TOUCH_ACTION_COMPUTE = 'compute';
 +
var TOUCH_ACTION_AUTO = 'auto';
 +
var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
 +
var TOUCH_ACTION_NONE = 'none';
 +
var TOUCH_ACTION_PAN_X = 'pan-x';
 +
var TOUCH_ACTION_PAN_Y = 'pan-y';
 +
 
 +
/**
 +
* Touch Action
 +
* sets the touchAction property or uses the js alternative
 +
* @param {Manager} manager
 +
* @param {String} value
 +
* @constructor
 +
*/
 +
function TouchAction(manager, value) {
 +
    this.manager = manager;
 +
    this.set(value);
 +
}
 +
 
 +
TouchAction.prototype = {
 +
    /**
 +
    * set the touchAction value on the element or enable the polyfill
 +
    * @param {String} value
 +
    */
 +
    set: function(value) {
 +
        // find out the touch-action by the event handlers
 +
        if (value == TOUCH_ACTION_COMPUTE) {
 +
            value = this.compute();
 +
        }
 +
 
 +
        if (NATIVE_TOUCH_ACTION) {
 +
            this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
 +
        }
 +
        this.actions = value.toLowerCase().trim();
 +
    },
 +
 
 +
    /**
 +
    * just re-set the touchAction value
 +
    */
 +
    update: function() {
 +
        this.set(this.manager.options.touchAction);
 +
    },
 +
 
 +
    /**
 +
    * compute the value for the touchAction property based on the recognizer's settings
 +
    * @returns {String} value
 +
    */
 +
    compute: function() {
 +
        var actions = [];
 +
        each(this.manager.recognizers, function(recognizer) {
 +
            if (boolOrFn(recognizer.options.enable, [recognizer])) {
 +
                actions = actions.concat(recognizer.getTouchAction());
 +
            }
 +
        });
 +
        return cleanTouchActions(actions.join(' '));
 +
    },
 +
 
 +
    /**
 +
    * this method is called on each input cycle and provides the preventing of the browser behavior
 +
    * @param {Object} input
 +
    */
 +
    preventDefaults: function(input) {
 +
        // not needed with native support for the touchAction property
 +
        if (NATIVE_TOUCH_ACTION) {
 +
            return;
 +
        }
 +
 
 +
        var srcEvent = input.srcEvent;
 +
        var direction = input.offsetDirection;
 +
 
 +
        // if the touch action did prevented once this session
 +
        if (this.manager.session.prevented) {
 +
            srcEvent.preventDefault();
 +
            return;
 +
        }
 +
 
 +
        var actions = this.actions;
 +
        var hasNone = inStr(actions, TOUCH_ACTION_NONE);
 +
        var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
 +
        var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
 +
 
 +
        if (hasNone ||
 +
            (hasPanY && direction & DIRECTION_HORIZONTAL) ||
 +
            (hasPanX && direction & DIRECTION_VERTICAL)) {
 +
            return this.preventSrc(srcEvent);
 +
        }
 +
    },
 +
 
 +
    /**
 +
    * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
 +
    * @param {Object} srcEvent
 +
    */
 +
    preventSrc: function(srcEvent) {
 +
        this.manager.session.prevented = true;
 +
        srcEvent.preventDefault();
 +
    }
 +
};
 +
 
 +
/**
 +
* when the touchActions are collected they are not a valid value, so we need to clean things up. *
 +
* @param {String} actions
 +
* @returns {*}
 +
*/
 +
function cleanTouchActions(actions) {
 +
    // none
 +
    if (inStr(actions, TOUCH_ACTION_NONE)) {
 +
        return TOUCH_ACTION_NONE;
 +
    }
 +
 
 +
    var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
 +
    var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
 +
 
 +
    // pan-x and pan-y can be combined
 +
    if (hasPanX && hasPanY) {
 +
        return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
 +
    }
 +
 
 +
    // pan-x OR pan-y
 +
    if (hasPanX || hasPanY) {
 +
        return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
 +
    }
 +
 
 +
    // manipulation
 +
    if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
 +
        return TOUCH_ACTION_MANIPULATION;
 +
    }
 +
 
 +
    return TOUCH_ACTION_AUTO;
 +
}
 +
 
 +
/**
 +
* Recognizer flow explained; *
 +
* All recognizers have the initial state of POSSIBLE when a input session starts.
 +
* The definition of a input session is from the first input until the last input, with all it's movement in it. *
 +
* Example session for mouse-input: mousedown -> mousemove -> mouseup
 
  *
 
  *
  * jqTransform
+
  * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
* by mathieu vilaplana mvilaplana@dfc-e.com
+
  * which determines with state it should be.
  * Designer ghyslain armand garmand@dfc-e.com
+
 
  *
 
  *
 +
* If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
 +
* POSSIBLE to give it another change on the next cycle.
 
  *
 
  *
  * Version 1.0 25.09.08
+
  *               Possible
  * Version 1.1 06.08.09
+
  *                 |
  * Add event click on Checkbox and Radio
+
  *           +-----+---------------+
  * Auto calculate the size of a select element
+
  *           |                    |
  * Can now, disabled the elements
+
  *     +-----+-----+              |
  * Correct bug in ff if click on select (overflow=hidden)
+
  *     |          |              |
  * No need any more preloading !!
+
  *   Failed      Cancelled          |
  *  
+
  *                         +-------+------+
  ******************************************** */
+
  *                         |              |
   
+
*                     Recognized      Began
(function($){
+
*                                         |
var defaultOptions = {preloadImg:true};
+
*                                     Changed
var jqTransformImgPreloaded = false;
+
*                                         |
 +
*                                 Ended/Recognized
 +
  */
 +
var STATE_POSSIBLE = 1;
 +
var STATE_BEGAN = 2;
 +
var STATE_CHANGED = 4;
 +
var STATE_ENDED = 8;
 +
var STATE_RECOGNIZED = STATE_ENDED;
 +
var STATE_CANCELLED = 16;
 +
var STATE_FAILED = 32;
  
var jqTransformPreloadHoverFocusImg = function(strImgUrl) {
+
/**
//guillemets to remove for ie
+
* Recognizer
strImgUrl = strImgUrl.replace(/^url\((.*)\)/,'$1').replace(/^\"(.*)\"$/,'$1');
+
* Every recognizer needs to extend from this class.
var imgHover = new Image();
+
* @constructor
imgHover.src = strImgUrl.replace(/\.([a-zA-Z]*)$/,'-hover.$1');
+
* @param {Object} options
var imgFocus = new Image();
+
*/
imgFocus.src = strImgUrl.replace(/\.([a-zA-Z]*)$/,'-focus.$1');
+
function Recognizer(options) {
};
+
    this.id = uniqueId();
  
+
    this.manager = null;
/***************************
+
    this.options = merge(options || {}, this.defaults);
  Labels
+
***************************/
+
var jqTransformGetLabel = function(objfield){
+
var selfForm = $(objfield.get(0).form);
+
var oLabel = objfield.next();
+
if(!oLabel.is('label')) {
+
oLabel = objfield.prev();
+
if(oLabel.is('label')){
+
var inputname = objfield.attr('id');
+
if(inputname){
+
oLabel = selfForm.find('label[for="'+inputname+'"]');
+
}  
+
}
+
}
+
if(oLabel.is('label')){return oLabel.css('cursor','pointer');}
+
return false;
+
};
+
+
/* Hide all open selects */
+
var jqTransformHideSelect = function(oTarget){
+
var ulVisible = $('.jqTransformSelectWrapper ul:visible');
+
ulVisible.each(function(){
+
var oSelect = $(this).parents(".jqTransformSelectWrapper:first").find("select").get(0);
+
//do not hide if click on the label object associated to the select
+
if( !(oTarget && oSelect.oLabel && oSelect.oLabel.get(0) == oTarget.get(0)) ){$(this).hide();}
+
});
+
};
+
/* Check for an external click */
+
var jqTransformCheckExternalClick = function(event) {
+
if ($(event.target).parents('.jqTransformSelectWrapper').length === 0) { jqTransformHideSelect($(event.target)); }
+
};
+
  
/* Apply document listener */
+
    // default is enable true
var jqTransformAddDocumentListener = function (){
+
    this.options.enable = ifUndefined(this.options.enable, true);
$(document).mousedown(jqTransformCheckExternalClick);
+
};
+
+
/* Add a new handler for the reset action */
+
var jqTransformReset = function(f){
+
var sel;
+
$('.jqTransformSelectWrapper select', f).each(function(){sel = (this.selectedIndex<0) ? 0 : this.selectedIndex; $('ul', $(this).parent()).each(function(){$('a:eq('+ sel +')', this).click();});});
+
$('a.jqTransformCheckbox, a.jqTransformRadio', f).removeClass('jqTransformChecked');
+
$('input:checkbox, input:radio', f).each(function(){if(this.checked){$('a', $(this).parent()).addClass('jqTransformChecked');}});
+
};
+
  
/***************************
+
    this.state = STATE_POSSIBLE;
  Buttons
+
***************************/
+
$.fn.jqTransInputButton = function(){
+
return this.each(function(){
+
var newBtn = $('<button id="'+ this.id +'" name="'+ this.name +'" type="'+ this.type +'" class="'+ this.className +' jqTransformButton"><span><span>'+ $(this).attr('value') +'</span></span>')
+
.hover(function(){newBtn.addClass('jqTransformButton_hover');},function(){newBtn.removeClass('jqTransformButton_hover')})
+
.mousedown(function(){newBtn.addClass('jqTransformButton_click')})
+
.mouseup(function(){newBtn.removeClass('jqTransformButton_click')})
+
;
+
$(this).replaceWith(newBtn);
+
});
+
};
+
+
/***************************
+
  Text Fields
+
***************************/
+
$.fn.jqTransInputText = function(){
+
return this.each(function(){
+
var $input = $(this);
+
+
if($input.hasClass('jqtranformdone') || !$input.is('input')) {return;}
+
$input.addClass('jqtranformdone');
+
+
var oLabel = jqTransformGetLabel($(this));
+
oLabel && oLabel.bind('click',function(){$input.focus();});
+
+
var inputSize=$input.width();
+
if($input.attr('size')){
+
inputSize = $input.attr('size')*10;
+
$input.css('width',inputSize);
+
}
+
+
$input.addClass("jqTransformInput").wrap('<div class="jqTransformInputWrapper"><div class="jqTransformInputInner"><div></div></div></div>');
+
var $wrapper = $input.parent().parent().parent();
+
$wrapper.css("width", inputSize+10);
+
$input
+
.focus(function(){$wrapper.addClass("jqTransformInputWrapper_focus");})
+
.blur(function(){$wrapper.removeClass("jqTransformInputWrapper_focus");})
+
.hover(function(){$wrapper.addClass("jqTransformInputWrapper_hover");},function(){$wrapper.removeClass("jqTransformInputWrapper_hover");})
+
;
+
+
/* If this is safari we need to add an extra class */
+
$.browser.safari && $wrapper.addClass('jqTransformSafari');
+
$.browser.safari && $input.css('width',$wrapper.width()+16);
+
this.wrapper = $wrapper;
+
+
});
+
};
+
+
/***************************
+
  Check Boxes
+
***************************/
+
$.fn.jqTransCheckBox = function(){
+
return this.each(function(){
+
if($(this).hasClass('jqTransformHidden')) {return;}
+
  
var $input = $(this);
+
    this.simultaneous = {};
var inputSelf = this;
+
    this.requireFail = [];
 +
}
 +
 
 +
Recognizer.prototype = {
 +
    /**
 +
    * @virtual
 +
    * @type {Object}
 +
    */
 +
    defaults: {},
 +
 
 +
    /**
 +
    * set options
 +
    * @param {Object} options
 +
    * @return {Recognizer}
 +
    */
 +
    set: function(options) {
 +
        extend(this.options, options);
 +
 
 +
        // also update the touchAction, in case something changed about the directions/enabled state
 +
        this.manager && this.manager.touchAction.update();
 +
        return this;
 +
    },
 +
 
 +
    /**
 +
    * recognize simultaneous with an other recognizer.
 +
    * @param {Recognizer} otherRecognizer
 +
    * @returns {Recognizer} this
 +
    */
 +
    recognizeWith: function(otherRecognizer) {
 +
        if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
 +
            return this;
 +
        }
 +
 
 +
        var simultaneous = this.simultaneous;
 +
        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
 +
        if (!simultaneous[otherRecognizer.id]) {
 +
            simultaneous[otherRecognizer.id] = otherRecognizer;
 +
            otherRecognizer.recognizeWith(this);
 +
        }
 +
        return this;
 +
    },
 +
 
 +
    /**
 +
    * drop the simultaneous link. it doesnt remove the link on the other recognizer.
 +
    * @param {Recognizer} otherRecognizer
 +
    * @returns {Recognizer} this
 +
    */
 +
    dropRecognizeWith: function(otherRecognizer) {
 +
        if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
 +
            return this;
 +
        }
 +
 
 +
        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
 +
        delete this.simultaneous[otherRecognizer.id];
 +
        return this;
 +
    },
 +
 
 +
    /**
 +
    * recognizer can only run when an other is failing
 +
    * @param {Recognizer} otherRecognizer
 +
    * @returns {Recognizer} this
 +
    */
 +
    requireFailure: function(otherRecognizer) {
 +
        if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
 +
            return this;
 +
        }
 +
 
 +
        var requireFail = this.requireFail;
 +
        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
 +
        if (inArray(requireFail, otherRecognizer) === -1) {
 +
            requireFail.push(otherRecognizer);
 +
            otherRecognizer.requireFailure(this);
 +
        }
 +
        return this;
 +
    },
 +
 
 +
    /**
 +
    * drop the requireFailure link. it does not remove the link on the other recognizer.
 +
    * @param {Recognizer} otherRecognizer
 +
    * @returns {Recognizer} this
 +
    */
 +
    dropRequireFailure: function(otherRecognizer) {
 +
        if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
 +
            return this;
 +
        }
 +
 
 +
        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
 +
        var index = inArray(this.requireFail, otherRecognizer);
 +
        if (index > -1) {
 +
            this.requireFail.splice(index, 1);
 +
        }
 +
        return this;
 +
    },
 +
 
 +
    /**
 +
    * has require failures boolean
 +
    * @returns {boolean}
 +
    */
 +
    hasRequireFailures: function() {
 +
        return this.requireFail.length > 0;
 +
    },
 +
 
 +
    /**
 +
    * if the recognizer can recognize simultaneous with an other recognizer
 +
    * @param {Recognizer} otherRecognizer
 +
    * @returns {Boolean}
 +
    */
 +
    canRecognizeWith: function(otherRecognizer) {
 +
        return !!this.simultaneous[otherRecognizer.id];
 +
    },
 +
 
 +
    /**
 +
    * You should use `tryEmit` instead of `emit` directly to check
 +
    * that all the needed recognizers has failed before emitting.
 +
    * @param {Object} input
 +
    */
 +
    emit: function(input) {
 +
        var self = this;
 +
        var state = this.state;
 +
 
 +
        function emit(withState) {
 +
            self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input);
 +
        }
 +
 
 +
        // 'panstart' and 'panmove'
 +
        if (state < STATE_ENDED) {
 +
            emit(true);
 +
        }
 +
 
 +
        emit(); // simple 'eventName' events
 +
 
 +
        // panend and pancancel
 +
        if (state >= STATE_ENDED) {
 +
            emit(true);
 +
        }
 +
    },
 +
 
 +
    /**
 +
    * Check that all the require failure recognizers has failed,
 +
    * if true, it emits a gesture event,
 +
    * otherwise, setup the state to FAILED.
 +
    * @param {Object} input
 +
    */
 +
    tryEmit: function(input) {
 +
        if (this.canEmit()) {
 +
            return this.emit(input);
 +
        }
 +
        // it's failing anyway
 +
        this.state = STATE_FAILED;
 +
    },
 +
 
 +
    /**
 +
    * can we emit?
 +
    * @returns {boolean}
 +
    */
 +
    canEmit: function() {
 +
        var i = 0;
 +
        while (i < this.requireFail.length) {
 +
            if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
 +
                return false;
 +
            }
 +
            i++;
 +
        }
 +
        return true;
 +
    },
 +
 
 +
    /**
 +
    * update the recognizer
 +
    * @param {Object} inputData
 +
    */
 +
    recognize: function(inputData) {
 +
        // make a new copy of the inputData
 +
        // so we can change the inputData without messing up the other recognizers
 +
        var inputDataClone = extend({}, inputData);
 +
 
 +
        // is is enabled and allow recognizing?
 +
        if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
 +
            this.reset();
 +
            this.state = STATE_FAILED;
 +
            return;
 +
        }
 +
 
 +
        // reset when we've reached the end
 +
        if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
 +
            this.state = STATE_POSSIBLE;
 +
        }
 +
 
 +
        this.state = this.process(inputDataClone);
 +
 
 +
        // the recognizer has recognized a gesture
 +
        // so trigger an event
 +
        if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
 +
            this.tryEmit(inputDataClone);
 +
        }
 +
    },
 +
 
 +
    /**
 +
    * return the state of the recognizer
 +
    * the actual recognizing happens in this method
 +
    * @virtual
 +
    * @param {Object} inputData
 +
    * @returns {Const} STATE
 +
    */
 +
    process: function(inputData) { }, // jshint ignore:line
 +
 
 +
    /**
 +
    * return the preferred touch-action
 +
    * @virtual
 +
    * @returns {Array}
 +
    */
 +
    getTouchAction: function() { },
 +
 
 +
    /**
 +
    * called when the gesture isn't allowed to recognize
 +
    * like when another is being recognized or it is disabled
 +
    * @virtual
 +
    */
 +
    reset: function() { }
 +
};
 +
 
 +
/**
 +
* get a usable string, used as event postfix
 +
* @param {Const} state
 +
* @returns {String} state
 +
*/
 +
function stateStr(state) {
 +
    if (state & STATE_CANCELLED) {
 +
        return 'cancel';
 +
    } else if (state & STATE_ENDED) {
 +
        return 'end';
 +
    } else if (state & STATE_CHANGED) {
 +
        return 'move';
 +
    } else if (state & STATE_BEGAN) {
 +
        return 'start';
 +
    }
 +
    return '';
 +
}
 +
 
 +
/**
 +
* direction cons to string
 +
* @param {Const} direction
 +
* @returns {String}
 +
*/
 +
function directionStr(direction) {
 +
    if (direction == DIRECTION_DOWN) {
 +
        return 'down';
 +
    } else if (direction == DIRECTION_UP) {
 +
        return 'up';
 +
    } else if (direction == DIRECTION_LEFT) {
 +
        return 'left';
 +
    } else if (direction == DIRECTION_RIGHT) {
 +
        return 'right';
 +
    }
 +
    return '';
 +
}
 +
 
 +
/**
 +
* get a recognizer by name if it is bound to a manager
 +
* @param {Recognizer|String} otherRecognizer
 +
* @param {Recognizer} recognizer
 +
* @returns {Recognizer}
 +
*/
 +
function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
 +
    var manager = recognizer.manager;
 +
    if (manager) {
 +
        return manager.get(otherRecognizer);
 +
    }
 +
    return otherRecognizer;
 +
}
 +
 
 +
/**
 +
* This recognizer is just used as a base for the simple attribute recognizers.
 +
* @constructor
 +
* @extends Recognizer
 +
*/
 +
function AttrRecognizer() {
 +
    Recognizer.apply(this, arguments);
 +
}
 +
 
 +
inherit(AttrRecognizer, Recognizer, {
 +
    /**
 +
    * @namespace
 +
    * @memberof AttrRecognizer
 +
    */
 +
    defaults: {
 +
        /**
 +
        * @type {Number}
 +
        * @default 1
 +
        */
 +
        pointers: 1
 +
    },
 +
 
 +
    /**
 +
    * Used to check if it the recognizer receives valid input, like input.distance > 10.
 +
    * @memberof AttrRecognizer
 +
    * @param {Object} input
 +
    * @returns {Boolean} recognized
 +
    */
 +
    attrTest: function(input) {
 +
        var optionPointers = this.options.pointers;
 +
        return optionPointers === 0 || input.pointers.length === optionPointers;
 +
    },
 +
 
 +
    /**
 +
    * Process the input and return the state for the recognizer
 +
    * @memberof AttrRecognizer
 +
    * @param {Object} input
 +
    * @returns {*} State
 +
    */
 +
    process: function(input) {
 +
        var state = this.state;
 +
        var eventType = input.eventType;
 +
 
 +
        var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
 +
        var isValid = this.attrTest(input);
 +
 
 +
        // on cancel input and we've recognized before, return STATE_CANCELLED
 +
        if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
 +
            return state | STATE_CANCELLED;
 +
        } else if (isRecognized || isValid) {
 +
            if (eventType & INPUT_END) {
 +
                return state | STATE_ENDED;
 +
            } else if (!(state & STATE_BEGAN)) {
 +
                return STATE_BEGAN;
 +
            }
 +
            return state | STATE_CHANGED;
 +
        }
 +
        return STATE_FAILED;
 +
    }
 +
});
 +
 
 +
/**
 +
* Pan
 +
* Recognized when the pointer is down and moved in the allowed direction.
 +
* @constructor
 +
* @extends AttrRecognizer
 +
*/
 +
function PanRecognizer() {
 +
    AttrRecognizer.apply(this, arguments);
 +
 
 +
    this.pX = null;
 +
    this.pY = null;
 +
}
 +
 
 +
inherit(PanRecognizer, AttrRecognizer, {
 +
    /**
 +
    * @namespace
 +
    * @memberof PanRecognizer
 +
    */
 +
    defaults: {
 +
        event: 'pan',
 +
        threshold: 10,
 +
        pointers: 1,
 +
        direction: DIRECTION_ALL
 +
    },
 +
 
 +
    getTouchAction: function() {
 +
        var direction = this.options.direction;
 +
        var actions = [];
 +
        if (direction & DIRECTION_HORIZONTAL) {
 +
            actions.push(TOUCH_ACTION_PAN_Y);
 +
        }
 +
        if (direction & DIRECTION_VERTICAL) {
 +
            actions.push(TOUCH_ACTION_PAN_X);
 +
        }
 +
        return actions;
 +
    },
 +
 
 +
    directionTest: function(input) {
 +
        var options = this.options;
 +
        var hasMoved = true;
 +
        var distance = input.distance;
 +
        var direction = input.direction;
 +
        var x = input.deltaX;
 +
        var y = input.deltaY;
 +
 
 +
        // lock to axis?
 +
        if (!(direction & options.direction)) {
 +
            if (options.direction & DIRECTION_HORIZONTAL) {
 +
                direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
 +
                hasMoved = x != this.pX;
 +
                distance = Math.abs(input.deltaX);
 +
            } else {
 +
                direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
 +
                hasMoved = y != this.pY;
 +
                distance = Math.abs(input.deltaY);
 +
            }
 +
        }
 +
        input.direction = direction;
 +
        return hasMoved && distance > options.threshold && direction & options.direction;
 +
    },
 +
 
 +
    attrTest: function(input) {
 +
        return AttrRecognizer.prototype.attrTest.call(this, input) &&
 +
            (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
 +
    },
 +
 
 +
    emit: function(input) {
 +
        this.pX = input.deltaX;
 +
        this.pY = input.deltaY;
 +
 
 +
        var direction = directionStr(input.direction);
 +
        if (direction) {
 +
            this.manager.emit(this.options.event + direction, input);
 +
        }
 +
 
 +
        this._super.emit.call(this, input);
 +
    }
 +
});
 +
 
 +
/**
 +
* Pinch
 +
* Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
 +
* @constructor
 +
* @extends AttrRecognizer
 +
*/
 +
function PinchRecognizer() {
 +
    AttrRecognizer.apply(this, arguments);
 +
}
 +
 
 +
inherit(PinchRecognizer, AttrRecognizer, {
 +
    /**
 +
    * @namespace
 +
    * @memberof PinchRecognizer
 +
    */
 +
    defaults: {
 +
        event: 'pinch',
 +
        threshold: 0,
 +
        pointers: 2
 +
    },
 +
 
 +
    getTouchAction: function() {
 +
        return [TOUCH_ACTION_NONE];
 +
    },
 +
 
 +
    attrTest: function(input) {
 +
        return this._super.attrTest.call(this, input) &&
 +
            (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
 +
    },
 +
 
 +
    emit: function(input) {
 +
        this._super.emit.call(this, input);
 +
        if (input.scale !== 1) {
 +
            var inOut = input.scale < 1 ? 'in' : 'out';
 +
            this.manager.emit(this.options.event + inOut, input);
 +
        }
 +
    }
 +
});
 +
 
 +
/**
 +
* Press
 +
* Recognized when the pointer is down for x ms without any movement.
 +
* @constructor
 +
* @extends Recognizer
 +
*/
 +
function PressRecognizer() {
 +
    Recognizer.apply(this, arguments);
 +
 
 +
    this._timer = null;
 +
    this._input = null;
 +
}
 +
 
 +
inherit(PressRecognizer, Recognizer, {
 +
    /**
 +
    * @namespace
 +
    * @memberof PressRecognizer
 +
    */
 +
    defaults: {
 +
        event: 'press',
 +
        pointers: 1,
 +
        time: 500, // minimal time of the pointer to be pressed
 +
        threshold: 5 // a minimal movement is ok, but keep it low
 +
    },
 +
 
 +
    getTouchAction: function() {
 +
        return [TOUCH_ACTION_AUTO];
 +
    },
 +
 
 +
    process: function(input) {
 +
        var options = this.options;
 +
        var validPointers = input.pointers.length === options.pointers;
 +
        var validMovement = input.distance < options.threshold;
 +
        var validTime = input.deltaTime > options.time;
 +
 
 +
        this._input = input;
 +
 
 +
        // we only allow little movement
 +
        // and we've reached an end event, so a tap is possible
 +
        if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
 +
            this.reset();
 +
        } else if (input.eventType & INPUT_START) {
 +
            this.reset();
 +
            this._timer = setTimeoutContext(function() {
 +
                this.state = STATE_RECOGNIZED;
 +
                this.tryEmit();
 +
            }, options.time, this);
 +
        } else if (input.eventType & INPUT_END) {
 +
            return STATE_RECOGNIZED;
 +
        }
 +
        return STATE_FAILED;
 +
    },
 +
 
 +
    reset: function() {
 +
        clearTimeout(this._timer);
 +
    },
 +
 
 +
    emit: function(input) {
 +
        if (this.state !== STATE_RECOGNIZED) {
 +
            return;
 +
        }
 +
 
 +
        if (input && (input.eventType & INPUT_END)) {
 +
            this.manager.emit(this.options.event + 'up', input);
 +
        } else {
 +
            this._input.timeStamp = now();
 +
            this.manager.emit(this.options.event, this._input);
 +
        }
 +
    }
 +
});
 +
 
 +
/**
 +
* Rotate
 +
* Recognized when two or more pointer are moving in a circular motion.
 +
* @constructor
 +
* @extends AttrRecognizer
 +
*/
 +
function RotateRecognizer() {
 +
    AttrRecognizer.apply(this, arguments);
 +
}
 +
 
 +
inherit(RotateRecognizer, AttrRecognizer, {
 +
    /**
 +
    * @namespace
 +
    * @memberof RotateRecognizer
 +
    */
 +
    defaults: {
 +
        event: 'rotate',
 +
        threshold: 0,
 +
        pointers: 2
 +
    },
 +
 
 +
    getTouchAction: function() {
 +
        return [TOUCH_ACTION_NONE];
 +
    },
 +
 
 +
    attrTest: function(input) {
 +
        return this._super.attrTest.call(this, input) &&
 +
            (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
 +
    }
 +
});
 +
 
 +
/**
 +
* Swipe
 +
* Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
 +
* @constructor
 +
* @extends AttrRecognizer
 +
*/
 +
function SwipeRecognizer() {
 +
    AttrRecognizer.apply(this, arguments);
 +
}
 +
 
 +
inherit(SwipeRecognizer, AttrRecognizer, {
 +
    /**
 +
    * @namespace
 +
    * @memberof SwipeRecognizer
 +
    */
 +
    defaults: {
 +
        event: 'swipe',
 +
        threshold: 10,
 +
        velocity: 0.65,
 +
        direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
 +
        pointers: 1
 +
    },
 +
 
 +
    getTouchAction: function() {
 +
        return PanRecognizer.prototype.getTouchAction.call(this);
 +
    },
 +
 
 +
    attrTest: function(input) {
 +
        var direction = this.options.direction;
 +
        var velocity;
 +
 
 +
        if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
 +
            velocity = input.velocity;
 +
        } else if (direction & DIRECTION_HORIZONTAL) {
 +
            velocity = input.velocityX;
 +
        } else if (direction & DIRECTION_VERTICAL) {
 +
            velocity = input.velocityY;
 +
        }
 +
 
 +
        return this._super.attrTest.call(this, input) &&
 +
            direction & input.direction &&
 +
            input.distance > this.options.threshold &&
 +
            abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
 +
    },
 +
 
 +
    emit: function(input) {
 +
        var direction = directionStr(input.direction);
 +
        if (direction) {
 +
            this.manager.emit(this.options.event + direction, input);
 +
        }
 +
 
 +
        this.manager.emit(this.options.event, input);
 +
    }
 +
});
 +
 
 +
/**
 +
* A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
 +
* between the given interval and position. The delay option can be used to recognize multi-taps without firing
 +
* a single tap.
 +
*
 +
* The eventData from the emitted event contains the property `tapCount`, which contains the amount of
 +
* multi-taps being recognized.
 +
* @constructor
 +
* @extends Recognizer
 +
*/
 +
function TapRecognizer() {
 +
    Recognizer.apply(this, arguments);
 +
 
 +
    // previous time and center,
 +
    // used for tap counting
 +
    this.pTime = false;
 +
    this.pCenter = false;
 +
 
 +
    this._timer = null;
 +
    this._input = null;
 +
    this.count = 0;
 +
}
 +
 
 +
inherit(TapRecognizer, Recognizer, {
 +
    /**
 +
    * @namespace
 +
    * @memberof PinchRecognizer
 +
    */
 +
    defaults: {
 +
        event: 'tap',
 +
        pointers: 1,
 +
        taps: 1,
 +
        interval: 300, // max time between the multi-tap taps
 +
        time: 250, // max time of the pointer to be down (like finger on the screen)
 +
        threshold: 2, // a minimal movement is ok, but keep it low
 +
        posThreshold: 10 // a multi-tap can be a bit off the initial position
 +
    },
 +
 
 +
    getTouchAction: function() {
 +
        return [TOUCH_ACTION_MANIPULATION];
 +
    },
 +
 
 +
    process: function(input) {
 +
        var options = this.options;
 +
 
 +
        var validPointers = input.pointers.length === options.pointers;
 +
        var validMovement = input.distance < options.threshold;
 +
        var validTouchTime = input.deltaTime < options.time;
 +
 
 +
        this.reset();
 +
 
 +
        if ((input.eventType & INPUT_START) && (this.count === 0)) {
 +
            return this.failTimeout();
 +
        }
 +
 
 +
        // we only allow little movement
 +
        // and we've reached an end event, so a tap is possible
 +
        if (validMovement && validTouchTime && validPointers) {
 +
            if (input.eventType != INPUT_END) {
 +
                return this.failTimeout();
 +
            }
 +
 
 +
            var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
 +
            var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
 +
 
 +
            this.pTime = input.timeStamp;
 +
            this.pCenter = input.center;
 +
 
 +
            if (!validMultiTap || !validInterval) {
 +
                this.count = 1;
 +
            } else {
 +
                this.count += 1;
 +
            }
 +
 
 +
            this._input = input;
 +
 
 +
            // if tap count matches we have recognized it,
 +
            // else it has began recognizing...
 +
            var tapCount = this.count % options.taps;
 +
            if (tapCount === 0) {
 +
                // no failing requirements, immediately trigger the tap event
 +
                // or wait as long as the multitap interval to trigger
 +
                if (!this.hasRequireFailures()) {
 +
                    return STATE_RECOGNIZED;
 +
                } else {
 +
                    this._timer = setTimeoutContext(function() {
 +
                        this.state = STATE_RECOGNIZED;
 +
                        this.tryEmit();
 +
                    }, options.interval, this);
 +
                    return STATE_BEGAN;
 +
                }
 +
            }
 +
        }
 +
        return STATE_FAILED;
 +
    },
 +
 
 +
    failTimeout: function() {
 +
        this._timer = setTimeoutContext(function() {
 +
            this.state = STATE_FAILED;
 +
        }, this.options.interval, this);
 +
        return STATE_FAILED;
 +
    },
 +
 
 +
    reset: function() {
 +
        clearTimeout(this._timer);
 +
    },
 +
 
 +
    emit: function() {
 +
        if (this.state == STATE_RECOGNIZED ) {
 +
            this._input.tapCount = this.count;
 +
            this.manager.emit(this.options.event, this._input);
 +
        }
 +
    }
 +
});
 +
 
 +
/**
 +
* Simple way to create an manager with a default set of recognizers.
 +
* @param {HTMLElement} element
 +
* @param {Object} [options]
 +
* @constructor
 +
*/
 +
function Hammer(element, options) {
 +
    options = options || {};
 +
    options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
 +
    return new Manager(element, options);
 +
}
 +
 
 +
/**
 +
* @const {string}
 +
*/
 +
Hammer.VERSION = '2.0.4';
 +
 
 +
/**
 +
* default settings
 +
* @namespace
 +
*/
 +
Hammer.defaults = {
 +
    /**
 +
    * set if DOM events are being triggered.
 +
    * But this is slower and unused by simple implementations, so disabled by default.
 +
    * @type {Boolean}
 +
    * @default false
 +
    */
 +
    domEvents: false,
 +
 
 +
    /**
 +
    * The value for the touchAction property/fallback.
 +
    * When set to `compute` it will magically set the correct value based on the added recognizers.
 +
    * @type {String}
 +
    * @default compute
 +
    */
 +
    touchAction: TOUCH_ACTION_COMPUTE,
 +
 
 +
    /**
 +
    * @type {Boolean}
 +
    * @default true
 +
    */
 +
    enable: true,
 +
 
 +
    /**
 +
    * EXPERIMENTAL FEATURE -- can be removed/changed
 +
    * Change the parent input target element.
 +
    * If Null, then it is being set the to main element.
 +
    * @type {Null|EventTarget}
 +
    * @default null
 +
    */
 +
    inputTarget: null,
 +
 
 +
    /**
 +
    * force an input class
 +
    * @type {Null|Function}
 +
    * @default null
 +
    */
 +
    inputClass: null,
 +
 
 +
    /**
 +
    * Default recognizer setup when calling `Hammer()`
 +
    * When creating a new Manager these will be skipped.
 +
    * @type {Array}
 +
    */
 +
    preset: [
 +
        // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
 +
        [RotateRecognizer, { enable: false }],
 +
        [PinchRecognizer, { enable: false }, ['rotate']],
 +
        [SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }],
 +
        [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']],
 +
        [TapRecognizer],
 +
        [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']],
 +
        [PressRecognizer]
 +
    ],
 +
 
 +
    /**
 +
    * Some CSS properties can be used to improve the working of Hammer.
 +
    * Add them to this method and they will be set when creating a new Manager.
 +
    * @namespace
 +
    */
 +
    cssProps: {
 +
        /**
 +
        * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
 +
        * @type {String}
 +
        * @default 'none'
 +
        */
 +
        userSelect: 'none',
 +
 
 +
        /**
 +
        * Disable the Windows Phone grippers when pressing an element.
 +
        * @type {String}
 +
        * @default 'none'
 +
        */
 +
        touchSelect: 'none',
 +
 
 +
        /**
 +
        * Disables the default callout shown when you touch and hold a touch target.
 +
        * On iOS, when you touch and hold a touch target such as a link, Safari displays
 +
        * a callout containing information about the link. This property allows you to disable that callout.
 +
        * @type {String}
 +
        * @default 'none'
 +
        */
 +
        touchCallout: 'none',
 +
 
 +
        /**
 +
        * Specifies whether zooming is enabled. Used by IE10>
 +
        * @type {String}
 +
        * @default 'none'
 +
        */
 +
        contentZooming: 'none',
 +
 
 +
        /**
 +
        * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
 +
        * @type {String}
 +
        * @default 'none'
 +
        */
 +
        userDrag: 'none',
 +
 
 +
        /**
 +
        * Overrides the highlight color shown when the user taps a link or a JavaScript
 +
        * clickable element in iOS. This property obeys the alpha value, if specified.
 +
        * @type {String}
 +
        * @default 'rgba(0,0,0,0)'
 +
        */
 +
        tapHighlightColor: 'rgba(0,0,0,0)'
 +
    }
 +
};
 +
 
 +
var STOP = 1;
 +
var FORCED_STOP = 2;
 +
 
 +
/**
 +
* Manager
 +
* @param {HTMLElement} element
 +
* @param {Object} [options]
 +
* @constructor
 +
*/
 +
function Manager(element, options) {
 +
    options = options || {};
 +
 
 +
    this.options = merge(options, Hammer.defaults);
 +
    this.options.inputTarget = this.options.inputTarget || element;
 +
 
 +
    this.handlers = {};
 +
    this.session = {};
 +
    this.recognizers = [];
 +
 
 +
    this.element = element;
 +
    this.input = createInputInstance(this);
 +
    this.touchAction = new TouchAction(this, this.options.touchAction);
 +
 
 +
    toggleCssProps(this, true);
 +
 
 +
    each(options.recognizers, function(item) {
 +
        var recognizer = this.add(new (item[0])(item[1]));
 +
        item[2] && recognizer.recognizeWith(item[2]);
 +
        item[3] && recognizer.requireFailure(item[3]);
 +
    }, this);
 +
}
 +
 
 +
Manager.prototype = {
 +
    /**
 +
    * set options
 +
    * @param {Object} options
 +
    * @returns {Manager}
 +
    */
 +
    set: function(options) {
 +
        extend(this.options, options);
 +
 
 +
        // Options that need a little more setup
 +
        if (options.touchAction) {
 +
            this.touchAction.update();
 +
        }
 +
        if (options.inputTarget) {
 +
            // Clean up existing event listeners and reinitialize
 +
            this.input.destroy();
 +
            this.input.target = options.inputTarget;
 +
            this.input.init();
 +
        }
 +
        return this;
 +
    },
 +
 
 +
    /**
 +
    * stop recognizing for this session.
 +
    * This session will be discarded, when a new [input]start event is fired.
 +
    * When forced, the recognizer cycle is stopped immediately.
 +
    * @param {Boolean} [force]
 +
    */
 +
    stop: function(force) {
 +
        this.session.stopped = force ? FORCED_STOP : STOP;
 +
    },
 +
 
 +
    /**
 +
    * run the recognizers!
 +
    * called by the inputHandler function on every movement of the pointers (touches)
 +
    * it walks through all the recognizers and tries to detect the gesture that is being made
 +
    * @param {Object} inputData
 +
    */
 +
    recognize: function(inputData) {
 +
        var session = this.session;
 +
        if (session.stopped) {
 +
            return;
 +
        }
 +
 
 +
        // run the touch-action polyfill
 +
        this.touchAction.preventDefaults(inputData);
 +
 
 +
        var recognizer;
 +
        var recognizers = this.recognizers;
 +
 
 +
        // this holds the recognizer that is being recognized.
 +
        // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
 +
        // if no recognizer is detecting a thing, it is set to `null`
 +
        var curRecognizer = session.curRecognizer;
 +
 
 +
        // reset when the last recognizer is recognized
 +
        // or when we're in a new session
 +
        if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
 +
            curRecognizer = session.curRecognizer = null;
 +
        }
 +
 
 +
        var i = 0;
 +
        while (i < recognizers.length) {
 +
            recognizer = recognizers[i];
 +
 
 +
            // find out if we are allowed try to recognize the input for this one.
 +
            // 1.  allow if the session is NOT forced stopped (see the .stop() method)
 +
            // 2.  allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
 +
            //      that is being recognized.
 +
            // 3.  allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
 +
            //      this can be setup with the `recognizeWith()` method on the recognizer.
 +
            if (session.stopped !== FORCED_STOP && ( // 1
 +
                    !curRecognizer || recognizer == curRecognizer || // 2
 +
                    recognizer.canRecognizeWith(curRecognizer))) { // 3
 +
                recognizer.recognize(inputData);
 +
            } else {
 +
                recognizer.reset();
 +
            }
 +
 
 +
            // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
 +
            // current active recognizer. but only if we don't already have an active recognizer
 +
            if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
 +
                curRecognizer = session.curRecognizer = recognizer;
 +
            }
 +
            i++;
 +
        }
 +
    },
 +
 
 +
    /**
 +
    * get a recognizer by its event name.
 +
    * @param {Recognizer|String} recognizer
 +
    * @returns {Recognizer|Null}
 +
    */
 +
    get: function(recognizer) {
 +
        if (recognizer instanceof Recognizer) {
 +
            return recognizer;
 +
        }
 +
 
 +
        var recognizers = this.recognizers;
 +
        for (var i = 0; i < recognizers.length; i++) {
 +
            if (recognizers[i].options.event == recognizer) {
 +
                return recognizers[i];
 +
            }
 +
        }
 +
        return null;
 +
    },
 +
 
 +
    /**
 +
    * add a recognizer to the manager
 +
    * existing recognizers with the same event name will be removed
 +
    * @param {Recognizer} recognizer
 +
    * @returns {Recognizer|Manager}
 +
    */
 +
    add: function(recognizer) {
 +
        if (invokeArrayArg(recognizer, 'add', this)) {
 +
            return this;
 +
        }
 +
 
 +
        // remove existing
 +
        var existing = this.get(recognizer.options.event);
 +
        if (existing) {
 +
            this.remove(existing);
 +
        }
 +
 
 +
        this.recognizers.push(recognizer);
 +
        recognizer.manager = this;
 +
 
 +
        this.touchAction.update();
 +
        return recognizer;
 +
    },
 +
 
 +
    /**
 +
    * remove a recognizer by name or instance
 +
    * @param {Recognizer|String} recognizer
 +
    * @returns {Manager}
 +
    */
 +
    remove: function(recognizer) {
 +
        if (invokeArrayArg(recognizer, 'remove', this)) {
 +
            return this;
 +
        }
 +
 
 +
        var recognizers = this.recognizers;
 +
        recognizer = this.get(recognizer);
 +
        recognizers.splice(inArray(recognizers, recognizer), 1);
 +
 
 +
        this.touchAction.update();
 +
        return this;
 +
    },
 +
 
 +
    /**
 +
    * bind event
 +
    * @param {String} events
 +
    * @param {Function} handler
 +
    * @returns {EventEmitter} this
 +
    */
 +
    on: function(events, handler) {
 +
        var handlers = this.handlers;
 +
        each(splitStr(events), function(event) {
 +
            handlers[event] = handlers[event] || [];
 +
            handlers[event].push(handler);
 +
        });
 +
        return this;
 +
    },
 +
 
 +
    /**
 +
    * unbind event, leave emit blank to remove all handlers
 +
    * @param {String} events
 +
    * @param {Function} [handler]
 +
    * @returns {EventEmitter} this
 +
    */
 +
    off: function(events, handler) {
 +
        var handlers = this.handlers;
 +
        each(splitStr(events), function(event) {
 +
            if (!handler) {
 +
                delete handlers[event];
 +
            } else {
 +
                handlers[event].splice(inArray(handlers[event], handler), 1);
 +
            }
 +
        });
 +
        return this;
 +
    },
 +
 
 +
    /**
 +
    * emit event to the listeners
 +
    * @param {String} event
 +
    * @param {Object} data
 +
    */
 +
    emit: function(event, data) {
 +
        // we also want to trigger dom events
 +
        if (this.options.domEvents) {
 +
            triggerDomEvent(event, data);
 +
        }
 +
 
 +
        // no handlers, so skip it all
 +
        var handlers = this.handlers[event] && this.handlers[event].slice();
 +
        if (!handlers || !handlers.length) {
 +
            return;
 +
        }
 +
 
 +
        data.type = event;
 +
        data.preventDefault = function() {
 +
            data.srcEvent.preventDefault();
 +
        };
 +
 
 +
        var i = 0;
 +
        while (i < handlers.length) {
 +
            handlers[i](data);
 +
            i++;
 +
        }
 +
    },
 +
 
 +
    /**
 +
    * destroy the manager and unbinds all events
 +
    * it doesn't unbind dom events, that is the user own responsibility
 +
    */
 +
    destroy: function() {
 +
        this.element && toggleCssProps(this, false);
 +
 
 +
        this.handlers = {};
 +
        this.session = {};
 +
        this.input.destroy();
 +
        this.element = null;
 +
    }
 +
};
 +
 
 +
/**
 +
* add/remove the css properties as defined in manager.options.cssProps
 +
* @param {Manager} manager
 +
* @param {Boolean} add
 +
*/
 +
function toggleCssProps(manager, add) {
 +
    var element = manager.element;
 +
    each(manager.options.cssProps, function(value, name) {
 +
        element.style[prefixed(element.style, name)] = add ? value : '';
 +
    });
 +
}
 +
 
 +
/**
 +
* trigger dom event
 +
* @param {String} event
 +
* @param {Object} data
 +
*/
 +
function triggerDomEvent(event, data) {
 +
    var gestureEvent = document.createEvent('Event');
 +
    gestureEvent.initEvent(event, true, true);
 +
    gestureEvent.gesture = data;
 +
    data.target.dispatchEvent(gestureEvent);
 +
}
  
//set the click on the label
+
extend(Hammer, {
var oLabel=jqTransformGetLabel($input);
+
    INPUT_START: INPUT_START,
oLabel && oLabel.click(function(){aLink.trigger('click');});
+
    INPUT_MOVE: INPUT_MOVE,
+
    INPUT_END: INPUT_END,
var aLink = $('<a href="#" class="jqTransformCheckbox"></a>');
+
    INPUT_CANCEL: INPUT_CANCEL,
//wrap and add the link
+
$input.addClass('jqTransformHidden').wrap('<span class="jqTransformCheckboxWrapper"></span>').parent().prepend(aLink);
+
//on change, change the class of the link
+
$input.change(function(){
+
this.checked && aLink.addClass('jqTransformChecked') || aLink.removeClass('jqTransformChecked');
+
return true;
+
});
+
// Click Handler, trigger the click and change event on the input
+
aLink.click(function(){
+
//do nothing if the original input is disabled
+
if($input.attr('disabled')){return false;}
+
//trigger the envents on the input object
+
$input.trigger('click').trigger("change");
+
return false;
+
});
+
  
// set the default state
+
    STATE_POSSIBLE: STATE_POSSIBLE,
this.checked && aLink.addClass('jqTransformChecked');
+
    STATE_BEGAN: STATE_BEGAN,
});
+
    STATE_CHANGED: STATE_CHANGED,
};
+
    STATE_ENDED: STATE_ENDED,
/***************************
+
    STATE_RECOGNIZED: STATE_RECOGNIZED,
  Radio Buttons
+
    STATE_CANCELLED: STATE_CANCELLED,
***************************/
+
    STATE_FAILED: STATE_FAILED,
$.fn.jqTransRadio = function(){
+
return this.each(function(){
+
if($(this).hasClass('jqTransformHidden')) {return;}
+
  
var $input = $(this);
+
    DIRECTION_NONE: DIRECTION_NONE,
var inputSelf = this;
+
    DIRECTION_LEFT: DIRECTION_LEFT,
+
    DIRECTION_RIGHT: DIRECTION_RIGHT,
oLabel = jqTransformGetLabel($input);
+
    DIRECTION_UP: DIRECTION_UP,
oLabel && oLabel.click(function(){aLink.trigger('click');});
+
    DIRECTION_DOWN: DIRECTION_DOWN,
+
    DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
var aLink = $('<a href="#" class="jqTransformRadio" rel="'+ this.name +'"></a>');
+
    DIRECTION_VERTICAL: DIRECTION_VERTICAL,
$input.addClass('jqTransformHidden').wrap('<span class="jqTransformRadioWrapper"></span>').parent().prepend(aLink);
+
    DIRECTION_ALL: DIRECTION_ALL,
+
$input.change(function(){
+
inputSelf.checked && aLink.addClass('jqTransformChecked') || aLink.removeClass('jqTransformChecked');
+
return true;
+
});
+
// Click Handler
+
aLink.click(function(){
+
if($input.attr('disabled')){return false;}
+
$input.trigger('click').trigger('change');
+
+
// uncheck all others of same name input radio elements
+
$('input[name="'+$input.attr('name')+'"]',inputSelf.form).not($input).each(function(){
+
$(this).attr('type')=='radio' && $(this).trigger('change');
+
});
+
+
return false;
+
});
+
// set the default state
+
inputSelf.checked && aLink.addClass('jqTransformChecked');
+
});
+
};
+
+
/***************************
+
  TextArea
+
***************************/
+
$.fn.jqTransTextarea = function(){
+
return this.each(function(){
+
var textarea = $(this);
+
+
if(textarea.hasClass('jqtransformdone')) {return;}
+
textarea.addClass('jqtransformdone');
+
+
oLabel = jqTransformGetLabel(textarea);
+
oLabel && oLabel.click(function(){textarea.focus();});
+
+
var strTable = '<table cellspacing="0" cellpadding="0" border="0" class="jqTransformTextarea">';
+
strTable +='<tr><td id="jqTransformTextarea-tl"></td><td id="jqTransformTextarea-tm"></td><td id="jqTransformTextarea-tr"></td></tr>';
+
strTable +='<tr><td id="jqTransformTextarea-ml">&nbsp;</td><td id="jqTransformTextarea-mm"><div></div></td><td id="jqTransformTextarea-mr">&nbsp;</td></tr>';
+
strTable +='<tr><td id="jqTransformTextarea-bl"></td><td id="jqTransformTextarea-bm"></td><td id="jqTransformTextarea-br"></td></tr>';
+
strTable +='</table>';
+
var oTable = $(strTable)
+
.insertAfter(textarea)
+
.hover(function(){
+
!oTable.hasClass('jqTransformTextarea-focus') && oTable.addClass('jqTransformTextarea-hover');
+
},function(){
+
oTable.removeClass('jqTransformTextarea-hover');
+
})
+
;
+
+
textarea
+
.focus(function(){oTable.removeClass('jqTransformTextarea-hover').addClass('jqTransformTextarea-focus');})
+
.blur(function(){oTable.removeClass('jqTransformTextarea-focus');})
+
.appendTo($('#jqTransformTextarea-mm div',oTable))
+
;
+
this.oTable = oTable;
+
if($.browser.safari){
+
$('#jqTransformTextarea-mm',oTable)
+
.addClass('jqTransformSafariTextarea')
+
.find('div')
+
.css('height',textarea.height())
+
.css('width',textarea.width())
+
;
+
}
+
});
+
};
+
+
/***************************
+
  Select
+
***************************/
+
$.fn.jqTransSelect = function(){
+
return this.each(function(index){
+
var $select = $(this);
+
  
if($select.hasClass('jqTransformHidden')) {return;}
+
    Manager: Manager,
if($select.attr('multiple')) {return;}
+
    Input: Input,
 +
    TouchAction: TouchAction,
  
var oLabel  =  jqTransformGetLabel($select);
+
    TouchInput: TouchInput,
/* First thing we do is Wrap it */
+
    MouseInput: MouseInput,
var $wrapper = $select
+
    PointerEventInput: PointerEventInput,
.addClass('jqTransformHidden')
+
    TouchMouseInput: TouchMouseInput,
.wrap('<div class="jqTransformSelectWrapper"></div>')
+
    SingleTouchInput: SingleTouchInput,
.parent()
+
.css({zIndex: 10-index})
+
;
+
+
/* Now add the html for the select */
+
$wrapper.prepend('<div><span></span><a href="#" class="jqTransformSelectOpen"></a></div><ul></ul>');
+
var $ul = $('ul', $wrapper).css('width',$select.width()).hide();
+
/* Now we add the options */
+
$('option', this).each(function(i){
+
var oLi = $('<li><a href="#" index="'+ i +'">'+ $(this).html() +'</a></li>');
+
$ul.append(oLi);
+
});
+
+
/* Add click handler to the a */
+
$ul.find('a').click(function(){
+
$('a.selected', $wrapper).removeClass('selected');
+
$(this).addClass('selected');
+
/* Fire the onchange event */
+
if ($select[0].selectedIndex != $(this).attr('index') && $select[0].onchange) { $select[0].selectedIndex = $(this).attr('index'); $select[0].onchange(); }
+
$select[0].selectedIndex = $(this).attr('index');
+
$('span:eq(0)', $wrapper).html($(this).html());
+
$ul.hide();
+
return false;
+
});
+
/* Set the default */
+
$('a:eq('+ this.selectedIndex +')', $ul).click();
+
$('span:first', $wrapper).click(function(){$("a.jqTransformSelectOpen",$wrapper).trigger('click');});
+
oLabel && oLabel.click(function(){$("a.jqTransformSelectOpen",$wrapper).trigger('click');});
+
this.oLabel = oLabel;
+
+
/* Apply the click handler to the Open */
+
var oLinkOpen = $('a.jqTransformSelectOpen', $wrapper)
+
.click(function(){
+
//Check if box is already open to still allow toggle, but close all other selects
+
if( $ul.css('display') == 'none' ) {jqTransformHideSelect();}
+
if($select.attr('disabled')){return false;}
+
  
$ul.slideToggle('fast', function(){
+
    Recognizer: Recognizer,
var offSet = ($('a.selected', $ul).offset().top - $ul.offset().top);
+
    AttrRecognizer: AttrRecognizer,
$ul.animate({scrollTop: offSet});
+
    Tap: TapRecognizer,
});
+
    Pan: PanRecognizer,
return false;
+
    Swipe: SwipeRecognizer,
})
+
    Pinch: PinchRecognizer,
;
+
    Rotate: RotateRecognizer,
 +
    Press: PressRecognizer,
  
// Set the new width
+
    on: addEventListeners,
var iSelectWidth = $select.outerWidth();
+
    off: removeEventListeners,
var oSpan = $('span:first',$wrapper);
+
    each: each,
var newWidth = (iSelectWidth > oSpan.innerWidth())?iSelectWidth+oLinkOpen.outerWidth():$wrapper.width();
+
    merge: merge,
$wrapper.css('width',newWidth);
+
    extend: extend,
$ul.css('width',newWidth-2);
+
    inherit: inherit,
oSpan.css({width:iSelectWidth});
+
    bindFn: bindFn,
+
    prefixed: prefixed
// Calculate the height if necessary, less elements that the default height
+
});
//show the ul to calculate the block, if ul is not displayed li height value is 0
+
$ul.css({display:'block',visibility:'hidden'});
+
var iSelectHeight = ($('li',$ul).length)*($('li:first',$ul).height());//+1 else bug ff
+
(iSelectHeight < $ul.height()) && $ul.css({height:iSelectHeight,'overflow':'hidden'});//hidden else bug with ff
+
$ul.css({display:'none',visibility:'visible'});
+
+
});
+
};
+
$.fn.jqTransform = function(options){
+
var opt = $.extend({},defaultOptions,options);
+
+
/* each form */
+
return this.each(function(){
+
var selfForm = $(this);
+
if(selfForm.hasClass('jqtransformdone')) {return;}
+
selfForm.addClass('jqtransformdone');
+
+
$('input:submit, input:reset, input[type="button"]', this).jqTransInputButton();
+
$('input:text, input:password', this).jqTransInputText();
+
$('input:checkbox', this).jqTransCheckBox();
+
$('input:radio', this).jqTransRadio();
+
$('textarea', this).jqTransTextarea();
+
+
if( $('select', this).jqTransSelect().length > 0 ){jqTransformAddDocumentListener();}
+
selfForm.bind('reset',function(){var action = function(){jqTransformReset(this);}; window.setTimeout(action, 10);});
+
+
//preloading dont needed anymore since normal, focus and hover image are the same one
+
/*if(opt.preloadImg && !jqTransformImgPreloaded){
+
jqTransformImgPreloaded = true;
+
var oInputText = $('input:text:first', selfForm);
+
if(oInputText.length > 0){
+
//pour ie on eleve les ""
+
var strWrapperImgUrl = oInputText.get(0).wrapper.css('background-image');
+
jqTransformPreloadHoverFocusImg(strWrapperImgUrl);
+
var strInnerImgUrl = $('div.jqTransformInputInner',$(oInputText.get(0).wrapper)).css('background-image');
+
jqTransformPreloadHoverFocusImg(strInnerImgUrl);
+
}
+
+
var oTextarea = $('textarea',selfForm);
+
if(oTextarea.length > 0){
+
var oTable = oTextarea.get(0).oTable;
+
$('td',oTable).each(function(){
+
var strImgBack = $(this).css('background-image');
+
jqTransformPreloadHoverFocusImg(strImgBack);
+
});
+
}
+
}*/
+
+
+
}); /* End Form each */
+
+
};/* End the Plugin */
+
  
})(jQuery);
+
if (typeof module != 'undefined' && module.exports) {
 
+
    module.exports = Hammer;
 +
} else {
 +
    window[exportName] = Hammer;
 +
}
  
</script>
+
})(window, document, 'Hammer');
</html>
+

Latest revision as of 08:44, 27 August 2015

/****************************

   custom js
                                                          • /

jQuery(function($) {

 // Mobile sidebars
 $.fn.expandableSidebar = function(expandedClass) {
   var $me = this;
   $me.on('click', function() {
     if(!$me.hasClass(expandedClass)) {
       $me.addClass(expandedClass);
     } else {
       $me.removeClass(expandedClass);
     }
   });
 }
 
 // Interval loop
 $.fn.intervalLoop = function(condition, action, duration, limit) {
   var counter = 0;
   var looper = setInterval(function(){
     if (counter >= limit || $.fn.checkIfElementExists(condition)) {
       clearInterval(looper);
     } else {
       action();
       counter++;
     }
   }, duration);
 }
 // Check if element exists
 $.fn.checkIfElementExists = function(selector) {
   return $(selector).length;
 }
 var parisController = {
   init: function(opts) {
     var base = this;
     // Add classes to elements
     base._addClasses();
     setTimeout(function(){
       base._checkCartItems();
       base._attachEvents();
     }, 1000);
   },
   _addClasses: function() {
     var base = this;
     // Add fade in class to nav + logo
     $('.landing-page').addClass('fade-in');
     // Add class to nav items with subnav
     $('.wsite-menu-default').find('li.wsite-menu-item-wrap').each(function(){
       var $me = $(this);
       if($me.children('.wsite-menu-wrap').length > 0) {
         $me.addClass('has-submenu');
         $('').insertAfter($me.children('a.wsite-menu-item'));
       }
     });
     // ADd class to subnav items with subnav
     $('.wsite-menu').find('li.wsite-menu-subitem-wrap').each(function(){
       var $me = $(this);
       if($me.children('.wsite-menu-wrap').length > 0) {
         $me.addClass('has-submenu');
         $('').insertAfter($me.children('a.wsite-menu-subitem'));
       }
     });
     // Keep subnav open if submenu item is active
     $('.wsite-menu-wrap').find('li.wsite-menu-subitem-wrap').each(function(){
       var $me = $(this);
       if($me.hasClass('wsite-nav-current')) {
         $me.parents().addClass('open');
       }
     });
     // Add placeholder text to inputs
     $('.wsite-form-sublabel').each(function(){
       var sublabel = $(this).text();
       $(this).prev('.wsite-form-input').attr('placeholder', sublabel);
     });
     // Add fullwidth class to gallery thumbs if less than 6
     $('.imageGallery').each(function(){
       if ($(this).children('div').length <= 6) {
         $(this).children('div').addClass('fullwidth-mobile');
       }
     });
   },
   _checkCartItems: function() {
     var base = this;
     
     if($('#wsite-mini-cart').find('li.wsite-product-item').length > 0) {
       $('body').addClass('cart-full');
     } else {
       $('body').removeClass('cart-full');
     }
   },
   _moveLogin: function() {
     var loginDetach = $('#member-login').detach();
     $('.mobile-nav .wsite-menu-default li:last-child').after(loginDetach);
   },
   _attachEvents: function() {
     var base = this;
     // Move cart + login
     if ($(window).width() <= 992) {
       $.fn.intervalLoop('.mobile-nav #member-login', base._moveLogin, 800, 5);
     }
     // Subnav toggle
     $('li.has-submenu').each(function(){
       var $me = $(this);
       var caret = $me.children('span.icon-caret');
       caret.on('click', function(){          
         if($me.children('.wsite-menu-wrap.open').length > 0) {
           caret.siblings('.wsite-menu-wrap').removeClass('open');
         } else {
           caret.siblings('.wsite-menu-wrap').addClass('open');
         }
       });
     });
     // Scroll on landing page
     $('#contentArrow').on('click', function() {
       $('html, body').animate({
         scrollTop: $('.main-wrap').offset().top - $('.paris-header').outerHeight()
       }, 500);
     });
     // Store category dropdown
     $('.wsite-com-sidebar').expandableSidebar('sidebar-expanded');
     // Search filters dropdown
     $('#wsite-search-sidebar').expandableSidebar('sidebar-expanded');
     // Init fancybox swipe on mobile
     if ('ontouchstart' in window) {
       $('body').on('click', 'a.w-fancybox', function() {
         base._initSwipeGallery();
       });
     }
   },
   _initSwipeGallery: function() {
     var base = this;
     setTimeout(function(){
       var touchGallery = document.getElementsByClassName('fancybox-wrap')[0];
       var mc = new Hammer(touchGallery);
       mc.on("panleft panright", function(ev) {
         if (ev.type == "panleft") {
           $("a.fancybox-next").trigger("click");
         } else if (ev.type == "panright") {
           $("a.fancybox-prev").trigger("click");
         }
         base._initSwipeGallery();
       });
     }, 500);
   }
 } 


 $(document).ready(function(){
   parisController.init();
 }); 

});

/***********************************

   Plugin js
                                                                      • /

/*! Hammer.JS - v2.0.4 - 2014-09-28

* http://hammerjs.github.io/
*
* Copyright (c) 2014 Jorik Tangelder;
* Licensed under the MIT license */

(function(window, document, exportName, undefined) {

 'use strict';

var VENDOR_PREFIXES = [, 'webkit', 'moz', 'MS', 'ms', 'o']; var TEST_ELEMENT = document.createElement('div');

var TYPE_FUNCTION = 'function';

var round = Math.round; var abs = Math.abs; var now = Date.now;

/**

* set a timeout with a given scope
* @param {Function} fn
* @param {Number} timeout
* @param {Object} context
* @returns {number}
*/

function setTimeoutContext(fn, timeout, context) {

   return setTimeout(bindFn(fn, context), timeout);

}

/**

* if the argument is an array, we want to execute the fn on each entry
* if it aint an array we don't want to do a thing.
* this is used by all the methods that accept a single and array argument.
* @param {*|Array} arg
* @param {String} fn
* @param {Object} [context]
* @returns {Boolean}
*/

function invokeArrayArg(arg, fn, context) {

   if (Array.isArray(arg)) {
       each(arg, context[fn], context);
       return true;
   }
   return false;

}

/**

* walk objects and arrays
* @param {Object} obj
* @param {Function} iterator
* @param {Object} context
*/

function each(obj, iterator, context) {

   var i;
   if (!obj) {
       return;
   }
   if (obj.forEach) {
       obj.forEach(iterator, context);
   } else if (obj.length !== undefined) {
       i = 0;
       while (i < obj.length) {
           iterator.call(context, obj[i], i, obj);
           i++;
       }
   } else {
       for (i in obj) {
           obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
       }
   }

}

/**

* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} dest
* @param {Object} src
* @param {Boolean} [merge]
* @returns {Object} dest
*/

function extend(dest, src, merge) {

   var keys = Object.keys(src);
   var i = 0;
   while (i < keys.length) {
       if (!merge || (merge && dest[keys[i]] === undefined)) {
           dest[keys[i]] = src[keys[i]];
       }
       i++;
   }
   return dest;

}

/**

* merge the values from src in the dest.
* means that properties that exist in dest will not be overwritten by src
* @param {Object} dest
* @param {Object} src
* @returns {Object} dest
*/

function merge(dest, src) {

   return extend(dest, src, true);

}

/**

* simple class inheritance
* @param {Function} child
* @param {Function} base
* @param {Object} [properties]
*/

function inherit(child, base, properties) {

   var baseP = base.prototype,
       childP;
   childP = child.prototype = Object.create(baseP);
   childP.constructor = child;
   childP._super = baseP;
   if (properties) {
       extend(childP, properties);
   }

}

/**

* simple function bind
* @param {Function} fn
* @param {Object} context
* @returns {Function}
*/

function bindFn(fn, context) {

   return function boundFn() {
       return fn.apply(context, arguments);
   };

}

/**

* let a boolean value also be a function that must return a boolean
* this first item in args will be used as the context
* @param {Boolean|Function} val
* @param {Array} [args]
* @returns {Boolean}
*/

function boolOrFn(val, args) {

   if (typeof val == TYPE_FUNCTION) {
       return val.apply(args ? args[0] || undefined : undefined, args);
   }
   return val;

}

/**

* use the val2 when val1 is undefined
* @param {*} val1
* @param {*} val2
* @returns {*}
*/

function ifUndefined(val1, val2) {

   return (val1 === undefined) ? val2 : val1;

}

/**

* addEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/

function addEventListeners(target, types, handler) {

   each(splitStr(types), function(type) {
       target.addEventListener(type, handler, false);
   });

}

/**

* removeEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/

function removeEventListeners(target, types, handler) {

   each(splitStr(types), function(type) {
       target.removeEventListener(type, handler, false);
   });

}

/**

* find if a node is in the given parent
* @method hasParent
* @param {HTMLElement} node
* @param {HTMLElement} parent
* @return {Boolean} found
*/

function hasParent(node, parent) {

   while (node) {
       if (node == parent) {
           return true;
       }
       node = node.parentNode;
   }
   return false;

}

/**

* small indexOf wrapper
* @param {String} str
* @param {String} find
* @returns {Boolean} found
*/

function inStr(str, find) {

   return str.indexOf(find) > -1;

}

/**

* split string on whitespace
* @param {String} str
* @returns {Array} words
*/

function splitStr(str) {

   return str.trim().split(/\s+/g);

}

/**

* find if a array contains the object using indexOf or a simple polyFill
* @param {Array} src
* @param {String} find
* @param {String} [findByKey]
* @return {Boolean|Number} false when not found, or the index
*/

function inArray(src, find, findByKey) {

   if (src.indexOf && !findByKey) {
       return src.indexOf(find);
   } else {
       var i = 0;
       while (i < src.length) {
           if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
               return i;
           }
           i++;
       }
       return -1;
   }

}

/**

* convert array-like objects to real arrays
* @param {Object} obj
* @returns {Array}
*/

function toArray(obj) {

   return Array.prototype.slice.call(obj, 0);

}

/**

* unique array with objects based on a key (like 'id') or just by the array's value
* @param {Array} src [{id:1},{id:2},{id:1}]
* @param {String} [key]
* @param {Boolean} [sort=False]
* @returns {Array} [{id:1},{id:2}]
*/

function uniqueArray(src, key, sort) {

   var results = [];
   var values = [];
   var i = 0;
   while (i < src.length) {
       var val = key ? src[i][key] : src[i];
       if (inArray(values, val) < 0) {
           results.push(src[i]);
       }
       values[i] = val;
       i++;
   }
   if (sort) {
       if (!key) {
           results = results.sort();
       } else {
           results = results.sort(function sortUniqueArray(a, b) {
               return a[key] > b[key];
           });
       }
   }
   return results;

}

/**

* get the prefixed property
* @param {Object} obj
* @param {String} property
* @returns {String|Undefined} prefixed
*/

function prefixed(obj, property) {

   var prefix, prop;
   var camelProp = property[0].toUpperCase() + property.slice(1);
   var i = 0;
   while (i < VENDOR_PREFIXES.length) {
       prefix = VENDOR_PREFIXES[i];
       prop = (prefix) ? prefix + camelProp : property;
       if (prop in obj) {
           return prop;
       }
       i++;
   }
   return undefined;

}

/**

* get a unique id
* @returns {number} uniqueId
*/

var _uniqueId = 1; function uniqueId() {

   return _uniqueId++;

}

/**

* get the window object of an element
* @param {HTMLElement} element
* @returns {DocumentView|Window}
*/

function getWindowForElement(element) {

   var doc = element.ownerDocument;
   return (doc.defaultView || doc.parentWindow);

}

var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;

var SUPPORT_TOUCH = ('ontouchstart' in window); var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);

var INPUT_TYPE_TOUCH = 'touch'; var INPUT_TYPE_PEN = 'pen'; var INPUT_TYPE_MOUSE = 'mouse'; var INPUT_TYPE_KINECT = 'kinect';

var COMPUTE_INTERVAL = 25;

var INPUT_START = 1; var INPUT_MOVE = 2; var INPUT_END = 4; var INPUT_CANCEL = 8;

var DIRECTION_NONE = 1; var DIRECTION_LEFT = 2; var DIRECTION_RIGHT = 4; var DIRECTION_UP = 8; var DIRECTION_DOWN = 16;

var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;

var PROPS_XY = ['x', 'y']; var PROPS_CLIENT_XY = ['clientX', 'clientY'];

/**

* create new input type manager
* @param {Manager} manager
* @param {Function} callback
* @returns {Input}
* @constructor
*/

function Input(manager, callback) {

   var self = this;
   this.manager = manager;
   this.callback = callback;
   this.element = manager.element;
   this.target = manager.options.inputTarget;
   // smaller wrapper around the handler, for the scope and the enabled state of the manager,
   // so when disabled the input events are completely bypassed.
   this.domHandler = function(ev) {
       if (boolOrFn(manager.options.enable, [manager])) {
           self.handler(ev);
       }
   };
   this.init();

}

Input.prototype = {

   /**
    * should handle the inputEvent data and trigger the callback
    * @virtual
    */
   handler: function() { },
   /**
    * bind the events
    */
   init: function() {
       this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
       this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
       this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
   },
   /**
    * unbind the events
    */
   destroy: function() {
       this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
       this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
       this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
   }

};

/**

* create new input type manager
* called by the Manager constructor
* @param {Hammer} manager
* @returns {Input}
*/

function createInputInstance(manager) {

   var Type;
   var inputClass = manager.options.inputClass;
   if (inputClass) {
       Type = inputClass;
   } else if (SUPPORT_POINTER_EVENTS) {
       Type = PointerEventInput;
   } else if (SUPPORT_ONLY_TOUCH) {
       Type = TouchInput;
   } else if (!SUPPORT_TOUCH) {
       Type = MouseInput;
   } else {
       Type = TouchMouseInput;
   }
   return new (Type)(manager, inputHandler);

}

/**

* handle input events
* @param {Manager} manager
* @param {String} eventType
* @param {Object} input
*/

function inputHandler(manager, eventType, input) {

   var pointersLen = input.pointers.length;
   var changedPointersLen = input.changedPointers.length;
   var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
   var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
   input.isFirst = !!isFirst;
   input.isFinal = !!isFinal;
   if (isFirst) {
       manager.session = {};
   }
   // source event is the normalized value of the domEvents
   // like 'touchstart, mouseup, pointerdown'
   input.eventType = eventType;
   // compute scale, rotation etc
   computeInputData(manager, input);
   // emit secret event
   manager.emit('hammer.input', input);
   manager.recognize(input);
   manager.session.prevInput = input;

}

/**

* extend the data with some usable properties like scale, rotate, velocity etc
* @param {Object} manager
* @param {Object} input
*/

function computeInputData(manager, input) {

   var session = manager.session;
   var pointers = input.pointers;
   var pointersLength = pointers.length;
   // store the first input to calculate the distance and direction
   if (!session.firstInput) {
       session.firstInput = simpleCloneInputData(input);
   }
   // to compute scale and rotation we need to store the multiple touches
   if (pointersLength > 1 && !session.firstMultiple) {
       session.firstMultiple = simpleCloneInputData(input);
   } else if (pointersLength === 1) {
       session.firstMultiple = false;
   }
   var firstInput = session.firstInput;
   var firstMultiple = session.firstMultiple;
   var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
   var center = input.center = getCenter(pointers);
   input.timeStamp = now();
   input.deltaTime = input.timeStamp - firstInput.timeStamp;
   input.angle = getAngle(offsetCenter, center);
   input.distance = getDistance(offsetCenter, center);
   computeDeltaXY(session, input);
   input.offsetDirection = getDirection(input.deltaX, input.deltaY);
   input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
   input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
   computeIntervalInputData(session, input);
   // find the correct target
   var target = manager.element;
   if (hasParent(input.srcEvent.target, target)) {
       target = input.srcEvent.target;
   }
   input.target = target;

}

function computeDeltaXY(session, input) {

   var center = input.center;
   var offset = session.offsetDelta || {};
   var prevDelta = session.prevDelta || {};
   var prevInput = session.prevInput || {};
   if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
       prevDelta = session.prevDelta = {
           x: prevInput.deltaX || 0,
           y: prevInput.deltaY || 0
       };
       offset = session.offsetDelta = {
           x: center.x,
           y: center.y
       };
   }
   input.deltaX = prevDelta.x + (center.x - offset.x);
   input.deltaY = prevDelta.y + (center.y - offset.y);

}

/**

* velocity is calculated every x ms
* @param {Object} session
* @param {Object} input
*/

function computeIntervalInputData(session, input) {

   var last = session.lastInterval || input,
       deltaTime = input.timeStamp - last.timeStamp,
       velocity, velocityX, velocityY, direction;
   if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
       var deltaX = last.deltaX - input.deltaX;
       var deltaY = last.deltaY - input.deltaY;
       var v = getVelocity(deltaTime, deltaX, deltaY);
       velocityX = v.x;
       velocityY = v.y;
       velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
       direction = getDirection(deltaX, deltaY);
       session.lastInterval = input;
   } else {
       // use latest velocity info if it doesn't overtake a minimum period
       velocity = last.velocity;
       velocityX = last.velocityX;
       velocityY = last.velocityY;
       direction = last.direction;
   }
   input.velocity = velocity;
   input.velocityX = velocityX;
   input.velocityY = velocityY;
   input.direction = direction;

}

/**

* create a simple clone from the input used for storage of firstInput and firstMultiple
* @param {Object} input
* @returns {Object} clonedInputData
*/

function simpleCloneInputData(input) {

   // make a simple copy of the pointers because we will get a reference if we don't
   // we only need clientXY for the calculations
   var pointers = [];
   var i = 0;
   while (i < input.pointers.length) {
       pointers[i] = {
           clientX: round(input.pointers[i].clientX),
           clientY: round(input.pointers[i].clientY)
       };
       i++;
   }
   return {
       timeStamp: now(),
       pointers: pointers,
       center: getCenter(pointers),
       deltaX: input.deltaX,
       deltaY: input.deltaY
   };

}

/**

* get the center of all the pointers
* @param {Array} pointers
* @return {Object} center contains `x` and `y` properties
*/

function getCenter(pointers) {

   var pointersLength = pointers.length;
   // no need to loop when only one touch
   if (pointersLength === 1) {
       return {
           x: round(pointers[0].clientX),
           y: round(pointers[0].clientY)
       };
   }
   var x = 0, y = 0, i = 0;
   while (i < pointersLength) {
       x += pointers[i].clientX;
       y += pointers[i].clientY;
       i++;
   }
   return {
       x: round(x / pointersLength),
       y: round(y / pointersLength)
   };

}

/**

* calculate the velocity between two points. unit is in px per ms.
* @param {Number} deltaTime
* @param {Number} x
* @param {Number} y
* @return {Object} velocity `x` and `y`
*/

function getVelocity(deltaTime, x, y) {

   return {
       x: x / deltaTime || 0,
       y: y / deltaTime || 0
   };

}

/**

* get the direction between two points
* @param {Number} x
* @param {Number} y
* @return {Number} direction
*/

function getDirection(x, y) {

   if (x === y) {
       return DIRECTION_NONE;
   }
   if (abs(x) >= abs(y)) {
       return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
   }
   return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;

}

/**

* calculate the absolute distance between two points
* @param {Object} p1 {x, y}
* @param {Object} p2 {x, y}
* @param {Array} [props] containing x and y keys
* @return {Number} distance
*/

function getDistance(p1, p2, props) {

   if (!props) {
       props = PROPS_XY;
   }
   var x = p2[props[0]] - p1[props[0]],
       y = p2[props[1]] - p1[props[1]];
   return Math.sqrt((x * x) + (y * y));

}

/**

* calculate the angle between two coordinates
* @param {Object} p1
* @param {Object} p2
* @param {Array} [props] containing x and y keys
* @return {Number} angle
*/

function getAngle(p1, p2, props) {

   if (!props) {
       props = PROPS_XY;
   }
   var x = p2[props[0]] - p1[props[0]],
       y = p2[props[1]] - p1[props[1]];
   return Math.atan2(y, x) * 180 / Math.PI;

}

/**

* calculate the rotation degrees between two pointersets
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} rotation
*/

function getRotation(start, end) {

   return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);

}

/**

* calculate the scale factor between two pointersets
* no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} scale
*/

function getScale(start, end) {

   return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);

}

var MOUSE_INPUT_MAP = {

   mousedown: INPUT_START,
   mousemove: INPUT_MOVE,
   mouseup: INPUT_END

};

var MOUSE_ELEMENT_EVENTS = 'mousedown'; var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';

/**

* Mouse events input
* @constructor
* @extends Input
*/

function MouseInput() {

   this.evEl = MOUSE_ELEMENT_EVENTS;
   this.evWin = MOUSE_WINDOW_EVENTS;
   this.allow = true; // used by Input.TouchMouse to disable mouse events
   this.pressed = false; // mousedown state
   Input.apply(this, arguments);

}

inherit(MouseInput, Input, {

   /**
    * handle mouse events
    * @param {Object} ev
    */
   handler: function MEhandler(ev) {
       var eventType = MOUSE_INPUT_MAP[ev.type];
       // on start we want to have the left mouse button down
       if (eventType & INPUT_START && ev.button === 0) {
           this.pressed = true;
       }
       if (eventType & INPUT_MOVE && ev.which !== 1) {
           eventType = INPUT_END;
       }
       // mouse must be down, and mouse events are allowed (see the TouchMouse input)
       if (!this.pressed || !this.allow) {
           return;
       }
       if (eventType & INPUT_END) {
           this.pressed = false;
       }
       this.callback(this.manager, eventType, {
           pointers: [ev],
           changedPointers: [ev],
           pointerType: INPUT_TYPE_MOUSE,
           srcEvent: ev
       });
   }

});

var POINTER_INPUT_MAP = {

   pointerdown: INPUT_START,
   pointermove: INPUT_MOVE,
   pointerup: INPUT_END,
   pointercancel: INPUT_CANCEL,
   pointerout: INPUT_CANCEL

};

// in IE10 the pointer types is defined as an enum var IE10_POINTER_TYPE_ENUM = {

   2: INPUT_TYPE_TOUCH,
   3: INPUT_TYPE_PEN,
   4: INPUT_TYPE_MOUSE,
   5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816

};

var POINTER_ELEMENT_EVENTS = 'pointerdown'; var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';

// IE10 has prefixed support, and case-sensitive if (window.MSPointerEvent) {

   POINTER_ELEMENT_EVENTS = 'MSPointerDown';
   POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';

}

/**

* Pointer events input
* @constructor
* @extends Input
*/

function PointerEventInput() {

   this.evEl = POINTER_ELEMENT_EVENTS;
   this.evWin = POINTER_WINDOW_EVENTS;
   Input.apply(this, arguments);
   this.store = (this.manager.session.pointerEvents = []);

}

inherit(PointerEventInput, Input, {

   /**
    * handle mouse events
    * @param {Object} ev
    */
   handler: function PEhandler(ev) {
       var store = this.store;
       var removePointer = false;
       var eventTypeNormalized = ev.type.toLowerCase().replace('ms', );
       var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
       var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
       var isTouch = (pointerType == INPUT_TYPE_TOUCH);
       // get index of the event in the store
       var storeIndex = inArray(store, ev.pointerId, 'pointerId');
       // start and mouse must be down
       if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
           if (storeIndex < 0) {
               store.push(ev);
               storeIndex = store.length - 1;
           }
       } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
           removePointer = true;
       }
       // it not found, so the pointer hasn't been down (so it's probably a hover)
       if (storeIndex < 0) {
           return;
       }
       // update the event in the store
       store[storeIndex] = ev;
       this.callback(this.manager, eventType, {
           pointers: store,
           changedPointers: [ev],
           pointerType: pointerType,
           srcEvent: ev
       });
       if (removePointer) {
           // remove from the store
           store.splice(storeIndex, 1);
       }
   }

});

var SINGLE_TOUCH_INPUT_MAP = {

   touchstart: INPUT_START,
   touchmove: INPUT_MOVE,
   touchend: INPUT_END,
   touchcancel: INPUT_CANCEL

};

var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';

/**

* Touch events input
* @constructor
* @extends Input
*/

function SingleTouchInput() {

   this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
   this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
   this.started = false;
   Input.apply(this, arguments);

}

inherit(SingleTouchInput, Input, {

   handler: function TEhandler(ev) {
       var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
       // should we handle the touch events?
       if (type === INPUT_START) {
           this.started = true;
       }
       if (!this.started) {
           return;
       }
       var touches = normalizeSingleTouches.call(this, ev, type);
       // when done, reset the started state
       if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
           this.started = false;
       }
       this.callback(this.manager, type, {
           pointers: touches[0],
           changedPointers: touches[1],
           pointerType: INPUT_TYPE_TOUCH,
           srcEvent: ev
       });
   }

});

/**

* @this {TouchInput}
* @param {Object} ev
* @param {Number} type flag
* @returns {undefined|Array} [all, changed]
*/

function normalizeSingleTouches(ev, type) {

   var all = toArray(ev.touches);
   var changed = toArray(ev.changedTouches);
   if (type & (INPUT_END | INPUT_CANCEL)) {
       all = uniqueArray(all.concat(changed), 'identifier', true);
   }
   return [all, changed];

}

var TOUCH_INPUT_MAP = {

   touchstart: INPUT_START,
   touchmove: INPUT_MOVE,
   touchend: INPUT_END,
   touchcancel: INPUT_CANCEL

};

var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';

/**

* Multi-user touch events input
* @constructor
* @extends Input
*/

function TouchInput() {

   this.evTarget = TOUCH_TARGET_EVENTS;
   this.targetIds = {};
   Input.apply(this, arguments);

}

inherit(TouchInput, Input, {

   handler: function MTEhandler(ev) {
       var type = TOUCH_INPUT_MAP[ev.type];
       var touches = getTouches.call(this, ev, type);
       if (!touches) {
           return;
       }
       this.callback(this.manager, type, {
           pointers: touches[0],
           changedPointers: touches[1],
           pointerType: INPUT_TYPE_TOUCH,
           srcEvent: ev
       });
   }

});

/**

* @this {TouchInput}
* @param {Object} ev
* @param {Number} type flag
* @returns {undefined|Array} [all, changed]
*/

function getTouches(ev, type) {

   var allTouches = toArray(ev.touches);
   var targetIds = this.targetIds;
   // when there is only one touch, the process can be simplified
   if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
       targetIds[allTouches[0].identifier] = true;
       return [allTouches, allTouches];
   }
   var i,
       targetTouches,
       changedTouches = toArray(ev.changedTouches),
       changedTargetTouches = [],
       target = this.target;
   // get target touches from touches
   targetTouches = allTouches.filter(function(touch) {
       return hasParent(touch.target, target);
   });
   // collect touches
   if (type === INPUT_START) {
       i = 0;
       while (i < targetTouches.length) {
           targetIds[targetTouches[i].identifier] = true;
           i++;
       }
   }
   // filter changed touches to only contain touches that exist in the collected target ids
   i = 0;
   while (i < changedTouches.length) {
       if (targetIds[changedTouches[i].identifier]) {
           changedTargetTouches.push(changedTouches[i]);
       }
       // cleanup removed touches
       if (type & (INPUT_END | INPUT_CANCEL)) {
           delete targetIds[changedTouches[i].identifier];
       }
       i++;
   }
   if (!changedTargetTouches.length) {
       return;
   }
   return [
       // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
       uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
       changedTargetTouches
   ];

}

/**

* Combined touch and mouse input
*
* Touch has a higher priority then mouse, and while touching no mouse events are allowed.
* This because touch devices also emit mouse events while doing a touch.
*
* @constructor
* @extends Input
*/

function TouchMouseInput() {

   Input.apply(this, arguments);
   var handler = bindFn(this.handler, this);
   this.touch = new TouchInput(this.manager, handler);
   this.mouse = new MouseInput(this.manager, handler);

}

inherit(TouchMouseInput, Input, {

   /**
    * handle mouse and touch events
    * @param {Hammer} manager
    * @param {String} inputEvent
    * @param {Object} inputData
    */
   handler: function TMEhandler(manager, inputEvent, inputData) {
       var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
           isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
       // when we're in a touch event, so  block all upcoming mouse events
       // most mobile browser also emit mouseevents, right after touchstart
       if (isTouch) {
           this.mouse.allow = false;
       } else if (isMouse && !this.mouse.allow) {
           return;
       }
       // reset the allowMouse when we're done
       if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
           this.mouse.allow = true;
       }
       this.callback(manager, inputEvent, inputData);
   },
   /**
    * remove the event listeners
    */
   destroy: function destroy() {
       this.touch.destroy();
       this.mouse.destroy();
   }

});

var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;

// magical touchAction value var TOUCH_ACTION_COMPUTE = 'compute'; var TOUCH_ACTION_AUTO = 'auto'; var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented var TOUCH_ACTION_NONE = 'none'; var TOUCH_ACTION_PAN_X = 'pan-x'; var TOUCH_ACTION_PAN_Y = 'pan-y';

/**

* Touch Action
* sets the touchAction property or uses the js alternative
* @param {Manager} manager
* @param {String} value
* @constructor
*/

function TouchAction(manager, value) {

   this.manager = manager;
   this.set(value);

}

TouchAction.prototype = {

   /**
    * set the touchAction value on the element or enable the polyfill
    * @param {String} value
    */
   set: function(value) {
       // find out the touch-action by the event handlers
       if (value == TOUCH_ACTION_COMPUTE) {
           value = this.compute();
       }
       if (NATIVE_TOUCH_ACTION) {
           this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
       }
       this.actions = value.toLowerCase().trim();
   },
   /**
    * just re-set the touchAction value
    */
   update: function() {
       this.set(this.manager.options.touchAction);
   },
   /**
    * compute the value for the touchAction property based on the recognizer's settings
    * @returns {String} value
    */
   compute: function() {
       var actions = [];
       each(this.manager.recognizers, function(recognizer) {
           if (boolOrFn(recognizer.options.enable, [recognizer])) {
               actions = actions.concat(recognizer.getTouchAction());
           }
       });
       return cleanTouchActions(actions.join(' '));
   },
   /**
    * this method is called on each input cycle and provides the preventing of the browser behavior
    * @param {Object} input
    */
   preventDefaults: function(input) {
       // not needed with native support for the touchAction property
       if (NATIVE_TOUCH_ACTION) {
           return;
       }
       var srcEvent = input.srcEvent;
       var direction = input.offsetDirection;
       // if the touch action did prevented once this session
       if (this.manager.session.prevented) {
           srcEvent.preventDefault();
           return;
       }
       var actions = this.actions;
       var hasNone = inStr(actions, TOUCH_ACTION_NONE);
       var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
       var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
       if (hasNone ||
           (hasPanY && direction & DIRECTION_HORIZONTAL) ||
           (hasPanX && direction & DIRECTION_VERTICAL)) {
           return this.preventSrc(srcEvent);
       }
   },
   /**
    * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
    * @param {Object} srcEvent
    */
   preventSrc: function(srcEvent) {
       this.manager.session.prevented = true;
       srcEvent.preventDefault();
   }

};

/**

* when the touchActions are collected they are not a valid value, so we need to clean things up. *
* @param {String} actions
* @returns {*}
*/

function cleanTouchActions(actions) {

   // none
   if (inStr(actions, TOUCH_ACTION_NONE)) {
       return TOUCH_ACTION_NONE;
   }
   var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
   var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
   // pan-x and pan-y can be combined
   if (hasPanX && hasPanY) {
       return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
   }
   // pan-x OR pan-y
   if (hasPanX || hasPanY) {
       return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
   }
   // manipulation
   if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
       return TOUCH_ACTION_MANIPULATION;
   }
   return TOUCH_ACTION_AUTO;

}

/**

* Recognizer flow explained; *
* All recognizers have the initial state of POSSIBLE when a input session starts.
* The definition of a input session is from the first input until the last input, with all it's movement in it. *
* Example session for mouse-input: mousedown -> mousemove -> mouseup
*
* On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
* which determines with state it should be.
*
* If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
* POSSIBLE to give it another change on the next cycle.
*
*               Possible
*                  |
*            +-----+---------------+
*            |                     |
*      +-----+-----+               |
*      |           |               |
*   Failed      Cancelled          |
*                          +-------+------+
*                          |              |
*                      Recognized       Began
*                                         |
*                                      Changed
*                                         |
*                                  Ended/Recognized
*/

var STATE_POSSIBLE = 1; var STATE_BEGAN = 2; var STATE_CHANGED = 4; var STATE_ENDED = 8; var STATE_RECOGNIZED = STATE_ENDED; var STATE_CANCELLED = 16; var STATE_FAILED = 32;

/**

* Recognizer
* Every recognizer needs to extend from this class.
* @constructor
* @param {Object} options
*/

function Recognizer(options) {

   this.id = uniqueId();
   this.manager = null;
   this.options = merge(options || {}, this.defaults);
   // default is enable true
   this.options.enable = ifUndefined(this.options.enable, true);
   this.state = STATE_POSSIBLE;
   this.simultaneous = {};
   this.requireFail = [];

}

Recognizer.prototype = {

   /**
    * @virtual
    * @type {Object}
    */
   defaults: {},
   /**
    * set options
    * @param {Object} options
    * @return {Recognizer}
    */
   set: function(options) {
       extend(this.options, options);
       // also update the touchAction, in case something changed about the directions/enabled state
       this.manager && this.manager.touchAction.update();
       return this;
   },
   /**
    * recognize simultaneous with an other recognizer.
    * @param {Recognizer} otherRecognizer
    * @returns {Recognizer} this
    */
   recognizeWith: function(otherRecognizer) {
       if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
           return this;
       }
       var simultaneous = this.simultaneous;
       otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
       if (!simultaneous[otherRecognizer.id]) {
           simultaneous[otherRecognizer.id] = otherRecognizer;
           otherRecognizer.recognizeWith(this);
       }
       return this;
   },
   /**
    * drop the simultaneous link. it doesnt remove the link on the other recognizer.
    * @param {Recognizer} otherRecognizer
    * @returns {Recognizer} this
    */
   dropRecognizeWith: function(otherRecognizer) {
       if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
           return this;
       }
       otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
       delete this.simultaneous[otherRecognizer.id];
       return this;
   },
   /**
    * recognizer can only run when an other is failing
    * @param {Recognizer} otherRecognizer
    * @returns {Recognizer} this
    */
   requireFailure: function(otherRecognizer) {
       if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
           return this;
       }
       var requireFail = this.requireFail;
       otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
       if (inArray(requireFail, otherRecognizer) === -1) {
           requireFail.push(otherRecognizer);
           otherRecognizer.requireFailure(this);
       }
       return this;
   },
   /**
    * drop the requireFailure link. it does not remove the link on the other recognizer.
    * @param {Recognizer} otherRecognizer
    * @returns {Recognizer} this
    */
   dropRequireFailure: function(otherRecognizer) {
       if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
           return this;
       }
       otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
       var index = inArray(this.requireFail, otherRecognizer);
       if (index > -1) {
           this.requireFail.splice(index, 1);
       }
       return this;
   },
   /**
    * has require failures boolean
    * @returns {boolean}
    */
   hasRequireFailures: function() {
       return this.requireFail.length > 0;
   },
   /**
    * if the recognizer can recognize simultaneous with an other recognizer
    * @param {Recognizer} otherRecognizer
    * @returns {Boolean}
    */
   canRecognizeWith: function(otherRecognizer) {
       return !!this.simultaneous[otherRecognizer.id];
   },
   /**
    * You should use `tryEmit` instead of `emit` directly to check
    * that all the needed recognizers has failed before emitting.
    * @param {Object} input
    */
   emit: function(input) {
       var self = this;
       var state = this.state;
       function emit(withState) {
           self.manager.emit(self.options.event + (withState ? stateStr(state) : ), input);
       }
       // 'panstart' and 'panmove'
       if (state < STATE_ENDED) {
           emit(true);
       }
       emit(); // simple 'eventName' events
       // panend and pancancel
       if (state >= STATE_ENDED) {
           emit(true);
       }
   },
   /**
    * Check that all the require failure recognizers has failed,
    * if true, it emits a gesture event,
    * otherwise, setup the state to FAILED.
    * @param {Object} input
    */
   tryEmit: function(input) {
       if (this.canEmit()) {
           return this.emit(input);
       }
       // it's failing anyway
       this.state = STATE_FAILED;
   },
   /**
    * can we emit?
    * @returns {boolean}
    */
   canEmit: function() {
       var i = 0;
       while (i < this.requireFail.length) {
           if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
               return false;
           }
           i++;
       }
       return true;
   },
   /**
    * update the recognizer
    * @param {Object} inputData
    */
   recognize: function(inputData) {
       // make a new copy of the inputData
       // so we can change the inputData without messing up the other recognizers
       var inputDataClone = extend({}, inputData);
       // is is enabled and allow recognizing?
       if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
           this.reset();
           this.state = STATE_FAILED;
           return;
       }
       // reset when we've reached the end
       if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
           this.state = STATE_POSSIBLE;
       }
       this.state = this.process(inputDataClone);
       // the recognizer has recognized a gesture
       // so trigger an event
       if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
           this.tryEmit(inputDataClone);
       }
   },
   /**
    * return the state of the recognizer
    * the actual recognizing happens in this method
    * @virtual
    * @param {Object} inputData
    * @returns {Const} STATE
    */
   process: function(inputData) { }, // jshint ignore:line
   /**
    * return the preferred touch-action
    * @virtual
    * @returns {Array}
    */
   getTouchAction: function() { },
   /**
    * called when the gesture isn't allowed to recognize
    * like when another is being recognized or it is disabled
    * @virtual
    */
   reset: function() { }

};

/**

* get a usable string, used as event postfix
* @param {Const} state
* @returns {String} state
*/

function stateStr(state) {

   if (state & STATE_CANCELLED) {
       return 'cancel';
   } else if (state & STATE_ENDED) {
       return 'end';
   } else if (state & STATE_CHANGED) {
       return 'move';
   } else if (state & STATE_BEGAN) {
       return 'start';
   }
   return ;

}

/**

* direction cons to string
* @param {Const} direction
* @returns {String}
*/

function directionStr(direction) {

   if (direction == DIRECTION_DOWN) {
       return 'down';
   } else if (direction == DIRECTION_UP) {
       return 'up';
   } else if (direction == DIRECTION_LEFT) {
       return 'left';
   } else if (direction == DIRECTION_RIGHT) {
       return 'right';
   }
   return ;

}

/**

* get a recognizer by name if it is bound to a manager
* @param {Recognizer|String} otherRecognizer
* @param {Recognizer} recognizer
* @returns {Recognizer}
*/

function getRecognizerByNameIfManager(otherRecognizer, recognizer) {

   var manager = recognizer.manager;
   if (manager) {
       return manager.get(otherRecognizer);
   }
   return otherRecognizer;

}

/**

* This recognizer is just used as a base for the simple attribute recognizers.
* @constructor
* @extends Recognizer
*/

function AttrRecognizer() {

   Recognizer.apply(this, arguments);

}

inherit(AttrRecognizer, Recognizer, {

   /**
    * @namespace
    * @memberof AttrRecognizer
    */
   defaults: {
       /**
        * @type {Number}
        * @default 1
        */
       pointers: 1
   },
   /**
    * Used to check if it the recognizer receives valid input, like input.distance > 10.
    * @memberof AttrRecognizer
    * @param {Object} input
    * @returns {Boolean} recognized
    */
   attrTest: function(input) {
       var optionPointers = this.options.pointers;
       return optionPointers === 0 || input.pointers.length === optionPointers;
   },
   /**
    * Process the input and return the state for the recognizer
    * @memberof AttrRecognizer
    * @param {Object} input
    * @returns {*} State
    */
   process: function(input) {
       var state = this.state;
       var eventType = input.eventType;
       var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
       var isValid = this.attrTest(input);
       // on cancel input and we've recognized before, return STATE_CANCELLED
       if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
           return state | STATE_CANCELLED;
       } else if (isRecognized || isValid) {
           if (eventType & INPUT_END) {
               return state | STATE_ENDED;
           } else if (!(state & STATE_BEGAN)) {
               return STATE_BEGAN;
           }
           return state | STATE_CHANGED;
       }
       return STATE_FAILED;
   }

});

/**

* Pan
* Recognized when the pointer is down and moved in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/

function PanRecognizer() {

   AttrRecognizer.apply(this, arguments);
   this.pX = null;
   this.pY = null;

}

inherit(PanRecognizer, AttrRecognizer, {

   /**
    * @namespace
    * @memberof PanRecognizer
    */
   defaults: {
       event: 'pan',
       threshold: 10,
       pointers: 1,
       direction: DIRECTION_ALL
   },
   getTouchAction: function() {
       var direction = this.options.direction;
       var actions = [];
       if (direction & DIRECTION_HORIZONTAL) {
           actions.push(TOUCH_ACTION_PAN_Y);
       }
       if (direction & DIRECTION_VERTICAL) {
           actions.push(TOUCH_ACTION_PAN_X);
       }
       return actions;
   },
   directionTest: function(input) {
       var options = this.options;
       var hasMoved = true;
       var distance = input.distance;
       var direction = input.direction;
       var x = input.deltaX;
       var y = input.deltaY;
       // lock to axis?
       if (!(direction & options.direction)) {
           if (options.direction & DIRECTION_HORIZONTAL) {
               direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
               hasMoved = x != this.pX;
               distance = Math.abs(input.deltaX);
           } else {
               direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
               hasMoved = y != this.pY;
               distance = Math.abs(input.deltaY);
           }
       }
       input.direction = direction;
       return hasMoved && distance > options.threshold && direction & options.direction;
   },
   attrTest: function(input) {
       return AttrRecognizer.prototype.attrTest.call(this, input) &&
           (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
   },
   emit: function(input) {
       this.pX = input.deltaX;
       this.pY = input.deltaY;
       var direction = directionStr(input.direction);
       if (direction) {
           this.manager.emit(this.options.event + direction, input);
       }
       this._super.emit.call(this, input);
   }

});

/**

* Pinch
* Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
* @constructor
* @extends AttrRecognizer
*/

function PinchRecognizer() {

   AttrRecognizer.apply(this, arguments);

}

inherit(PinchRecognizer, AttrRecognizer, {

   /**
    * @namespace
    * @memberof PinchRecognizer
    */
   defaults: {
       event: 'pinch',
       threshold: 0,
       pointers: 2
   },
   getTouchAction: function() {
       return [TOUCH_ACTION_NONE];
   },
   attrTest: function(input) {
       return this._super.attrTest.call(this, input) &&
           (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
   },
   emit: function(input) {
       this._super.emit.call(this, input);
       if (input.scale !== 1) {
           var inOut = input.scale < 1 ? 'in' : 'out';
           this.manager.emit(this.options.event + inOut, input);
       }
   }

});

/**

* Press
* Recognized when the pointer is down for x ms without any movement.
* @constructor
* @extends Recognizer
*/

function PressRecognizer() {

   Recognizer.apply(this, arguments);
   this._timer = null;
   this._input = null;

}

inherit(PressRecognizer, Recognizer, {

   /**
    * @namespace
    * @memberof PressRecognizer
    */
   defaults: {
       event: 'press',
       pointers: 1,
       time: 500, // minimal time of the pointer to be pressed
       threshold: 5 // a minimal movement is ok, but keep it low
   },
   getTouchAction: function() {
       return [TOUCH_ACTION_AUTO];
   },
   process: function(input) {
       var options = this.options;
       var validPointers = input.pointers.length === options.pointers;
       var validMovement = input.distance < options.threshold;
       var validTime = input.deltaTime > options.time;
       this._input = input;
       // we only allow little movement
       // and we've reached an end event, so a tap is possible
       if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
           this.reset();
       } else if (input.eventType & INPUT_START) {
           this.reset();
           this._timer = setTimeoutContext(function() {
               this.state = STATE_RECOGNIZED;
               this.tryEmit();
           }, options.time, this);
       } else if (input.eventType & INPUT_END) {
           return STATE_RECOGNIZED;
       }
       return STATE_FAILED;
   },
   reset: function() {
       clearTimeout(this._timer);
   },
   emit: function(input) {
       if (this.state !== STATE_RECOGNIZED) {
           return;
       }
       if (input && (input.eventType & INPUT_END)) {
           this.manager.emit(this.options.event + 'up', input);
       } else {
           this._input.timeStamp = now();
           this.manager.emit(this.options.event, this._input);
       }
   }

});

/**

* Rotate
* Recognized when two or more pointer are moving in a circular motion.
* @constructor
* @extends AttrRecognizer
*/

function RotateRecognizer() {

   AttrRecognizer.apply(this, arguments);

}

inherit(RotateRecognizer, AttrRecognizer, {

   /**
    * @namespace
    * @memberof RotateRecognizer
    */
   defaults: {
       event: 'rotate',
       threshold: 0,
       pointers: 2
   },
   getTouchAction: function() {
       return [TOUCH_ACTION_NONE];
   },
   attrTest: function(input) {
       return this._super.attrTest.call(this, input) &&
           (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
   }

});

/**

* Swipe
* Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/

function SwipeRecognizer() {

   AttrRecognizer.apply(this, arguments);

}

inherit(SwipeRecognizer, AttrRecognizer, {

   /**
    * @namespace
    * @memberof SwipeRecognizer
    */
   defaults: {
       event: 'swipe',
       threshold: 10,
       velocity: 0.65,
       direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
       pointers: 1
   },
   getTouchAction: function() {
       return PanRecognizer.prototype.getTouchAction.call(this);
   },
   attrTest: function(input) {
       var direction = this.options.direction;
       var velocity;
       if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
           velocity = input.velocity;
       } else if (direction & DIRECTION_HORIZONTAL) {
           velocity = input.velocityX;
       } else if (direction & DIRECTION_VERTICAL) {
           velocity = input.velocityY;
       }
       return this._super.attrTest.call(this, input) &&
           direction & input.direction &&
           input.distance > this.options.threshold &&
           abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
   },
   emit: function(input) {
       var direction = directionStr(input.direction);
       if (direction) {
           this.manager.emit(this.options.event + direction, input);
       }
       this.manager.emit(this.options.event, input);
   }

});

/**

* A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
* between the given interval and position. The delay option can be used to recognize multi-taps without firing
* a single tap.
*
* The eventData from the emitted event contains the property `tapCount`, which contains the amount of
* multi-taps being recognized.
* @constructor
* @extends Recognizer
*/

function TapRecognizer() {

   Recognizer.apply(this, arguments);
   // previous time and center,
   // used for tap counting
   this.pTime = false;
   this.pCenter = false;
   this._timer = null;
   this._input = null;
   this.count = 0;

}

inherit(TapRecognizer, Recognizer, {

   /**
    * @namespace
    * @memberof PinchRecognizer
    */
   defaults: {
       event: 'tap',
       pointers: 1,
       taps: 1,
       interval: 300, // max time between the multi-tap taps
       time: 250, // max time of the pointer to be down (like finger on the screen)
       threshold: 2, // a minimal movement is ok, but keep it low
       posThreshold: 10 // a multi-tap can be a bit off the initial position
   },
   getTouchAction: function() {
       return [TOUCH_ACTION_MANIPULATION];
   },
   process: function(input) {
       var options = this.options;
       var validPointers = input.pointers.length === options.pointers;
       var validMovement = input.distance < options.threshold;
       var validTouchTime = input.deltaTime < options.time;
       this.reset();
       if ((input.eventType & INPUT_START) && (this.count === 0)) {
           return this.failTimeout();
       }
       // we only allow little movement
       // and we've reached an end event, so a tap is possible
       if (validMovement && validTouchTime && validPointers) {
           if (input.eventType != INPUT_END) {
               return this.failTimeout();
           }
           var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
           var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
           this.pTime = input.timeStamp;
           this.pCenter = input.center;
           if (!validMultiTap || !validInterval) {
               this.count = 1;
           } else {
               this.count += 1;
           }
           this._input = input;
           // if tap count matches we have recognized it,
           // else it has began recognizing...
           var tapCount = this.count % options.taps;
           if (tapCount === 0) {
               // no failing requirements, immediately trigger the tap event
               // or wait as long as the multitap interval to trigger
               if (!this.hasRequireFailures()) {
                   return STATE_RECOGNIZED;
               } else {
                   this._timer = setTimeoutContext(function() {
                       this.state = STATE_RECOGNIZED;
                       this.tryEmit();
                   }, options.interval, this);
                   return STATE_BEGAN;
               }
           }
       }
       return STATE_FAILED;
   },
   failTimeout: function() {
       this._timer = setTimeoutContext(function() {
           this.state = STATE_FAILED;
       }, this.options.interval, this);
       return STATE_FAILED;
   },
   reset: function() {
       clearTimeout(this._timer);
   },
   emit: function() {
       if (this.state == STATE_RECOGNIZED ) {
           this._input.tapCount = this.count;
           this.manager.emit(this.options.event, this._input);
       }
   }

});

/**

* Simple way to create an manager with a default set of recognizers.
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/

function Hammer(element, options) {

   options = options || {};
   options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
   return new Manager(element, options);

}

/**

* @const {string}
*/

Hammer.VERSION = '2.0.4';

/**

* default settings
* @namespace
*/

Hammer.defaults = {

   /**
    * set if DOM events are being triggered.
    * But this is slower and unused by simple implementations, so disabled by default.
    * @type {Boolean}
    * @default false
    */
   domEvents: false,
   /**
    * The value for the touchAction property/fallback.
    * When set to `compute` it will magically set the correct value based on the added recognizers.
    * @type {String}
    * @default compute
    */
   touchAction: TOUCH_ACTION_COMPUTE,
   /**
    * @type {Boolean}
    * @default true
    */
   enable: true,
   /**
    * EXPERIMENTAL FEATURE -- can be removed/changed
    * Change the parent input target element.
    * If Null, then it is being set the to main element.
    * @type {Null|EventTarget}
    * @default null
    */
   inputTarget: null,
   /**
    * force an input class
    * @type {Null|Function}
    * @default null
    */
   inputClass: null,
   /**
    * Default recognizer setup when calling `Hammer()`
    * When creating a new Manager these will be skipped.
    * @type {Array}
    */
   preset: [
       // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
       [RotateRecognizer, { enable: false }],
       [PinchRecognizer, { enable: false }, ['rotate']],
       [SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }],
       [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']],
       [TapRecognizer],
       [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']],
       [PressRecognizer]
   ],
   /**
    * Some CSS properties can be used to improve the working of Hammer.
    * Add them to this method and they will be set when creating a new Manager.
    * @namespace
    */
   cssProps: {
       /**
        * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
        * @type {String}
        * @default 'none'
        */
       userSelect: 'none',
       /**
        * Disable the Windows Phone grippers when pressing an element.
        * @type {String}
        * @default 'none'
        */
       touchSelect: 'none',
       /**
        * Disables the default callout shown when you touch and hold a touch target.
        * On iOS, when you touch and hold a touch target such as a link, Safari displays
        * a callout containing information about the link. This property allows you to disable that callout.
        * @type {String}
        * @default 'none'
        */
       touchCallout: 'none',
       /**
        * Specifies whether zooming is enabled. Used by IE10>
        * @type {String}
        * @default 'none'
        */
       contentZooming: 'none',
       /**
        * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
        * @type {String}
        * @default 'none'
        */
       userDrag: 'none',
       /**
        * Overrides the highlight color shown when the user taps a link or a JavaScript
        * clickable element in iOS. This property obeys the alpha value, if specified.
        * @type {String}
        * @default 'rgba(0,0,0,0)'
        */
       tapHighlightColor: 'rgba(0,0,0,0)'
   }

};

var STOP = 1; var FORCED_STOP = 2;

/**

* Manager
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/

function Manager(element, options) {

   options = options || {};
   this.options = merge(options, Hammer.defaults);
   this.options.inputTarget = this.options.inputTarget || element;
   this.handlers = {};
   this.session = {};
   this.recognizers = [];
   this.element = element;
   this.input = createInputInstance(this);
   this.touchAction = new TouchAction(this, this.options.touchAction);
   toggleCssProps(this, true);
   each(options.recognizers, function(item) {
       var recognizer = this.add(new (item[0])(item[1]));
       item[2] && recognizer.recognizeWith(item[2]);
       item[3] && recognizer.requireFailure(item[3]);
   }, this);

}

Manager.prototype = {

   /**
    * set options
    * @param {Object} options
    * @returns {Manager}
    */
   set: function(options) {
       extend(this.options, options);
       // Options that need a little more setup
       if (options.touchAction) {
           this.touchAction.update();
       }
       if (options.inputTarget) {
           // Clean up existing event listeners and reinitialize
           this.input.destroy();
           this.input.target = options.inputTarget;
           this.input.init();
       }
       return this;
   },
   /**
    * stop recognizing for this session.
    * This session will be discarded, when a new [input]start event is fired.
    * When forced, the recognizer cycle is stopped immediately.
    * @param {Boolean} [force]
    */
   stop: function(force) {
       this.session.stopped = force ? FORCED_STOP : STOP;
   },
   /**
    * run the recognizers!
    * called by the inputHandler function on every movement of the pointers (touches)
    * it walks through all the recognizers and tries to detect the gesture that is being made
    * @param {Object} inputData
    */
   recognize: function(inputData) {
       var session = this.session;
       if (session.stopped) {
           return;
       }
       // run the touch-action polyfill
       this.touchAction.preventDefaults(inputData);
       var recognizer;
       var recognizers = this.recognizers;
       // this holds the recognizer that is being recognized.
       // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
       // if no recognizer is detecting a thing, it is set to `null`
       var curRecognizer = session.curRecognizer;
       // reset when the last recognizer is recognized
       // or when we're in a new session
       if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
           curRecognizer = session.curRecognizer = null;
       }
       var i = 0;
       while (i < recognizers.length) {
           recognizer = recognizers[i];
           // find out if we are allowed try to recognize the input for this one.
           // 1.   allow if the session is NOT forced stopped (see the .stop() method)
           // 2.   allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
           //      that is being recognized.
           // 3.   allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
           //      this can be setup with the `recognizeWith()` method on the recognizer.
           if (session.stopped !== FORCED_STOP && ( // 1
                   !curRecognizer || recognizer == curRecognizer || // 2
                   recognizer.canRecognizeWith(curRecognizer))) { // 3
               recognizer.recognize(inputData);
           } else {
               recognizer.reset();
           }
           // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
           // current active recognizer. but only if we don't already have an active recognizer
           if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
               curRecognizer = session.curRecognizer = recognizer;
           }
           i++;
       }
   },
   /**
    * get a recognizer by its event name.
    * @param {Recognizer|String} recognizer
    * @returns {Recognizer|Null}
    */
   get: function(recognizer) {
       if (recognizer instanceof Recognizer) {
           return recognizer;
       }
       var recognizers = this.recognizers;
       for (var i = 0; i < recognizers.length; i++) {
           if (recognizers[i].options.event == recognizer) {
               return recognizers[i];
           }
       }
       return null;
   },
   /**
    * add a recognizer to the manager
    * existing recognizers with the same event name will be removed
    * @param {Recognizer} recognizer
    * @returns {Recognizer|Manager}
    */
   add: function(recognizer) {
       if (invokeArrayArg(recognizer, 'add', this)) {
           return this;
       }
       // remove existing
       var existing = this.get(recognizer.options.event);
       if (existing) {
           this.remove(existing);
       }
       this.recognizers.push(recognizer);
       recognizer.manager = this;
       this.touchAction.update();
       return recognizer;
   },
   /**
    * remove a recognizer by name or instance
    * @param {Recognizer|String} recognizer
    * @returns {Manager}
    */
   remove: function(recognizer) {
       if (invokeArrayArg(recognizer, 'remove', this)) {
           return this;
       }
       var recognizers = this.recognizers;
       recognizer = this.get(recognizer);
       recognizers.splice(inArray(recognizers, recognizer), 1);
       this.touchAction.update();
       return this;
   },
   /**
    * bind event
    * @param {String} events
    * @param {Function} handler
    * @returns {EventEmitter} this
    */
   on: function(events, handler) {
       var handlers = this.handlers;
       each(splitStr(events), function(event) {
           handlers[event] = handlers[event] || [];
           handlers[event].push(handler);
       });
       return this;
   },
   /**
    * unbind event, leave emit blank to remove all handlers
    * @param {String} events
    * @param {Function} [handler]
    * @returns {EventEmitter} this
    */
   off: function(events, handler) {
       var handlers = this.handlers;
       each(splitStr(events), function(event) {
           if (!handler) {
               delete handlers[event];
           } else {
               handlers[event].splice(inArray(handlers[event], handler), 1);
           }
       });
       return this;
   },
   /**
    * emit event to the listeners
    * @param {String} event
    * @param {Object} data
    */
   emit: function(event, data) {
       // we also want to trigger dom events
       if (this.options.domEvents) {
           triggerDomEvent(event, data);
       }
       // no handlers, so skip it all
       var handlers = this.handlers[event] && this.handlers[event].slice();
       if (!handlers || !handlers.length) {
           return;
       }
       data.type = event;
       data.preventDefault = function() {
           data.srcEvent.preventDefault();
       };
       var i = 0;
       while (i < handlers.length) {
           handlers[i](data);
           i++;
       }
   },
   /**
    * destroy the manager and unbinds all events
    * it doesn't unbind dom events, that is the user own responsibility
    */
   destroy: function() {
       this.element && toggleCssProps(this, false);
       this.handlers = {};
       this.session = {};
       this.input.destroy();
       this.element = null;
   }

};

/**

* add/remove the css properties as defined in manager.options.cssProps
* @param {Manager} manager
* @param {Boolean} add
*/

function toggleCssProps(manager, add) {

   var element = manager.element;
   each(manager.options.cssProps, function(value, name) {
       element.style[prefixed(element.style, name)] = add ? value : ;
   });

}

/**

* trigger dom event
* @param {String} event
* @param {Object} data
*/

function triggerDomEvent(event, data) {

   var gestureEvent = document.createEvent('Event');
   gestureEvent.initEvent(event, true, true);
   gestureEvent.gesture = data;
   data.target.dispatchEvent(gestureEvent);

}

extend(Hammer, {

   INPUT_START: INPUT_START,
   INPUT_MOVE: INPUT_MOVE,
   INPUT_END: INPUT_END,
   INPUT_CANCEL: INPUT_CANCEL,
   STATE_POSSIBLE: STATE_POSSIBLE,
   STATE_BEGAN: STATE_BEGAN,
   STATE_CHANGED: STATE_CHANGED,
   STATE_ENDED: STATE_ENDED,
   STATE_RECOGNIZED: STATE_RECOGNIZED,
   STATE_CANCELLED: STATE_CANCELLED,
   STATE_FAILED: STATE_FAILED,
   DIRECTION_NONE: DIRECTION_NONE,
   DIRECTION_LEFT: DIRECTION_LEFT,
   DIRECTION_RIGHT: DIRECTION_RIGHT,
   DIRECTION_UP: DIRECTION_UP,
   DIRECTION_DOWN: DIRECTION_DOWN,
   DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
   DIRECTION_VERTICAL: DIRECTION_VERTICAL,
   DIRECTION_ALL: DIRECTION_ALL,
   Manager: Manager,
   Input: Input,
   TouchAction: TouchAction,
   TouchInput: TouchInput,
   MouseInput: MouseInput,
   PointerEventInput: PointerEventInput,
   TouchMouseInput: TouchMouseInput,
   SingleTouchInput: SingleTouchInput,
   Recognizer: Recognizer,
   AttrRecognizer: AttrRecognizer,
   Tap: TapRecognizer,
   Pan: PanRecognizer,
   Swipe: SwipeRecognizer,
   Pinch: PinchRecognizer,
   Rotate: RotateRecognizer,
   Press: PressRecognizer,
   on: addEventListeners,
   off: removeEventListeners,
   each: each,
   merge: merge,
   extend: extend,
   inherit: inherit,
   bindFn: bindFn,
   prefixed: prefixed

});

if (typeof module != 'undefined' && module.exports) {

   module.exports = Hammer;

} else {

   window[exportName] = Hammer;

}

})(window, document, 'Hammer');