Source: sample-ui.js

(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else if(typeof exports === 'object')
		exports["sampleUI"] = factory();
	else
		root["sampleUI"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};

/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {

/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId])
/******/ 			return installedModules[moduleId].exports;

/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			exports: {},
/******/ 			id: moduleId,
/******/ 			loaded: false
/******/ 		};

/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/ 		// Flag the module as loaded
/******/ 		module.loaded = true;

/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}


/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;

/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;

/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";

/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
	    __webpack_require__(1),
	    __webpack_require__(13),
	    __webpack_require__(5),
	    __webpack_require__(15),
	    __webpack_require__(16),
	    __webpack_require__(9),
	    __webpack_require__(10),
	    __webpack_require__(17),
	    __webpack_require__(18),
	  ], __WEBPACK_AMD_DEFINE_RESULT__ = function (
	    commonUI,
	    PlayerNameManager,
	    dialog,
	    DPad,
	    input,
	    misc,
	    mobileHacks,
	    strings,
	    touch) {
	  return {
	    commonUI: commonUI,
	    PlayerNameManager: PlayerNameManager,
	    dialog: dialog,
	    DPad: DPad,
	    input: input,
	    misc: misc,
	    mobileHacks: mobileHacks,
	    strings: strings,
	    touch: touch,
	  };
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));



/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	/**
	 * Implements the common UI parts of HappyFunTimes for
	 * contollers.
	 * @module CommonUI
	 */
	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
	    __webpack_require__(3),
	    __webpack_require__(8),
	    __webpack_require__(4),
	    __webpack_require__(5),
	    __webpack_require__(6),
	    __webpack_require__(2),
	    __webpack_require__(9),
	    __webpack_require__(10),
	    __webpack_require__(11),
	    __webpack_require__(12),
	  ], __WEBPACK_AMD_DEFINE_RESULT__ = function(
	    IO,
	    HFTSplash,
	    Cookie,
	    dialog,
	    fullScreen,
	    logger,
	    misc,
	    mobilehacks,
	    orientation,
	    PlayerNameHandler) {

	  var $ = function(id) {
	    return document.getElementById(id);
	  };

	  var g = {
	    logger: new logger.NullLogger(),
	  };
	  var s = {
	  };

	  var requireLandscapeHTML = [
	      '<div id="hft-portrait" class="hft-fullsize hft-fullcenter">',
	      '  <div class="hft-portrait-rot90">                         ',
	      '    <div class="hft-instruction">                          ',
	      '      Turn the Screen                                      ',
	      '    </div>                                                 ',
	      '    <div class="hft-xlarge">                               ',
	      '      &#x21ba;                                             ',
	      '    </div>                                                 ',
	      '  </div>                                                   ',
	      '</div>                                                     ',
	  ].join("\n");
	  var requirePortraitHTML = [
	    '<div id="hft-landscape" class="hft-fullsize hft-fullcenter">',
	    '  <div class="hft-landscape-rot90">                         ',
	    '    <div class="hft-instruction">                           ',
	    '      Turn the Screen                                       ',
	    '    </div>                                                  ',
	    '    <div class="hft-xlarge">                                ',
	    '      &#x21bb;                                              ',
	    '    </div>                                                  ',
	    '  </div>                                                    ',
	    '</div>                                                      ',
	  ].join("\n");
	  var orientationDiv;

	  //function isSemiValidOrientation(o) {
	  //  o = o || "";
	  //  return o.indexOf("portrait") >= 0 || o.indexOf("landscape") >= 0;
	  //}

	  function setOrientationHTML(desiredOrientation) {
	    desiredOrientation = desiredOrientation || "";
	    if (!orientationDiv) {
	      orientationDiv = document.createElement("div");
	      //document.body.appendChild(orientationDiv);
	      var h = document.getElementById("hft-menu");
	      h.parentNode.insertBefore(orientationDiv, h);
	    }
	    if (desiredOrientation.indexOf("portrait") >= 0) {
	      orientationDiv.innerHTML = requirePortraitHTML;
	    } else if (desiredOrientation.indexOf("landscape") >= 0) {
	      orientationDiv.innerHTML = requireLandscapeHTML;
	    } else {
	      orientationDiv.innerHTML = "";
	    }
	  }

	  function resetOrientation() {
	    if (fullScreen.isFullScreen()) {
	      orientation.set(g.orientation);
	    }
	  }

	  /**
	   * Sets the orientation of the screen. Doesn't work on desktop
	   * nor iOS unless in app.
	   * @param {string} [desiredOrientation] The orientation. Valid options are
	   *
	   *     "portrait-primary"    // normal way people hold phones
	   *     "portrait-secondary"  // upsidedown
	   *     "landscape-primary"   // phone turned clockwise 90 degrees from normal
	   *     "landscape-secondary" // phone turned counter-clockwise 90 degrees from normal
	   *     "none" (or undefined) // unlocked
	   * @param {bool} [orientationOptional]
	   */
	  function setOrientation(desiredOrientation, orientationOptional) {
	    g.orientation = desiredOrientation;
	    if (orientation.canOrient()) {
	      resetOrientation();
	    } else {
	      var orient = g.orientation;
	      if (orientationOptional) {
	        orient = "none";
	      }
	      setOrientationHTML(orient);
	    }
	  }

	  function showRequireApp() {
	    dialog.modal({
	      title: "HappyFunTimes",
	      msg: "This game requires the HappyFunTimes App<br/>Please download it from your app store",
	    }, function() {
	      var ua = window.navigator.userAgent;
	      if (ua.indexOf("iPhone") >= 0 ||
	          ua.indexOf("iPad") >= 0) {
	        misc.gotoIFrame("itms://itunes.apple.com/");
	      } else if (ua.indexOf("Android") >= 0) {
	        misc.gotoIFrame("market://details?id=com.greggman.HappyFunTimes");
	      } else {
	        showRequiredApp();
	      }
	    });
	  }

	  function showMenu(show) {
	    var menuElement = $("hft-menu");
	    menuElement.style.display = show ? "block" : "none";
	  }

	  /**
	   * @typedef {Object} ControllerUI~Options
	   * @property {callback} [connectFn] function to call when controller
	   *           connects to HappyFunTimes
	   * @property {callback} [disconnectFn] function to call when controller is
	   *           disconncted from game.
	   * @property {boolean} [debug] True displays a status and debug
	   *           html element
	   * @property {number} [numConsoleLines] number of lines to show for the debug console.
	   * @property {string} [orienation] The orientation to set the phone. Only works on Android or the App. See {@link setOrientation}.
	   * @property {boolean} [orientationOptional] Don't ask the user to orient the phone if their device does not support orientation
	   * @property {boolean} [requireApp] If true and we're not in the app will present a dialog saying you must use the app
	   * @memberOf module:CommonUI
	   */

	  /**
	   * Sets up the standard UI for a happyFunTimes controller
	   * (phone). Including handling being disconnected from the
	   * current game and switching to new games as well as name
	   * input and the gear menu.
	   *
	   * @param {GameClient} client The `GameClient` for the phone
	   * @param {module:CommonUI.ControllerUI~Options} [options] the options
	   * @memberOf module:CommonUI
	   */
	  var setupStandardControllerUI = function(client, options) {
	    options = options || {};
	    var settingsElement = $("hft-settings");
	    var disconnectedElement = $("hft-disconnected");
	    var touchStartElement = $("hft-touchstart");

	    var menuElement = $("hft-menu");
	    menuElement.addEventListener('click', function() {
	      settingsElement.style.display = "block";
	    }, false);
	    menuElement.addEventListener('touchstart', function() {
	      settingsElement.style.display = "block";
	    }, false);

	    showMenu(false);

	    function makeHFTPingRequest(fn) {
	      IO.sendJSON(window.location.href, {cmd: 'happyFunTimesPing'}, function(err, obj) {
	        fn(err, obj);
	      }, { timeout: 2000 });
	    };

	    // setup full screen support
	    var requestFullScreen = function() {
	      if (!fullScreen.isFullScreen()) {
	        touchStartElement.removeEventListener('touchstart', requestFullScreen, false);
	        touchStartElement.style.display = "none";
	        fullScreen.requestFullScreen(document.body);
	      }
	    };

	    var goFullScreenIfNotFullScreen = function() {
	      if (fullScreen.isFullScreen()) {
	        resetOrientation();
	      } else {
	        if (fullScreen.canGoFullScreen()) {
	          touchStartElement.addEventListener('touchstart', requestFullScreen, false);
	          touchStartElement.style.display = "block";
	        }
	      }
	    };
	    fullScreen.onFullScreenChange(document.body, goFullScreenIfNotFullScreen);

	    if (mobilehacks.isMobile()) {
	       goFullScreenIfNotFullScreen();
	    }

	    s.playerNameHandler = new PlayerNameHandler(client, $("hft-name"));

	    $("hft-setname").addEventListener('click', function() {
	      settingsElement.style.display = "none";
	      s.playerNameHandler.startNameEntry();
	    }, false);
	    $("hft-restart").addEventListener('click', function() {
	      window.location.reload();
	    }, false);
	    $("hft-back").addEventListener('click', function() {
	      settingsElement.style.display = "none";
	    });

	//    $("hft-mainmenu").addEventListener('click', function() {
	//      window.location.href = "/";
	//    }, false);
	//    $("hft-reload").addEventListener('click', function() {
	//      window.location.reload();
	//    });

	    // This is not currently needed. The idea
	    // was to show something else like "connecting"
	    // until the user connected but that's mostly
	    // pointless.
	    client.addEventListener('connect', function() {
	      disconnectedElement.style.display = "none";
	      if (options.connectFn) {
	        options.connectFn();
	      }
	    });

	    client.addEventListener('disconnect', function() {
	      disconnectedElement.style.display = "block";
	      if (options.disconnectFn) {
	        options.disconnectFn();
	      }

	      function waitForPing() {
	        makeHFTPingRequest(function(err, obj) {
	          if (err) {
	            setTimeout(waitForPing, 1000);
	            return;
	          }
	          window.location.href = "/";
	        });
	      }
	      // give it a moment to recover
	      setTimeout(waitForPing, 2000);

	      // Maybe needed for app?
	      // var redirCookie = new Cookie("redir");
	      // var url = redirCookie.get() || "http://happyfuntimes.net";
	      // console.log("goto: " + url);
	      // window.location.href = url;
	    });

	    client.addEventListener('_hft_redirect_', function(data) {
	      window.location.href = data.url;
	    });

	    setOrientation(options.orientation, options.orientationOptional);

	    if (options.requireApp) {
	      showRequireApp();
	    }

	    if (options.debug) {
	      g.statusNode = document.createTextNode("");
	      $("hft-status").appendChild(g.statusNode);
	      var debugCSS = misc.findCSSStyleRule("#hft-debug");
	      debugCSS.style.display = "block";

	      g.logger = new logger.HTMLLogger($("hft-console"), options.numConsoleLines);
	    }

	    if (options.consoleTarget) {
	      switch (options.consoleTarget.toLowerCase()) {
	        case "html":
	          g.logger = g.logger || new logger.HTMLLogger($("hft-console"), options.numConsoleLines);
	          console.log = g.logger.log.bind(g.logger);
	          console.error = g.logger.error.bind(g.logger);
	          window.addEventListener('error', function(errorMsg, url, lineNo) {
	             console.error(url, lineNo, errorMsg);
	          });
	          break;
	        case "game":
	          g.logger = new logger.GameLogger(client);
	          console.log = g.logger.log.bind(g.logger);
	          console.error = g.logger.error.bind(g.logger);
	          window.addEventListener('error', function(errorMsg, url, lineNo) {
	             console.error(url, lineNo, errorMsg);
	          });
	          break;
	      }
	      console.log("here");
	    }
	  };

	  var askForNameOnce = function() {
	    askForNameOnce = function() {};
	    if (!s.playerNameHandler.isNameSet()) {
	      s.playerNameHandler.startNameEntry();
	    }
	  };

	  /**
	   * Sets the content of the status element. Only visible of debug
	   * is true.
	   * @memberOf module:CommonUI
	   * @param {string} str value to set the status
	   */
	  var setStatus = function(msg) {
	    if (g.statusNode) {
	      g.statusNode.nodeValue = msg;
	    }
	  };

	  /**
	   * Logs a msg to the HTML based console that is only visible
	   * when debug = true.
	   * @memberOf module:CommonUI
	   * @param {string} str msg to add to log
	   */
	  var log = function(str) {
	    g.logger.log(str);
	  };

	  /**
	   * Logs an error to the HTML based console that is only visible
	   * when debug = true.
	   * @memberOf module:CommonUI
	   * @param {string} str msg to add to log
	   */
	  var error = function(str) {
	    g.logger.error(str);
	  };

	  return {
	    askForNameOnce: askForNameOnce,
	    log: log,
	    error: error,
	    setOrientation: setOrientation,
	    setStatus: setStatus,
	    setupStandardControllerUI: setupStandardControllerUI,
	    showMenu: showMenu,
	  };
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));





/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */

	"use strict";

	!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
	  var NullLogger = function() {
	  };

	  NullLogger.prototype.log = function() {
	  };

	  NullLogger.prototype.error = function() {
	  };

	  var ConsoleLogger = function() {
	  };

	  ConsoleLogger.prototype.log = function() {
	    console.log.apply(console, arguments);
	  };

	  ConsoleLogger.prototype.error = function() {
	    console.error.apply(console, arguments);
	  };

	  var HTMLLogger = function(element, opt_maxLines) {
	    this.container = element;
	    this.maxLines = opt_maxLines || 10;
	    this.lines = [];
	  };

	  HTMLLogger.prototype.addLine_ = function(msg, color) {
	    var line;
	    var text;
	    if (this.lines.length < this.maxLines) {
	      line = document.createElement("div");
	      text = document.createTextNode("");
	      line.appendChild(text);
	    } else {
	      line = this.lines.shift();
	      line.parentNode.removeChild(line);
	      text = line.firstChild;
	    }

	    this.lines.push(line);
	    text.nodeValue = msg;
	    line.style.color = color;
	    this.container.appendChild(line);
	  };

	  // FIX! or move to strings.js
	  var argsToString = function(args) {
	    var lastArgWasNumber = false;
	    var numArgs = args.length;
	    var strs = [];
	    for (var ii = 0; ii < numArgs; ++ii) {
	      var arg = args[ii];
	      if (arg === undefined) {
	        strs.push('undefined');
	      } else if (typeof arg === 'number') {
	        if (lastArgWasNumber) {
	          strs.push(", ");
	        }
	        if (arg === Math.floor(arg)) {
	          strs.push(arg.toFixed(0));
	        } else {
	        strs.push(arg.toFixed(3));
	        }
	        lastArgWasNumber = true;
	      } else if (window.Float32Array && arg instanceof Float32Array) {
	        // TODO(gman): Make this handle other types of arrays.
	        strs.push(tdl.string.argsToString(arg));
	      } else {
	        strs.push(arg.toString());
	        lastArgWasNumber = false;
	      }
	    }
	    return strs.join("");
	  };

	  HTMLLogger.prototype.log = function() {
	    this.addLine_(argsToString(arguments), undefined);
	  };

	  HTMLLogger.prototype.error = function() {
	    this.addLine_(argsToString(arguments), "red");
	  };

	  var GameLogger = function(client) {
	    this.log = client.logImpl.bind(client);
	    this.error = client.errorImpl.bind(client);
	  };

	  return {
	    ConsoleLogger: ConsoleLogger,
	    GameLogger: GameLogger,
	    HTMLLogger: HTMLLogger,
	    NullLogger: NullLogger,
	  };
	}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));




