/**
 * @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
 * Available via the MIT or new BSD license.
 * see: http://github.com/jrburke/almond for details
 */
//Going sloppy to avoid 'use strict' string cost, but strict practices should
//be followed.
/*jslint sloppy: true */
/*global setTimeout: false */

var requirejs, require, define;
(function (undef) {
    var main, req, makeMap, handlers,
        defined = {},
        waiting = {},
        config = {},
        defining = {},
        hasOwn = Object.prototype.hasOwnProperty,
        aps = [].slice,
        jsSuffixRegExp = /\.js$/;

    function hasProp(obj, prop) {
        return hasOwn.call(obj, prop);
    }

    /**
     * Given a relative module name, like ./something, normalize it to
     * a real name that can be mapped to a path.
     * @param {String} name the relative name
     * @param {String} baseName a real name that the name arg is relative
     * to.
     * @returns {String} normalized name
     */
    function normalize(name, baseName) {
        var nameParts, nameSegment, mapValue, foundMap, lastIndex,
            foundI, foundStarMap, starI, i, j, part,
            baseParts = baseName && baseName.split("/"),
            map = config.map,
            starMap = (map && map['*']) || {};

        //Adjust any relative paths.
        if (name && name.charAt(0) === ".") {
            //If have a base name, try to normalize against it,
            //otherwise, assume it is a top-level require that will
            //be relative to baseUrl in the end.
            if (baseName) {
                //Convert baseName to array, and lop off the last part,
                //so that . matches that "directory" and not name of the baseName's
                //module. For instance, baseName of "one/two/three", maps to
                //"one/two/three.js", but we want the directory, "one/two" for
                //this normalization.
                baseParts = baseParts.slice(0, baseParts.length - 1);
                name = name.split('/');
                lastIndex = name.length - 1;

                // Node .js allowance:
                if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
                    name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
                }

                name = baseParts.concat(name);

                //start trimDots
                for (i = 0; i < name.length; i += 1) {
                    part = name[i];
                    if (part === ".") {
                        name.splice(i, 1);
                        i -= 1;
                    } else if (part === "..") {
                        if (i === 1 && (name[2] === '..' || name[0] === '..')) {
                            //End of the line. Keep at least one non-dot
                            //path segment at the front so it can be mapped
                            //correctly to disk. Otherwise, there is likely
                            //no path mapping for a path starting with '..'.
                            //This can still fail, but catches the most reasonable
                            //uses of ..
                            break;
                        } else if (i > 0) {
                            name.splice(i - 1, 2);
                            i -= 2;
                        }
                    }
                }
                //end trimDots

                name = name.join("/");
            } else if (name.indexOf('./') === 0) {
                // No baseName, so this is ID is resolved relative
                // to baseUrl, pull off the leading dot.
                name = name.substring(2);
            }
        }

        //Apply map config if available.
        if ((baseParts || starMap) && map) {
            nameParts = name.split('/');

            for (i = nameParts.length; i > 0; i -= 1) {
                nameSegment = nameParts.slice(0, i).join("/");

                if (baseParts) {
                    //Find the longest baseName segment match in the config.
                    //So, do joins on the biggest to smallest lengths of baseParts.
                    for (j = baseParts.length; j > 0; j -= 1) {
                        mapValue = map[baseParts.slice(0, j).join('/')];

                        //baseName segment has  config, find if it has one for
                        //this name.
                        if (mapValue) {
                            mapValue = mapValue[nameSegment];
                            if (mapValue) {
                                //Match, update name to the new value.
                                foundMap = mapValue;
                                foundI = i;
                                break;
                            }
                        }
                    }
                }

                if (foundMap) {
                    break;
                }

                //Check for a star map match, but just hold on to it,
                //if there is a shorter segment match later in a matching
                //config, then favor over this star map.
                if (!foundStarMap && starMap && starMap[nameSegment]) {
                    foundStarMap = starMap[nameSegment];
                    starI = i;
                }
            }

            if (!foundMap && foundStarMap) {
                foundMap = foundStarMap;
                foundI = starI;
            }

            if (foundMap) {
                nameParts.splice(0, foundI, foundMap);
                name = nameParts.join('/');
            }
        }

        return name;
    }

    function makeRequire(relName, forceSync) {
        return function () {
            //A version of a require function that passes a moduleName
            //value for items that may need to
            //look up paths relative to the moduleName
            return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
        };
    }

    function makeNormalize(relName) {
        return function (name) {
            return normalize(name, relName);
        };
    }

    function makeLoad(depName) {
        return function (value) {
            defined[depName] = value;
        };
    }

    function callDep(name) {
        if (hasProp(waiting, name)) {
            var args = waiting[name];
            delete waiting[name];
            defining[name] = true;
            main.apply(undef, args);
        }

        if (!hasProp(defined, name) && !hasProp(defining, name)) {
            throw new Error('No ' + name);
        }
        return defined[name];
    }

    //Turns a plugin!resource to [plugin, resource]
    //with the plugin being undefined if the name
    //did not have a plugin prefix.
    function splitPrefix(name) {
        var prefix,
            index = name ? name.indexOf('!') : -1;
        if (index > -1) {
            prefix = name.substring(0, index);
            name = name.substring(index + 1, name.length);
        }
        return [prefix, name];
    }

    /**
     * Makes a name map, normalizing the name, and using a plugin
     * for normalization if necessary. Grabs a ref to plugin
     * too, as an optimization.
     */
    makeMap = function (name, relName) {
        var plugin,
            parts = splitPrefix(name),
            prefix = parts[0];

        name = parts[1];

        if (prefix) {
            prefix = normalize(prefix, relName);
            plugin = callDep(prefix);
        }

        //Normalize according
        if (prefix) {
            if (plugin && plugin.normalize) {
                name = plugin.normalize(name, makeNormalize(relName));
            } else {
                name = normalize(name, relName);
            }
        } else {
            name = normalize(name, relName);
            parts = splitPrefix(name);
            prefix = parts[0];
            name = parts[1];
            if (prefix) {
                plugin = callDep(prefix);
            }
        }

        //Using ridiculous property names for space reasons
        return {
            f: prefix ? prefix + '!' + name : name, //fullName
            n: name,
            pr: prefix,
            p: plugin
        };
    };

    function makeConfig(name) {
        return function () {
            return (config && config.config && config.config[name]) || {};
        };
    }

    handlers = {
        require: function (name) {
            return makeRequire(name);
        },
        exports: function (name) {
            var e = defined[name];
            if (typeof e !== 'undefined') {
                return e;
            } else {
                return (defined[name] = {});
            }
        },
        module: function (name) {
            return {
                id: name,
                uri: '',
                exports: defined[name],
                config: makeConfig(name)
            };
        }
    };

    main = function (name, deps, callback, relName) {
        var cjsModule, depName, ret, map, i,
            args = [],
            callbackType = typeof callback,
            usingExports;

        //Use name if no relName
        relName = relName || name;

        //Call the callback to define the module, if necessary.
        if (callbackType === 'undefined' || callbackType === 'function') {
            //Pull out the defined dependencies and pass the ordered
            //values to the callback.
            //Default to [require, exports, module] if no deps
            deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
            for (i = 0; i < deps.length; i += 1) {
                map = makeMap(deps[i], relName);
                depName = map.f;

                //Fast path CommonJS standard dependencies.
                if (depName === "require") {
                    args[i] = handlers.require(name);
                } else if (depName === "exports") {
                    //CommonJS module spec 1.1
                    args[i] = handlers.exports(name);
                    usingExports = true;
                } else if (depName === "module") {
                    //CommonJS module spec 1.1
                    cjsModule = args[i] = handlers.module(name);
                } else if (hasProp(defined, depName) ||
                           hasProp(waiting, depName) ||
                           hasProp(defining, depName)) {
                    args[i] = callDep(depName);
                } else if (map.p) {
                    map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
                    args[i] = defined[depName];
                } else {
                    throw new Error(name + ' missing ' + depName);
                }
            }

            ret = callback ? callback.apply(defined[name], args) : undefined;

            if (name) {
                //If setting exports via "module" is in play,
                //favor that over return value and exports. After that,
                //favor a non-undefined return value over exports use.
                if (cjsModule && cjsModule.exports !== undef &&
                        cjsModule.exports !== defined[name]) {
                    defined[name] = cjsModule.exports;
                } else if (ret !== undef || !usingExports) {
                    //Use the return value from the function.
                    defined[name] = ret;
                }
            }
        } else if (name) {
            //May just be an object definition for the module. Only
            //worry about defining if have a module name.
            defined[name] = callback;
        }
    };

    requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
        if (typeof deps === "string") {
            if (handlers[deps]) {
                //callback in this case is really relName
                return handlers[deps](callback);
            }
            //Just return the module wanted. In this scenario, the
            //deps arg is the module name, and second arg (if passed)
            //is just the relName.
            //Normalize module name, if it contains . or ..
            return callDep(makeMap(deps, callback).f);
        } else if (!deps.splice) {
            //deps is a config object, not an array.
            config = deps;
            if (config.deps) {
                req(config.deps, config.callback);
            }
            if (!callback) {
                return;
            }

            if (callback.splice) {
                //callback is an array, which means it is a dependency list.
                //Adjust args if there are dependencies
                deps = callback;
                callback = relName;
                relName = null;
            } else {
                deps = undef;
            }
        }

        //Support require(['a'])
        callback = callback || function () {};

        //If relName is a function, it is an errback handler,
        //so remove it.
        if (typeof relName === 'function') {
            relName = forceSync;
            forceSync = alt;
        }

        //Simulate async callback;
        if (forceSync) {
            main(undef, deps, callback, relName);
        } else {
            //Using a non-zero value because of concern for what old browsers
            //do, and latest browsers "upgrade" to 4 if lower value is used:
            //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
            //If want a value immediately, use require('id') instead -- something
            //that works in almond on the global level, but not guaranteed and
            //unlikely to work in other AMD implementations.
            setTimeout(function () {
                main(undef, deps, callback, relName);
            }, 4);
        }

        return req;
    };

    /**
     * Just drops the config on the floor, but returns req in case
     * the config return value is used.
     */
    req.config = function (cfg) {
        return req(cfg);
    };

    /**
     * Expose module registry for debugging and tooling
     */
    requirejs._defined = defined;

    define = function (name, deps, callback) {

        //This module may not have dependencies
        if (!deps.splice) {
            //deps is not an array, so probably means
            //an object literal or factory function for
            //the value. Adjust args.
            callback = deps;
            deps = [];
        }

        if (!hasProp(defined, name) && !hasProp(waiting, name)) {
            waiting[name] = [name, deps, callback];
        }
    };

    define.amd = {
        jQuery: true
    };
}());