/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */

	"use strict";

	/**
	 * Misc IO functions
	 * @module IO
	 */
	!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
	  var log = function() { };
	  //var log = console.log.bind(console);

	  /**
	   * @typedef {Object} SendJson~Options
	   * @memberOf module:IO
	   * @property {number?} timeout. Timeout in ms to abort.
	   *        Default = no-timeout
	   */

	  /**
	   * sends a JSON 'POST' request, returns JSON repsonse
	   * @memberOf module:IO
	   * @param {string} url url to POST to.
	   * @param {Object=} jsonObject JavaScript object on which to
	   *        call JSON.stringify.
	   * @param {!function(error, object)} callback Function to call
	   *        on success or failure. If successful error will be
	   *        null, object will be json result from request.
	   * @param {module:IO~SendJson~Options?} options
	   */
	  var sendJSON = function(url, jsonObject, callback, option) {
	    option = option || { };
	//    var error = 'sendJSON failed to load url "' + url + '"';
	    var request = new XMLHttpRequest();
	    if (request.overrideMimeType) {
	      request.overrideMimeType('text/plain');
	    }
	    var timeout = option.timeout || 0;
	    if (timeout) {
	      request.timeout = timeout;
	      log("set timeout to: " + request.timeout);
	    }
	    request.open('POST', url, true);
	    var js = JSON.stringify(jsonObject);
	    var callCallback = function(error, json) {
	      if (callback) {
	        log("calling-callback:" + (error ? " has error" : "success"));
	        callback(error, json);
	        callback = undefined;  // only call it once.
	      }
	    };
	//    var handleAbort = function(e) {
	//      log("--abort--");
	//      callCallback("error (abort) sending json to " + url);
	//    }
	    var handleError = function(/*e*/) {
	      log("--error--");
	      callCallback("error sending json to " + url);
	    };
	    var handleTimeout = function(/*e*/) {
	      log("--timeout--");
	      callCallback("timeout sending json to " + url);
	    };
	    var handleForcedTimeout = function(/*e*/) {
	      if (callback) {
	        log("--forced timeout--");
	        request.abort();
	        callCallback("forced timeout sending json to " + url);
	      }
	    };
	    var handleFinish = function() {
	      log("--finish--");
	      var json = undefined;
	      // HTTP reports success with a 200 status. The file protocol reports
	      // success with zero. HTTP does not use zero as a status code (they
	      // start at 100).
	      // https://developer.mozilla.org/En/Using_XMLHttpRequest
	      var success = request.status === 200 || request.status === 0;
	      if (success) {
	        try {
	          json = JSON.parse(request.responseText);
	        } catch (e) {
	          success = false;
	        }
	      }
	      callCallback(success ? null : 'could not load: ' + url, json);
	    };
	    try {
	      // Safari 7 seems to ignore the timeout.
	      if (timeout) {
	        setTimeout(handleForcedTimeout, timeout + 50);
	      }
	      request.addEventListener('load', handleFinish, false);
	      request.addEventListener('timeout', handleTimeout, false);
	      request.addEventListener('error', handleError, false);
	      request.setRequestHeader("Content-type", "application/json");
	      request.send(js);
	      log("--sent: " + url);
	    } catch (e) {
	      log("--exception--");
	      setTimeout(function() {
	        callCallback('could not load: ' + url, null);
	      }, 0);
	    }
	  };

	  return {
	    sendJSON: sendJSON,
	  };
	}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));



/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */

	/*eslint strict:0*/

	!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {

	  /**
	   * Represents a cookie.
	   *
	   * This is an object, that way you set the name just once so
	   * calling set or get you don't have to worry about getting the
	   * name wrong.
	   *
	   * @example
	   *     var fooCookie = new Cookie("foo");
	   *     var value = fooCookie.get();
	   *     fooCookie.set(newValue);
	   *     fooCookie.erase();
	   *
	   * @constructor
	   * @alias Cookie
	   * @param {string} name of cookie
	   * @param {string?} opt_path path for cookie. Default "/"
	   */
	  var Cookie = function(name, opt_path) {
	    var path = opt_path || "/";

	    /**
	     * Sets the cookie
	     * @param {string} value value for cookie
	     * @param {number?} opt_days number of days until cookie
	     *        expires. Default = none
	     */
	    this.set = function(value, opt_days) {
	      if (value === undefined) {
	        this.erase();
	        return;
	      }
	      // Cordova/Phonegap doesn't support cookies so use localStorage?
	      if (window.hftSettings && window.hftSettings.inApp) {
	        window.localStorage.setItem(name, value);
	        return;
	      }
	      var expires = "";
	      opt_days = opt_days || 9999;
	      var date = new Date();
	      date.setTime(Date.now() + Math.floor(opt_days * 24 * 60 * 60 * 1000));  // > 32bits. Don't use | 0
	      expires = "; expires=" + date.toGMTString();
	      var cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=" + path;
	      document.cookie = cookie;
	    };

	    /**
	     * Gets the value of the cookie
	     * @return {string?} value of cookie
	     */
	    this.get = function() {
	      // Cordova/Phonegap doesn't support cookies so use localStorage?
	      if (window.hftSettings && window.hftSettings.inApp) {
	        return window.localStorage.getItem(name);
	      }

	      var nameEQ = encodeURIComponent(name) + "=";
	      var ca = document.cookie.split(';');
	      for (var i = 0; i < ca.length; ++i) {
	        var c = ca[i];
	        while (c.charAt(0) === ' ') {
	          c = c.substring(1, c.length);
	        }
	        if (c.indexOf(nameEQ) === 0) {
	          return decodeURIComponent(c.substring(nameEQ.length, c.length));
	        }
	      }
	    };

	    /**
	     * Erases the cookie.
	     */
	    this.erase = function() {
	      if (window.hftSettings && window.hftSettings.inApp) {
	        return window.localStorage.removeItem(name);
	      }
	      document.cookie = this.set(" ", -1);
	    };
	  };

	  return Cookie;
	}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));




/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */

	"use strict";

	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function() {
	  var zIndex = 15000;

	  function create(name, options) {
	    var elem = document.createElement(name);
	    if (options.className) {
	      elem.className = options.className;
	    }
	    var style = options.style;
	    if (style) {
	      Object.keys(style).forEach(function(key) {
	        elem.style[key] = style[key];
	      });
	    }
	    if (options.parent) {
	      options.parent.appendChild(elem);
	    }
	    return elem;
	  }

	  function addElem(content, options) {
	    var elem = create("div", options);
	    if (content instanceof HTMLElement) {
	      elem.appendChild(content);
	    } else {
	      elem.innerHTML = content;
	    }
	    return elem;
	  }

	  function close(elem) {
	    elem.parentNode.removeChild(elem);
	    --zIndex;
	  }

	  /**
	   * @typedef {Object} Dialog~Choice
	   * @property {string} msg message to display
	   * @property {function} [callback] callback if this choice is picked.
	   */

	  /**
	   * @typedef {Object} Dialog~Options
	   * @property {string} [title] unused?
	   * @property {(string|HTMLElement)} [msg]
	   * @property {Dialog~Choice[]} [choices]
	   */

	  /**
	   * Puts up a fullscreen dialog
	   * @param {Dialog~Options} options options for dialog.
	   * @param {function(?)) [callback] callback when dialog closes
	   */
	  function modal(options, callback) {
	    if (!callback) {
	      callback = function() {};
	    }

	    var cover     = create("div", { className: "hft-dialog-cover", style: { zIndex: zIndex++ } });
	    var filler    = create("div", { className: "hft-fullcenter", parent: cover });
	    var container = create("div", { className: "hft-dialog-container", parent: filler });

	    var closeIt = function() {
	      close(cover);
	      callback();
	    };

	    if (options.title) {
	      addElem(options.title, { className: "hft-dialog-title", parent: container });
	    }

	    addElem(options.msg, { className: "hft-dialog-content", parent: container });

	    function addObjectChoice(choice, ndx) {
	      var div = addElem("div", { className: "hft-dialog-choice", parent: container });
	      div.innerHTML = choice.msg;
	      var choiceCallback = function() {
	        close(cover);
	        (choice.callback || callback)(ndx);
	      };
	      div.addEventListener('click', choiceCallback);
	      div.addEventListener('touchend', choiceCallback);
	      return div;
	    }

	    function addStringChoice(msg, ndx) {
	      addObjectChoice({
	        msg: msg,
	        callback: function() {
	          callback(ndx);
	        },
	      });
	    }

	    if (options.choices) {
	      options.choices.forEach(function(choice, ndx) {
	       if (typeof choice === 'string') {
	         addStringChoice(choice, ndx);
	       } else {
	         addObjectChoice(choice, ndx);
	       }
	      });
	    } else if (callback) {
	      container.addEventListener('click', closeIt, false);
	      container.addEventListener('touchend', closeIt, false);
	    }

	    document.body.appendChild(cover);
	  }

	  return {
	    modal: modal,
	  };
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));