define("almond", function(){});

define('Utility',[],function() {
    

    var methods = {};

    methods.removeEventListener = function(el, eventName, handler) {
        if (el.removeEventListener) {
            el.removeEventListener(eventName, handler);
        } else {
            el.detachEvent('on' + eventName, handler);
        }
    };

    methods.addEventListener = function(el, eventName, handler) {
        if (el.addEventListener) {
            el.addEventListener(eventName, handler);
        } else {
            el.attachEvent('on' + eventName, handler);
        }
    };

    methods.hasClass = function(el, className) {
        if (el.classList) {
            return el.classList.contains(className);
        } else {
            return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className);
        }
    };

    methods.addClass = function(el, className) {
        if (el.classList) {
            el.classList.add(className);
        } else {
            el.className += ' ' + className;
        }
    };

    methods.removeClass = function(el, className) {
        if (el.classList) {
            el.classList.remove(className);
        } else {
            el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
        }
    };

    methods.toggleClass = function(el, className) {
        if (el.classList) {
            el.classList.toggle(className);
        } else {
            var classes = el.className.split(' ');
            var existingIndex = -1;
            for (var i = classes.length; i--;) {
                if (classes[i] === className) {
                    existingIndex = i;
                }
            }

            if (existingIndex >= 0) {
                classes.splice(existingIndex, 1);
            } else {
                classes.push(className);
            }

            el.className = classes.join(' ');
        }
    };

    methods.ready = function(fn) {
        if (document.addEventListener) {
            document.addEventListener('DOMContentLoaded', fn);
        } else {
            document.attachEvent('onreadystatechange', function() {
                if (document.readyState === 'interactive') {
                    fn();
                }
            });
        }
    };

    methods.loadComponentModule = function(component, module) {
        require([module], function(Module) {
            Module.init(component);
        });
    };

    methods.getJSON = function(url, success, failure) {
        request = new XMLHttpRequest();
        request.open('GET', url, true);

        request.onreadystatechange = function() {
            if (this.readyState === 4){
                if (this.status >= 200 && this.status < 400){
                    success(JSON.parse(this.responseText), this.status);
                } else {
                    failure();
                }
            }
        };

        request.send();
        request = null;
    };

    return methods;
});
/*! Hammer.JS - v1.0.6 - 2014-01-02
 * http://eightmedia.github.com/hammer.js
 *
 * Copyright (c) 2014 Jorik Tangelder <j.tangelder@gmail.com>;
 * Licensed under the MIT license */

(function(window, undefined) {
  

/**
 * Hammer
 * use this to create instances
 * @param   {HTMLElement}   element
 * @param   {Object}        options
 * @returns {Hammer.Instance}
 * @constructor
 */
var Hammer = function(element, options) {
  return new Hammer.Instance(element, options || {});
};

// default settings
Hammer.defaults = {
  // add styles and attributes to the element to prevent the browser from doing
  // its native behavior. this doesnt prevent the scrolling, but cancels
  // the contextmenu, tap highlighting etc
  // set to false to disable this
  stop_browser_behavior: {
    // this also triggers onselectstart=false for IE
    userSelect       : 'none',
    // this makes the element blocking in IE10 >, you could experiment with the value
    // see for more options this issue; https://github.com/EightMedia/hammer.js/issues/241
    touchAction      : 'none',
    touchCallout     : 'none',
    contentZooming   : 'none',
    userDrag         : 'none',
    tapHighlightColor: 'rgba(0,0,0,0)'
  }

  //
  // more settings are defined per gesture at gestures.js
  //
};

// detect touchevents
Hammer.HAS_POINTEREVENTS = window.navigator.pointerEnabled || window.navigator.msPointerEnabled;
Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window);

// dont use mouseevents on mobile devices
Hammer.MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android|silk/i;
Hammer.NO_MOUSEEVENTS = Hammer.HAS_TOUCHEVENTS && window.navigator.userAgent.match(Hammer.MOBILE_REGEX);

// eventtypes per touchevent (start, move, end)
// are filled by Hammer.event.determineEventTypes on setup
Hammer.EVENT_TYPES = {};

// direction defines
Hammer.DIRECTION_DOWN = 'down';
Hammer.DIRECTION_LEFT = 'left';
Hammer.DIRECTION_UP = 'up';
Hammer.DIRECTION_RIGHT = 'right';

// pointer type
Hammer.POINTER_MOUSE = 'mouse';
Hammer.POINTER_TOUCH = 'touch';
Hammer.POINTER_PEN = 'pen';

// touch event defines
Hammer.EVENT_START = 'start';
Hammer.EVENT_MOVE = 'move';
Hammer.EVENT_END = 'end';

// hammer document where the base events are added at
Hammer.DOCUMENT = window.document;

// plugins and gestures namespaces
Hammer.plugins = Hammer.plugins || {};
Hammer.gestures = Hammer.gestures || {};

// if the window events are set...
Hammer.READY = false;

/**
 * setup events to detect gestures on the document
 */
function setup() {
  if(Hammer.READY) {
    return;
  }

  // find what eventtypes we add listeners to
  Hammer.event.determineEventTypes();

  // Register all gestures inside Hammer.gestures
  Hammer.utils.each(Hammer.gestures, function(gesture){
    Hammer.detection.register(gesture);
  });

  // Add touch events on the document
  Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_MOVE, Hammer.detection.detect);
  Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_END, Hammer.detection.detect);

  // Hammer is ready...!
  Hammer.READY = true;
}

Hammer.utils = {
  /**
   * extend method,
   * also used for cloning when dest is an empty object
   * @param   {Object}    dest
   * @param   {Object}    src
   * @parm  {Boolean}  merge    do a merge
   * @returns {Object}    dest
   */
  extend: function extend(dest, src, merge) {
    for(var key in src) {
      if(dest[key] !== undefined && merge) {
        continue;
      }
      dest[key] = src[key];
    }
    return dest;
  },


  /**
   * for each
   * @param obj
   * @param iterator
   */
  each: function(obj, iterator, context) {
    var i, length;
    // native forEach on arrays
    if ('forEach' in obj) {
      obj.forEach(iterator, context);
    }
    // arrays
    else if(obj.length !== undefined) {
      for (i = 0, length = obj.length; i < length; i++) {
        if (iterator.call(context, obj[i], i, obj) === false) {
          return;
        }
      }
    }
    // objects
    else {
      for (i in obj) {
        if (obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj) === false) {
          return;
        }
      }
    }
  },

  /**
   * find if a node is in the given parent
   * used for event delegation tricks
   * @param   {HTMLElement}   node
   * @param   {HTMLElement}   parent
   * @returns {boolean}       has_parent
   */
  hasParent: function(node, parent) {
    while(node) {
      if(node == parent) {
        return true;
      }
      node = node.parentNode;
    }
    return false;
  },


  /**
   * get the center of all the touches
   * @param   {Array}     touches
   * @returns {Object}    center
   */
  getCenter: function getCenter(touches) {
    var valuesX = [], valuesY = [];

    Hammer.utils.each(touches, function(touch) {
      // I prefer clientX because it ignore the scrolling position
      valuesX.push(typeof touch.clientX !== 'undefined' ? touch.clientX : touch.pageX );
      valuesY.push(typeof touch.clientY !== 'undefined' ? touch.clientY : touch.pageY );
    });

    return {
      pageX: ((Math.min.apply(Math, valuesX) + Math.max.apply(Math, valuesX)) / 2),
      pageY: ((Math.min.apply(Math, valuesY) + Math.max.apply(Math, valuesY)) / 2)
    };
  },


  /**
   * calculate the velocity between two points
   * @param   {Number}    delta_time
   * @param   {Number}    delta_x
   * @param   {Number}    delta_y
   * @returns {Object}    velocity
   */
  getVelocity: function getVelocity(delta_time, delta_x, delta_y) {
    return {
      x: Math.abs(delta_x / delta_time) || 0,
      y: Math.abs(delta_y / delta_time) || 0
    };
  },


  /**
   * calculate the angle between two coordinates
   * @param   {Touch}     touch1
   * @param   {Touch}     touch2
   * @returns {Number}    angle
   */
  getAngle: function getAngle(touch1, touch2) {
    var y = touch2.pageY - touch1.pageY,
      x = touch2.pageX - touch1.pageX;
    return Math.atan2(y, x) * 180 / Math.PI;
  },


  /**
   * angle to direction define
   * @param   {Touch}     touch1
   * @param   {Touch}     touch2
   * @returns {String}    direction constant, like Hammer.DIRECTION_LEFT
   */
  getDirection: function getDirection(touch1, touch2) {
    var x = Math.abs(touch1.pageX - touch2.pageX),
      y = Math.abs(touch1.pageY - touch2.pageY);

    if(x >= y) {
      return touch1.pageX - touch2.pageX > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
    }
    else {
      return touch1.pageY - touch2.pageY > 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
    }
  },


  /**
   * calculate the distance between two touches
   * @param   {Touch}     touch1
   * @param   {Touch}     touch2
   * @returns {Number}    distance
   */
  getDistance: function getDistance(touch1, touch2) {
    var x = touch2.pageX - touch1.pageX,
      y = touch2.pageY - touch1.pageY;
    return Math.sqrt((x * x) + (y * y));
  },


  /**
   * calculate the scale factor between two touchLists (fingers)
   * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
   * @param   {Array}     start
   * @param   {Array}     end
   * @returns {Number}    scale
   */
  getScale: function getScale(start, end) {
    // need two fingers...
    if(start.length >= 2 && end.length >= 2) {
      return this.getDistance(end[0], end[1]) /
        this.getDistance(start[0], start[1]);
    }
    return 1;
  },


  /**
   * calculate the rotation degrees between two touchLists (fingers)
   * @param   {Array}     start
   * @param   {Array}     end
   * @returns {Number}    rotation
   */
  getRotation: function getRotation(start, end) {
    // need two fingers
    if(start.length >= 2 && end.length >= 2) {
      return this.getAngle(end[1], end[0]) -
        this.getAngle(start[1], start[0]);
    }
    return 0;
  },


  /**
   * boolean if the direction is vertical
   * @param    {String}    direction
   * @returns  {Boolean}   is_vertical
   */
  isVertical: function isVertical(direction) {
    return (direction == Hammer.DIRECTION_UP || direction == Hammer.DIRECTION_DOWN);
  },


  /**
   * stop browser default behavior with css props
   * @param   {HtmlElement}   element
   * @param   {Object}        css_props
   */
  stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_props) {
    if(!css_props || !element || !element.style) {
      return;
    }

    // with css properties for modern browsers
    Hammer.utils.each(['webkit', 'khtml', 'moz', 'Moz', 'ms', 'o', ''], function(vendor) {
      Hammer.utils.each(css_props, function(prop) {
          // vender prefix at the property
          if(vendor) {
            prop = vendor + prop.substring(0, 1).toUpperCase() + prop.substring(1);
          }
          // set the style
          if(prop in element.style) {
            element.style[prop] = prop;
          }
      });
    });

    // also the disable onselectstart
    if(css_props.userSelect == 'none') {
      element.onselectstart = function() {
        return false;
      };
    }

    // and disable ondragstart
    if(css_props.userDrag == 'none') {
      element.ondragstart = function() {
        return false;
      };
    }
  }
};