/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
	    __webpack_require__(7),
	  ], __WEBPACK_AMD_DEFINE_RESULT__ = function(
	    hftSettings) {

	  var requestFullScreen = function(element) {
	    if (element.requestFullscreen) {
	      element.requestFullscreen();
	    } else if (element.msRequestFullscreen) {
	      element.msRequestFullscreen();
	    } else if (element.webkitRequestFullScreen) {
	      element.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
	    } else if (element.webkitRequestFullscreen) {
	      element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
	    } else if (element.mozRequestFullScreen) {
	      element.mozRequestFullScreen();
	    } else if (element.mozRequestFullscreen) {
	      element.mozRequestFullscreen();
	    }
	  };

	  var noop = function() {
	  };

	  var cancelFullScreen = (
	      document.exitFullscreen ||
	      document.exitFullScreen ||
	      document.msExitFullscreen ||
	      document.msExitFullScreen ||
	      document.webkitCancelFullscreen ||
	      document.webkitCancelFullScreen ||
	      document.mozCancelFullScreen ||
	      document.mozCancelFullscreen ||
	      noop).bind(document);

	  function isFullScreen() {
	    var f = document.fullscreenElement ||
	            document.fullScreenElement ||
	            document.webkitFullscreenElement ||
	            document.mozFullScreenElement ||
	            document.webkitIsFullScreen;
	    return (f !== undefined && f !== null && f !== false) || hftSettings.isApp;
	  }

	  var onFullScreenChange = function(element, callback) {
	    document.addEventListener('fullscreenchange', function(/*event*/) {
	        callback(isFullScreen());
	      });
	    element.addEventListener('webkitfullscreenchange', function(/*event*/) {
	        callback(isFullScreen());
	      });
	    document.addEventListener('mozfullscreenchange', function(/*event*/) {
	        callback(isFullScreen());
	      });
	  };

	  function canGoFullScreen() {
	    var body = window.document.body || {};
	    var r = body.requestFullscreen ||
	            body.requestFullScreen ||
	            body.msRequestFullscreen ||
	            body.msRequestFullScreen ||
	            body.webkitRequestFullScreen ||
	            body.webkitRequestFullscreen ||
	            body.mozRequestFullScreen ||
	            body.mozRequestFullscreen;
	    return r !== undefined && r !== null;
	  }

	  return {
	    cancelFullScreen: cancelFullScreen,
	    isFullScreen: isFullScreen,
	    canGoFullScreen: canGoFullScreen,
	    onFullScreenChange: onFullScreenChange,
	    requestFullScreen: requestFullScreen,
	  };
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ },
/* 7 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";
	(function(window) {

	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function() {

	  if (window === undefined) {
	    window = {};
	  }
	  window.hftSettings = window.hftSettings || {};

	  return window.hftSettings;
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
	}(this));



/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
	  console.log(
	    [ "%c",
	      "---------------------------------------------------------------------------------------------",
	      "If you'd like to know more about this sytem check out http://docs.happyfuntimes.net/docs",
	      "---------------------------------------------------------------------------------------------",
	      "",
	    ].join("\n"), "color:blue; font-weight: bold;");

	  return {
	  };
	}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));






/***/ },
/* 9 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	/**
	 * @module Misc
	 */
	!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
	  /**
	   * Copies properties from obj to dst recursively.
	   * @param {Object} obj Object with new settings.
	   * @param {Object} dst Object to receive new settings.
	   * @param {number?} opt_overwriteBehavior
	   *     *   0/falsy = overwrite
	   *
	   *         src    = {foo:'bar'}
	   *         dst    = {foo:'abc'}
	   *         result = {foo:'bar'}
	   *
	   *     *   1 = don't overwrite but descend if deeper
	   *
	   *         src    = {foo:{bar:'moo','abc':def}}
	   *         dst    = {foo:{bar:'ghi'}}
	   *         result = {foo:{bar:'ghi','abc':def}}
	   *
	   *         'foo' exists but we still go deeper and apply 'abc'
	   *
	   *     *   2 = don't overwrite don't descend
	   *
	   *             src    = {foo:{bar:'moo','abc':def}}
	   *             dst    = {foo:{bar:'ghi'}}
	   *             result = {foo:{bar:'ghi'}}
	   *
	   *         'foo' exists so we don't go any deeper
	   *
	   */
	  var copyProperties = function(src, dst, opt_overwriteBehavior) {
	    Object.keys(src).forEach(function(key) {
	      if (opt_overwriteBehavior === 2 && dst[key] !== undefined) {
	        return;
	      }
	      var value = src[key];
	      if (value instanceof Array) {
	        var newDst = dst[key];
	        if (!newDst) {
	          newDst = [];
	          dst[name] = newDst;
	        }
	        copyProperties(value, newDst, opt_overwriteBehavior);
	      } else if (value instanceof Object &&
	                 !(value instanceof Function) &&
	                 !(value instanceof HTMLElement)) {
	        var newDst2 = dst[key];
	        if (!newDst2) {
	          newDst2 = {};
	          dst[key] = newDst2;
	        }
	        copyProperties(value, newDst2, opt_overwriteBehavior);
	      } else {
	        if (opt_overwriteBehavior === 1 && dst[key] !== undefined) {
	          return;
	        }
	        dst[key] = value;
	      }
	    });
	    return dst;
	  };

	  function searchStringToObject(str, opt_obj) {
	    if (str[0] === '?') {
	      str = str.substring(1);
	    }
	    var results = opt_obj || {};
	    str.split("&").forEach(function(part) {
	      var pair = part.split("=").map(decodeURIComponent);
	      results[pair[0]] = pair[1] !== undefined ? pair[1] : true;
	    });
	    return results;
	  }

	  function objectToSearchString(obj) {
	    return "?" + Object.keys(obj).filter(function(key) {
	      return obj[key] !== undefined;
	    }).map(function(key) {
	      return encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
	    }).join("&");
	  }

	  /**
	   * Reads the query values from a URL like string.
	   * @param {String} url URL like string eg. http://foo?key=value
	   * @param {Object} [opt_obj] Object to attach key values to
	   * @return {Object} Object with key values from URL
	   * @memberOf module:Misc
	   */
	  var parseUrlQueryString = function(str, opt_obj) {
	    var dst = opt_obj || {};
	    try {
	      var q = str.indexOf("?");
	      var e = str.indexOf("#");
	      if (e < 0) {
	        e = str.length;
	      }
	      var query = str.substring(q + 1, e);
	      searchStringToObject(query, dst);
	    } catch (e) {
	      console.error(e);
	    }
	    return dst;
	  };

	  /**
	   * Reads the query values from the current URL.
	   * @param {Object=} opt_obj Object to attach key values to
	   * @return {Object} Object with key values from URL
	   * @memberOf module:Misc
	   */
	  var parseUrlQuery = function(opt_obj) {
	    return searchStringToObject(window.location.search, opt_obj);
	  };

	  /**
	   * Read `settings` from URL. Assume settings it a
	   * JSON like URL as in http://foo?settings={key:value},
	   * Note that unlike real JSON we don't require quoting
	   * keys if they are alpha_numeric.
	   *
	   * @param {Object=} opt_obj object to apply settings to.
	   * @param {String=} opt_argumentName name of key for settings, default = 'settings'.
	   * @return {Object} object with settings
	   * @func applyUrlSettings
	   * @memberOf module:Misc
	   */
	  var fixKeysRE = new RegExp("([a-zA-Z0-9_]+)\:", "g");

	  var applyUrlSettings = function(opt_obj, opt_argumentName) {
	    var argumentName = opt_argumentName || 'settings';
	    var src = parseUrlQuery();
	    var dst = opt_obj || {};
	    var settingsStr = src[argumentName];
	    if (settingsStr) {
	      var json = settingsStr.replace(fixKeysRE, '"$1":');
	      var settings = JSON.parse(json);
	      copyProperties(settings, dst);
	    }
	    return dst;
	  };

	  /**
	   * Gets a function checking for prefixed versions
	   *
	   * example:
	   *
	   *     var lockOrientation = misc.getFunctionByPrefix(window.screen, "lockOrientation");
	   *
	   * @param {object} obj object that has function
	   * @param {string} funcName name of function
	   * @return {function?} or undefined if it doesn't exist
	   */
	  var prefixes = ["", "moz", "webkit", "ms"];
	  function getFunctionByPrefix(obj, funcName) {
	    var capitalName = funcName.substr(0, 1).toUpperCase() + funcName.substr(1);
	    for (var ii = 0; ii < prefixes.length; ++ii) {
	      var prefix = prefixes[ii];
	      var name = prefix + prefix ? capitalName : funcName;
	      var func = obj[name];
	      if (func) {
	        return func.bind(obj);
	      }
	    }
	  }

	  /**
	   * Creates an invisible iframe and sets the src
	   * @param {string} src the source for the iframe
	   * @return {HTMLIFrameElement} The iframe
	   */
	  function gotoIFrame(src) {
	    var iframe = document.createElement("iframe");
	    iframe.style.display = "none";
	    iframe.src = src;
	    document.body.appendChild(iframe);
	    return iframe;
	  }

	  /**
	   * get a random int
	   * @param {number} value max value exclusive. 5 = random 0 to 4
	   * @return {number} random int
	   * @memberOf module:Misc
	   */
	  var randInt = function(value) {
	    return Math.floor(Math.random() * value);
	  };

	  /**
	   * get a random CSS color
	   * @param {function(number): number?) opt_randFunc function to generate random numbers
	   * @return {string} random css color
	   * @memberOf module:Misc
	   */
	  var randCSSColor = function(opt_randFunc) {
	    var randFunc = opt_randFunc || randInt;
	    var strong = randFunc(3);
	    var colors = [];
	    for (var ii = 0; ii < 3; ++ii) {
	      colors.push(randFunc(128) + (ii === strong ? 128 : 64));
	    }
	    return "rgb(" + colors.join(",") + ")";
	  };

	  /**
	   * Gets a random element from array
	   * @param {Array<*>} array array to select element from
	   * @return {*} picked element
	   */
	  function pickRandomElement(array) {
	    return array[randInt(array.length)];
	  }

	  /**
	   * get a random 32bit color
	   * @param {function(number): number?) opt_randFunc function to generate random numbers
	   * @return {string} random 32bit color
	   * @memberOf module:Misc
	   */
	  var rand32BitColor = function(opt_randFunc) {
	    var randFunc = opt_randFunc || randInt;
	    var strong = randFunc(3);
	    var color = 0xFF;
	    for (var ii = 0; ii < 3; ++ii) {
	      color = (color << 8) | (randFunc(128) + (ii === strong ? 128 : 64));
	    }
	    return color;
	  };

	  /**
	   * finds a CSS rule.
	   * @param {string} selector
	   * @return {Rule?} matching css rule
	   * @memberOf module:Misc
	   */
	  var findCSSStyleRule = function(selector) {
	    for (var ii = 0; ii < document.styleSheets.length; ++ii) {
	      var styleSheet = document.styleSheets[ii];
	      var rules = styleSheet.cssRules || styleSheet.rules;
	      if (rules) {
	        for (var rr = 0; rr < rules.length; ++rr) {
	          var rule = rules[rr];
	          if (rule.selectorText === selector) {
	            return rule;
	          }
	        }
	      }
	    }
	  };

	  /**
	   * Inserts a text node into an element
	   * @param {HTMLElement} element element to have text node insert
	   * @return {HTMLTextNode} the created text node
	   * @memberOf module:Misc
	   */
	  var createTextNode = function(element) {
	    var txt = document.createTextNode("");
	    element.appendChild(txt);
	    return txt;
	  };

	  /**
	   * Returns the absolute position of an element for certain browsers.
	   * @param {HTMLElement} element The element to get a position
	   *        for.
	   * @returns {Object} An object containing x and y as the
	   *        absolute position of the given element.
	   * @memberOf module:Misc
	   */
	  var getAbsolutePosition = function(element) {
	    var r = { x: element.offsetLeft, y: element.offsetTop };
	    if (element.offsetParent) {
	      var tmp = getAbsolutePosition(element.offsetParent);
	      r.x += tmp.x;
	      r.y += tmp.y;
	    }
	    return r;
	  };

	  /**
	   * Clamp value
	   * @param {Number} v value to clamp
	   * @param {Number} min min value to clamp to
	   * @param {Number} max max value to clamp to
	   * @returns {Number} v clamped to min and max.
	   * @memberOf module:Misc
	   */
	  var clamp = function(v, min, max) {
	    return Math.max(min, Math.min(max, v));
	  };

	  /**
	   * Clamp in both positive and negative directions.
	   * Same as clamp(v, -max, +max)
	   *
	   * @param {Number} v value to clamp
	   * @param {Number} max max value to clamp to
	   * @returns {Number} v clamped to -max and max.
	   * @memberOf module:Misc
	   */
	  var clampPlusMinus = function(v, max) {
	    return clamp(v, -max, max);
	  };

	  /**
	   * Return sign of value
	   *
	   * @param {Number} v value
	   * @returns {Number} -1 if v < 0, 1 if v > 0, 0 if v == 0
	   * @memberOf module:Misc
	   */
	  var sign = function(v) {
	    return v < 0 ? -1 : (v > 0 ? 1 : 0);
	  };

	  /**
	   * Takes which ever is closer to zero
	   * In other words minToZero(-2, -1) = -1 and minToZero(2, 1) = 1
	   *
	   * @param {Number} v value to min
	   * @param {Number} min min value to use if v is less then -min
	   *        or greater than +min
	   * @returns {Number} min or v, which ever is closer to zero
	   * @memberOf module:Misc
	   */
	  var minToZero = function(v, min) {
	    return Math.abs(v) < Math.abs(min) ? v : min;
	  };

	  /**
	   * flips 0->max to max<-0 and 0->min to min->0
	   * In otherwords
	   *     max: 3, v: 2.7  =  0.3
	   *     max: 3, v:-2.7  = -0.3
	   *     max: 3, v: 0.2  =  2.8
	   *     max: 3, v:-0.2  = -2.8
	   *
	   * @param {Number} v value to flip.
	   * @param {Number} max range to flip inside.
	   * @returns {Number} flipped value.
	   * @memberOf module:Misc
	   */
	  var invertPlusMinusRange = function(v, max) {
	    return sign(v) * (max - Math.min(max, Math.abs(v)));
	  };

	  /**
	   * Convert degrees to radians
	   *
	   * @param {Number} d value in degrees
	   * @returns {Number} d in radians
	   * @memberOf module:Misc
	   */
	  var degToRad = function(d) {
	    return d * Math.PI / 180;
	  };

	  /**
	   * Converts radians to degrees
	   * @param {Number} r value in radians
	   * @returns {Number} r in degrees
	   * @memberOf module:Misc
	   */
	  var radToDeg = function(r) {
	    return r * 180 / Math.PI;
	  };

	  /**
	   * Resizes a cavnas to match its CSS displayed size.
	   * @param {Canvas} canvas canvas to resize.
	   * @param {boolean?} useDevicePixelRatio if true canvas will be
	   *        created to match devicePixelRatio.
	   * @memberOf module:Misc
	   */
	  var resize = function(canvas, useDevicePixelRatio) {
	    var mult = useDevicePixelRatio ? window.devicePixelRatio : 1;
	    mult = mult || 1;
	    var width  = Math.floor(canvas.clientWidth  * mult);
	    var height = Math.floor(canvas.clientHeight * mult);
	    if (canvas.width !== width ||
	        canvas.height !== height) {
	      canvas.width = width;
	      canvas.height = height;
	      return true;
	    }
	  };

	  /**
	   * Copies all the src properties to the dst
	   * @param {Object} src an object with some properties
	   * @param {Object} dst an object to receive copes of the properties
	   * @return returns the dst object.
	   */
	  function applyObject(src, dst) {
	    Object.keys(src).forEach(function(key) {
	      dst[key] = src[key];
	    });
	    return dst;
	  }

	  /**
	   * Merges the proprties of all objects into a new object
	   *
	   * Example:
	   *
	   *     var a = { abc: "def" };
	   *     var b = { xyz: "123" };
	   *     var c = Misc.mergeObjects(a, b);
	   *
	   *     // c = { abc: "def", xyz: "123" };
	   *
	   * Later object properties take precedence
	   *
	   *     var a = { abc: "def" };
	   *     var b = { abc: "123" };
	   *     var c = Misc.mergeObjects(a, b);
	   *
	   *     // c = { abc: "123" };
	   *
	   * @param {...Object} object objects to merge.
	   * @return an object containing the merged properties
	   */
	  function mergeObjects(object) {  // eslint-disable-line
	    var merged = {};
	    Array.prototype.slice.call(arguments).forEach(function(src) {
	      if (src) {
	        applyObject(src, merged);
	      }
	    });
	    return merged;
	  }

	  /**
	   * Creates a random id
	   * @param {number} [digits] number of digits. default 16
	   */
	  function makeRandomId(digits) {
	    digits = digits || 16;
	    var id = "";
	    for (var ii = 0; ii < digits; ++ii) {
	      id = id + ((Math.random() * 16 | 0)).toString(16);
	    }
	    return id;
	  }

	  /**
	   * Applies an object of listeners to an emitter.
	   *
	   * Example:
	   *
	   *     applyListeners(someDivElement, {
	   *       mousedown: someFunc1,
	   *       mousemove: someFunc2,
	   *       mouseup: someFunc3,
	   *     });
	   *
	   * Which is the same as
	   *
	   *     someDivElement.addEventListener("mousedown", someFunc1);
	   *     someDivElement.addEventListener("mousemove", someFunc2);
	   *     someDivElement.addEventListener("mouseup", someFunc3);
	   *
	   * @param {Emitter} emitter some object that emits events and has a function `addEventListener`
	   * @param {Object.<string, function>} listeners eventname function pairs.
	   */
	  function applyListeners(emitter, listeners) {
	    Object.keys(listeners).forEach(function(name) {
	      emitter.addEventListener(name, listeners[name]);
	    });
	  }

	  return {
	    applyObject: applyObject,
	    applyUrlSettings: applyUrlSettings,
	    applyListeners: applyListeners,
	    clamp: clamp,
	    clampPlusMinus: clampPlusMinus,
	    copyProperties: copyProperties,
	    createTextNode: createTextNode,
	    degToRad: degToRad,
	    findCSSStyleRule: findCSSStyleRule,
	    getAbsolutePosition: getAbsolutePosition,
	    getFunctionByPrefix: getFunctionByPrefix,
	    gotoIFrame: gotoIFrame,
	    invertPlusMinusRange: invertPlusMinusRange,
	    makeRandomId: makeRandomId,
	    mergeObjects: mergeObjects,
	    minToZero: minToZero,
	    objectToSearchString: objectToSearchString,
	    parseUrlQuery: parseUrlQuery,
	    parseUrlQueryString: parseUrlQueryString,
	    pickRandomElement: pickRandomElement,
	    radToDeg: radToDeg,
	    randInt: randInt,
	    randCSSColor: randCSSColor,
	    rand32BitColor: rand32BitColor,
	    resize: resize,
	    sign: sign,
	    searchStringToObject: searchStringToObject,
	  };
	}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));