/**
 * create new hammer instance
 * all methods should return the instance itself, so it is chainable.
 * @param   {HTMLElement}       element
 * @param   {Object}            [options={}]
 * @returns {Hammer.Instance}
 * @constructor
 */
Hammer.Instance = function(element, options) {
  var self = this;

  // setup HammerJS window events and register all gestures
  // this also sets up the default options
  setup();

  this.element = element;

  // start/stop detection option
  this.enabled = true;

  // merge options
  this.options = Hammer.utils.extend(
    Hammer.utils.extend({}, Hammer.defaults),
    options || {});

  // add some css to the element to prevent the browser from doing its native behavoir
  if(this.options.stop_browser_behavior) {
    Hammer.utils.stopDefaultBrowserBehavior(this.element, this.options.stop_browser_behavior);
  }

  // start detection on touchstart
  Hammer.event.onTouch(element, Hammer.EVENT_START, function(ev) {
    if(self.enabled) {
      Hammer.detection.startDetect(self, ev);
    }
  });

  // return instance
  return this;
};


Hammer.Instance.prototype = {
  /**
   * bind events to the instance
   * @param   {String}      gesture
   * @param   {Function}    handler
   * @returns {Hammer.Instance}
   */
  on: function onEvent(gesture, handler) {
    var gestures = gesture.split(' ');
    Hammer.utils.each(gestures, function(gesture) {
      this.element.addEventListener(gesture, handler, false);
    }, this);
    return this;
  },


  /**
   * unbind events to the instance
   * @param   {String}      gesture
   * @param   {Function}    handler
   * @returns {Hammer.Instance}
   */
  off: function offEvent(gesture, handler) {
    var gestures = gesture.split(' ');
    Hammer.utils.each(gestures, function(gesture) {
      this.element.removeEventListener(gesture, handler, false);
    }, this);
    return this;
  },


  /**
   * trigger gesture event
   * @param   {String}      gesture
   * @param   {Object}      [eventData]
   * @returns {Hammer.Instance}
   */
  trigger: function triggerEvent(gesture, eventData) {
    // optional
    if(!eventData) {
      eventData = {};
    }

    // create DOM event
    var event = Hammer.DOCUMENT.createEvent('Event');
    event.initEvent(gesture, true, true);
    event.gesture = eventData;

    // trigger on the target if it is in the instance element,
    // this is for event delegation tricks
    var element = this.element;
    if(Hammer.utils.hasParent(eventData.target, element)) {
      element = eventData.target;
    }

    element.dispatchEvent(event);
    return this;
  },


  /**
   * enable of disable hammer.js detection
   * @param   {Boolean}   state
   * @returns {Hammer.Instance}
   */
  enable: function enable(state) {
    this.enabled = state;
    return this;
  }
};


/**
 * this holds the last move event,
 * used to fix empty touchend issue
 * see the onTouch event for an explanation
 * @type {Object}
 */
var last_move_event = null;


/**
 * when the mouse is hold down, this is true
 * @type {Boolean}
 */
var enable_detect = false;


/**
 * when touch events have been fired, this is true
 * @type {Boolean}
 */
var touch_triggered = false;


Hammer.event = {
  /**
   * simple addEventListener
   * @param   {HTMLElement}   element
   * @param   {String}        type
   * @param   {Function}      handler
   */
  bindDom: function(element, type, handler) {
    var types = type.split(' ');
    Hammer.utils.each(types, function(type){
      element.addEventListener(type, handler, false);
    });
  },


  /**
   * touch events with mouse fallback
   * @param   {HTMLElement}   element
   * @param   {String}        eventType        like Hammer.EVENT_MOVE
   * @param   {Function}      handler
   */
  onTouch: function onTouch(element, eventType, handler) {
    var self = this;

    this.bindDom(element, Hammer.EVENT_TYPES[eventType], function bindDomOnTouch(ev) {
      var sourceEventType = ev.type.toLowerCase();

      // onmouseup, but when touchend has been fired we do nothing.
      // this is for touchdevices which also fire a mouseup on touchend
      if(sourceEventType.match(/mouse/) && touch_triggered) {
        return;
      }

      // mousebutton must be down or a touch event
      else if(sourceEventType.match(/touch/) ||   // touch events are always on screen
        sourceEventType.match(/pointerdown/) || // pointerevents touch
        (sourceEventType.match(/mouse/) && ev.which === 1)   // mouse is pressed
        ) {
        enable_detect = true;
      }

      // mouse isn't pressed
      else if(sourceEventType.match(/mouse/) && !ev.which) {
        enable_detect = false;
      }


      // we are in a touch event, set the touch triggered bool to true,
      // this for the conflicts that may occur on ios and android
      if(sourceEventType.match(/touch|pointer/)) {
        touch_triggered = true;
      }

      // count the total touches on the screen
      var count_touches = 0;

      // when touch has been triggered in this detection session
      // and we are now handling a mouse event, we stop that to prevent conflicts
      if(enable_detect) {
        // update pointerevent
        if(Hammer.HAS_POINTEREVENTS && eventType != Hammer.EVENT_END) {
          count_touches = Hammer.PointerEvent.updatePointer(eventType, ev);
        }
        // touch
        else if(sourceEventType.match(/touch/)) {
          count_touches = ev.touches.length;
        }
        // mouse
        else if(!touch_triggered) {
          count_touches = sourceEventType.match(/up/) ? 0 : 1;
        }

        // if we are in a end event, but when we remove one touch and
        // we still have enough, set eventType to move
        if(count_touches > 0 && eventType == Hammer.EVENT_END) {
          eventType = Hammer.EVENT_MOVE;
        }
        // no touches, force the end event
        else if(!count_touches) {
          eventType = Hammer.EVENT_END;
        }

        // store the last move event
        if(count_touches || last_move_event === null) {
          last_move_event = ev;
        }

        // trigger the handler
        handler.call(Hammer.detection, self.collectEventData(element, eventType, self.getTouchList(last_move_event, eventType), ev));

        // remove pointerevent from list
        if(Hammer.HAS_POINTEREVENTS && eventType == Hammer.EVENT_END) {
          count_touches = Hammer.PointerEvent.updatePointer(eventType, ev);
        }
      }

      // on the end we reset everything
      if(!count_touches) {
        last_move_event = null;
        enable_detect = false;
        touch_triggered = false;
        Hammer.PointerEvent.reset();
      }
    });
  },


  /**
   * we have different events for each device/browser
   * determine what we need and set them in the Hammer.EVENT_TYPES constant
   */
  determineEventTypes: function determineEventTypes() {
    // determine the eventtype we want to set
    var types;

    // pointerEvents magic
    if(Hammer.HAS_POINTEREVENTS) {
      types = Hammer.PointerEvent.getEvents();
    }
    // on Android, iOS, blackberry, windows mobile we dont want any mouseevents
    else if(Hammer.NO_MOUSEEVENTS) {
      types = [
        'touchstart',
        'touchmove',
        'touchend touchcancel'];
    }
    // for non pointer events browsers and mixed browsers,
    // like chrome on windows8 touch laptop
    else {
      types = [
        'touchstart mousedown',
        'touchmove mousemove',
        'touchend touchcancel mouseup'];
    }

    Hammer.EVENT_TYPES[Hammer.EVENT_START] = types[0];
    Hammer.EVENT_TYPES[Hammer.EVENT_MOVE] = types[1];
    Hammer.EVENT_TYPES[Hammer.EVENT_END] = types[2];
  },


  /**
   * create touchlist depending on the event
   * @param   {Object}    ev
   * @param   {String}    eventType   used by the fakemultitouch plugin
   */
  getTouchList: function getTouchList(ev/*, eventType*/) {
    // get the fake pointerEvent touchlist
    if(Hammer.HAS_POINTEREVENTS) {
      return Hammer.PointerEvent.getTouchList();
    }
    // get the touchlist
    else if(ev.touches) {
      return ev.touches;
    }
    // make fake touchlist from mouse position
    else {
      ev.identifier = 1;
      return [ev];
    }
  },


  /**
   * collect event data for Hammer js
   * @param   {HTMLElement}   element
   * @param   {String}        eventType        like Hammer.EVENT_MOVE
   * @param   {Object}        eventData
   */
  collectEventData: function collectEventData(element, eventType, touches, ev) {
    // find out pointerType
    var pointerType = Hammer.POINTER_TOUCH;
    if(ev.type.match(/mouse/) || Hammer.PointerEvent.matchType(Hammer.POINTER_MOUSE, ev)) {
      pointerType = Hammer.POINTER_MOUSE;
    }

    return {
      center     : Hammer.utils.getCenter(touches),
      timeStamp  : new Date().getTime(),
      target     : ev.target,
      touches    : touches,
      eventType  : eventType,
      pointerType: pointerType,
      srcEvent   : ev,

      /**
       * prevent the browser default actions
       * mostly used to disable scrolling of the browser
       */
      preventDefault: function() {
        if(this.srcEvent.preventManipulation) {
          this.srcEvent.preventManipulation();
        }

        if(this.srcEvent.preventDefault) {
          this.srcEvent.preventDefault();
        }
      },

      /**
       * stop bubbling the event up to its parents
       */
      stopPropagation: function() {
        this.srcEvent.stopPropagation();
      },

      /**
       * immediately stop gesture detection
       * might be useful after a swipe was detected
       * @return {*}
       */
      stopDetect: function() {
        return Hammer.detection.stopDetect();
      }
    };
  }
};