/***/ },
/* 10 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	/**
	 * Various hacks to try to get mobile browsers to do what I want but that
	 * probably wouldn't be needed if I actually understood the platform.
	 *
	 * @module MobileHacks
	 */
	!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {

	  var $ = document.getElementById.bind(document);

	  // shit hacks for iOS8 because iOS8 barfs toolbars on the screen and
	  // (a) the user can NOT dismiss them and (b) there is no way for the
	  // webpage to see they exist. This only happens on iPhone 4/4s/5/s.
	  //var isIOS;
	  var shittyOldIPhoneWithShittyIOS8Plus = function() {
	    var iPhone4 = (window.screen.height === (960 / 2));
	    var iPhone5 = (window.screen.height === (1136 / 2));
	    var iOS8Plus = function() {
	      if (/iP(hone|od|ad)/.test(navigator.platform)) {
	        // supports iOS 2.0 and later: <http://bit.ly/TJjs1V>
	        var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
	        //isIOS = true;
	        return parseInt(v[1], 10) >= 8;
	      }
	    }();
	    return iOS8Plus && (iPhone4 || iPhone5);
	  }();

	  var isIOS8OrNewerAndiPhone4OrIPhone5 = function() {
	    return shittyOldIPhoneWithShittyIOS8Plus;
	  };

	  var isIOS = function() {
	    var itsIOS = (/iP(hone|od|ad)/i).test(navigator.platform);
	    return function() {
	      return itsIOS;
	    };
	  }();

	  var isMobile = function() {
	    // yes I know I should feature detect. FUCK YOU!
	    var mobile = (/Android|webOS|Phone|Pad|Pod|Tablet|BlackBerry/i).test(navigator.userAgent);
	    return function() {
	      return mobile;
	    };
	  }();

	  /**
	   * resets the height of any element with CSS class "fixeight"
	   * by setting its hight to the cliehgtHeight of its parent
	   *
	   * The problem this is trying to solve is sometimes you have
	   * an element set to 100% but when the phone rotates
	   * the browser does not reset the size of the element even
	   * though it's parent has been resized.
	   *
	   * This will be called automatically when the phone rotates
	   * or the window is resized but I found I often needed to
	   * call it manually at the start of a controller
	   *
	   * @memberOf module:MobileHacks
	   */
	  var fixHeightHack = function() {
	    // Also fix all fucked up sizing
	    var elements = document.querySelectorAll(".fixheight");
	    for (var ii = 0; ii < elements.length; ++ii) {
	      var element = elements[ii];
	      var parent = element.parentNode;
	      if (parseInt(element.style.height) !== parent.clientHeight) {
	        element.style.height = parent.clientHeight + "px";
	      }
	    }
	  };

	  var adjustCSSBasedOnPhone = function(perPhoneClasses) {
	    perPhoneClasses.forEach(function(phone) {
	      if (phone.test()) {
	        Array.prototype.forEach.call(document.styleSheets, function(sheet) {
	          var classes = sheet.rules || document.sheet.cssRules;
	          Array.prototype.forEach.call(classes, function(c) {
	            var adjustments = phone.styles[c.selectorText];
	            if (adjustments) {
	              Object.keys(adjustments).forEach(function(key) {
	//console.log(key + ": old " + c.style[key]);
	                if (c.style.setProperty) {
	                  c.style.setProperty(key, adjustments[key]);
	                } else {
	                  c.style[key] = adjustments[key];
	                }
	//console.log(key + ": new " + c.style[key]);
	              });
	            }
	          });
	        });
	      }
	    });
	  };

	  var fixupAfterSizeChange = function() {
	    window.scrollTo(0, 0);
	    fixHeightHack();
	    window.scrollTo(0, 0);
	  };

	  // When the device re-orients, at least on iOS, the page is scrolled down :(
	  window.addEventListener('orientationchange', fixupAfterSizeChange, false);
	  window.addEventListener('resize', fixupAfterSizeChange, false);

	  // Prevents the browser from sliding the page when the user slides their finger.
	  // At least on iOS.
	  var stopSliding = function() {
	    if (!document.body) {
	      setTimeout(stopSliding, 4);
	    } else {
	      document.body.addEventListener('touchmove', function(e) {
	        e.preventDefault();
	      }, false);
	    }
	  };
	  stopSliding();


	  // This DOESN'T WORK! I'm leaving it here so I can revisit it.
	  // The issue is all kinds of things mess up. Events are not rotated,
	  // the page does strange things.
	  var forceLandscape = function() {
	    // Note: This code is for games that require a certain orientation
	    // on phones only. I'm making the assuption that tablets don't need
	    // this.
	    //
	    // The issue I ran into is I tried to show several people games
	    // and they had their phone orientation locked to portrait. Having
	    // to go unlock just to play the game was frustrating. So, for
	    // controllers than require landscape just try to make the page
	    // show up in landscape. They'll understand they need to turn the phone.
	    //
	    // If the orientation is unlocked they'll turn and the page will
	    // switch to landscape. If the orientation is locked then turning
	    // the phone will not switch to landscape NOR will we get an orientation
	    // event.
	    var everything = $("hft-everything");
	    var detectPortrait = function() {
	      if (screen.width < screen.height) {
	        everything.className = "hft-portrait-to-landscape";
	        everything.style.width = window.innerHeight + "px";
	        everything.style.height = window.innerWidth + "px";

	        var viewport = document.querySelector("meta[name=viewport]");
	        viewport.setAttribute('content', 'width=device-height, initial-scale=1.0, maximum-scale=1, user-scalable=no, minimal-ui');
	      } else {
	        everything.className = "";
	      }
	    };

	    detectPortrait();
	    window.addEventListener('resize', detectPortrait, false);
	  };

	  function preventEvent(e) {
	    e.preventDefault();
	    return false;
	  }

	  /**
	   * Disable the context menus!
	   * At least on Android if you long press on an image it asks if you
	   * want to save it. I'd think "user-select: none" CSS should handle that
	   * but nope
	   */
	  function disableContextMenu() {
	    // for now just images.
	    Array.prototype.forEach.call(document.getElementsByTagName("img"), function(img) {
	      img.addEventListener('contextmenu', preventEvent, false);
	    });
	  }


	  window.scrollTo(0, 0);

	  return {
	    disableContextMenu: disableContextMenu,
	    fixHeightHack: fixHeightHack,
	    forceLandscape: forceLandscape,
	    adjustCSSBasedOnPhone: adjustCSSBasedOnPhone,
	    isIOS8OrNewerAndiPhone4OrIPhone5: isIOS8OrNewerAndiPhone4OrIPhone5,
	    isIOS: isIOS,
	    isMobile: isMobile,
	  };
	}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));



/***/ },
/* 11 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
	    __webpack_require__(7),
	    __webpack_require__(9),
	  ], __WEBPACK_AMD_DEFINE_RESULT__ = function(
	    hftSettings,
	    misc) {

	  var lockOrientation = misc.getFunctionByPrefix(window.screen, "lockOrientation");
	  var unlockOrientation = misc.getFunctionByPrefix(window.screen, "unlockOrientation");
	  var currentOrientation = "none";
	  var _canOrient = true;

	  if (!hftSettings.isApp && window.screen.orientation && window.screen.orientation.lock) {
	    lockOrientation = function(orientation) {
	      window.screen.orientation.lock(orientation).then(function() {
	        console.log("orientation set");
	      }, function(err) {
	        console.error("can not set orientation:", err);
	      });
	    };
	    unlockOrientation = function() {
	      window.screen.orientation.unlock().then(function() {
	        console.log("orientation unlocked");
	      }, function(err) {
	        console.error("can not unlock orientation:", err);
	      });
	    };
	  }

	  if (!lockOrientation) {
	    _canOrient = false;
	    lockOrientation = function() {
	      console.warn("orientation locking not supported");
	    };
	    unlockOrientation = function() {
	    };
	  }

	  /**
	   * Sets the orientation of the screen
	   * @param {string} [orienation] The orientation to set the phone.
	   *   Only works on Android or the App.
	   *
	   *   Valid values are:
	   *
	   *     "portrait-primary"    // normal way people hold phones
	   *     "portrait-secondary"  // upsidedown
	   *     "landscape-primary"   // phone turned clockwise 90 degrees from normal
	   *     "landscape-secondary" // phone turned counter-clockwise 90 degrees from normal
	   *     "none" (or undefined) // unlocked
	   */
	  function set(orientation) {
	    orientation = orientation || "none";
	    if (orientation !== currentOrientation) {
	      currentOrientation = orientation;
	      if (currentOrientation === "none") {
	        console.log("unlock orienation");
	        unlockOrientation();
	      } else {
	        console.log("set orienation: " + orientation);
	        lockOrientation(orientation);
	      }
	    }
	  }

	  /**
	   * Returns true if orientation is supported.
	   * @return {boolean} true if orientation is supported
	   */
	  function canOrient() {
	    return _canOrient;
	  }

	  return {
	    set: set,
	    canOrient: canOrient,
	  };
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ },
/* 12 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	// Functions for dealing with the player's name. The name events are arguably
	// not part of the HappyFunTimes library but they are used in most of the samples
	// so I put them here.
	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
	    __webpack_require__(4),
	    __webpack_require__(9),
	  ], __WEBPACK_AMD_DEFINE_RESULT__ = function(
	    Cookie,
	    misc) {

	  var $ = function(id) {
	    return document.getElementById(id);
	  };

	  var q = misc.parseUrlQuery();

	  var startingNames = [
	    "Aaron",    "Momo",
	    "Michael",  "Remi",
	    "Matt",     "Aya",
	    "John",     "Stephanie",
	    "Brian",    "Anna",
	    "Danny",    "Jen",
	    "Gregg",    "Tami",
	    "Vincent",  "Carole",
	    "Eric",     "Anita",
	    "Rick",     "Joss",
	    "Colin",    "Angel",
	  ];

	  var PlayerNameHandler = function(client, element) {
	    var nameCookie = new Cookie("name");
	    var name = nameCookie.get() || q.name || "";

	    // UGH! I guess this name stuff should move to CommonUI. At one point
	    // it seemed separte
	    var nameentry = $("hft-nameentry");
	    var content = $("hft-content");
	    var contentOriginalDisplay = content.style.display;

	    var copyNameToElement = function() {
	      element.value = name;
	    };

	    this.getName = function() {
	      return name;
	    };

	    var nameIsPlayerRE = /^player\d*$/i;
	    function isNameSet() {
	      return name && !nameIsPlayerRE.test(name);
	    };

	    this.isNameSet = isNameSet;

	    var handleSetNameMsg = function(msg) {
	      name = msg.name;
	      copyNameToElement();
	    };

	    var sendName = function() {
	      client.sendCmd('_hft_setname_', {
	          name: name,
	      });
	    };

	    var sendBusy = function(busy) {
	      client.sendCmd('_hft_busy_', {
	          busy: busy,
	      });
	    };

	    var startEnteringName = function() {
	      // Allow the game to help the player by, for example, removing her character
	      // while she's entering her name. Unfortunately that could be used to cheat
	      // as in just before she's about to be hit she clicks the name. It's up the individual
	      // game to decide if it want's to pay attention to the 'busy' event.
	      sendBusy(true);
	    };

	    var finishEnteringName = function(e) {
	      // Unfortunately hiding the controls screwed up iOS Safari. After editing
	      // the name the page would be scrolled down a certain number of pixels
	      // like a 2/3rd of the page worth. No idea why. So again I needed
	      // to do some hacky fix like scroll back to the top.
	      content.style.display = contentOriginalDisplay;
	      nameentry.style.display = "none";
	      window.scroll(0, 1);
	      window.scroll(0, 0);
	      e.preventDefault();
	      element.blur();
	      var newName = element.value.replace(/[<>]/g, '');
	      if (newName.length > 16) {
	        newName = newName.substring(0, 16);
	      }
	      if (newName.length === 0) {
	        element.value = name;
	      } else if (newName !== name) {
	        name = newName;
	        nameCookie.set(name, 700);
	        sendName();
	      }
	      sendBusy(false);
	    };

	    this.startNameEntry = function() {
	      // Chrome on Android seems to really mess up here. Or rather my CSS-fu sucks
	      // so where as on iOS when you edit your name it just works, on Chrome
	      // the controls fly up over the input=text area. This was the hacky
	      // solution. Just hide the controls while entering the name. (see below)
	      nameentry.style.display = "block";
	      content.style.display = "none";
	      if (!isNameSet()) {
	        element.value = misc.pickRandomElement(startingNames);
	      }
	      element.focus();
	    };

	    // If the user's name is "" the game may send a name.
	    client.addEventListener('_hft_setname_', handleSetNameMsg);

	    element.addEventListener('click', startEnteringName, false);
	    element.addEventListener('change', finishEnteringName, false);
	    element.addEventListener('blur', finishEnteringName, false);
	    element.addEventListener('focus', startEnteringName, false);

	    if (element.form) {
	      element.form.addEventListener('submit', finishEnteringName, false);
	    }

	    copyNameToElement();
	    sendName();
	  };

	  return PlayerNameHandler;
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));



/***/ },
/* 13 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
	  __webpack_require__(14),
	], __WEBPACK_AMD_DEFINE_RESULT__ = function(
	  EventEmitter
	) {

	  class PlayerNameManager extends EventEmitter {
	    constructor(netPlayer) {
	      super();
	      this._netPlayer = netPlayer;
	      this._name = "";
	      this._busy = false;

	      this._handleSetName = this._handleSetName.bind(this);
	      this._handleBusy = this._handleBusy.bind(this);

	      netPlayer.on('_hft_setname_', this._handleSetName);
	      netPlayer.on('_hft_busy_', this._handleBusy);
	    }

	    _handleSetName(data) {
	      if (data.name && data.name !== this._name) {
	        this._name = data.name;
	        this.emit('setName', this._name);
	      }
	    }

	    _handleBusy(data) {
	      if (data.busy !== this._busy) {
	        this._busy = data.busy;
	        this.emit('busy', this._busy);
	      }
	    }

	    get name() {
	      return this._name;
	    }

	    get busy() {
	      return this._busy;
	    }
	  }

	  return PlayerNameManager;

	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ },
/* 14 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */

	"use strict";

	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function() {

	  /**
	   * This is similar to node's EventEmitter.
	   * The major difference is calling `addEventListner`
	   * or `removeEventListener` with `null` or `undefined`
	   * will remove all listeners for that event.
	   */
	  var EventEmitter = function() {
	    var events = {};
	    var maxListeners = 10;

	    var setMaxListeners = function(max) {
	      maxListeners = max;
	    };

	    var addEventListener = function(name, handler) {
	      if (!handler) {
	        delete events[name];
	        return;
	      }

	      var handlers = events[name];
	      if (!handlers) {
	        handlers = [];
	        events[name] = handlers;
	      }
	      handlers.push(handler);
	      if (handlers.length > maxListeners) {
	        console.warn("More than " + maxListeners + " added to event " + name);
	      }
	    };

	    var removeEventListener = function(name, handler) {
	      if (!handler) {
	        delete events[name];
	        return;
	      }

	      var handlers = events[name];
	      if (handlers) {
	        var ndx = handlers.indexOf(handler);
	        if (ndx >= 0) {
	          handlers.splice(ndx, 1);
	        }
	      }
	    };

	    var removeAllEventListeners = function() {
	      events = {};
	    };

	    var emit = function(name) {
	      var handlers = events[name];
	      if (handlers) {
	        var args = Array.prototype.slice.call(arguments, 1);
	        handlers.forEach(function(handler) {
	          handler.apply(this, args);
	        });
	      }
	    };

	    var listeners = function(name) {
	      return events[name];
	    };

	    this.on = addEventListener;
	    this.addEventListener = addEventListener;
	    this.removeEventListener = removeEventListener;
	    this.removeAllEventListeners = removeAllEventListeners;
	    this.addListener = addEventListener;
	    this.removeListener = removeEventListener;
	    this.removeAllListeners = removeAllEventListeners;
	    this.emit = emit;
	    this.listeners = listeners;
	    this.setMaxListeners = setMaxListeners;
	  };

	  return EventEmitter;
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));



/***/ },
/* 15 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ ], __WEBPACK_AMD_DEFINE_RESULT__ = function() {

	  var drawCircle = function(ctx, x, y, radius, flags) {
	    flags = flags || 1;
	    ctx.beginPath();
	    ctx.arc(x, y, radius, 0, Math.PI * 2);
	    if (flags & 1) {
	      ctx.fill();
	    }
	    if (flags & 2) {
	      ctx.stroke();
	    }
	  };

	  /**
	   * @typedef {Object} DPad~Options
	   * @property {HTMLElement} elemnt element to put dpad inside.
	   *           DPad will be resized to fit this element or to the
	   *           size specified
	   * @property {number} size size in CSS pixels to make DPad
	   */

	  /**
	   * Renders a Dpad
	   *
	   * @constructor
	   * @alias DPad
	   * @param {DPad~Options} options
	   */
	  var DPad = function(options) {
	    this.element = options.element;
	    this.size = options.size;
	    this.canvas = document.createElement("canvas");
	    // We have to put this in the DOM before asking it's size.
	    this.element.appendChild(this.canvas);
	    this.resize();
	    this.ctx = this.canvas.getContext("2d");
	    this.drawBits(0);
	  };

	  /**
	   * Gets the size of the dpad
	   * @return {number} size of dpad in CSS pixels
	   */
	  DPad.prototype.getSize = function() {
	    var size = this.size;
	    if (!size) {
	      size = Math.min(this.canvas.width, this.canvas.height);
	    }
	    return size;
	  };

	  /**
	   * Draws the dpad given a set of bits
	   * @param {number} bits where 0x1 is right, 0x2 is left, 0x4 is
	   *        up and 0x8 is down.
	   */
	  DPad.prototype.drawBits = function(bits) {
	    var size = this.getSize();
	    var w6 = Math.floor(size / 6.5);
	    var cx = Math.floor(size / 2);
	    var cy = Math.floor(size / 2);
	    var w = Math.floor(size / 2 * 0.8);
	    var h = Math.floor(size / 2 * 0.8);
	    var ctx = this.ctx;

	    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
	    ctx.save();
	    ctx.translate(cx, cy);
	    ctx.fillStyle = "#AAA";
	    ctx.stokeStyle = "#444";
	    drawCircle(ctx, 0, 0, cx * 0.95, 3);

	    ctx.fillStyle = "#888";
	    ctx.fillRect(-w6, -h, w6 * 2, h * 2);
	    ctx.fillRect(-w, -w6, w * 2, w6 * 2);

	    ctx.fillStyle = "#CCC";

	    if (bits & 0x1) {
	      //ctx.fillRect(  w - w6 * 2, -w6         , w6 * 2, w6 * 2 ); }
	      drawCircle(ctx,  w - w6, 0, w6 * 0.8);
	    }
	    if (bits & 0x2) {
	      //ctx.fillRect( -w         , -w6         , w6 * 2, w6 * 2 ); }
	      drawCircle(ctx, -w + w6, 0, w6 * 0.8);
	    }
	    if (bits & 0x4) {
	      //ctx.fillRect( -w6        , -h          , w6 * 2, w6 * 2); }
	      drawCircle(ctx, 0, -w + w6, w6 * 0.8);
	    }
	    if (bits & 0x8) {
	      //ctx.fillRect( -w6        ,  h - w6 * 2 , w6 * 2, w6 * 2); }
	      drawCircle(ctx, 0,  w - w6, w6 * 0.8);
	    }

	    ctx.restore();
	  };

	  /**
	   * Draws the dpad given a DirInfo
	   * @param {module:Input.DirectionInfo} dirInfo
	   */
	  DPad.prototype.draw = function(dirInfo) {
	    this.drawBits(dirInfo.bits);
	  };

	  /**
	   * Resizes the DPad to match its container or to the size
	   * specified at creation time
	   */
	  DPad.prototype.resize = function() {
	    var size = this.size;
	    if (!size) {
	      size = Math.min(this.canvas.clientWidth, this.canvas.clientHeight);
	    }
	    if (this.canvas.width !== size ||
	        this.canvas.height !== size) {
	      this.canvas.width = size;
	      this.canvas.height = size;
	    }
	  };

	  return DPad;
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));





/***/ },
/* 16 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	/**
	 * Various functions to help with user input
	 * @module Input
	 */
	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(9)], __WEBPACK_AMD_DEFINE_RESULT__ = function(Misc) {

	  /**
	   * The cursor key values. Can be used to register keys
	   * @enum {number}
	   * @memberOf module:Input
	   */
	  var cursorKeys = {
	    kLeft: 37,
	    kRight: 39,
	    kUp: 38,
	    kDown: 40,
	  };

	  /**
	   * You can use these to make your own options for setupKeyboardDPadKeys
	   * @const {number[]} kCursorKeys
	   * @memberOf module:Input
	   */
	  var kCursorKeys = [37, 39, 38, 40];
	  /**
	   * You can use these to make your own options for setupKeyboardDPadKeys
	   * @const {number[]} kASWDKeys
	   * @memberOf module:Input
	   */
	  var kASWDKeys = [65, 68, 87, 83];

	  /**
	   * You can use these to make your own options for setupKeyboardDPadKeys
	   *
	   *     Input.setupKeyboardDPadKeys(callback, Input.kASWDPadOnly);
	   *
	   * @const {module:Input.DPadKeys~Options} kASWDPadOnly
	   * @memberOf module:Input
	   */
	  var kASWDPadOnly = {
	    pads: [
	      { keys: kASWDKeys, },
	    ],
	  };
	  /**
	   * You can use these to make your own options for setupKeyboardDPadKeys
	   *
	   *     Input.setupKeyboardDPadKeys(callback, Input.kCursorPadOnly);
	   *
	   * @const {module:Input.DPadKeys~Options} kCursorPadOnly
	   * @memberOf module:Input
	   */
	  var kCursorPadOnly = {
	    pads: [
	      { keys: kCursorKeys, },
	    ],
	  };

	  var isNumRE = /^\d+$/;

	  // Provides a map from direction to various info.
	  //
	  // Example:
	  //
	  //   Input.setupKeyboardDPadsKeys(container, function(event) {
	  //     console.log("dir: " + event.info.symbol]);
	  //   });
	  var RIGHT = 0x1;
	  var LEFT = 0x2;
	  var UP = 0x4;
	  var DOWN = 0x8;

	  /**
	   * Various info for a given 8 direction direction.
	   *
	   *        2     -1 = no touch
	   *      3 | 1
	   *       \|/
	   *     4--+--0
	   *       /|\
	   *      5 | 7
	   *        6
	   *
	   * @typedef {Object} DirectionInfo
	   * @memberOf module:Input
	   * @property {number} direction -1 to 7
	   * @property {number} dx -1, 0, or 1
	   * @property {number} dy -1, 0, or 1
	   * @property {number} bits where `R = 0x1, L = 0x2, U = 0x4, D =
	   *           0x8`
	   * @property {string} unicode arrow simple for direction.
	   */

	  var dirInfo = { };
	  dirInfo[-1] = { direction: -1, dx:  0, dy:  0, bits: 0           , symbol: String.fromCharCode(0x2751), };
	  dirInfo[ 0] = { direction:  0, dx:  1, dy:  0, bits: RIGHT       , symbol: String.fromCharCode(0x2192), }; // right
	  dirInfo[ 1] = { direction:  1, dx:  1, dy:  1, bits: UP | RIGHT  , symbol: String.fromCharCode(0x2197), }; // up-right
	  dirInfo[ 2] = { direction:  2, dx:  0, dy:  1, bits: UP          , symbol: String.fromCharCode(0x2191), }; // up
	  dirInfo[ 3] = { direction:  3, dx: -1, dy:  1, bits: UP | LEFT   , symbol: String.fromCharCode(0x2196), }; // up-left
	  dirInfo[ 4] = { direction:  4, dx: -1, dy:  0, bits: LEFT        , symbol: String.fromCharCode(0x2190), }; // left
	  dirInfo[ 5] = { direction:  5, dx: -1, dy: -1, bits: DOWN | LEFT , symbol: String.fromCharCode(0x2199), }; // down-left
	  dirInfo[ 6] = { direction:  6, dx:  0, dy: -1, bits: DOWN        , symbol: String.fromCharCode(0x2193), }; // down
	  dirInfo[ 7] = { direction:  7, dx:  1, dy: -1, bits: DOWN | RIGHT, symbol: String.fromCharCode(0x2198), }; // down-right

	  /**
	   * @typedef {Object} EventInfo
	   * @property {number} pad the pad id 0, 1, 2, etc.
	   * @property {module:Input.DirectionInfo} info the direction
	   *           info for the event.
	   * @memberOf module:Input
	   */

	  /**
	   * Creates an EventInfo for a given padId
	   * @returns {module:Input.EventInfo}
	   * @memberOf module:Input
	   */
	  var createDirectionEventInfo = function(padId) {
	    return {
	      pad: padId,
	      info: undefined,
	    };
	  };

	  /**
	   * @param {number} padId id of pad. eg. 0, 1, 2
	   * @param {number} direction direction pad is being pressed -1
	   *        to 7.
	   * @param {EventInfo} eventInfo from createDirectionEventInfo.
	   * @param {callback} callback to pass eventInfo once it's been
	   *        filled out.
	   * @memberOf module:Input
	   */
	  var emitDirectionEvent = function(padId, direction, eventInfo, callback) {
	    var info = dirInfo[direction];
	    eventInfo.pad = padId;
	    eventInfo.info = info;
	    callback(eventInfo);
	  };

	  /**
	   * Given a direction returns a direction info
	   * @param {number} direction -1 to 7
	   * @return {module:Input.DirectionInfo}
	   * @memberOf module:Input
	   */
	  var getDirectionInfo = function(direction) {
	    return dirInfo[direction];
	  };

	  /**
	   * @typedef {Object} Coordinate
	   * @property {number} x the x coordinate
	   * @property {number} y the y coordinate
	   * @memberOf module:Input
	   */

	  /**
	   * Gets the relative coordinates for an event
	   * @func
	   * @param {HTMLElement} reference html elemetn to reference
	   * @param {Event} event from HTML mouse event
	   * @returns {module:Input.Coordinate} the relative position
	   * @memberOf module:Input
	   */
	  var getRelativeCoordinates = function(reference, event) {
	    // Use absolute coordinates
	    var pos = Misc.getAbsolutePosition(reference);
	    var x = event.pageX - pos.x;
	    var y = event.pageY - pos.y;
	    return { x: x, y: y };
	  };

	  /**
	   * Sets up controller key functions
	   * @param {callback(code, down)} keyDownFn a function to be
	   *        called when a key is pressed. It's passed the keycode
	   *        and true.
	   * @param {callback(code, down)} keyUpFn a function to be called
	   *        when a key is released. It's passed the keycode and
	   *        false.
	   * @memberOf module:Input
	   */
	  var setupControllerKeys = function(keyDownFn, keyUpFn) {
	    var g_keyState = {};
	    var g_oldKeyState = {};

	    var updateKey = function(keyCode, state) {
	      g_keyState[keyCode] = state;
	      if (g_oldKeyState !== g_keyState) {
	        g_oldKeyState = state;
	        if (state) {
	          keyDownFn(keyCode);
	        } else {
	          keyUpFn(keyCode);
	        }
	      }
	    };

	    var keyUp = function(event) {
	      updateKey(event.keyCode, false);
	    };

	    var keyDown = function(event) {
	      updateKey(event.keyCode, true);
	    };

	    window.addEventListener("keyup", keyUp, false);
	    window.addEventListener("keydown", keyDown, false);
	  };

	  /**
	   * @typedef {Object} DPadKeys
	   * @property {number[]} keys Array of 4 key codes that make a
	   *           keyboard dpad in LRUD order.
	   * @memberOf module:Input
	   */

	  /**
	   * @typedef {Object} DPadKeys~Options
	   * @property {module:Input.DPadKeys[]} pads Array of dpad keys
	   * @memberOf module:Input
	   */

	  /**
	   * Simulates N virtual dpads using keys
	   * asdw for pad 0, arrow keys for pad 1
	   *
	   * For each change in direction callback will be
	   * called with pad id (0 left, 1 right) and direction
	   * where
	   *
	   *        2     -1 = not pressed
	   *      3 | 1
	   *       \|/
	   *     4--+--0
	   *       /|\
	   *      5 | 7
	   *        6
	   *
	   * Note: this matches trig functions you can do this
	   *
	   *     var angle = dir * Math.PI / 4;
	   *     var dx    = Math.cos(angle);
	   *     var dy    = Math.sin(angle);
	   *
	   * for +y up (ie, normal for 3d)
	   *
	   * In 2d you'd probably want
	   *
	   *     var angle =  dir * Math.PI / 4;
	   *     var dx    =  Math.cos(angle);
	   *     var dy    = -Math.sin(angle);
	   *
	   *
	   * @param {callback} callback callback will be called with
	   *        EventInfo objects when pads change their direction
	   * @param {module:Input.DPadKeys~Options?} options If no options
	   *        are passed in assumes 2 DPads one on ASWD the other on
	   *        the cursor keys
	   * @memberOf module:Input
	   */
	  var setupKeyboardDPadKeys = function(callback, options) {
	    if (!options) {
	      options = {
	        pads: [
	         { keys: kASWDKeys,   }, // LRUD
	         { keys: kCursorKeys, }, // LRUD
	        ],
	      };
	    }

	    var g_dirBits = [];
	    var g_excludeBits = [];
	    var g_dir = [];
	    var g_eventInfos = [];

	    var bitInfos = [
	      { bit: 1, exclude: 2, mask: 0x3 }, // left
	      { bit: 2, exclude: 1, mask: 0x3 }, // right
	      { bit: 4, exclude: 8, mask: 0xC }, // up
	      { bit: 8, exclude: 4, mask: 0xC }, // down
	    ];

	    var keyToBit = { };

	    for (var ii = 0; ii < options.pads.length; ++ii) {
	      var pad = options.pads[ii];
	      g_dirBits.push(0);
	      g_excludeBits.push(0);
	      g_dir.push(-1);
	      g_eventInfos.push(createDirectionEventInfo(ii));
	      for (var kk = 0; kk < 4; ++kk) {
	        var bitInfo = bitInfos[kk];
	        var keyInfo = { pad: ii, };
	        Misc.copyProperties(bitInfo, keyInfo);
	        keyToBit[pad.keys[kk]] = keyInfo;
	      }
	    }

	    var bitsToDir = [
	      -1, // 0
	       4, // 1      l
	       0, // 2     r
	      -1, // 3     rl
	       2, // 4    u
	       3, // 5    u l
	       1, // 6    ur
	      -1, // 7    url
	       6, // 8   d
	       5, // 9   d  l
	       7, // 10  d r
	      -1, // 11  d rl
	      -1, // 12  du
	      -1, // 13  du l
	      -1, // 14  dur
	      -1, // 15  durl
	    ];

	    var setBit = function(keyCode, value) {
	      // get info for this key
	      var info = keyToBit[keyCode];
	      if (info) {
	        // or in or and out bit for button
	        var pad = info.pad;
	        var bit = info.bit;
	        var bits = g_dirBits[pad];
	        if (value) {
	          bits |= bit;
	          g_excludeBits[pad] = (g_excludeBits[pad] & ~info.mask) | info.exclude;
	        } else {
	          bits &= ~bit;
	          g_excludeBits[pad] &= ~info.mask;
	        }
	        // If they've changed
	        if (bits !== g_dirBits[pad]) {
	          g_dirBits[pad] = bits;
	          var dir = bitsToDir[bits & ~g_excludeBits[pad]];
	          // If the dir has changed.
	          if (dir !== g_dir[pad]) {
	            g_dir[pad] = dir;
	            emitDirectionEvent(pad, dir, g_eventInfos[pad], callback);
	          }
	        }
	      }
	    };

	    var keyUp = function(keyCode) {
	      setBit(keyCode, 0);
	    };

	    var keyDown = function(keyCode) {
	      setBit(keyCode, 1);
	    };

	    setupControllerKeys(keyDown, keyUp);
	  };

	  /**
	   * @typedef {Object} KeyEvent
	   * @property {number} keyCode
	   * @property {boolean} pressed true if pressed, false if
	   *           released
	   * @memberOf module:Input
	   */

	  /**
	   * Sets up handlers for specific keys
	   * @memberOf module:Input
	   * @param {Object.<string, callback>} array of keys to handler
	   *        functions. Handlers are called with a KeyEvent
	   *
	   * @example
	   *
	   *      var keys = { };
	   *      keys["Z".charCodeAt(0)] = handleJump;
	   *      keys["X".charCodeAt(0)] = handleShow
	   *      keys["C".charCodeAt(0)] = handleTestSound;
	   *      keys[Input.cursorKeys.kRight] = handleMoveRight;
	   *      Input.setupKeys(keys);
	   *
	   */
	  var setupKeys = function(keys) {
	    var keyCodes = {};

	    // Convert single characters to char codes.
	    Object.keys(keys).forEach(function(key) {
	      var value = keys[key];
	      if (!isNumRE.test(key)) {
	        if (key.length !== 1) {
	          throw "bad key code: '" + key + "'";
	        }
	        key = key.charCodeAt(0);
	      }
	      keyCodes[key] = value;
	    });

	    var handleKey = function(keyCode, state, pressed) {
	      var key = keyCodes[keyCode];
	      if (key) {
	        key({keyCode: keyCode, pressed:pressed});
	      }
	    };

	    var handleKeyDown = function(keyCode, state) {
	      handleKey(keyCode, state, true);
	    };
	    var handleKeyUp = function(keyCode, state) {
	      handleKey(keyCode, state, false);
	    };

	    setupControllerKeys(handleKeyDown, handleKeyUp);
	  };

	  return {
	    cursorKeys: cursorKeys,
	    createDirectionEventInfo: createDirectionEventInfo,
	    emitDirectionEvent: emitDirectionEvent,
	    getDirectionInfo: getDirectionInfo,
	    kCursorKeys: kCursorKeys,
	    kCursorPadOnly: kCursorPadOnly,
	    kASWDKeys: kASWDKeys,
	    kASWDPadOnly: kASWDPadOnly,
	    getRelativeCoordinates: getRelativeCoordinates,
	    setupControllerKeys: setupControllerKeys,
	    setupKeyboardDPadKeys: setupKeyboardDPadKeys,
	    setupKeys: setupKeys,
	  };
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));