Hammer.PointerEvent = {
  /**
   * holds all pointers
   * @type {Object}
   */
  pointers: {},

  /**
   * get a list of pointers
   * @returns {Array}     touchlist
   */
  getTouchList: function() {
    var self = this;
    var touchlist = [];

    // we can use forEach since pointerEvents only is in IE10
    Hammer.utils.each(self.pointers, function(pointer){
      touchlist.push(pointer);
    });
    
    return touchlist;
  },

  /**
   * update the position of a pointer
   * @param   {String}   type             Hammer.EVENT_END
   * @param   {Object}   pointerEvent
   */
  updatePointer: function(type, pointerEvent) {
    if(type == Hammer.EVENT_END) {
      this.pointers = {};
    }
    else {
      pointerEvent.identifier = pointerEvent.pointerId;
      this.pointers[pointerEvent.pointerId] = pointerEvent;
    }

    return Object.keys(this.pointers).length;
  },

  /**
   * check if ev matches pointertype
   * @param   {String}        pointerType     Hammer.POINTER_MOUSE
   * @param   {PointerEvent}  ev
   */
  matchType: function(pointerType, ev) {
    if(!ev.pointerType) {
      return false;
    }

    var pt = ev.pointerType,
      types = {};
    types[Hammer.POINTER_MOUSE] = (pt === ev.MSPOINTER_TYPE_MOUSE || pt === Hammer.POINTER_MOUSE);
    types[Hammer.POINTER_TOUCH] = (pt === ev.MSPOINTER_TYPE_TOUCH || pt === Hammer.POINTER_TOUCH);
    types[Hammer.POINTER_PEN] = (pt === ev.MSPOINTER_TYPE_PEN || pt === Hammer.POINTER_PEN);
    return types[pointerType];
  },


  /**
   * get events
   */
  getEvents: function() {
    return [
      'pointerdown MSPointerDown',
      'pointermove MSPointerMove',
      'pointerup pointercancel MSPointerUp MSPointerCancel'
    ];
  },

  /**
   * reset the list
   */
  reset: function() {
    this.pointers = {};
  }
};


Hammer.detection = {
  // contains all registred Hammer.gestures in the correct order
  gestures: [],

  // data of the current Hammer.gesture detection session
  current : null,

  // the previous Hammer.gesture session data
  // is a full clone of the previous gesture.current object
  previous: null,

  // when this becomes true, no gestures are fired
  stopped : false,


  /**
   * start Hammer.gesture detection
   * @param   {Hammer.Instance}   inst
   * @param   {Object}            eventData
   */
  startDetect: function startDetect(inst, eventData) {
    // already busy with a Hammer.gesture detection on an element
    if(this.current) {
      return;
    }

    this.stopped = false;

    this.current = {
      inst      : inst, // reference to HammerInstance we're working for
      startEvent: Hammer.utils.extend({}, eventData), // start eventData for distances, timing etc
      lastEvent : false, // last eventData
      name      : '' // current gesture we're in/detected, can be 'tap', 'hold' etc
    };

    this.detect(eventData);
  },


  /**
   * Hammer.gesture detection
   * @param   {Object}    eventData
   */
  detect: function detect(eventData) {
    if(!this.current || this.stopped) {
      return;
    }

    // extend event data with calculations about scale, distance etc
    eventData = this.extendEventData(eventData);

    // instance options
    var inst_options = this.current.inst.options;

    // call Hammer.gesture handlers
    Hammer.utils.each(this.gestures, function(gesture) {
      // only when the instance options have enabled this gesture
      if(!this.stopped && inst_options[gesture.name] !== false) {
        // if a handler returns false, we stop with the detection
        if(gesture.handler.call(gesture, eventData, this.current.inst) === false) {
          this.stopDetect();
          return false;
        }
      }
    }, this);

    // store as previous event event
    if(this.current) {
      this.current.lastEvent = eventData;
    }

    // endevent, but not the last touch, so dont stop
    if(eventData.eventType == Hammer.EVENT_END && !eventData.touches.length - 1) {
      this.stopDetect();
    }

    return eventData;
  },


  /**
   * clear the Hammer.gesture vars
   * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected
   * to stop other Hammer.gestures from being fired
   */
  stopDetect: function stopDetect() {
    // clone current data to the store as the previous gesture
    // used for the double tap gesture, since this is an other gesture detect session
    this.previous = Hammer.utils.extend({}, this.current);

    // reset the current
    this.current = null;

    // stopped!
    this.stopped = true;
  },


  /**
   * extend eventData for Hammer.gestures
   * @param   {Object}   ev
   * @returns {Object}   ev
   */
  extendEventData: function extendEventData(ev) {
    var startEv = this.current.startEvent;

    // if the touches change, set the new touches over the startEvent touches
    // this because touchevents don't have all the touches on touchstart, or the
    // user must place his fingers at the EXACT same time on the screen, which is not realistic
    // but, sometimes it happens that both fingers are touching at the EXACT same time
    if(startEv && (ev.touches.length != startEv.touches.length || ev.touches === startEv.touches)) {
      // extend 1 level deep to get the touchlist with the touch objects
      startEv.touches = [];
      Hammer.utils.each(ev.touches, function(touch) {
        startEv.touches.push(Hammer.utils.extend({}, touch));
      });
    }

    var delta_time = ev.timeStamp - startEv.timeStamp
      , delta_x = ev.center.pageX - startEv.center.pageX
      , delta_y = ev.center.pageY - startEv.center.pageY
      , velocity = Hammer.utils.getVelocity(delta_time, delta_x, delta_y)
      , interimAngle
      , interimDirection;

    // end events (e.g. dragend) don't have useful values for interimDirection & interimAngle
    // because the previous event has exactly the same coordinates
    // so for end events, take the previous values of interimDirection & interimAngle
    // instead of recalculating them and getting a spurious '0'
    if(ev.eventType === 'end') {
      interimAngle = this.current.lastEvent && this.current.lastEvent.interimAngle;
      interimDirection = this.current.lastEvent && this.current.lastEvent.interimDirection;
    }
    else {
      interimAngle = this.current.lastEvent && Hammer.utils.getAngle(this.current.lastEvent.center, ev.center);
      interimDirection = this.current.lastEvent && Hammer.utils.getDirection(this.current.lastEvent.center, ev.center);
    }

    Hammer.utils.extend(ev, {
      deltaTime: delta_time,

      deltaX: delta_x,
      deltaY: delta_y,

      velocityX: velocity.x,
      velocityY: velocity.y,

      distance: Hammer.utils.getDistance(startEv.center, ev.center),

      angle: Hammer.utils.getAngle(startEv.center, ev.center),
      interimAngle: interimAngle,

      direction: Hammer.utils.getDirection(startEv.center, ev.center),
      interimDirection: interimDirection,

      scale: Hammer.utils.getScale(startEv.touches, ev.touches),
      rotation: Hammer.utils.getRotation(startEv.touches, ev.touches),

      startEvent: startEv
    });

    return ev;
  },


  /**
   * register new gesture
   * @param   {Object}    gesture object, see gestures.js for documentation
   * @returns {Array}     gestures
   */
  register: function register(gesture) {
    // add an enable gesture options if there is no given
    var options = gesture.defaults || {};
    if(options[gesture.name] === undefined) {
      options[gesture.name] = true;
    }

    // extend Hammer default options with the Hammer.gesture options
    Hammer.utils.extend(Hammer.defaults, options, true);

    // set its index
    gesture.index = gesture.index || 1000;

    // add Hammer.gesture to the list
    this.gestures.push(gesture);

    // sort the list by index
    this.gestures.sort(function(a, b) {
      if(a.index < b.index) { return -1; }
      if(a.index > b.index) { return 1; }
      return 0;
    });

    return this.gestures;
  }
};


/**
 * Drag
 * Move with x fingers (default 1) around on the page. Blocking the scrolling when
 * moving left and right is a good practice. When all the drag events are blocking
 * you disable scrolling on that area.
 * @events  drag, drapleft, dragright, dragup, dragdown
 */
Hammer.gestures.Drag = {
  name     : 'drag',
  index    : 50,
  defaults : {
    drag_min_distance            : 10,
    
    // Set correct_for_drag_min_distance to true to make the starting point of the drag
    // be calculated from where the drag was triggered, not from where the touch started.
    // Useful to avoid a jerk-starting drag, which can make fine-adjustments
    // through dragging difficult, and be visually unappealing.
    correct_for_drag_min_distance: true,
    
    // set 0 for unlimited, but this can conflict with transform
    drag_max_touches             : 1,
    
    // prevent default browser behavior when dragging occurs
    // be careful with it, it makes the element a blocking element
    // when you are using the drag gesture, it is a good practice to set this true
    drag_block_horizontal        : false,
    drag_block_vertical          : false,
    
    // drag_lock_to_axis keeps the drag gesture on the axis that it started on,
    // It disallows vertical directions if the initial direction was horizontal, and vice versa.
    drag_lock_to_axis            : false,
    
    // drag lock only kicks in when distance > drag_lock_min_distance
    // This way, locking occurs only when the distance has become large enough to reliably determine the direction
    drag_lock_min_distance       : 25
  },
  
  triggered: false,
  handler  : function dragGesture(ev, inst) {
    // current gesture isnt drag, but dragged is true
    // this means an other gesture is busy. now call dragend
    if(Hammer.detection.current.name != this.name && this.triggered) {
      inst.trigger(this.name + 'end', ev);
      this.triggered = false;
      return;
    }

    // max touches
    if(inst.options.drag_max_touches > 0 &&
      ev.touches.length > inst.options.drag_max_touches) {
      return;
    }

    switch(ev.eventType) {
      case Hammer.EVENT_START:
        this.triggered = false;
        break;

      case Hammer.EVENT_MOVE:
        // when the distance we moved is too small we skip this gesture
        // or we can be already in dragging
        if(ev.distance < inst.options.drag_min_distance &&
          Hammer.detection.current.name != this.name) {
          return;
        }

        // we are dragging!
        if(Hammer.detection.current.name != this.name) {
          Hammer.detection.current.name = this.name;
          if(inst.options.correct_for_drag_min_distance && ev.distance > 0) {
            // When a drag is triggered, set the event center to drag_min_distance pixels from the original event center.
            // Without this correction, the dragged distance would jumpstart at drag_min_distance pixels instead of at 0.
            // It might be useful to save the original start point somewhere
            var factor = Math.abs(inst.options.drag_min_distance / ev.distance);
            Hammer.detection.current.startEvent.center.pageX += ev.deltaX * factor;
            Hammer.detection.current.startEvent.center.pageY += ev.deltaY * factor;

            // recalculate event data using new start point
            ev = Hammer.detection.extendEventData(ev);
          }
        }

        // lock drag to axis?
        if(Hammer.detection.current.lastEvent.drag_locked_to_axis || (inst.options.drag_lock_to_axis && inst.options.drag_lock_min_distance <= ev.distance)) {
          ev.drag_locked_to_axis = true;
        }
        var last_direction = Hammer.detection.current.lastEvent.direction;
        if(ev.drag_locked_to_axis && last_direction !== ev.direction) {
          // keep direction on the axis that the drag gesture started on
          if(Hammer.utils.isVertical(last_direction)) {
            ev.direction = (ev.deltaY < 0) ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
          }
          else {
            ev.direction = (ev.deltaX < 0) ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
          }
        }

        // first time, trigger dragstart event
        if(!this.triggered) {
          inst.trigger(this.name + 'start', ev);
          this.triggered = true;
        }

        // trigger normal event
        inst.trigger(this.name, ev);

        // direction event, like dragdown
        inst.trigger(this.name + ev.direction, ev);

        // block the browser events
        if((inst.options.drag_block_vertical && Hammer.utils.isVertical(ev.direction)) ||
          (inst.options.drag_block_horizontal && !Hammer.utils.isVertical(ev.direction))) {
          ev.preventDefault();
        }
        break;

      case Hammer.EVENT_END:
        // trigger dragend
        if(this.triggered) {
          inst.trigger(this.name + 'end', ev);
        }

        this.triggered = false;
        break;
    }
  }
};

/**
 * Hold
 * Touch stays at the same place for x time
 * @events  hold
 */
Hammer.gestures.Hold = {
  name    : 'hold',
  index   : 10,
  defaults: {
    hold_timeout  : 500,
    hold_threshold: 1
  },
  timer   : null,
  handler : function holdGesture(ev, inst) {
    switch(ev.eventType) {
      case Hammer.EVENT_START:
        // clear any running timers
        clearTimeout(this.timer);

        // set the gesture so we can check in the timeout if it still is
        Hammer.detection.current.name = this.name;

        // set timer and if after the timeout it still is hold,
        // we trigger the hold event
        this.timer = setTimeout(function() {
          if(Hammer.detection.current.name == 'hold') {
            inst.trigger('hold', ev);
          }
        }, inst.options.hold_timeout);
        break;

      // when you move or end we clear the timer
      case Hammer.EVENT_MOVE:
        if(ev.distance > inst.options.hold_threshold) {
          clearTimeout(this.timer);
        }
        break;

      case Hammer.EVENT_END:
        clearTimeout(this.timer);
        break;
    }
  }
};

/**
 * Release
 * Called as last, tells the user has released the screen
 * @events  release
 */
Hammer.gestures.Release = {
  name   : 'release',
  index  : Infinity,
  handler: function releaseGesture(ev, inst) {
    if(ev.eventType == Hammer.EVENT_END) {
      inst.trigger(this.name, ev);
    }
  }
};

/**
 * Swipe
 * triggers swipe events when the end velocity is above the threshold
 * @events  swipe, swipeleft, swiperight, swipeup, swipedown
 */
Hammer.gestures.Swipe = {
  name    : 'swipe',
  index   : 40,
  defaults: {
    // set 0 for unlimited, but this can conflict with transform
    swipe_min_touches: 1,
    swipe_max_touches: 1,
    swipe_velocity   : 0.7
  },
  handler : function swipeGesture(ev, inst) {
    if(ev.eventType == Hammer.EVENT_END) {
      // max touches
      if(inst.options.swipe_max_touches > 0 &&
        ev.touches.length < inst.options.swipe_min_touches &&
        ev.touches.length > inst.options.swipe_max_touches) {
        return;
      }

      // when the distance we moved is too small we skip this gesture
      // or we can be already in dragging
      if(ev.velocityX > inst.options.swipe_velocity ||
        ev.velocityY > inst.options.swipe_velocity) {
        // trigger swipe events
        inst.trigger(this.name, ev);
        inst.trigger(this.name + ev.direction, ev);
      }
    }
  }
};

/**
 * Tap/DoubleTap
 * Quick touch at a place or double at the same place
 * @events  tap, doubletap
 */
Hammer.gestures.Tap = {
  name    : 'tap',
  index   : 100,
  defaults: {
    tap_max_touchtime : 250,
    tap_max_distance  : 10,
    tap_always        : true,
    doubletap_distance: 20,
    doubletap_interval: 300
  },
  handler : function tapGesture(ev, inst) {
    if(ev.eventType == Hammer.EVENT_END && ev.srcEvent.type != 'touchcancel') {
      // previous gesture, for the double tap since these are two different gesture detections
      var prev = Hammer.detection.previous,
        did_doubletap = false;

      // when the touchtime is higher then the max touch time
      // or when the moving distance is too much
      if(ev.deltaTime > inst.options.tap_max_touchtime ||
        ev.distance > inst.options.tap_max_distance) {
        return;
      }

      // check if double tap
      if(prev && prev.name == 'tap' &&
        (ev.timeStamp - prev.lastEvent.timeStamp) < inst.options.doubletap_interval &&
        ev.distance < inst.options.doubletap_distance) {
        inst.trigger('doubletap', ev);
        did_doubletap = true;
      }

      // do a single tap
      if(!did_doubletap || inst.options.tap_always) {
        Hammer.detection.current.name = 'tap';
        inst.trigger(Hammer.detection.current.name, ev);
      }
    }
  }
};

/**
 * Touch
 * Called as first, tells the user has touched the screen
 * @events  touch
 */
Hammer.gestures.Touch = {
  name    : 'touch',
  index   : -Infinity,
  defaults: {
    // call preventDefault at touchstart, and makes the element blocking by
    // disabling the scrolling of the page, but it improves gestures like
    // transforming and dragging.
    // be careful with using this, it can be very annoying for users to be stuck
    // on the page
    prevent_default    : false,

    // disable mouse events, so only touch (or pen!) input triggers events
    prevent_mouseevents: false
  },
  handler : function touchGesture(ev, inst) {
    if(inst.options.prevent_mouseevents && ev.pointerType == Hammer.POINTER_MOUSE) {
      ev.stopDetect();
      return;
    }

    if(inst.options.prevent_default) {
      ev.preventDefault();
    }

    if(ev.eventType == Hammer.EVENT_START) {
      inst.trigger(this.name, ev);
    }
  }
};

/**
 * Transform
 * User want to scale or rotate with 2 fingers
 * @events  transform, pinch, pinchin, pinchout, rotate
 */