/***/ },
/* 17 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	/**
	 * Miscellaneous string functions
	 * @module Strings
	 */
	!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {

	  /**
	   * Returns a padding string large enough for the given size.
	   * @param {string} padChar character for padding string
	   * @param {number} len minimum length of padding.
	   * @returns {string} string with len or more of padChar.
	   */
	  var getPadding = (function() {
	    var paddingDb = {};

	    return function(padChar, len) {
	      var padStr = paddingDb[padChar];
	      if (!padStr || padStr.length < len) {
	        padStr = new Array(len + 1).join(padChar);
	        paddingDb[padChar] = padStr;
	      }
	      return padStr;
	    };
	  }());

	  /**
	   * Turn an unknown object into a string if it's not already.
	   * Do I really needs this? I could just always do .toString even
	   * on a string.
	   */
	  var stringIt = function(str) {
	    return (typeof str === 'string') ? str : str.toString();
	  };

	  /**
	   * Pad string on right
	   * @param {string} str string to pad
	   * @param {number} len number of characters to pad to
	   * @param {string} padChar character to pad with
	   * @returns {string} padded string.
	   * @memberOf module:Strings
	   */
	  var padRight = function(str, len, padChar) {
	    str = stringIt(str);
	    if (str.length >= len) {
	      return str;
	    }
	    var padStr = getPadding(padChar, len);
	    return str + padStr.substr(str.length - len);
	  };

	  /**
	   * Pad string on left
	   * @param {string} str string to pad
	   * @param {number} len number of characters to pad to
	   * @param {string} padChar character to pad with
	   * @returns {string} padded string.
	   * @memberOf module:Strings
	   */
	  var padLeft = function(str, len, padChar) {
	    str = stringIt(str);
	    if (str.length >= len) {
	      return str;
	    }
	    var padStr = getPadding(padChar, len);
	    return padStr.substr(str.length - len) + str;
	  };

	  /**
	   * Replace %(id)s in strings with values in objects(s)
	   *
	   * Given a string like `"Hello %(name)s from $(user.country)s"`
	   * and an object like `{name:"Joe",user:{country:"USA"}}` would
	   * return `"Hello Joe from USA"`.
	   *
	   * @function
	   * @param {string} str string to do replacements in
	   * @param {Object|Object[]} params one or more objects.
	   * @returns {string} string with replaced parts
	   * @memberOf module:Strings
	   */
	  var replaceParams = (function() {
	    var replaceParamsRE = /%\(([^\)]+)\)s/g;

	    return function(str, params) {
	      if (!params.length) {
	        params = [params];
	      }

	      return str.replace(replaceParamsRE, function(match, key) {
	        var keys = key.split('.');
	        for (var ii = 0; ii < params.length; ++ii) {
	          var obj = params[ii];
	          for (var jj = 0; jj < keys.length; ++jj) {
	            var part = keys[jj];
	            obj = obj[part];
	            if (obj === undefined) {
	              break;
	            }
	          }
	          if (obj !== undefined) {
	            return obj;
	          }
	        }
	        console.error("unknown key: " + key);
	        return "%(" + key + ")s";
	      });
	    };
	  }());

	  /**
	   * True if string starts with prefix
	   * @static
	   * @param {String} str string to check for start
	   * @param {String} prefix start value
	   * @returns {Boolean} true if str starts with prefix
	   * @memberOf module:Strings
	   */
	  var startsWith = function(str, start) {
	    return (str.length >= start.length &&
	            str.substr(0, start.length) === start);
	  };

	  /**
	   * True if string ends with suffix
	   * @param {String} str string to check for start
	   * @param {String} suffix start value
	   * @returns {Boolean} true if str starts with suffix
	   * @memberOf module:Strings
	   */
	  var endsWith = function(str, end) {
	    return (str.length >= end.length &&
	            str.substring(str.length - end.length) === end);
	  };

	  /**
	   * Make a string from unicode code points
	   * @function
	   * @param {Number} codePoint one or more code points
	   * @returns {string} unicode string. Note a single code point
	   *          can return a string with length > 1.
	   * @memberOf module:Strings
	   */
	  var fromCodePoint = String.fromCodePoint ? String.fromCodePoint : (function() {
	    var stringFromCharCode = String.fromCharCode;
	    var floor = Math.floor;
	    var fromCodePoint = function() {
	      var MAX_SIZE = 0x4000;
	      var codeUnits = [];
	      var highSurrogate;
	      var lowSurrogate;
	      var index = -1;
	      var length = arguments.length;
	      if (!length) {
	        return '';
	      }
	      var result = '';
	      while (++index < length) {
	        var codePoint = Number(arguments[index]);
	        if (
	          !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
	          codePoint < 0 || // not a valid Unicode code point
	          codePoint > 0x10FFFF || // not a valid Unicode code point
	          floor(codePoint) !== codePoint // not an integer
	        ) {
	          throw new RangeError('Invalid code point: ' + codePoint);
	        }
	        if (codePoint <= 0xFFFF) { // BMP code point
	          codeUnits.push(codePoint);
	        } else { // Astral code point; split in surrogate halves
	          // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
	          codePoint -= 0x10000;
	          highSurrogate = (codePoint >> 10) + 0xD800;
	          lowSurrogate = (codePoint % 0x400) + 0xDC00;
	          codeUnits.push(highSurrogate, lowSurrogate);
	        }
	        if (index + 1 === length || codeUnits.length > MAX_SIZE) {
	          result += stringFromCharCode.apply(null, codeUnits);
	          codeUnits.length = 0;
	        }
	      }
	      return result;
	    };
	    return fromCodePoint;
	  }());

	  var exports = {
	    endsWith: endsWith,
	    fromCodePoint: fromCodePoint,
	    padLeft: padLeft,
	    padRight: padRight,
	    replaceParams: replaceParams,
	    startsWith: startsWith,
	  };

	  return exports;
	}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));