Hammer.gestures.Transform = {
  name     : 'transform',
  index    : 45,
  defaults : {
    // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1
    transform_min_scale   : 0.01,
    // rotation in degrees
    transform_min_rotation: 1,
    // prevent default browser behavior when two touches are on the screen
    // but it makes the element a blocking element
    // when you are using the transform gesture, it is a good practice to set this true
    transform_always_block: false
  },
  triggered: false,
  handler  : function transformGesture(ev, inst) {
    // current gesture isnt drag, but dragged is true
    // this means an other gesture is busy. now call dragend
    if(Hammer.detection.current.name != this.name && this.triggered) {
      inst.trigger(this.name + 'end', ev);
      this.triggered = false;
      return;
    }

    // atleast multitouch
    if(ev.touches.length < 2) {
      return;
    }

    // prevent default when two fingers are on the screen
    if(inst.options.transform_always_block) {
      ev.preventDefault();
    }

    switch(ev.eventType) {
      case Hammer.EVENT_START:
        this.triggered = false;
        break;

      case Hammer.EVENT_MOVE:
        var scale_threshold = Math.abs(1 - ev.scale);
        var rotation_threshold = Math.abs(ev.rotation);

        // when the distance we moved is too small we skip this gesture
        // or we can be already in dragging
        if(scale_threshold < inst.options.transform_min_scale &&
          rotation_threshold < inst.options.transform_min_rotation) {
          return;
        }

        // we are transforming!
        Hammer.detection.current.name = this.name;

        // first time, trigger dragstart event
        if(!this.triggered) {
          inst.trigger(this.name + 'start', ev);
          this.triggered = true;
        }

        inst.trigger(this.name, ev); // basic transform event

        // trigger rotate event
        if(rotation_threshold > inst.options.transform_min_rotation) {
          inst.trigger('rotate', ev);
        }

        // trigger pinch event
        if(scale_threshold > inst.options.transform_min_scale) {
          inst.trigger('pinch', ev);
          inst.trigger('pinch' + ((ev.scale < 1) ? 'in' : 'out'), ev);
        }
        break;

      case Hammer.EVENT_END:
        // trigger dragend
        if(this.triggered) {
          inst.trigger(this.name + 'end', ev);
        }

        this.triggered = false;
        break;
    }
  }
};

  // Based off Lo-Dash's excellent UMD wrapper (slightly modified) - https://github.com/bestiejs/lodash/blob/master/lodash.js#L5515-L5543
  // some AMD build optimizers, like r.js, check for specific condition patterns like the following:
  if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
    // define as an anonymous module
    define('Hammer',[],function() {
      return Hammer;
    });
    // check for `exports` after `define` in case a build optimizer adds an `exports` object
  }
  else if(typeof module === 'object' && typeof module.exports === 'object') {
    module.exports = Hammer;
  }
  else {
    window.Hammer = Hammer;
  }
})(this);
define('PrimaryNav',['Utility', 'Hammer'], function(Utility, Hammer) {
    

    var methods = {};
    var PrimaryNav;
    var NavLink;

    methods.init = function() {
        PrimaryNav = document.querySelectorAll('#PrimaryNav')[0];
        NavLink    = document.querySelectorAll('.pageheader-skiplink-nav')[0];

        // Hook up the primary menu button
        new Hammer(document.querySelectorAll('.pageheader-skiplink-nav')[0])
            .on('click', function(e) {
                e.preventDefault();
                e.stopPropagation();
                Utility.toggleClass(PrimaryNav, 'is_open');
                Utility.toggleClass(NavLink, 'is_open');
            });
    };

    methods.close = function() {
        Utility.removeClass(PrimaryNav, 'is_open');
        Utility.removeClass(NavLink, 'is_open');
    };

    return methods;
});
define('SecondaryNav',['Utility', 'Hammer'], function(Utility, Hammer) {
    

    var methods = {};
    var SecondaryNav;

    methods.init = function() {
        SecondaryNav = document.querySelectorAll('#SecondaryNav')[0];

        if(typeof SecondaryNav !== 'undefined') {
            var secondaryHeight = SecondaryNav.offsetHeight;
            SecondaryNav.style.maxHeight = secondaryHeight + 'px';
            Utility.addClass(SecondaryNav, 'is_closed');

            // Hook up the secondary menu button
            new Hammer(document.querySelectorAll('.secondarynav-button')[0])
                .on('click', function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    Utility.toggleClass(SecondaryNav, 'is_closed');
                });
        }
    };

    return methods;
});
define('SiteSearch',['Utility', 'PrimaryNav', 'Hammer'], function(Utility, PrimaryNav, Hammer) {
    

    var methods = {};
    var SiteSearch;

    methods.init = function() {
        SiteSearch = document.querySelectorAll('#SiteSearch')[0];

        // Hook up the search button
        new Hammer(document.querySelectorAll('.pageheader-skiplink-search')[0])
            .on('click', function(e) {
                e.preventDefault();
                e.stopPropagation();
                PrimaryNav.close();
                Utility.addClass(SiteSearch, 'is_open');
            });

        // Hook up the search close button
        new Hammer(document.querySelectorAll('.sitesearch-close')[0])
            .on('click', function(e) {
                e.preventDefault();
                e.stopPropagation();
                Utility.removeClass(SiteSearch, 'is_open');
            });
    };

    return methods;
});
define('ClickTracking',['Utility'], function(Utility) {
    

    function _gaLt(event){
        var el = event.srcElement || event.target;

        // Loop up the tree through parent elements if clicked element is not a link (eg: an image in a link)
        while(el && (typeof el.tagName === 'undefined' || el.tagName.toLowerCase() !== 'a' || !el.href)) {
            el = el.parentNode;
        }

        if(el && el.href){
            if(el.href.indexOf(location.host) === -1){ // external link
                ga('send', 'event', 'Outgoing Links', el.href, document.location.pathname + document.location.search);
                // if target not set then delay opening of window by 0.5s to allow tracking
                if(!el.target || el.target.match(/^_(self|parent|top)$/i)){
                    setTimeout(function(){
                        document.location.href = el.href;
                    }.bind(el),500);
                    // Prevent standard click
                    if(event.preventDefault) {
                        event.preventDefault();
                    } else {
                        event.returnValue = !1;
                    }
                }
            }
        }
    }

    function addClickTracker(el, category, action, label) {
        if(el) {
            Utility.addEventListener(el, 'click', function(e) {
                ga('send', 'event', category, action, label);
            });
        }
    }

    function addDownloadClickTracker(el, category, action) {
        var i;

        for(i = 0; i < el.length; i++) {
            addClickTracker(el[i], category, action, el[i].href);
        }
    }

    var methods = {};
    methods.init = function() {

        if(window.ga) {
            // Add the tracking for the newsletter button
            addClickTracker(document.querySelectorAll('.newsletter-signup')[0], 'Newsletter', 'Signup', 'Form');

            // Add the tracking for the social buttons button
            addClickTracker(document.querySelectorAll('.addthis_button_facebook')[0], 'Social_share', 'Click', 'ESF_facebook_share');
            addClickTracker(document.querySelectorAll('.addthis_button_twitter')[0], 'Social_share', 'Click', 'ESF_twitter_share');
            addClickTracker(document.querySelectorAll('.addthis_button_google_plusone_share')[0], 'Social_share', 'Click', 'ESF_google_share');

            // Add the tracking for the social accounts (on homepage)
            addClickTracker(document.querySelectorAll('.sociallink-facebook')[0], 'Social_profile', 'Click', 'ESF_facebook_profile');
            addClickTracker(document.querySelectorAll('.sociallink-twitter')[0], 'Social_profile', 'Click', 'ESF_twitter_profile');
            addClickTracker(document.querySelectorAll('.sociallink-google')[0], 'Social_profile', 'Click', 'ESF_google_profile');
            addClickTracker(document.querySelectorAll('.sociallink-pinterest')[0], 'Social_profile', 'Click', 'ESF_pinterest_profile');
            addClickTracker(document.querySelectorAll('.sociallink-youtube')[0], 'Social_profile', 'Click', 'ESF_youtube_profile');

            // Add the tracking for the newsletter button
            addDownloadClickTracker(document.querySelectorAll('.downloads-item a'), 'PDF', 'Download');

            // Attach the Google analytics event to all clicks in the document
            var d = document.body;
            if(d.addEventListener) {
                d.addEventListener('click',_gaLt,!1);
            } else if(d.attachEvent) {
                d.attachEvent('onclick',_gaLt);
            }
        }
    };

    return methods;
});
define('YoutubeTracking',['Utility'], function(Utility) {

	

	var ORIGIN_PARAMATER = 'origin=';
	var JSAPI_PARAMATER  = 'enablejsapi=1';

	//Then as a drop of rain we create two heavenly arrays
	//who may hold in thy endless bossom our value
	//necessary they may be not, but what is love
	//but the ample clevage of an array
	var videoArray = [];
	var playerArray = [];
	var pauseFlagArray = [];
	//And a third, new to Part 3
	//whereupon we now shall seek the title of our fair video
	//with thanks to Alex Moore @almoo
	//who is known by the Romanians
	var videoTitle = [];
	//and what is life without a magical switch
	//for true we show the true title of the player
	//unhindered by disguise or shadow
	//for false we leave it obscured by darkness
	//and the player id
	//so rings the clock
	//1 doth show the player title
	//2 doth show the player id
	//3 doth show both as concatenated by the hand of man
	var showTitle = 3;
	//and if you are not tracking
	//even with this code
	//try setting this line to 1
	//for with it we force the youtube beast
	//and reload the frames
	//which oft works
	var reloadFrames = 1;

	// Get the YouTube api
	function getYoutubeApi() {
		var firstScriptTag = document.getElementsByTagName('script')[0];
		var tag = document.createElement('script');
		tag.src = "//www.youtube.com/iframe_api";
		firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
	}

	//To obtain the real titles of our noble videos
	//rather than the gibberish jumble
	//as provided to by the wizard Alex Moore
	function getRealTitles(playerIndex) {
		Utility.getJSON('http://gdata.youtube.com/feeds/api/videos/'+videoArray[playerIndex]+'?v=2&alt=json', function(data) {
			//and lo the monster repsonds
			//it's whispers flowing as mist
			//through the mountain crag
			videoTitle[playerIndex] = data.entry.title.$t;
			registerVideoForApi(playerIndex);
		});
	}

	function getVideoUrlWithOrigin(src) {
		var i;
		var newOrigin;
		var originRegex  = /origin=.*/;
		var searchResult = src.match(originRegex);

		if(searchResult){
			for(i = 0; i < searchResult.length; i++) {
				// Make sure that the origin is set to the current URL
				newOrigin = ORIGIN_PARAMATER + window.location.hostname;
				src = src.replace(originRegex, newOrigin);
			}
		} else {
			// No source was set so updated it to the current URL
			src = src + '&' + ORIGIN_PARAMATER + window.location.hostname;
		}

		return src;
	}

	function forcePlayerFrameReload(videoSrc, videoElement) {
		var frameRegex = /(?:https?:)?\/\/www\.youtube\.com\/embed\/([\w-]{11})(\?)?/;

		// Force SSL
		videoSrc = videoSrc.replace('http', 'https');
		//next some trickery
		//has it the foul stench of the demon parameter
		var SourceCheckA = videoSrc.match(frameRegex);
		if(SourceCheckA[2]=='?'){
			// There are settings on the URL
			// Check if it is enabled for the js api
			if(!videoSrc.match(/enablejsapi=1/)){
				// No, update the URL so that it is
				videoSrc = videoSrc + '&'+ JSAPI_PARAMATER;
			}

			videoSrc = getVideoUrlWithOrigin(videoSrc);
		} else {
			// There are no settings on the URL, update it to include the usual parameters
			videoSrc = videoSrc + '?'+ JSAPI_PARAMATER + '&' + ORIGIN_PARAMATER + window.location.hostname;
		}

		videoElement.setAttribute('src', videoSrc);

		return videoSrc;
	}

	function registerVideoForApi(playerIndex) {
		playerArray[playerIndex] = new YT.Player(videoArray[playerIndex], {
			videoId: videoArray[playerIndex],
			forceSSL: true,
			events: {
				'onStateChange': onPlayerStateChange
			}
		});
	}

	function setupVideoIframe(el) {
		var srcResult;
		var playerIndex;
		var locationRegex = /(?:https?:)?\/\/www\.youtube\.com\/embed\/([\w-]{11})(?:\?.*)?/;
		var videoSrc      = el.getAttribute('src');

		// Check that we have a src value
		if(videoSrc) {
			srcResult = videoSrc.match(locationRegex);
			// Make sure that the iframe is a Youtube video iframe
			if(srcResult && srcResult.length > 1){
				// Check if we are reloading frames
				if(reloadFrames) {
					videoSrc = forcePlayerFrameReload(videoSrc, el);
				}

				// Add the video id to the video array
				playerIndex = videoArray.push(srcResult[1]) - 1;
				// Add the id to the iframe so that it can be easily accessed
				el.setAttribute('id', srcResult[1]);
				// Get the real titles of the videos
				if(showTitle !== 2) {
					getRealTitles(playerIndex);
				} else {
					registerVideoForApi(playerIndex);
				}
			}
		}
	}

	function onPlayerStateChange(event) {
		//Let us accept the player which was massaged
		//by the mousey hands of woman or man
		var videoURL = event.target.getVideoUrl();
		//We must strip from it, the true identity
		var regex = /v=(.+)$/;
		var matches = videoURL.match(regex);
		var videoID = matches[1];
		//and prepare for it's true title
		var thisVideoTitle = '';
		var i;
		//we look through all the array
		//which at first glance may seem unfocused
		//but tis the off kilter response
		//from the magical moore json
		//which belies  this approach
		//Tis a hack? A kludge?
		//These are fighting words, sir!
		for(i=0; i < videoArray.length; i++) {
			//tis the video a match?
			if (videoArray[i] == videoID) {
				//apply the true title!
				thisVideoTitle = videoTitle[i] || '';
				//should we have a title, alas naught else
				if(thisVideoTitle.length > 0) {
					if(showTitle === 3){
						thisVideoTitle = thisVideoTitle + " | " + videoID;
					} else if(showTitle === 2) {
						thisVideoTitle = videoID;
					}
				} else {
					thisVideoTitle = videoID;
				}



				//Should the video rear it's head
				if (event.data == YT.PlayerState.PLAYING) {
					ga('send', 'event', 'Videos', 'Play', thisVideoTitle);
					//thy video plays
					//reaffirm the pausal beast is not with us
					pauseFlagArray[i] = false;
				}
				//should the video tire out and cease
				if (event.data == YT.PlayerState.ENDED){
					ga('send', 'event', 'Videos', 'Watch to End', thisVideoTitle);
				}
				//and should we tell it to halt, cease, heal.
				//confirm the pause has but one head and it flies not its flag
				//lo the pause event will spawn a many headed monster
				//with events overflowing
				if (event.data == YT.PlayerState.PAUSED && pauseFlagArray[i] !== true){
					ga('send', 'event', 'Videos', 'Pause', thisVideoTitle);
					//tell the monster it may have
					//but one head
					pauseFlagArray[i] = true;
				}
				//and should the monster think, before it doth play
				//after we command it to move
				if (event.data == YT.PlayerState.BUFFERING){
					ga('send', 'event', 'Videos', 'Buffering', thisVideoTitle);
				}
				//and should it cue
				//for why not track this as well.
				if (event.data == YT.PlayerState.CUED){
					ga('send', 'event', 'Videos', 'Cueing', thisVideoTitle);
				}

			}
		}
	}

	//
	//And Then Lo We Tracked The Frames
	//with hounds ere the dark of knight
	//we sought them to blood our first array
	function trackYouTube() {
		var i;
		var elements = document.querySelectorAll('iframe');

		for(i = 0; i < elements.length; i++) {
			setupVideoIframe(elements[i]);
		}
	}

	getYoutubeApi();

	//once we started our story with a document ready
	//from the jquery
	//but oft this caused problems
	//as the youtube monster would instantiate too quickly
	//in a rush, it would beat the jquery to completetion
	//and instantiate it's elements prior to our array
	//so we wait. for the page to load fully
	//which may cause problems with thy pages
	//should your other elements not comply and load quickly
	//forsooth they are the problem not i
	Utility.addEventListener(window, 'load', trackYouTube);

});