/***/ },
/* 18 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	 * Copyright 2014, Gregg Tavares.
	 * All rights reserved.
	 *
	 * Redistribution and use in source and binary forms, with or without
	 * modification, are permitted provided that the following conditions are
	 * met:
	 *
	 *     * Redistributions of source code must retain the above copyright
	 * notice, this list of conditions and the following disclaimer.
	 *     * Redistributions in binary form must reproduce the above
	 * copyright notice, this list of conditions and the following disclaimer
	 * in the documentation and/or other materials provided with the
	 * distribution.
	 *     * Neither the name of Gregg Tavares. nor the names of its
	 * contributors may be used to endorse or promote products derived from
	 * this software without specific prior written permission.
	 *
	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
	 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	 */
	"use strict";

	/**
	 * Various functions for touch input.
	 *
	 * @module Touch
	 */
	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(19),
	    __webpack_require__(16),
	    __webpack_require__(9),
	  ], __WEBPACK_AMD_DEFINE_RESULT__ = function(pep, Input, Misc) {

	  /**
	   * @typedef {Object} PadInfo
	   * @property {HTMLElement} referenceElement element that is reference for position of pad
	   * @property {number} offsetX offset from left of reference element to center of pad
	   * @property {number} offsetY offset from top of reference element to center of pad
	   * @memberOf module:Touch
	   */

	  /**
	   * @typedef {Object} TouchDPad~Options
	   * @property {HTMLElement} inputElement element used to capture input (for example window, body,)
	   * @property {callback} callback callback to pass event
	   * @property {boolean?} fixedCenter true = center stays the same place, false = each time finger touches a new center is picked
	   * @property {number?} deadSpaceRadius size of dead area in center of pad.
	   * @property {number?} axisSize use axis.
	   * @property {module:Touch~PadInfo[]} pads array of PadInfos, one for each DPad
	   * @memberOf module:Touch
	   */

	  /**
	   * Simulates N virtual dpads using touch events
	   *
	   * For each change in direction callback will be
	   * called with an event info where
	   *
	   *     pad = (index of pad)
	   *     direction =
	   *
	   *
	   *        2     -1 = no touch
	   *      3 | 1
	   *       \|/
	   *     4--+--0
	   *       /|\
	   *      5 | 7
	   *        6
	   *
	   *     dx   = -1, 0, 1
	   *     dy   = -1, 0, 1
	   *     bits = 1 for right, 2 for left, 4 for up, 8 for down
	   *
	   * Note: this matches trig functions so you can do this
	   *
	   *     if (dir >= 0) {
	   *       var angle = dir * Math.PI / 4;
	   *       var dx    = Math.cos(angle);
	   *       var dy    = Math.sin(angle);
	   *     }
	   *
	   * for +y up (ie, normal for 3d)
	   *
	   * In 2d you'd probably want to flip dy
	   *
	   *     if (dir >= 0) {
	   *       var angle =  dir * Math.PI / 4;
	   *       var dx    =  Math.cos(angle);
	   *       var dy    = -Math.sin(angle);
	   *     }
	   *
	   * The default way of figuring out the direction is to take the angle from the center to
	   * the place of touch, compute an angle, divide a circle into octants, which ever octant is the direction
	   *
	   * If axisSize is passed in then instead the space is divided into 3x3 boxes. Which ever box the finger is
	   * in is the direction. axisSize determines the width height of the axis boxes
	   *
	   *          | ax |
	   *          | is |
	   *     -----+----+-----
	   *          |    | axis
	   *     -----+----+-----
	   *          |    |
	   *          |    |
	   *
	   * if `divisions: 4` is passed in then instead of getting 8 directions decided
	   * by octant you get 4 decided by quadrant as in
	   *
	   *            2
	   *         \  |  /
	   *          \ | /
	   *     4 <---   ---> 0
	   *          / | \
	   *         /  V  \
	   *            6
	   *
	   * @param {module:Touch.TouchDPad~Options} options
	   * @memberOf module:Touch
	   */

	  var setupVirtualDPads = function(options) {
	    var callback = options.callback;
	    var container = options.inputElement;
	    options.deadSpaceRadius = options.deadSpaceRadius || 10;
	    var deadSpaceRadiusSq = options.deadSpaceRadius * options.deadSpaceRadius;

	    var Vector2 = function(x, y) {
	      this.reset(x, y);
	    };

	    Vector2.prototype.reset = function(x, y) {
	      this.x = x;
	      this.y = y;
	      return this;
	    };

	    Vector2.prototype.copyFrom = function(src) {
	      this.x = src.x;
	      this.y = src.y;
	    };

	    Vector2.prototype.minusEq = function(v) {
	      this.x -= v.x;
	      this.y -= v.y;
	      return this;
	    };

	    var makePad = function(padId) {
	      return {
	        pointerId: -1,                      // touch id
	        pointerPos: new Vector2(0, 0),      // current position
	        pointerStartPos: new Vector2(0, 0), // position when first touched
	        vector: new Vector2(0, 0),          // vector from start to current position
	        dir: -1,                            // octant
	        lastDir: 0,                         // previous octant
	        event: Input.createDirectionEventInfo(padId),
	      };
	    };

	    var pads = [];
	    for (var ii = 0; ii < options.pads.length; ++ii) {
	      pads.push(makePad(ii));
	    }

	    var computeDirByAngle = function(x, y) {
	      var angle = Math.atan2(-y, x) + Math.PI * 2 + Math.PI / 8;
	      return (Math.floor(angle / (Math.PI / 4))) % 8;
	    };

	    var computeDirByAngle4 = function(x, y) {
	      if (Math.abs(x) < Math.abs(y)) {
	        return y < 0 ? 2 : 6;
	      } else {
	        return x < 0 ? 4 : 0;
	      }
	    };

	    //      |   |
	    //      | V |x
	    // -----+---+-----
	    //  H   |HV |x H
	    // -----+---+-----
	    //  y   | Vy|xy
	    //      |   |

	    var axisBitsToDir = [
	       3, // 0
	       4, // 1   h
	       2, // 2    v
	      -1, // 3   hv
	       1, // 4     x
	       0, // 5   h x
	       2, // 6    vx
	      -1, // 7   hvx
	       5, // 8      y
	       4, // 9   h  y
	       6, // 10   v y
	      -1, // 11  hv y
	       7, // 12    xy
	       0, // 13  h xy
	       6, // 14   vxy
	      -1, // 15  hvxy
	    ];

	    var computeDirByAxis = function(x, y) {
	      var h = (Math.abs(y) < options.axisSize / 2) ? 1 : 0;
	      var v = (Math.abs(x) < options.axisSize / 2) ? 2 : 0;
	      var bits = h | v |
	          (x > 0 ? 4 : 0) |
	          (y > 0 ? 8 : 0);
	      return axisBitsToDir[bits];
	    };

	    var computeDir = options.axisSize ? computeDirByAxis : computeDirByAngle;

	    if (options.divisions === 4) {
	      computeDir = computeDirByAngle4;
	    }

	    var callCallback = function(padId, dir) {
	      var pad = pads[padId];
	      Input.emitDirectionEvent(padId, dir, pad.event, callback);
	    };

	    var updatePad = function(pad, padId, out) {
	      var newDir = -1;
	      if (!out && pad.pointerId >= 0) {
	        var distSq = pad.vector.x * pad.vector.x + pad.vector.y * pad.vector.y;
	        if (distSq > deadSpaceRadiusSq) {
	          newDir = computeDir(pad.vector.x, pad.vector.y);
	          pad.lastDir = newDir;
	        }
	      }
	      if (pad.dir !== newDir) {
	        pad.dir = newDir;
	        callCallback(padId, newDir);
	      }
	    };

	    var checkStart = function(padId, e) {
	      var pad = pads[padId];
	      var padOptions = options.pads[padId];
	      pad.pointerId = e.pointerId;
	      var relPos = Input.getRelativeCoordinates(padOptions.referenceElement, e);
	      var x = relPos.x - (padOptions.offsetX || padOptions.referenceElement.clientWidth  / 2);
	      var y = relPos.y - (padOptions.offsetY || padOptions.referenceElement.clientHeight / 2);
	      if (options.fixedCenter) {
	        pad.pointerStartPos.reset(0, 0);
	        pad.pointerPos.reset(x, y);
	        pad.vector.reset(x, y);
	        updatePad(pad, padId);
	      } else {
	        pad.pointerStartPos.reset(x, y);
	        pad.pointerPos.copyFrom(pad.pointerStartPos);
	        pad.vector.reset(0, 0);
	        pad.dir = pad.lastDir;
	        callCallback(padId, pad.lastDir);
	      }
	    };

	    var getClosestPad = function(e) {
	      var closestId = 0;
	      var closestDist;
	      for (var ii = 0; ii < pads.length; ++ii) {
	        var padOptions = options.pads[ii];
	        var refElement = padOptions.referenceElement;
	        var relPos = Input.getRelativeCoordinates(refElement, e);
	        var centerX = refElement.clientWidth / 2;
	        var centerY = refElement.clientHeight / 2;
	        var dx = relPos.x - centerX;
	        var dy = relPos.y - centerY;
	        var distSq = dx * dx + dy * dy;
	        if (closestDist === undefined || distSq < closestDist) {
	          closestDist = distSq;
	          closestId = ii;
	        }
	      }
	      return closestId;
	    };

	    var onPointerDown = function(e) {
	      var padId = getClosestPad(e);
	      checkStart(padId, e);
	    };

	    var onPointerMove = function(e) {
	      for (var ii = 0; ii < pads.length; ++ii) {
	        var pad = pads[ii];
	        if (pad.pointerId === e.pointerId) {
	          var padOptions = options.pads[ii];
	          var relPos = Input.getRelativeCoordinates(padOptions.referenceElement, e);
	          var x = relPos.x - (padOptions.offsetX || padOptions.referenceElement.clientWidth / 2);
	          var y = relPos.y - (padOptions.offsetY || padOptions.referenceElement.clientHeight / 2);
	          pad.pointerPos.reset(x, y);
	          pad.vector.copyFrom(pad.pointerPos);
	          pad.vector.minusEq(pad.pointerStartPos);
	          updatePad(pad, ii);
	        }
	      }
	    };

	    var onPointerUp = function(e) {
	      for (var ii = 0; ii < pads.length; ++ii) {
	        var pad = pads[ii];
	        if (pad.pointerId === e.pointerId) {
	          pad.pointerId = -1;
	          pad.vector.reset(0, 0);
	          updatePad(pad, ii);
	        }
	      }
	    };

	    var onPointerOut = function(e) {
	      for (var ii = 0; ii < pads.length; ++ii) {
	        var pad = pads[ii];
	        if (pad.pointerId === e.pointerId) {
	          updatePad(pad, ii, true);
	        }
	      }
	    };

	    container.addEventListener('pointerdown', onPointerDown, false);
	    container.addEventListener('pointermove', onPointerMove, false);
	    container.addEventListener('pointerup', onPointerUp, false);
	    container.addEventListener('pointerout', onPointerOut, false);
	  };

	  /**
	   * @typedef {Object} ButtonInfo
	   * @property {HTMLElement} element element that represents area of buttton (need not be visible)
	   * @property {callback} callback function to call when button is pressed or released
	   * @memberOf module:Touch
	   */

	  /**
	   * @typedef {Object} Buttons~Options
	   * @property {HTMLElement} inputElement element that receives all input. Should be above all buttons
	   * @memberOf module:Touch
	   */

	  /**
	   * Sets up touch buttons.
	   *
	   * For example
	   *
	   *     var $ = document.getElementById.bind(document);
	   *
	   *     Touch.setupButtons({
	   *       inputElement: $("buttons"),
	   *       buttons: [
	   *         { element: $("abuttoninput"), callback: handleAbutton, },
	   *         { element: $("avatarinput"),  callback: handleShow, },
	   *       ],
	   *     });
	   *
	   * The code above sets up 2 buttons. The HTML elements "abuttoninput" and "avatarinput".
	   * The actual touch input events come from an HTML element "buttons" which is an div
	   * that covers the entire display.
	   *
	   * @param {module:Touch.Buttons~Options} options
	   * @memberOf module:Touch
	   */
	  var setupButtons = function(options) {
	    var buttonInfos = [];
	    var buttons = options.buttons;
	    //var expirationTime = 2000;  // 2 seconds, 2000ms

	    // I don't really know what to make this number
	    // If the person has steady hands they can make
	    // this fail but I'm going to assume most players
	    // most of the time won't hold steady for this long.
	    // On the other hand if the button does get stuck
	    // It will take this long to un-stick.

	    for (var ii = 0; ii < buttons.length; ++ii) {
	      var button = buttons[ii];
	      var buttonInfo = {
	        pointerIds: {},   // Pointers currently in this button
	        numPointerIds: 0, // Number of pointers in this button
	      };
	      Misc.copyProperties(button, buttonInfo);
	      buttonInfos.push(buttonInfo);
	    }

	    // var printButtonInfo = function(buttonInfo) {
	    //   console.log("button: " + buttonInfo.element.id + ", " + buttonInfo.numPointerIds);
	    // };

	    var addPointerId = function(buttonInfo, pointerId, timeStamp) {
	      if (!buttonInfo.pointerIds[pointerId]) {
	        buttonInfo.pointerIds[pointerId] = timeStamp;
	        ++buttonInfo.numPointerIds;
	        buttonInfo.callback({pressed: true});
	      }
	    };

	    var removePointerId = function(buttonInfo, pointerId) {
	      if (buttonInfo.pointerIds[pointerId]) {
	        delete buttonInfo.pointerIds[pointerId];
	        --buttonInfo.numPointerIds;
	        if (buttonInfo.numPointerIds === 0) {
	          buttonInfo.callback({pressed: false});
	        } else if (buttonInfo.numPointerIds < 0) {
	          throw ("numPointerIds went negative: how did I get here!?");
	        }
	      }
	    };

	    // This is because (maybe because my programming sucks?)
	    // sometimes it seems we miss an out/up event and buttons
	    // get stuck. So, for a particlar id, if no event has come in
	    // for a while assume it was released.
	    //var expireOldButtons = function() {
	    //  var now = Date.now();
	    //  buttonInfos.forEach(function(buttonInfo) {
	    //    Object.keys(buttonInfo.pointerIds).forEach(function(pointerId) {
	    //      var timeStamp = buttonInfo.pointerIds[pointerId];
	    //      var age = now - timeStamp;
	    //      if (age > expirationTime) {
	    //        removePointerId(buttonInfo, pointerId);
	    //      }
	    //    });
	    //  });
	    //};

	    var handleButtonDown = function(e, buttonInfo) {
	      addPointerId(buttonInfo, e.pointerId, e.timeStamp);
	    };

	    var handleButtonUp = function(e, buttonInfo) {
	      removePointerId(buttonInfo, e.pointerId, e.timeStamp);
	    };

	    var handleButtonMove = function(/*e, buttonInfo*/) {
	//      addPointerId(buttonInfo, e.pointerId, e.timeStamp);
	    };

	    var handleButtonOut = function(e, buttonInfo) {
	      removePointerId(buttonInfo, e.pointerId, e.timeStamp);
	    };

	    var handleButtonEnter = function(e, buttonInfo) {
	      addPointerId(buttonInfo, e.pointerId, e.timeStamp);
	    };

	    var handleButtonLeave = function(e, buttonInfo) {
	      removePointerId(buttonInfo, e.pointerId, e.timeStamp);
	    };

	    var handleButtonCancel = function(e, buttonInfo) {
	      removePointerId(buttonInfo, e.pointerId, e.timeStamp);
	    };

	    var funcs = {
	      pointerdown: handleButtonDown,
	      pointermove: handleButtonMove,
	      pointerup: handleButtonUp,
	      pointerout: handleButtonOut,
	      pointerenter: handleButtonEnter,
	      pointerleave: handleButtonLeave,
	      pointercancel: handleButtonCancel,
	    };

	    buttonInfos.forEach(function(buttonInfo) {
	      var elem = buttonInfo.element;
	      Object.keys(funcs).forEach(function(eventName) {
	        var func = funcs[eventName];
	        elem.addEventListener(eventName, function(buttonInfo) {
	          return function(e) {
	            func(e, buttonInfo);
	          };
	        }(buttonInfo));
	      });
	    });

	//    setInterval(expireOldButtons, 100);
	  };

	  return {
	    setupVirtualDPads: setupVirtualDPads,
	    setupButtons: setupButtons,
	  };
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));