require(
    ['Utility', 'PrimaryNav', 'SecondaryNav', 'SiteSearch', 'ClickTracking', 'YoutubeTracking'],
    function(Utility, PrimaryNav, SecondaryNav, SiteSearch, ClickTracking, YoutubeTracking) {
        

        // Check if there is placeholder support
        if(Modernizr.input.placeholder) {
            Utility.addClass(document.querySelectorAll('html')[0], 'placeholder');
        }

        // Initialise the site wide modules
        PrimaryNav.init();
        SecondaryNav.init();
        SiteSearch.init();
        ClickTracking.init();

        // Find all elements that have a require instruction
        var components = document.querySelectorAll('[data-require]');
        var i;

        // Loop through the components and load them
        for(i = 0; i < components.length; i++) {
            Utility.loadComponentModule(components[i], components[i].getAttribute('data-require'));
        }
    });
define("Main", function(){});

/*global window document clearTimeout setTimeout */

;(function (window, document, undefined) {
    

    var ssm = {},
        states = [],
        browserWidth = 0,
        currentStates = [],
        resizeTimeout = 10,
        resizeTimer = null,
        configOptions = [];

    var browserResizeDebounce = function () {
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(browserResizeWrapper, resizeTimeout);
    };

    //Added wrapper for the resize method
    var browserResizeWrapper = function() {
        browserWidth = getWidth();
        browserResize(browserWidth);
    };

    var browserResize = function (localBrowserWidth) {
        var totalStates = states.length,
            totalConfigOptions = configOptions.length,
            leaveMethods = [],
            resizeMethods = [],
            enterMethods = [],
            validState = true,
            tempObj = ssm;

        for (var i = 0; i < totalStates; i++) {
            
            validState = true;
            tempObj.state = states[i];
            tempObj.browserWidth = localBrowserWidth;

            for (var j = 0; j < totalConfigOptions; j++) {
                //Skip any config options the state does not define
                if(typeof tempObj.state[configOptions[j].name] !== "undefined"){
                    tempObj.callback = configOptions[j].test;
                    if(tempObj.callback() === false){
                        validState = false;
                        break;
                    }
                }
            }

            if(validState){
                
                if(objectInArray(currentStates, states[i])){
                    resizeMethods.push(states[i].onResize);
                }
                else{
                    currentStates.push(states[i]);
                    enterMethods.push(states[i].onEnter);
                }
            }
            else{
                if(objectInArray(currentStates, states[i])){
                    leaveMethods.push(states[i].onLeave);
                    currentStates = removeObjectInArray(currentStates,states[i]);
                }
            }
        }

        fireAllMethodsInArray(leaveMethods);
        fireAllMethodsInArray(enterMethods);
        fireAllMethodsInArray(resizeMethods);
    };

    ssm.browserResize = browserResize;

    ssm.getBrowserWidth = function(){
        return browserWidth;
    };

    //Add a new state
    ssm.addState = function (options) {
        //Setting sensible defaults for a state
        //Max width is set to 99999 for comparative purposes, is bigger than any display on market
        var defaultOptions = {
            id: makeID(),
            minWidth: 0,
            maxWidth: 99999,
            onEnter: function () {},
            onLeave: function () {},
            onResize: function () {}
        };

        //Merge options with defaults
        options = mergeOptions(defaultOptions, options);

        //Add state to the master states array
        states.push(options);

        //Sort 
        states = sortByKey(states, "minWidth");

        return this;
    };

    //Allow updating of an already added state
    ssm.updateState = function (stateId, options) {
        for (var i = states.length - 1; i >= 0; i--) {
            if (states[i].id === stateId) {
                states[i] = mergeOptions(states[i], options);
            }
        }

        return this;
    };

    //Find and remove the state from the array
    ssm.removeState = function (stateId) {
        for (var i = states.length - 1; i >= 0; i--) {
            if (states[i].id === stateId) {
                states.splice(i, 1);
            }
        }

        return this;
    };

    //Remove multiple states from an array
    ssm.removeStates = function (statesArray) {
        for (var i = statesArray.length - 1; i >= 0; i--) {
            ssm.removeState(statesArray[i]);
        }

        return this;
    };

    //Find and remove the state from the array
    ssm.removeAllStates = function () {
        states = currentStates = [];

        return this;
    };

    //Add multiple states from an array
    ssm.addStates = function (statesArray) {
        for (var i = statesArray.length - 1; i >= 0; i--) {
            ssm.addState(statesArray[i]);
        }

        return this;
    };

    ssm.getStates = function(idArr){
        var idCount = null, returnArr = [];

        if(typeof(idArr) === "undefined"){
            return states;
        }
        else{
            idCount = idArr.length;
            
            for (var i = 0; i < idCount; i++) {
                returnArr.push(getStateByID(idArr[i]));
            }

            return returnArr;
        }
    };

    ssm.addConfigOption = function(options){
        var defaultOptions = {
            name: "",
            test: null
        };

        //Merge options with defaults
        options = mergeOptions(defaultOptions, options);

        if(options.name !== "" && options.test !== null){
            configOptions.push(options);
        }
    };

    ssm.getConfigOption = function(name){
        if(typeof name === "string"){
            for (var i = configOptions.length - 1; i >= 0; i--) {
                if(configOptions[i].name === name){
                    return configOptions[i];
                }
            }
        }
        else{
            return configOptions;
        }
    };

    ssm.removeConfigOption = function(name){
        for (var i = configOptions.length - 1; i >= 0; i--) {
            if (configOptions[i].name === name) {
                configOptions.splice(i, 1);
            }
        }
    };

    ssm.isActive = function(name){
        for (var i = 0; i < currentStates.length; i++) {
            if(currentStates[i].id === name){
                return true;
            }
        }
        
        return false;
    };

    ssm.getCurrentStates = function(){
        return currentStates;
    };

    //Change the timeout before firing the resize function
    ssm.setResizeTimeout = function (milliSeconds) {
        resizeTimeout = milliSeconds;
    };

    //Change the timeout before firing the resize function
    ssm.getResizeTimeout = function () {
        return resizeTimeout;
    };

    ssm.ready = function () {
        //Update browser width
        browserWidth = getWidth();

        //Attach event for resizing
        if (window.attachEvent) {
            window.attachEvent("onresize", browserResizeDebounce);
        } else if (window.addEventListener) {
            window.addEventListener("resize", browserResizeDebounce, true);
        }

        browserResize(browserWidth);

        return this;
    };

    var makeID = function () {
        var text = "";
        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

        for (var i = 0; i < 10; i++) {
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        }
        return text;
    };

    var getWidth = function () {
        var x = 0;

        if(typeof window.matchMedia === "function"){
            //Browsers that support match media we will test our method does same as media queries
            if(window.matchMedia('(width:'+window.innerWidth+'px)').matches){
                x = window.innerWidth;
            }
            else if(window.matchMedia('(width:'+window.innerWidth+'px)').matches){
                x = window.outerWidth;
            }
            else if(window.matchMedia('(width:'+document.body.clientWidth+'px)').matches){
                x = document.body.clientWidth;
            }
        }
        else if (typeof(document.body.clientWidth) === "number") {
            // newest gen browsers
            x = document.body.clientWidth;
        }
        else if( typeof( window.innerWidth ) === "number" ) {
            x = window.innerWidth;
        }
        else if( document.documentElement && document.documentElement.clientWidth ) {
            //IE 6+ in 'standards compliant mode'
            x = document.documentElement.clientWidth;
        }

        return x;
    };


    var mergeOptions = function (obj1, obj2) {
        var obj3 = {};

        for (var attrname in obj1) {
            obj3[attrname] = obj1[attrname];
        }

        for (var attrname2 in obj2) {
            obj3[attrname2] = obj2[attrname2];
        }

        return obj3;
    };


    var sortByKey = function (array, key) {
        return array.sort(function (a, b) {
            var x = a[key];
            var y = b[key];
            return ((x < y) ? -1 : ((x > y) ? 1 : 0));
        });
    };

    //Method to get a state based on the ID
    var getStateByID = function(id){
        for (var i = states.length - 1; i >= 0; i--) {
            if(states[i].id === id){
                return states[i];
            }
        }
    };

    var objectInArray = function(arr, obj){
        for (var i = 0; i < arr.length; i++) {
            if(arr[i] === obj){
                return true;
            }
        }
    };

    var removeObjectInArray = function(arr,obj){
        var length = arr.length;

        for (var i = 0; i < length; i++) {
            if (arr[i] === obj) {
                arr.splice(i, 1);
            }
        }

        return arr;
    };

    var fireAllMethodsInArray = function(arr){
        var arrLength = arr.length;

        for (var i = 0; i < arrLength; i++) {
            arr[i]();
        }
    };

    //define the built in methods (required for compatabilty)
    ssm.addConfigOption({name:"minWidth", test: function(){
        if(typeof this.state.minWidth === "number" && this.state.minWidth <= this.browserWidth){
            return true;
        }
        else{
            return false;
        }
    }});

    ssm.addConfigOption({name:"maxWidth", test: function(){
        if(typeof this.state.maxWidth === "number" && this.state.maxWidth >= this.browserWidth){
            return true;
        }
        else{
            return false;
        }
    }});

    //Expose Simple State Manager
    window.ssm = ssm;

    if (typeof window.define === "function" && window.define.amd) {
        window.define("ssm", [], function () {
            return window.ssm;
        });
    }

})(window, document);
define("SSM", (function (global) {
    return function () {
        var ret, fn;
        return ret || global.ssm;
    };
}(this)));

define(
    'ScrollZone',['Utility', 'Hammer', 'SSM'],
    function(Utility, Hammer, ssm) {
    

    function ScrollZone(el, breakpoint) {
        this.el         = el;
        this.container  = null;
        this.children   = null;
        this.childWidth = 0;
        this.current    = 0;
        this.breakpoint = breakpoint;
        this.active     = false;
        this.transform  = Modernizr.prefixed('transform', el.style, false);
        this.getObjects();
        this.setupSSM();
        this.addHammer();
    }

    ScrollZone.prototype.getObjects = function() {
        this.container = this.el.querySelectorAll('[data-scrollzone="container"]')[0];
        this.children  = this.el.querySelectorAll('[data-scrollzone="item"]');
    };

    ScrollZone.prototype.setupSSM = function() {
        var self = this;
        ssm.addState({
            id: 'EnableScrollzone',
            maxWidth: this.breakpoint,
            onEnter: function() {
                self.enable();
            },
            onLeave: function() {
                self.disable();
            }
        });

        ssm.ready();
    };

    ScrollZone.prototype.addHammer = function() {
        var self = this;
        new Hammer(this.container)
            .on('swipeleft', function(ev) {
                if(self.active) {
                    ev.preventDefault();
                    ev.gesture.stopPropagation();
                    ev.gesture.preventDefault();
                    self.next();
                }
            })
            .on('swiperight', function(ev) {
                if(self.active) {
                    ev.preventDefault();
                    ev.gesture.stopPropagation();
                    ev.gesture.preventDefault();
                    self.prev();
                }
            });
    };

    ScrollZone.prototype.moveToCurrent = function() {
        if(this.transform !== false) {
            this.container.style[this.transform] = 'translate3d(' + this.current +'px, 0, 0)';
        } else {
            this.container.style.left = this.current +'px';
        }
    };

    ScrollZone.prototype.next = function() {
        //var maxLeft = this.el.offsetWidth / this.children.length
        var newLeft = this.current - this.childWidth;
        if(newLeft < this.current + this.childWidth) {
            this.current = newLeft;
            this.moveToCurrent();
        }
    };

    ScrollZone.prototype.prev = function() {
        var newLeft = this.current + this.childWidth;
        if(newLeft <= 0) {
            this.current = newLeft;
            this.moveToCurrent();
        }
    };

    ScrollZone.prototype.enable = function() {
        this.childWidth            = this.children[0].offsetWidth;
        this.container.style.width = (this.childWidth * this.children.length) + 'px';
        this.current               = 0;
        this.active                = true;
        this.moveToCurrent();
    };

    ScrollZone.prototype.disable = function() {
        // Strip out the styles that have been applied
        this.container.removeAttribute('style');
        this.active                = false;
    };

    var methods = {};
    methods.init = function(el, breakpoint) {
        return new ScrollZone(el, breakpoint);
    };

    return methods;
});
define('PagedList',['Utility', 'Hammer', 'SSM'], function(Utility, Hammer, ssm) {
    

    function PagedList(el, breakpoint) {
        this.el         = el;
        this.container  = null;
        this.children   = null;
        this.childWidth = 0;
        this.current    = 0;
        this.breakpoint = breakpoint;
        this.active     = false;
        this.transform  = Modernizr.prefixed('transform', el.style, false);
        this.getObjects();
        this.addPager();
        this.setupSSM();
    }

    PagedList.prototype.getObjects = function() {
        this.container = this.el.querySelectorAll('[data-pagedlist="container"]')[0];
        this.children  = this.el.querySelectorAll('[data-pagedlist="item"]');
    };

    PagedList.prototype.addPager = function() {
        var pageContainer = document.createElement('ol');
        Utility.addClass(pageContainer, 'slideshow-pager');
        var i;
        for(i = 0; i < this.children.length; i++) {
            pageContainer.appendChild(this.createPage(i));
        }

        this.el.appendChild(pageContainer);
    };

    PagedList.prototype.createPage = function(i) {
        var self = this;
        var page = document.createElement('li');
        page.appendChild(document.createTextNode(i));
        Utility.addClass(page, 'slideshow-pager-page');

        new Hammer(page)
            .on('click', function(){
                self.showIndex(i);
            });

        return page;
    };

    PagedList.prototype.setupSSM = function() {
        var self = this;
        ssm.addState({
            id: 'EnablePagedList',
            minWidth: this.breakpoint,
            onEnter: function() {
                self.enable();
            },
            onLeave: function() {
                self.disable();
            }
        });

        ssm.ready();
    };

    PagedList.prototype.showIndex = function(n) {
        if(this.current !== n && n < this.children.length) {
            var initial = -100 * n;
            var i;
            if(this.transform !== false) {
                for(i = 0; i < this.children.length; i++) {
                    this.children[i].style[this.transform] = 'translate3d(' + initial +'%, 0, 0)';
                    initial += 100;
                }
            } else {
                for(i = 0; i < this.children.length; i++) {
                    this.children[i].style.left = initial +'%';
                    initial += 100;
                }
            }

            this.current = n;
            this.updateNav();
        }
    };

    PagedList.prototype.enable = function() {
        this.childWidth            = this.children[0].offsetWidth;
        this.active                = true;
    };

    PagedList.prototype.disable = function() {
        this.active                = false;
    };

    PagedList.prototype.updateNav = function() {

    };

    var methods = {};
    methods.init = function(el, breakpoint) {
        return new PagedList(el, breakpoint);
    };

    return methods;
});
define('RealLifeStories',['Utility', 'ScrollZone', 'PagedList'], function(Utility, ScrollZone, PagedList) {
    

    var methods = {};

    methods.init = function(component) {
        if(Utility.hasClass(component, 'stories')) {
            ScrollZone.init(component, 859);
            PagedList.init(component, 860);
        }
    };

    return methods;
});
define('FrequentlyAskedQuestions',['Utility'], function(Utility) {
    

    var methods = {};

    function addClickHandler(el) {
        var handle = el.querySelectorAll('.faqs-faq-handle')[0];
        Utility.addEventListener(handle, 'click', function() {
            Utility.toggleClass(el, 'is_closed');
        });
    }

    methods.init = function(component) {
        var Faqs = component.querySelectorAll('.faqs-faq');
        if(Faqs.length) {
            var i;
            for(i = 0; i < Faqs.length; i++) {
                Faqs[i].querySelectorAll('.faqs-faq-content')[0].style.maxHeight = Faqs[i].offsetHeight + 'px';
                Utility.addClass(Faqs[i], 'is_closed');
                addClickHandler(Faqs[i]);
            }
        }
    };

    return methods;
});
define('SocialFeed',['Utility', 'ScrollZone'], function(Utility, ScrollZone) {
    

    var methods = {};

    methods.init = function(component) {
        if(Utility.hasClass(component, 'socialfeed')) {
            ScrollZone.init(component, 859);
        }
    };

    return methods;
});
define('ProductRecallSearch',['Utility'], function(Utility) {
    

    var methods = {};

    function addClickHandler(el, scope) {
        Utility.addEventListener(el, 'click', function(e) {
            scope.closeAll();
            scope.open(el);
            Utility.toggleClass(el, 'is_open');
        });
    }

    function RecallSearch(el) {
        this.blocks = el.querySelectorAll('.recall-search');
        if(this.blocks.length) {
            var i;
            for(i = 0; i < this.blocks.length; i++) {
                addClickHandler(this.blocks[i], this);
            }
        }
    }

    RecallSearch.prototype.closeAll = function() {
        var i;
        for(i = 0; i < this.blocks.length; i++) {
            Utility.addClass(this.blocks[i], 'is_closed');
        }
    };

    RecallSearch.prototype.open = function(el) {
        Utility.removeClass(el, 'is_closed');
    };

    methods.init = function(el) {
        return new RecallSearch(el);
    };

    return methods;
});

//# sourceMappingURL=main.min.js.map