/***/ },
/* 19 */
/***/ function(module, exports, __webpack_require__) {

	/*!
	 * PEP v0.4.1 | https://github.com/jquery/PEP
	 * Copyright jQuery Foundation and other contributors | http://jquery.org/license
	 */
	!function(a,b){ true?module.exports=b():"function"==typeof define&&define.amd?define(b):a.PointerEventsPolyfill=b()}(this,function(){"use strict";function a(a,b){b=b||Object.create(null);var c=document.createEvent("Event");c.initEvent(a,b.bubbles||!1,b.cancelable||!1);for(var d,e=2;e<k.length;e++)d=k[e],c[d]=b[d]||l[e];c.buttons=b.buttons||0;var f=0;return f=b.pressure?b.pressure:c.buttons?.5:0,c.x=c.clientX,c.y=c.clientY,c.pointerId=b.pointerId||0,c.width=b.width||0,c.height=b.height||0,c.pressure=f,c.tiltX=b.tiltX||0,c.tiltY=b.tiltY||0,c.pointerType=b.pointerType||"",c.hwTimestamp=b.hwTimestamp||0,c.isPrimary=b.isPrimary||!1,c}function b(){this.array=[],this.size=0}function c(a,b,c,d){this.addCallback=a.bind(d),this.removeCallback=b.bind(d),this.changedCallback=c.bind(d),B&&(this.observer=new B(this.mutationWatcher.bind(this)))}function d(a){return"body /shadow-deep/ "+e(a)}function e(a){return'[touch-action="'+a+'"]'}function f(a){return"{ -ms-touch-action: "+a+"; touch-action: "+a+"; touch-action-delay: none; }"}function g(){if(H){F.forEach(function(a){String(a)===a?(G+=e(a)+f(a)+"\n",I&&(G+=d(a)+f(a)+"\n")):(G+=a.selectors.map(e)+f(a.rule)+"\n",I&&(G+=a.selectors.map(d)+f(a.rule)+"\n"))});var a=document.createElement("style");a.textContent=G,document.head.appendChild(a)}}function h(){if(!window.PointerEvent){if(window.PointerEvent=m,window.navigator.msPointerEnabled){var a=window.navigator.msMaxTouchPoints;Object.defineProperty(window.navigator,"maxTouchPoints",{value:a,enumerable:!0}),v.registerSource("ms",ea)}else v.registerSource("mouse",Q),void 0!==window.ontouchstart&&v.registerSource("touch",aa);v.register(document)}}function i(a){if(!v.pointermap.has(a))throw new Error("InvalidPointerId")}function j(){window.Element&&!Element.prototype.setPointerCapture&&Object.defineProperties(Element.prototype,{setPointerCapture:{value:$},releasePointerCapture:{value:_}})}var k=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","pageX","pageY"],l=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0],m=a,n=window.Map&&window.Map.prototype.forEach,o=n?Map:b;b.prototype={set:function(a,b){return void 0===b?this["delete"](a):(this.has(a)||this.size++,void(this.array[a]=b))},has:function(a){return void 0!==this.array[a]},"delete":function(a){this.has(a)&&(delete this.array[a],this.size--)},get:function(a){return this.array[a]},clear:function(){this.array.length=0,this.size=0},forEach:function(a,b){return this.array.forEach(function(c,d){a.call(b,c,d,this)},this)}};var p=o,q=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","buttons","pointerId","width","height","pressure","tiltX","tiltY","pointerType","hwTimestamp","isPrimary","type","target","currentTarget","which","pageX","pageY","timeStamp"],r=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0,0,0,0,0,0,"",0,!1,"",null,null,0,0,0,0],s={pointerover:1,pointerout:1,pointerenter:1,pointerleave:1},t="undefined"!=typeof SVGElementInstance,u={pointermap:new p,eventMap:Object.create(null),captureInfo:Object.create(null),eventSources:Object.create(null),eventSourceList:[],registerSource:function(a,b){var c=b,d=c.events;d&&(d.forEach(function(a){c[a]&&(this.eventMap[a]=c[a].bind(c))},this),this.eventSources[a]=c,this.eventSourceList.push(c))},register:function(a){for(var b,c=this.eventSourceList.length,d=0;c>d&&(b=this.eventSourceList[d]);d++)b.register.call(b,a)},unregister:function(a){for(var b,c=this.eventSourceList.length,d=0;c>d&&(b=this.eventSourceList[d]);d++)b.unregister.call(b,a)},contains:function(a,b){try{return a.contains(b)}catch(c){return!1}},down:function(a){a.bubbles=!0,this.fireEvent("pointerdown",a)},move:function(a){a.bubbles=!0,this.fireEvent("pointermove",a)},up:function(a){a.bubbles=!0,this.fireEvent("pointerup",a)},enter:function(a){a.bubbles=!1,this.fireEvent("pointerenter",a)},leave:function(a){a.bubbles=!1,this.fireEvent("pointerleave",a)},over:function(a){a.bubbles=!0,this.fireEvent("pointerover",a)},out:function(a){a.bubbles=!0,this.fireEvent("pointerout",a)},cancel:function(a){a.bubbles=!0,this.fireEvent("pointercancel",a)},leaveOut:function(a){this.out(a),this.contains(a.target,a.relatedTarget)||this.leave(a)},enterOver:function(a){this.over(a),this.contains(a.target,a.relatedTarget)||this.enter(a)},eventHandler:function(a){if(!a._handledByPE){var b=a.type,c=this.eventMap&&this.eventMap[b];c&&c(a),a._handledByPE=!0}},listen:function(a,b){b.forEach(function(b){this.addEvent(a,b)},this)},unlisten:function(a,b){b.forEach(function(b){this.removeEvent(a,b)},this)},addEvent:function(a,b){a.addEventListener(b,this.boundHandler)},removeEvent:function(a,b){a.removeEventListener(b,this.boundHandler)},makeEvent:function(a,b){this.captureInfo[b.pointerId]&&(b.relatedTarget=null);var c=new m(a,b);return b.preventDefault&&(c.preventDefault=b.preventDefault),c._target=c._target||b.target,c},fireEvent:function(a,b){var c=this.makeEvent(a,b);return this.dispatchEvent(c)},cloneEvent:function(a){for(var b,c=Object.create(null),d=0;d<q.length;d++)b=q[d],c[b]=a[b]||r[d],!t||"target"!==b&&"relatedTarget"!==b||c[b]instanceof SVGElementInstance&&(c[b]=c[b].correspondingUseElement);return a.preventDefault&&(c.preventDefault=function(){a.preventDefault()}),c},getTarget:function(a){var b=this.captureInfo[a.pointerId];return b?a._target!==b&&a.type in s?void 0:b:a._target},setCapture:function(a,b){this.captureInfo[a]&&this.releaseCapture(a),this.captureInfo[a]=b;var c=document.createEvent("Event");c.initEvent("gotpointercapture",!0,!1),c.pointerId=a,this.implicitRelease=this.releaseCapture.bind(this,a),document.addEventListener("pointerup",this.implicitRelease),document.addEventListener("pointercancel",this.implicitRelease),c._target=b,this.asyncDispatchEvent(c)},releaseCapture:function(a){var b=this.captureInfo[a];if(b){var c=document.createEvent("Event");c.initEvent("lostpointercapture",!0,!1),c.pointerId=a,this.captureInfo[a]=void 0,document.removeEventListener("pointerup",this.implicitRelease),document.removeEventListener("pointercancel",this.implicitRelease),c._target=b,this.asyncDispatchEvent(c)}},dispatchEvent:function(a){var b=this.getTarget(a);return b?b.dispatchEvent(a):void 0},asyncDispatchEvent:function(a){requestAnimationFrame(this.dispatchEvent.bind(this,a))}};u.boundHandler=u.eventHandler.bind(u);var v=u,w={shadow:function(a){return a?a.shadowRoot||a.webkitShadowRoot:void 0},canTarget:function(a){return a&&Boolean(a.elementFromPoint)},targetingShadow:function(a){var b=this.shadow(a);return this.canTarget(b)?b:void 0},olderShadow:function(a){var b=a.olderShadowRoot;if(!b){var c=a.querySelector("shadow");c&&(b=c.olderShadowRoot)}return b},allShadows:function(a){for(var b=[],c=this.shadow(a);c;)b.push(c),c=this.olderShadow(c);return b},searchRoot:function(a,b,c){if(a){var d,e,f=a.elementFromPoint(b,c);for(e=this.targetingShadow(f);e;){if(d=e.elementFromPoint(b,c)){var g=this.targetingShadow(d);return this.searchRoot(g,b,c)||d}e=this.olderShadow(e)}return f}},owner:function(a){for(var b=a;b.parentNode;)b=b.parentNode;return b.nodeType!==Node.DOCUMENT_NODE&&b.nodeType!==Node.DOCUMENT_FRAGMENT_NODE&&(b=document),b},findTarget:function(a){var b=a.clientX,c=a.clientY,d=this.owner(a.target);return d.elementFromPoint(b,c)||(d=document),this.searchRoot(d,b,c)}},x=Array.prototype.forEach.call.bind(Array.prototype.forEach),y=Array.prototype.map.call.bind(Array.prototype.map),z=Array.prototype.slice.call.bind(Array.prototype.slice),A=Array.prototype.filter.call.bind(Array.prototype.filter),B=window.MutationObserver||window.WebKitMutationObserver,C="[touch-action]",D={subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0,attributeFilter:["touch-action"]};c.prototype={watchSubtree:function(a){this.observer&&w.canTarget(a)&&this.observer.observe(a,D)},enableOnSubtree:function(a){this.watchSubtree(a),a===document&&"complete"!==document.readyState?this.installOnLoad():this.installNewSubtree(a)},installNewSubtree:function(a){x(this.findElements(a),this.addElement,this)},findElements:function(a){return a.querySelectorAll?a.querySelectorAll(C):[]},removeElement:function(a){this.removeCallback(a)},addElement:function(a){this.addCallback(a)},elementChanged:function(a,b){this.changedCallback(a,b)},concatLists:function(a,b){return a.concat(z(b))},installOnLoad:function(){document.addEventListener("readystatechange",function(){"complete"===document.readyState&&this.installNewSubtree(document)}.bind(this))},isElement:function(a){return a.nodeType===Node.ELEMENT_NODE},flattenMutationTree:function(a){var b=y(a,this.findElements,this);return b.push(A(a,this.isElement)),b.reduce(this.concatLists,[])},mutationWatcher:function(a){a.forEach(this.mutationHandler,this)},mutationHandler:function(a){if("childList"===a.type){var b=this.flattenMutationTree(a.addedNodes);b.forEach(this.addElement,this);var c=this.flattenMutationTree(a.removedNodes);c.forEach(this.removeElement,this)}else"attributes"===a.type&&this.elementChanged(a.target,a.oldValue)}};var E=c,F=["none","auto","pan-x","pan-y",{rule:"pan-x pan-y",selectors:["pan-x pan-y","pan-y pan-x"]}],G="",H=window.PointerEvent||window.MSPointerEvent,I=!window.ShadowDOMPolyfill&&document.head.createShadowRoot,J=v.pointermap,K=25,L=[1,4,2,8,16],M=!1;try{M=1===new MouseEvent("test",{buttons:1}).buttons}catch(N){}var O,P={POINTER_ID:1,POINTER_TYPE:"mouse",events:["mousedown","mousemove","mouseup","mouseover","mouseout"],register:function(a){v.listen(a,this.events)},unregister:function(a){v.unlisten(a,this.events)},lastTouches:[],isEventSimulatedFromTouch:function(a){for(var b,c=this.lastTouches,d=a.clientX,e=a.clientY,f=0,g=c.length;g>f&&(b=c[f]);f++){var h=Math.abs(d-b.x),i=Math.abs(e-b.y);if(K>=h&&K>=i)return!0}},prepareEvent:function(a){var b=v.cloneEvent(a),c=b.preventDefault;return b.preventDefault=function(){a.preventDefault(),c()},b.pointerId=this.POINTER_ID,b.isPrimary=!0,b.pointerType=this.POINTER_TYPE,b},prepareButtonsForMove:function(a,b){var c=J.get(this.POINTER_ID);a.buttons=c?c.buttons:0,b.buttons=a.buttons},mousedown:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=J.get(this.POINTER_ID),c=this.prepareEvent(a);M||(c.buttons=L[c.button],b&&(c.buttons|=b.buttons),a.buttons=c.buttons),J.set(this.POINTER_ID,a),b?v.move(c):v.down(c)}},mousemove:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);M||this.prepareButtonsForMove(b,a),v.move(b)}},mouseup:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=J.get(this.POINTER_ID),c=this.prepareEvent(a);if(!M){var d=L[c.button];c.buttons=b?b.buttons&~d:0,a.buttons=c.buttons}J.set(this.POINTER_ID,a),0===c.buttons||c.buttons===L[c.button]?(this.cleanupMouse(),v.up(c)):v.move(c)}},mouseover:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);M||this.prepareButtonsForMove(b,a),v.enterOver(b)}},mouseout:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);M||this.prepareButtonsForMove(b,a),v.leaveOut(b)}},cancel:function(a){var b=this.prepareEvent(a);v.cancel(b),this.cleanupMouse()},cleanupMouse:function(){J["delete"](this.POINTER_ID)}},Q=P,R=v.captureInfo,S=w.findTarget.bind(w),T=w.allShadows.bind(w),U=v.pointermap,V=2500,W=200,X="touch-action",Y=!1,Z={events:["touchstart","touchmove","touchend","touchcancel"],register:function(a){Y?v.listen(a,this.events):O.enableOnSubtree(a)},unregister:function(a){Y&&v.unlisten(a,this.events)},elementAdded:function(a){var b=a.getAttribute(X),c=this.touchActionToScrollType(b);c&&(a._scrollType=c,v.listen(a,this.events),T(a).forEach(function(a){a._scrollType=c,v.listen(a,this.events)},this))},elementRemoved:function(a){a._scrollType=void 0,v.unlisten(a,this.events),T(a).forEach(function(a){a._scrollType=void 0,v.unlisten(a,this.events)},this)},elementChanged:function(a,b){var c=a.getAttribute(X),d=this.touchActionToScrollType(c),e=this.touchActionToScrollType(b);d&&e?(a._scrollType=d,T(a).forEach(function(a){a._scrollType=d},this)):e?this.elementRemoved(a):d&&this.elementAdded(a)},scrollTypes:{EMITTER:"none",XSCROLLER:"pan-x",YSCROLLER:"pan-y",SCROLLER:/^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/},touchActionToScrollType:function(a){var b=a,c=this.scrollTypes;return"none"===b?"none":b===c.XSCROLLER?"X":b===c.YSCROLLER?"Y":c.SCROLLER.exec(b)?"XY":void 0},POINTER_TYPE:"touch",firstTouch:null,isPrimaryTouch:function(a){return this.firstTouch===a.identifier},setPrimaryTouch:function(a){(0===U.size||1===U.size&&U.has(1))&&(this.firstTouch=a.identifier,this.firstXY={X:a.clientX,Y:a.clientY},this.scrolling=!1,this.cancelResetClickCount())},removePrimaryPointer:function(a){a.isPrimary&&(this.firstTouch=null,this.firstXY=null,this.resetClickCount())},clickCount:0,resetId:null,resetClickCount:function(){var a=function(){this.clickCount=0,this.resetId=null}.bind(this);this.resetId=setTimeout(a,W)},cancelResetClickCount:function(){this.resetId&&clearTimeout(this.resetId)},typeToButtons:function(a){var b=0;return("touchstart"===a||"touchmove"===a)&&(b=1),b},touchToPointer:function(a){var b=this.currentTouchEvent,c=v.cloneEvent(a),d=c.pointerId=a.identifier+2;c.target=R[d]||S(c),c.bubbles=!0,c.cancelable=!0,c.detail=this.clickCount,c.button=0,c.buttons=this.typeToButtons(b.type),c.width=a.radiusX||a.webkitRadiusX||0,c.height=a.radiusY||a.webkitRadiusY||0,c.pressure=a.force||a.webkitForce||.5,c.isPrimary=this.isPrimaryTouch(a),c.pointerType=this.POINTER_TYPE;var e=this;return c.preventDefault=function(){e.scrolling=!1,e.firstXY=null,b.preventDefault()},c},processTouches:function(a,b){var c=a.changedTouches;this.currentTouchEvent=a;for(var d,e=0;e<c.length;e++)d=c[e],b.call(this,this.touchToPointer(d))},shouldScroll:function(a){if(this.firstXY){var b,c=a.currentTarget._scrollType;if("none"===c)b=!1;else if("XY"===c)b=!0;else{var d=a.changedTouches[0],e=c,f="Y"===c?"X":"Y",g=Math.abs(d["client"+e]-this.firstXY[e]),h=Math.abs(d["client"+f]-this.firstXY[f]);b=g>=h}return this.firstXY=null,b}},findTouch:function(a,b){for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)if(c.identifier===b)return!0},vacuumTouches:function(a){var b=a.touches;if(U.size>=b.length){var c=[];U.forEach(function(a,d){if(1!==d&&!this.findTouch(b,d-2)){var e=a.out;c.push(e)}},this),c.forEach(this.cancelOut,this)}},touchstart:function(a){this.vacuumTouches(a),this.setPrimaryTouch(a.changedTouches[0]),this.dedupSynthMouse(a),this.scrolling||(this.clickCount++,this.processTouches(a,this.overDown))},overDown:function(a){U.set(a.pointerId,{target:a.target,out:a,outTarget:a.target}),v.over(a),v.enter(a),v.down(a)},touchmove:function(a){this.scrolling||(this.shouldScroll(a)?(this.scrolling=!0,this.touchcancel(a)):(a.preventDefault(),this.processTouches(a,this.moveOverOut)))},moveOverOut:function(a){var b=a,c=U.get(b.pointerId);if(c){var d=c.out,e=c.outTarget;v.move(b),d&&e!==b.target&&(d.relatedTarget=b.target,b.relatedTarget=e,d.target=e,b.target?(v.leaveOut(d),v.enterOver(b)):(b.target=e,b.relatedTarget=null,this.cancelOut(b))),c.out=b,c.outTarget=b.target}},touchend:function(a){this.dedupSynthMouse(a),this.processTouches(a,this.upOut)},upOut:function(a){this.scrolling||(v.up(a),v.out(a),v.leave(a)),this.cleanUpPointer(a)},touchcancel:function(a){this.processTouches(a,this.cancelOut)},cancelOut:function(a){v.cancel(a),v.out(a),v.leave(a),this.cleanUpPointer(a)},cleanUpPointer:function(a){U["delete"](a.pointerId),this.removePrimaryPointer(a)},dedupSynthMouse:function(a){var b=Q.lastTouches,c=a.changedTouches[0];if(this.isPrimaryTouch(c)){var d={x:c.clientX,y:c.clientY};b.push(d);var e=function(a,b){var c=a.indexOf(b);c>-1&&a.splice(c,1)}.bind(null,b,d);setTimeout(e,V)}}};Y||(O=new E(Z.elementAdded,Z.elementRemoved,Z.elementChanged,Z));var $,_,aa=Z,ba=v.pointermap,ca=window.MSPointerEvent&&"number"==typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE,da={events:["MSPointerDown","MSPointerMove","MSPointerUp","MSPointerOut","MSPointerOver","MSPointerCancel","MSGotPointerCapture","MSLostPointerCapture"],register:function(a){v.listen(a,this.events)},unregister:function(a){v.unlisten(a,this.events)},POINTER_TYPES:["","unavailable","touch","pen","mouse"],prepareEvent:function(a){var b=a;return ca&&(b=v.cloneEvent(a),b.pointerType=this.POINTER_TYPES[a.pointerType]),b},cleanup:function(a){ba["delete"](a)},MSPointerDown:function(a){ba.set(a.pointerId,a);var b=this.prepareEvent(a);v.down(b)},MSPointerMove:function(a){var b=this.prepareEvent(a);v.move(b)},MSPointerUp:function(a){var b=this.prepareEvent(a);v.up(b),this.cleanup(a.pointerId)},MSPointerOut:function(a){var b=this.prepareEvent(a);v.leaveOut(b)},MSPointerOver:function(a){var b=this.prepareEvent(a);v.enterOver(b)},MSPointerCancel:function(a){var b=this.prepareEvent(a);v.cancel(b),this.cleanup(a.pointerId)},MSLostPointerCapture:function(a){var b=v.makeEvent("lostpointercapture",a);v.dispatchEvent(b)},MSGotPointerCapture:function(a){var b=v.makeEvent("gotpointercapture",a);v.dispatchEvent(b)}},ea=da,fa=window.navigator;fa.msPointerEnabled?($=function(a){i(a),this.msSetPointerCapture(a)},_=function(a){i(a),this.msReleasePointerCapture(a)}):($=function(a){i(a),v.setCapture(a,this)},_=function(a){i(a),v.releaseCapture(a,this)}),g(),h(),j();var ga={dispatcher:v,Installer:E,PointerEvent:m,PointerMap:p,targetFinding:w};return ga});

/***/ }
/******/ ])
});
;