From f7cb7c094a27c33cecf07dcd2e1bca6790c6d299 Mon Sep 17 00:00:00 2001 From: Gavin McDonald Date: Wed, 17 Feb 2016 10:32:14 -0500 Subject: [PATCH] drawing shapes on a canvas when the user clicks --- .gitignore | 9 + package.json | 17 + public/css/main.css | 278 ++++++++++++++++ public/index.html | 14 + public/js/main.js | 788 ++++++++++++++++++++++++++++++++++++++++++++ src/hex.js | 123 +++++++ src/main.js | 7 + src/onTap.js | 24 ++ src/point.js | 15 + src/sketch.js | 37 +++ src/tessellate.js | 62 ++++ src/tile.js | 11 + src/tileCircle.js | 26 ++ src/tileDiamond.js | 45 +++ src/tileHex.js | 97 ++++++ src/tileSquare.js | 43 +++ src/utils.js | 48 +++ webpack.config.js | 21 ++ 18 files changed, 1665 insertions(+) create mode 100644 .gitignore create mode 100644 package.json create mode 100644 public/css/main.css create mode 100644 public/index.html create mode 100644 public/js/main.js create mode 100644 src/hex.js create mode 100644 src/main.js create mode 100644 src/onTap.js create mode 100644 src/point.js create mode 100644 src/sketch.js create mode 100644 src/tessellate.js create mode 100644 src/tile.js create mode 100644 src/tileCircle.js create mode 100644 src/tileDiamond.js create mode 100644 src/tileHex.js create mode 100644 src/tileSquare.js create mode 100644 src/utils.js create mode 100644 webpack.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78e1398 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*~ +*.swp +*.pyc +*.pyo +.DS_Store + +node_modules/* +hexmine + diff --git a/package.json b/package.json new file mode 100644 index 0000000..c18e20b --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "tessellate", + "version": "0.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "BSD-2-Clause", + "devDependencies": { + "webpack": "~1.12.13", + "babel-loader": "~6.2.2", + "babel-core": "~6.5.2", + "babel-preset-es2015": "~6.5.0" + } +} diff --git a/public/css/main.css b/public/css/main.css new file mode 100644 index 0000000..600fae6 --- /dev/null +++ b/public/css/main.css @@ -0,0 +1,278 @@ +/*! HTML5 Boilerplate v5.2.0 | MIT License | https://html5boilerplate.com/ */ + +/* + * What follows is the result of much research on cross-browser styling. + * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, + * Kroc Camen, and the H5BP dev community and team. + */ + +/* ========================================================================== + Base styles: opinionated defaults + ========================================================================== */ + +html { + color: #222; + font-size: 1em; + line-height: 1.4; +} + +/* + * Remove text-shadow in selection highlight: + * https://twitter.com/miketaylr/status/12228805301 + * + * These selection rule sets have to be separate. + * Customize the background color to match your design. + */ + +::-moz-selection { + background: #b3d4fc; + text-shadow: none; +} + +::selection { + background: #b3d4fc; + text-shadow: none; +} + +/* + * A better looking default horizontal rule + */ + +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ccc; + margin: 1em 0; + padding: 0; +} + +/* + * Remove the gap between audio, canvas, iframes, + * images, videos and the bottom of their containers: + * https://github.com/h5bp/html5-boilerplate/issues/440 + */ + +audio, +canvas, +iframe, +img, +svg, +video { + vertical-align: middle; +} + +/* + * Remove default fieldset styles. + */ + +fieldset { + border: 0; + margin: 0; + padding: 0; +} + +/* + * Allow only vertical resizing of textareas. + */ + +textarea { + resize: vertical; +} + +/* ========================================================================== + Browser Upgrade Prompt + ========================================================================== */ + +.browserupgrade { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} + +/* ========================================================================== + Author's custom styles + ========================================================================== */ + +body { + margin: 0; + padding: 0; + width: 100vw; + height: 100vh; + overflow: hidden; +} + +#container { + width: 100vw; + height: 100vh; + overflow: hidden; +} + +/* ========================================================================== + Helper classes + ========================================================================== */ + +/* + * Hide visually and from screen readers: + */ + +.hidden { + display: none !important; +} + +/* + * Hide only visually, but have it available for screen readers: + * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility + */ + +.visuallyhidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +/* + * Extends the .visuallyhidden class to allow the element + * to be focusable when navigated to via the keyboard: + * https://www.drupal.org/node/897638 + */ + +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; +} + +/* + * Hide visually and from screen readers, but maintain layout + */ + +.invisible { + visibility: hidden; +} + +/* + * Clearfix: contain floats + * + * For modern browsers + * 1. The space content is one way to avoid an Opera bug when the + * `contenteditable` attribute is included anywhere else in the document. + * Otherwise it causes space to appear at the top and bottom of elements + * that receive the `clearfix` class. + * 2. The use of `table` rather than `block` is only necessary if using + * `:before` to contain the top-margins of child elements. + */ + +.clearfix:before, +.clearfix:after { + content: " "; /* 1 */ + display: table; /* 2 */ +} + +.clearfix:after { + clear: both; +} + +/* ========================================================================== + EXAMPLE Media Queries for Responsive Design. + These examples override the primary ('mobile first') styles. + Modify as content requires. + ========================================================================== */ + +@media only screen and (min-width: 35em) { + /* Style adjustments for viewports that meet the condition */ +} + +@media print, + (-webkit-min-device-pixel-ratio: 1.25), + (min-resolution: 1.25dppx), + (min-resolution: 120dpi) { + /* Style adjustments for high resolution devices */ +} + +/* ========================================================================== + Print styles. + Inlined to avoid the additional HTTP request: + http://www.phpied.com/delay-loading-your-print-css/ + ========================================================================== */ + +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; /* Black prints faster: + http://www.sanbeiji.com/archives/953 */ + box-shadow: none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + /* + * Don't show links that are fragment identifiers, + * or use the `javascript:` pseudo protocol + */ + + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + /* + * Printing Tables: + * http://css-discuss.incutio.com/wiki/Printing_Tables + */ + + thead { + display: table-header-group; + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..1987d8f --- /dev/null +++ b/public/index.html @@ -0,0 +1,14 @@ + + + + + + Tessellate + + + +
+ + + + diff --git a/public/js/main.js b/public/js/main.js new file mode 100644 index 0000000..1d4656f --- /dev/null +++ b/public/js/main.js @@ -0,0 +1,788 @@ +(function(e, a) { for(var i in a) e[i] = a[i]; }(this, /******/ (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__) { + + 'use strict'; + + var _tessellate = __webpack_require__(1); + + var _tessellate2 = _interopRequireDefault(_tessellate); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + var tessellate = new _tessellate2.default({ + element: '#container' + }); + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + var _utils = __webpack_require__(2); + + var _onTap = __webpack_require__(3); + + var _onTap2 = _interopRequireDefault(_onTap); + + var _point = __webpack_require__(4); + + var _point2 = _interopRequireDefault(_point); + + var _sketch = __webpack_require__(5); + + var _sketch2 = _interopRequireDefault(_sketch); + + var _tileCircle = __webpack_require__(6); + + var _tileCircle2 = _interopRequireDefault(_tileCircle); + + var _tileDiamond = __webpack_require__(8); + + var _tileDiamond2 = _interopRequireDefault(_tileDiamond); + + var _tileHex = __webpack_require__(9); + + var _tileHex2 = _interopRequireDefault(_tileHex); + + var _tileSquare = __webpack_require__(10); + + var _tileSquare2 = _interopRequireDefault(_tileSquare); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var Tessellate = function () { + function Tessellate(settings) { + var _this = this; + + _classCallCheck(this, Tessellate); + + ['draw'].map(function (method) { + _this[method] = _this[method].bind(_this); + }); + + this.element = document.querySelector(settings.element); + + this.sketch = new _sketch2.default({ + element: this.element, + draw: this.draw + }); + + this.map = []; + + this.onTap = new _onTap2.default({ + element: this.element, + click: function click(event) { + var x = event.pageX; + var y = event.pageY; + var tiles = ['circle', 'diamond', 'hex', 'square']; + var styles = ['filled', 'outline']; + var scale = (0, _utils.random)(10, 50); + + _this.map.push({ + tile: tiles[(0, _utils.random)(tiles.length - 1)], + style: styles[(0, _utils.random)(styles.length - 1)], + x: x, + y: y, + scale: scale, + pointy: (0, _utils.random)(1) ? true : false, + color: 'rgba(' + (0, _utils.random)(255) + ', ' + (0, _utils.random)(255) + ', ' + (0, _utils.random)(255) + ', 0.5)' + }); + } + }); + + this.circle = new _tileCircle2.default({ context: this.sketch.getContext() }); + this.diamond = new _tileDiamond2.default({ context: this.sketch.getContext() }); + this.hex = new _tileHex2.default({ context: this.sketch.getContext() }); + this.square = new _tileSquare2.default({ context: this.sketch.getContext() }); + } + + _createClass(Tessellate, [{ + key: 'draw', + value: function draw(context, now, lastTime) { + var _this2 = this; + + var canvas = context.canvas; + var width = canvas.width; + var height = canvas.height; + + context.clearRect(0, 0, width, height); + + this.map.forEach(function (cell) { + return _this2[cell.tile][cell.style](cell); + }); + } + }]); + + return Tessellate; + }(); + + exports.default = Tessellate; + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.throttleEvent = throttleEvent; + exports.clone = clone; + exports.extend = extend; + exports.hypotenuse = hypotenuse; + exports.random = random; + function throttleEvent(type, name, obj) { + obj = obj || window; + var running = false; + + var throttle = function throttle() { + if (!running) { + running = true; + + requestAnimationFrame(function () { + obj.dispatchEvent(new CustomEvent(name)); + running = false; + }); + } + }; + + obj.addEventListener(type, throttle); + } + + throttleEvent('resize', 'optimizedResize'); + + function clone(obj) { + return JSON.parse(JSON.stringify(obj)); + } + + function extend(obj, src) { + for (var key in src) { + if (src.hasOwnProperty(key)) obj[key] = src[key]; + } + + return obj; + } + + function hypotenuse(a, b) { + if (b == null) b = a; + + return Math.sqrt(a * a + b * b); + } + + function random(min, max) { + if (max == null) { + max = min; + min = 0; + } + + return min + Math.floor(Math.random() * (max - min + 1)); + } + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var OnTap = function () { + function OnTap(settings) { + var _this = this; + + _classCallCheck(this, OnTap); + + this.element = settings.element || document.body; + + this.callbacks = {}; + + this.events = ['click']; + + this.events.map(function (method) { + _this[method] = _this[method].bind(_this); + + if (typeof settings[method] === 'function') { + _this.element.addEventListener(method, _this[method]); + _this.callbacks[method] = settings[method]; + } + }); + } + + _createClass(OnTap, [{ + key: 'click', + value: function click(event) { + this.callbacks.click(event); + } + }]); + + return OnTap; + }(); + + exports.default = OnTap; + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var Point = function () { + function Point(newX, newY) { + _classCallCheck(this, Point); + + this.x = newX; + this.y = newY; + } + + _createClass(Point, [{ + key: "getX", + value: function getX() { + return this.x; + } + }, { + key: "getY", + value: function getY() { + return this.y; + } + }, { + key: "setX", + value: function setX(newX) { + this.x = newX; + } + }, { + key: "setY", + value: function setY(newY) { + this.y = newY; + } + }, { + key: "getPoint", + value: function getPoint() { + return { x: this.x, y: this.y }; + } + }]); + + return Point; + }(); + + exports.default = Point; + ; + +/***/ }, +/* 5 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + //import {throttleEvent} from './utils.js'; + + var Sketch = function () { + function Sketch(settings) { + var _this = this; + + _classCallCheck(this, Sketch); + + this.lastTime = null; + + ['getContext', 'onResize', 'render'].map(function (method) { + return _this[method] = _this[method].bind(_this); + }); + + this.draw = settings.draw || function () {}; // () => {}; + this.container = settings.element || document.body; + + window.addEventListener('optimizedResize', this.onResize); + window.addEventListener('resize', function () { + return console.log('*** resize ***'); + }); + + this.canvas = document.createElement('canvas'); + this.canvas.width = this.container.offsetWidth; + this.canvas.height = this.container.offsetHeight; + + this.context = this.canvas.getContext('2d'); + + this.container.appendChild(this.canvas); + + requestAnimationFrame(this.render); + } + + _createClass(Sketch, [{ + key: 'getContext', + value: function getContext() { + return this.context; + } + }, { + key: 'onResize', + value: function onResize(event) { + console.log('sketch - onResize', arguments); + } + }, { + key: 'render', + value: function render(now) { + this.draw(this.context, now, this.lastTime); + + this.lastTime = now; + requestAnimationFrame(this.render); + } + }]); + + return Sketch; + }(); + + exports.default = Sketch; + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + var _tile = __webpack_require__(7); + + var _tile2 = _interopRequireDefault(_tile); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + var RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)'; + + var TileCircle = function (_Tile) { + _inherits(TileCircle, _Tile); + + function TileCircle(settings) { + _classCallCheck(this, TileCircle); + + return _possibleConstructorReturn(this, Object.getPrototypeOf(TileCircle).call(this, settings)); + } + + _createClass(TileCircle, [{ + key: 'filled', + value: function filled(settings) { + this.context.beginPath(); + this.context.arc(settings.x, settings.y, settings.scale, 0, 2 * Math.PI, false); + this.context.fillStyle = settings.color || RGBA_DEFAULT; + this.context.fill(); + } + }, { + key: 'outline', + value: function outline(settings) { + this.context.beginPath(); + this.context.arc(settings.x, settings.y, settings.scale, 0, 2 * Math.PI, false); + this.context.lineWidth = settings.width ? settings.width : 1; + this.context.strokeStyle = settings.color || RGBA_DEFAULT; + this.context.stroke(); + } + }]); + + return TileCircle; + }(_tile2.default); + + exports.default = TileCircle; + +/***/ }, +/* 7 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var Tile = function () { + function Tile(settings) { + _classCallCheck(this, Tile); + + this.context = settings.context || null; + } + + _createClass(Tile, [{ + key: "setContext", + value: function setContext(newContext) { + this.context = newContext || this.context; + } + }]); + + return Tile; + }(); + + exports.default = Tile; + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + var _tile = __webpack_require__(7); + + var _tile2 = _interopRequireDefault(_tile); + + var _utils = __webpack_require__(2); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + var RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)'; + + var TileDiamond = function (_Tile) { + _inherits(TileDiamond, _Tile); + + function TileDiamond(settings) { + _classCallCheck(this, TileDiamond); + + return _possibleConstructorReturn(this, Object.getPrototypeOf(TileDiamond).call(this, settings)); + } + + _createClass(TileDiamond, [{ + key: 'filled', + value: function filled(settings) { + var x = settings.x; + var y = settings.y; + var scale = settings.scale; + + this.context.beginPath(); + this.context.moveTo(x + (0, _utils.hypotenuse)(scale), y); + this.context.lineTo(x, y - (0, _utils.hypotenuse)(scale)); + this.context.lineTo(x - (0, _utils.hypotenuse)(scale), y); + this.context.lineTo(x, y + (0, _utils.hypotenuse)(scale)); + + this.context.fillStyle = settings.color || RGBA_DEFAULT; + this.context.fill(); + } + }, { + key: 'outline', + value: function outline(settings) { + var x = settings.x; + var y = settings.y; + var scale = settings.scale; + + this.context.beginPath(); + this.context.moveTo(x + (0, _utils.hypotenuse)(scale), y); + this.context.lineTo(x, y - (0, _utils.hypotenuse)(scale)); + this.context.lineTo(x - (0, _utils.hypotenuse)(scale), y); + this.context.lineTo(x, y + (0, _utils.hypotenuse)(scale)); + this.context.closePath(); + + this.context.lineWidth = settings.width ? settings.width : 1; + this.context.strokeStyle = settings.color || RGBA_DEFAULT; + this.context.stroke(); + } + }]); + + return TileDiamond; + }(_tile2.default); + + exports.default = TileDiamond; + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + var _tile = __webpack_require__(7); + + var _tile2 = _interopRequireDefault(_tile); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + var RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)'; + + var TileHex = function (_Tile) { + _inherits(TileHex, _Tile); + + function TileHex(settings) { + _classCallCheck(this, TileHex); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(TileHex).call(this, settings)); + + _this.sqrt3 = Math.sqrt(3); + + _this.hexSides = 6; + _this.pointyTopCornerX = []; + _this.pointyTopCornerY = []; + _this.flatTopCornerX = []; + _this.flatTopCornerY = []; + + // dice dots, 0,0 is center + // TODO: if pointy: [0].concat(flatTop), if flat: [0].concat(pointy) + // this.hexDotX = [0]; + // this.hexDotY = [0]; + + _this.hexSlices = 24; + _this.hexSliceX = [0]; + _this.hexSliceY = [0]; + + for (var hexSlice = 0; hexSlice < _this.hexSlices; hexSlice++) { + _this.hexSliceX[hexSlice] = parseInt(Math.cos(hexSlice / _this.hexSlices * _this.hexSides * (2 * Math.PI) / _this.hexSides) * 1000) / 1000; + _this.hexSliceY[hexSlice] = parseInt(Math.sin(hexSlice / _this.hexSlices * _this.hexSides * (2 * Math.PI) / _this.hexSides) * 1000) / 1000; + + if ((hexSlice - 2) % 4 === 0) { + var cur = (hexSlice - 2) / 4; + _this.pointyTopCornerX[cur] = _this.hexSliceX[hexSlice]; + _this.pointyTopCornerY[cur] = _this.hexSliceY[hexSlice]; + } + + if (hexSlice % 4 === 0) { + var cur = hexSlice / 4; + _this.flatTopCornerX[cur] = _this.hexSliceX[hexSlice]; + _this.flatTopCornerY[cur] = _this.hexSliceY[hexSlice]; + } + + // if (((hexSlice-2) % 4) === 0) { + // let cur = (hexSlice-2) / 4; + // this.hexCornerX[cur] = this.hexSliceX[hexSlice]; + // this.hexCornerY[cur] = this.hexSliceY[hexSlice]; + // } + // + // if ((hexSlice % 4) === 0) { + // let cur = hexSlice / 4; + // this.hexDotX[cur+1] = this.hexSliceX[hexSlice]; + // this.hexDotY[cur+1] = this.hexSliceY[hexSlice]; + // } + } + return _this; + } + + _createClass(TileHex, [{ + key: 'outline', + value: function outline(settings) { + var x = settings.x; + var y = settings.y; + var scale = settings.scale; + var hexCornerX = settings.pointy ? this.pointyTopCornerX : this.flatTopCornerX; + var hexCornerY = settings.pointy ? this.pointyTopCornerY : this.flatTopCornerY; + + this.context.beginPath(); + this.context.moveTo(x + scale * hexCornerX[0], y + scale * hexCornerY[0]); + this.context.lineTo(x + scale * hexCornerX[1], y + scale * hexCornerY[1]); + this.context.lineTo(x + scale * hexCornerX[2], y + scale * hexCornerY[2]); + this.context.lineTo(x + scale * hexCornerX[3], y + scale * hexCornerY[3]); + this.context.lineTo(x + scale * hexCornerX[4], y + scale * hexCornerY[4]); + this.context.lineTo(x + scale * hexCornerX[5], y + scale * hexCornerY[5]); + this.context.closePath(); + + this.context.lineWidth = settings.width ? settings.width : 1; + this.context.strokeStyle = settings.color || RGBA_DEFAULT; + this.context.stroke(); + } + }, { + key: 'filled', + value: function filled(settings) { + var x = settings.x; + var y = settings.y; + var scale = settings.scale; + var hexCornerX = settings.pointy ? this.pointyTopCornerX : this.flatTopCornerX; + var hexCornerY = settings.pointy ? this.pointyTopCornerY : this.flatTopCornerY; + + this.context.beginPath(); + this.context.moveTo(x + scale * hexCornerX[0], y + scale * hexCornerY[0]); + this.context.lineTo(x + scale * hexCornerX[1], y + scale * hexCornerY[1]); + this.context.lineTo(x + scale * hexCornerX[2], y + scale * hexCornerY[2]); + this.context.lineTo(x + scale * hexCornerX[3], y + scale * hexCornerY[3]); + this.context.lineTo(x + scale * hexCornerX[4], y + scale * hexCornerY[4]); + this.context.lineTo(x + scale * hexCornerX[5], y + scale * hexCornerY[5]); + + this.context.fillStyle = settings.color || RGBA_DEFAULT; + this.context.fill(); + } + }]); + + return TileHex; + }(_tile2.default); + + exports.default = TileHex; + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + var _tile = __webpack_require__(7); + + var _tile2 = _interopRequireDefault(_tile); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + var RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)'; + + var TileSquare = function (_Tile) { + _inherits(TileSquare, _Tile); + + function TileSquare(settings) { + _classCallCheck(this, TileSquare); + + return _possibleConstructorReturn(this, Object.getPrototypeOf(TileSquare).call(this, settings)); + } + + _createClass(TileSquare, [{ + key: 'filled', + value: function filled(settings) { + var x = settings.x; + var y = settings.y; + var scale = settings.scale; + + this.context.beginPath(); + this.context.moveTo(x + scale, y + scale); + this.context.lineTo(x + scale, y - scale); + this.context.lineTo(x - scale, y - scale); + this.context.lineTo(x - scale, y + scale); + + this.context.fillStyle = settings.color || RGBA_DEFAULT; + this.context.fill(); + } + }, { + key: 'outline', + value: function outline(settings) { + var x = settings.x; + var y = settings.y; + var scale = settings.scale; + + this.context.beginPath(); + this.context.moveTo(x + scale, y + scale); + this.context.lineTo(x + scale, y - scale); + this.context.lineTo(x - scale, y - scale); + this.context.lineTo(x - scale, y + scale); + this.context.closePath(); + + this.context.lineWidth = settings.width ? settings.width : 1; + this.context.strokeStyle = settings.color || RGBA_DEFAULT; + this.context.stroke(); + } + }]); + + return TileSquare; + }(_tile2.default); + + exports.default = TileSquare; + +/***/ } +/******/ ]))); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgMjY2ODliNTdjODNkNDNjMTQxMDciLCJ3ZWJwYWNrOi8vLy4vc3JjL21haW4uanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3Rlc3NlbGxhdGUuanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3V0aWxzLmpzIiwid2VicGFjazovLy8uL3NyYy9vblRhcC5qcyIsIndlYnBhY2s6Ly8vLi9zcmMvcG9pbnQuanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3NrZXRjaC5qcyIsIndlYnBhY2s6Ly8vLi9zcmMvdGlsZUNpcmNsZS5qcyIsIndlYnBhY2s6Ly8vLi9zcmMvdGlsZS5qcyIsIndlYnBhY2s6Ly8vLi9zcmMvdGlsZURpYW1vbmQuanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3RpbGVIZXguanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3RpbGVTcXVhcmUuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLHVCQUFlO0FBQ2Y7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7OztBQ25DQSxLQUFJLGFBQWEseUJBQWU7QUFDL0IsV0FBUyxZQUFUO0VBRGdCLENBQWIsQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0tDU2lCO0FBQ3BCLFdBRG9CLFVBQ3BCLENBQVksUUFBWixFQUFzQjs7O3lCQURGLFlBQ0U7O0FBQ3JCLElBQUMsTUFBRCxFQUFTLEdBQVQsQ0FBYSxrQkFBVTtBQUFDLFVBQUssTUFBTCxJQUFlLE1BQUssTUFBTCxFQUFhLElBQWIsT0FBZixDQUFEO0lBQVYsQ0FBYixDQURxQjs7QUFHckIsUUFBSyxPQUFMLEdBQWUsU0FBUyxhQUFULENBQXVCLFNBQVMs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LFNBQUssT0FBTCxDQUFhLElBQWIsR0FaZ0I7Ozs7MkJBZVQsVUFBVTtBQUNqQixRQUFJLElBQUksU0FBUyxDQUFULENBRFM7QUFFakIsUUFBSSxJQUFJLFNBQVMsQ0FBVCxDQUZTO0FBR2pCLFFBQUksUUFBUSxTQUFTLEtBQVQsQ0FISzs7QUFLakIsU0FBSyxPQUFMLENBQWEsU0FBYixHQUxpQjtBQU1qQixTQUFLLE9BQUwsQ0FBYSxNQUFiLENBQW9CLElBQUUsS0FBRixFQUFTLElBQUUsS0FBRixDQUE3QixDQU5pQjtBQU9qQixTQUFLLE9BQUwsQ0FBYSxNQUFiLENBQW9CLElBQUUsS0FBRixFQUFTLElBQUUsS0FBRixDQUE3QixDQVBpQjtBQVFqQixTQUFLLE9BQUwsQ0FBYSxNQUFiLENBQW9CLElBQUUsS0FBRixFQUFTLElBQUUsS0FBRixDQUE3QixDQVJpQjtBQVNqQixTQUFLLE9BQUwsQ0FBYSxNQUFiLENBQW9CLElBQUUsS0FBRixFQUFTLElBQUUsS0FBRixDQUE3QixDQVRpQjtBQVVqQixTQUFLLE9BQUwsQ0FBYSxTQUFiLEdBVmlCOztBQVlqQixTQUFLLE9BQUwsQ0FBYSxTQUFiLEdBQXlCLFNBQVMsS0FBVCxHQUFpQixTQUFTLEtBQVQsR0FBaUIsQ0FBbEMsQ0FaUjtBQWFqQixTQUFLLE9BQUwsQ0FBYSxXQUFiLEdBQTJCLFNBQVMsS0FBVCxJQUFrQixZQUFsQixDQWJWO0FBY2pCLFNBQUssT0FBTCxDQUFhLE1BQWIsR0FkaUI7Ozs7U0FwQkUiLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKVxuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuXG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRleHBvcnRzOiB7fSxcbiBcdFx0XHRpZDogbW9kdWxlSWQsXG4gXHRcdFx0bG9hZGVkOiBmYWxzZVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sb2FkZWQgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKDApO1xuXG5cblxuLyoqIFdFQlBBQ0sgRk9PVEVSICoqXG4gKiogd2VicGFjay9ib290c3RyYXAgMjY2ODliNTdjODNkNDNjMTQxMDdcbiAqKi8iLCJcbmltcG9ydCBUZXNzZWxsYXRlIGZyb20gJy4vdGVzc2VsbGF0ZS5qcyc7XG5cbnZhciB0ZXNzZWxsYXRlID0gbmV3IFRlc3NlbGxhdGUoe1xuXHRlbGVtZW50OiAnI2NvbnRhaW5lcidcbn0pO1xuXG5cblxuXG4vKiogV0VCUEFDSyBGT09URVIgKipcbiAqKiAuL3NyYy9tYWluLmpzXG4gKiovIiwiXG5pbXBvcnQge3JhbmRvbX0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmltcG9ydCBPblRhcCBmcm9tICcuL29uVGFwLmpzJztcbmltcG9ydCBQb2ludCBmcm9tICcuL3BvaW50LmpzJztcbmltcG9ydCBTa2V0Y2ggZnJvbSAnLi9za2V0Y2guanMnO1xuXG5pbXBvcnQgVGlsZUNpcmNsZSBmcm9tICcuL3RpbGVDaXJjbGUuanMnO1xuaW1wb3J0IFRpbGVEaWFtb25kIGZyb20gJy4vdGlsZURpYW1vbmQuanMnO1xuaW1wb3J0IFRpbGVIZXggICAgZnJvbSAnLi90aWxlSGV4LmpzJztcbmltcG9ydCBUaWxlU3F1YXJlIGZyb20gJy4vdGlsZVNxdWFyZS5qcyc7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRlc3NlbGxhdGUge1xuXHRjb25zdHJ1Y3RvcihzZXR0aW5ncykge1xuXHRcdFsnZHJhdyddLm1hcChtZXRob2QgPT4ge3RoaXNbbWV0aG9kXSA9IHRoaXNbbWV0aG9kXS5iaW5kKHRoaXMpfSk7XG5cblx0XHR0aGlzLmVsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKHNldHRpbmdzLmVsZW1lbnQpO1xuXG5cdFx0dGhpcy5za2V0Y2ggPSBuZXcgU2tldGNoKHtcblx0XHRcdGVsZW1lbnQ6IHRoaXMuZWxlbWVudCxcblx0XHRcdGRyYXc6IHRoaXMuZHJhd1xuXHRcdH0pO1xuXG5cdFx0dGhpcy5tYXAgPSBbXTtcblxuXHRcdHRoaXMub25UYXAgPSBuZXcgT25UYXAoe1xuXHRcdFx0ZWxlbWVudDogdGhpcy5lbGVtZW50LFxuXHRcdFx0Y2xpY2s6IChldmVudCkgPT4ge1xuXHRcdFx0XHRsZXQgeCA9IGV2ZW50LnBhZ2VYO1xuXHRcdFx0XHRsZXQgeSA9IGV2ZW50LnBhZ2VZO1xuXHRcdFx0XHRsZXQgdGlsZXMgPSBbJ2NpcmNsZScsICdkaWFtb25kJywgJ2hleCcsICdzcXVhcmUnXTtcblx0XHRcdFx0bGV0IHN0eWxlcyA9IFsnZmlsbGVkJywgJ291dGxpbmUnXTtcblx0XHRcdFx0bGV0IHNjYWxlID0gcmFuZG9tKDEwLCA1MCk7XG5cblx0XHRcdFx0dGhpcy5tYXAucHVzaCh7XG5cdFx0XHRcdFx0dGlsZTogdGlsZXNbcmFuZG9tKHRpbGVzLmxlbmd0aC0xKV0sXG5cdFx0XHRcdFx0c3R5bGU6IHN0eWxlc1tyYW5kb20oc3R5bGVzLmxlbmd0aC0xKV0sXG5cdFx0XHRcdFx0eCxcblx0XHRcdFx0XHR5LFxuXHRcdFx0XHRcdHNjYWxlLFxuXHRcdFx0XHRcdHBvaW50eTogcmFuZG9tKDEpID8gdHJ1ZSA6IGZhbHNlLFxuXHRcdFx0XHRcdGNvbG9yOiBgcmdiYSgkeyByYW5kb20oMjU1KSB9LCAkeyByYW5kb20oMjU1KSB9LCAkeyByYW5kb20oMjU1KSB9LCAwLjUpYFxuXHRcdFx0XHR9KTtcblx0XHRcdH1cblx0XHR9KTtcblxuXHRcdHRoaXMuY2lyY2xlICA9IG5ldyBUaWxlQ2lyY2xlKHtjb250ZXh0OiB0aGlzLnNrZXRjaC5nZXRDb250ZXh0KCl9KTtcblx0XHR0aGlzLmRpYW1vbmQgPSBuZXcgVGlsZURpYW1vbmQoe2NvbnRleHQ6IHRoaXMuc2tldGNoLmdldENvbnRleHQoKX0pO1xuXHRcdHRoaXMuaGV4ICAgICA9IG5ldyBUaWxlSGV4KHtjb250ZXh0OiB0aGlzLnNrZXRjaC5nZXRDb250ZXh0KCl9KTtcblx0XHR0aGlzLnNxdWFyZSAgPSBuZXcgVGlsZVNxdWFyZSh7Y29udGV4dDogdGhpcy5za2V0Y2guZ2V0Q29udGV4dCgpfSk7XG5cdH1cblxuXHRkcmF3KGNvbnRleHQsIG5vdywgbGFzdFRpbWUpIHtcblx0XHRsZXQgY2FudmFzID0gY29udGV4dC5jYW52YXM7XG5cdFx0bGV0IHdpZHRoID0gY2FudmFzLndpZHRoO1xuXHRcdGxldCBoZWlnaHQgPSBjYW52YXMuaGVpZ2h0O1xuXG5cdFx0Y29udGV4dC5jbGVhclJlY3QoMCwgMCwgd2lkdGgsIGhlaWdodCk7XG5cblx0XHR0aGlzLm1hcC5mb3JFYWNoKGNlbGwgPT4gdGhpc1tjZWxsLnRpbGVdW2NlbGwuc3R5bGVdKGNlbGwpKTtcblx0fVxufVxuXG5cblxuLyoqIFdFQlBBQ0sgRk9PVEVSICoqXG4gKiogLi9zcmMvdGVzc2VsbGF0ZS5qc1xuICoqLyIsIlxuZXhwb3J0IGZ1bmN0aW9uIHRocm90dGxlRXZlbnQodHlwZSwgbmFtZSwgb2JqKSB7XG5cdG9iaiA9IG9iaiB8fCB3aW5kb3c7XG5cdGxldCBydW5uaW5nID0gZmFsc2U7XG5cblx0bGV0IHRocm90dGxlID0gKCkgPT4ge1xuXHRcdGlmICghcnVubmluZykge1xuXHRcdFx0cnVubmluZyA9IHRydWU7XG5cblx0XHRcdHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiB7XG5cdFx0XHRcdG9iai5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChuYW1lKSk7XG5cdFx0XHRcdHJ1bm5pbmcgPSBmYWxzZTtcblx0XHRcdH0pO1xuXHRcdH1cblx0fVxuXG5cdG9iai5hZGRFdmVudExpc3RlbmVyKHR5cGUsIHRocm90dGxlKTtcbn1cblxudGhyb3R0bGVFdmVudCgncmVzaXplJywgJ29wdGltaXplZFJlc2l6ZScpO1xuXG5leHBvcnQgZnVuY3Rpb24gY2xvbmUob2JqKSB7XG5cdHJldHVybiBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KG9iaikpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZXh0ZW5kKG9iaiwgc3JjKSB7XG5cdGZvciAobGV0IGtleSBpbiBzcmMpIHtcblx0XHRpZiAoc3JjLmhhc093blByb3BlcnR5KGtleSkpIG9ialtrZXldID0gc3JjW2tleV07XG5cdH1cblxuXHRyZXR1cm4gb2JqO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaHlwb3RlbnVzZShhLCBiKSB7XG5cdGlmIChiID09IG51bGwpIGIgPSBhO1xuXG5cdHJldHVybiBNYXRoLnNxcnQoYSphICsgYipiKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJhbmRvbShtaW4sIG1heCkge1xuXHRpZiAobWF4ID09IG51bGwpIHtcblx0XHRtYXggPSBtaW47XG5cdFx0bWluID0gMDtcblx0fVxuXG5cdHJldHVybiBtaW4gKyBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAobWF4IC0gbWluICsgMSkpO1xufVxuXG5cblxuXG4vKiogV0VCUEFDSyBGT09URVIgKipcbiAqKiAuL3NyYy91dGlscy5qc1xuICoqLyIsIlxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgT25UYXAge1xuXHRjb25zdHJ1Y3RvcihzZXR0aW5ncykge1xuXHRcdHRoaXMuZWxlbWVudCA9IHNldHRpbmdzLmVsZW1lbnQgfHwgZG9jdW1lbnQuYm9keTtcblxuXHRcdHRoaXMuY2FsbGJhY2tzID0ge307XG5cblx0XHR0aGlzLmV2ZW50cyA9IFsnY2xpY2snXTtcblxuXHRcdHRoaXMuZXZlbnRzLm1hcChtZXRob2QgPT4ge1xuXHRcdFx0dGhpc1ttZXRob2RdID0gdGhpc1ttZXRob2RdLmJpbmQodGhpcyk7XG5cblx0XHRcdGlmICh0eXBlb2Ygc2V0dGluZ3NbbWV0aG9kXSA9PT0gJ2Z1bmN0aW9uJykge1xuXHRcdFx0XHR0aGlzLmVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihtZXRob2QsIHRoaXNbbWV0aG9kXSk7XG5cdFx0XHRcdHRoaXMuY2FsbGJhY2tzW21ldGhvZF0gPSBzZXR0aW5nc1ttZXRob2RdO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9XG5cblx0Y2xpY2soZXZlbnQpIHtcblx0XHR0aGlzLmNhbGxiYWNrcy5jbGljayhldmVudCk7XG5cdH1cbn1cblxuXG5cblxuLyoqIFdFQlBBQ0sgRk9PVEVSICoqXG4gKiogLi9zcmMvb25UYXAuanNcbiAqKi8iLCJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFBvaW50IHtcblx0Y29uc3RydWN0b3IobmV3WCwgbmV3WSkge1xuXHRcdHRoaXMueCA9IG5ld1g7XG5cdFx0dGhpcy55ID0gbmV3WTtcblx0fVxuXG5cdGdldFgoKSB7IHJldHVybiB0aGlzLng7IH1cblx0Z2V0WSgpIHsgcmV0dXJuIHRoaXMueTsgfVxuXG5cdHNldFgobmV3WCkgeyB0aGlzLnggPSBuZXdYOyB9XG5cdHNldFkobmV3WSkgeyB0aGlzLnkgPSBuZXdZOyB9XG5cblx0Z2V0UG9pbnQoKSB7IHJldHVybiB7eDogdGhpcy54LCB5OiB0aGlzLnl9OyB9XG59O1xuXG5cblxuLyoqIFdFQlBBQ0sgRk9PVEVSICoqXG4gKiogLi9zcmMvcG9pbnQuanNcbiAqKi8iLCJcbi8vaW1wb3J0IHt0aHJvdHRsZUV2ZW50fSBmcm9tICcuL3V0aWxzLmpzJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU2tldGNoIHtcblx0Y29uc3RydWN0b3Ioc2V0dGluZ3MpIHtcblx0XHR0aGlzLmxhc3RUaW1lID0gbnVsbDtcblxuXHRcdFsnZ2V0Q29udGV4dCcsICdvblJlc2l6ZScsICdyZW5kZXInXS5tYXAobWV0aG9kID0+IHRoaXNbbWV0aG9kXSA9IHRoaXNbbWV0aG9kXS5iaW5kKHRoaXMpKTtcblxuXHRcdHRoaXMuZHJhdyA9IHNldHRpbmdzLmRyYXcgfHwgZnVuY3Rpb24oKSB7fTsgLy8gKCkgPT4ge307XG5cdFx0dGhpcy5jb250YWluZXIgPSBzZXR0aW5ncy5lbGVtZW50IHx8IGRvY3VtZW50LmJvZHk7XG5cblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignb3B0aW1pemVkUmVzaXplJywgdGhpcy5vblJlc2l6ZSk7XG5cdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IGNvbnNvbGUubG9nKCcqKiogcmVzaXplICoqKicpKTtcblxuXHRcdHRoaXMuY2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG5cdFx0dGhpcy5jYW52YXMud2lkdGggPSB0aGlzLmNvbnRhaW5lci5vZmZzZXRXaWR0aDtcblx0XHR0aGlzLmNhbnZhcy5oZWlnaHQgPSB0aGlzLmNvbnRhaW5lci5vZmZzZXRIZWlnaHQ7XG5cblx0XHR0aGlzLmNvbnRleHQgPSB0aGlzLmNhbnZhcy5nZXRDb250ZXh0KCcyZCcpO1xuXG5cdFx0dGhpcy5jb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy5jYW52YXMpO1xuXG5cdFx0cmVxdWVzdEFuaW1hdGlvbkZyYW1lKHRoaXMucmVuZGVyKTtcblx0fVxuXG5cdGdldENvbnRleHQoKSB7IHJldHVybiB0aGlzLmNvbnRleHQ7IH1cblxuXHRvblJlc2l6ZShldmVudCkge1xuXHRcdGNvbnNvbGUubG9nKCdza2V0Y2ggLSBvblJlc2l6ZScsIGFyZ3VtZW50cyk7XG5cdH1cblxuXHRyZW5kZXIobm93KSB7XG5cdFx0dGhpcy5kcmF3KHRoaXMuY29udGV4dCwgbm93LCB0aGlzLmxhc3RUaW1lKTtcblxuXHRcdHRoaXMubGFzdFRpbWUgPSBub3c7XG5cdFx0cmVxdWVzdEFuaW1hdGlvbkZyYW1lKHRoaXMucmVuZGVyKTtcblx0fVxufVxuXG5cblxuXG4vKiogV0VCUEFDSyBGT09URVIgKipcbiAqKiAuL3NyYy9za2V0Y2guanNcbiAqKi8iLCJcbmltcG9ydCBUaWxlIGZyb20gJy4vdGlsZS5qcyc7XG5cbmxldCBSR0JBX0RFRkFVTFQgPSAncmdiYSgwLCAwLCAwLCAwLjUpJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgVGlsZUNpcmNsZSBleHRlbmRzIFRpbGUge1xuXHRjb25zdHJ1Y3RvcihzZXR0aW5ncykge1xuXHRcdHN1cGVyKHNldHRpbmdzKTtcblx0fVxuXG5cdGZpbGxlZChzZXR0aW5ncykge1xuXHRcdHRoaXMuY29udGV4dC5iZWdpblBhdGgoKTtcblx0XHR0aGlzLmNvbnRleHQuYXJjKHNldHRpbmdzLngsIHNldHRpbmdzLnksIHNldHRpbmdzLnNjYWxlLCAwLCAyKk1hdGguUEksIGZhbHNlKTtcblx0XHR0aGlzLmNvbnRleHQuZmlsbFN0eWxlID0gc2V0dGluZ3MuY29sb3IgfHwgUkdCQV9ERUZBVUxUO1xuXHRcdHRoaXMuY29udGV4dC5maWxsKCk7XG5cdH1cblxuXHRvdXRsaW5lKHNldHRpbmdzKSB7XG5cdFx0dGhpcy5jb250ZXh0LmJlZ2luUGF0aCgpO1xuXHRcdHRoaXMuY29udGV4dC5hcmMoc2V0dGluZ3MueCwgc2V0dGluZ3MueSwgc2V0dGluZ3Muc2NhbGUsIDAsIDIqTWF0aC5QSSwgZmFsc2UpO1xuXHRcdHRoaXMuY29udGV4dC5saW5lV2lkdGggPSBzZXR0aW5ncy53aWR0aCA/IHNldHRpbmdzLndpZHRoIDogMTtcblx0XHR0aGlzLmNvbnRleHQuc3Ryb2tlU3R5bGUgPSBzZXR0aW5ncy5jb2xvciB8fCBSR0JBX0RFRkFVTFQ7XG5cdFx0dGhpcy5jb250ZXh0LnN0cm9rZSgpO1xuXHR9XG59XG5cblxuXG5cbi8qKiBXRUJQQUNLIEZPT1RFUiAqKlxuICoqIC4vc3JjL3RpbGVDaXJjbGUuanNcbiAqKi8iLCJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRpbGUge1xuXHRjb25zdHJ1Y3RvcihzZXR0aW5ncykge1xuXHRcdHRoaXMuY29udGV4dCA9IHNldHRpbmdzLmNvbnRleHQgfHwgbnVsbDtcblx0fVxuXG5cdHNldENvbnRleHQobmV3Q29udGV4dCkge1xuXHRcdHRoaXMuY29udGV4dCA9IG5ld0NvbnRleHQgfHwgdGhpcy5jb250ZXh0O1xuXHR9XG59XG5cblxuXG5cbi8qKiBXRUJQQUNLIEZPT1RFUiAqKlxuICoqIC4vc3JjL3RpbGUuanNcbiAqKi8iLCJcbmltcG9ydCBUaWxlIGZyb20gJy4vdGlsZS5qcyc7XG5cbmltcG9ydCB7aHlwb3RlbnVzZX0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmxldCBSR0JBX0RFRkFVTFQgPSAncmdiYSgwLCAwLCAwLCAwLjUpJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgVGlsZURpYW1vbmQgZXh0ZW5kcyBUaWxlIHtcblx0Y29uc3RydWN0b3Ioc2V0dGluZ3MpIHtcblx0XHRzdXBlcihzZXR0aW5ncyk7XG5cdH1cblxuXHRmaWxsZWQoc2V0dGluZ3MpIHtcblx0XHRsZXQgeCA9IHNldHRpbmdzLng7XG5cdFx0bGV0IHkgPSBzZXR0aW5ncy55O1xuXHRcdGxldCBzY2FsZSA9IHNldHRpbmdzLnNjYWxlO1xuXG5cdFx0dGhpcy5jb250ZXh0LmJlZ2luUGF0aCgpO1xuXHRcdHRoaXMuY29udGV4dC5tb3ZlVG8oeCtoeXBvdGVudXNlKHNjYWxlKSwgeSk7XG5cdFx0dGhpcy5jb250ZXh0LmxpbmVUbyh4LCB5LWh5cG90ZW51c2Uoc2NhbGUpKTtcblx0XHR0aGlzLmNvbnRleHQubGluZVRvKHgtaHlwb3RlbnVzZShzY2FsZSksIHkpO1xuXHRcdHRoaXMuY29udGV4dC5saW5lVG8oeCwgeStoeXBvdGVudXNlKHNjYWxlKSk7XG5cblx0XHR0aGlzLmNvbnRleHQuZmlsbFN0eWxlID0gc2V0dGluZ3MuY29sb3IgfHwgUkdCQV9ERUZBVUxUO1xuXHRcdHRoaXMuY29udGV4dC5maWxsKCk7XG5cdH1cblxuXHRvdXRsaW5lKHNldHRpbmdzKSB7XG5cdFx0bGV0IHggPSBzZXR0aW5ncy54O1xuXHRcdGxldCB5ID0gc2V0dGluZ3MueTtcblx0XHRsZXQgc2NhbGUgPSBzZXR0aW5ncy5zY2FsZTtcblxuXHRcdHRoaXMuY29udGV4dC5iZWdpblBhdGgoKTtcblx0XHR0aGlzLmNvbnRleHQubW92ZVRvKHgraHlwb3RlbnVzZShzY2FsZSksIHkpO1xuXHRcdHRoaXMuY29udGV4dC5saW5lVG8oeCwgeS1oeXBvdGVudXNlKHNjYWxlKSk7XG5cdFx0dGhpcy5jb250ZXh0LmxpbmVUbyh4LWh5cG90ZW51c2Uoc2NhbGUpLCB5KTtcblx0XHR0aGlzLmNvbnRleHQubGluZVRvKHgsIHkraHlwb3RlbnVzZShzY2FsZSkpO1xuXHRcdHRoaXMuY29udGV4dC5jbG9zZVBhdGgoKTtcblxuXHRcdHRoaXMuY29udGV4dC5saW5lV2lkdGggPSBzZXR0aW5ncy53aWR0aCA/IHNldHRpbmdzLndpZHRoIDogMTtcblx0XHR0aGlzLmNvbnRleHQuc3Ryb2tlU3R5bGUgPSBzZXR0aW5ncy5jb2xvciB8fCBSR0JBX0RFRkFVTFQ7XG5cdFx0dGhpcy5jb250ZXh0LnN0cm9rZSgpO1xuXHR9XG59XG5cblxuXG5cbi8qKiBXRUJQQUNLIEZPT1RFUiAqKlxuICoqIC4vc3JjL3RpbGVEaWFtb25kLmpzXG4gKiovIiwiXG5pbXBvcnQgVGlsZSBmcm9tICcuL3RpbGUuanMnO1xuXG5sZXQgUkdCQV9ERUZBVUxUID0gJ3JnYmEoMCwgMCwgMCwgMC41KSc7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRpbGVIZXggZXh0ZW5kcyBUaWxlIHtcblx0Y29uc3RydWN0b3Ioc2V0dGluZ3MpIHtcblx0XHRzdXBlcihzZXR0aW5ncyk7XG5cblx0XHR0aGlzLnNxcnQzID0gTWF0aC5zcXJ0KDMpO1xuXG5cdFx0dGhpcy5oZXhTaWRlcyA9IDY7XG5cdFx0dGhpcy5wb2ludHlUb3BDb3JuZXJYID0gW107XG5cdFx0dGhpcy5wb2ludHlUb3BDb3JuZXJZID0gW107XG5cdFx0dGhpcy5mbGF0VG9wQ29ybmVyWCA9IFtdO1xuXHRcdHRoaXMuZmxhdFRvcENvcm5lclkgPSBbXTtcblxuXHRcdC8vIGRpY2UgZG90cywgMCwwIGlzIGNlbnRlclxuXHRcdC8vIFRPRE86IGlmIHBvaW50eTogWzBdLmNvbmNhdChmbGF0VG9wKSwgaWYgZmxhdDogWzBdLmNvbmNhdChwb2ludHkpXG4vL1x0XHR0aGlzLmhleERvdFggPSBbMF07XG4vL1x0XHR0aGlzLmhleERvdFkgPSBbMF07XG5cblx0XHR0aGlzLmhleFNsaWNlcyA9IDI0O1xuXHRcdHRoaXMuaGV4U2xpY2VYID0gWzBdO1xuXHRcdHRoaXMuaGV4U2xpY2VZID0gWzBdO1xuXG5cdFx0Zm9yIChsZXQgaGV4U2xpY2UgPSAwOyBoZXhTbGljZSA8IHRoaXMuaGV4U2xpY2VzOyBoZXhTbGljZSsrKSB7XG5cdFx0XHR0aGlzLmhleFNsaWNlWFtoZXhTbGljZV0gPSBwYXJzZUludChNYXRoLmNvcygoKGhleFNsaWNlL3RoaXMuaGV4U2xpY2VzKSp0aGlzLmhleFNpZGVzKSAqICgyICogTWF0aC5QSSkgLyB0aGlzLmhleFNpZGVzKSAqIDEwMDApIC8gMTAwMDtcblx0XHRcdHRoaXMuaGV4U2xpY2VZW2hleFNsaWNlXSA9IHBhcnNlSW50KE1hdGguc2luKCgoaGV4U2xpY2UvdGhpcy5oZXhTbGljZXMpKnRoaXMuaGV4U2lkZXMpICogKDIgKiBNYXRoLlBJKSAvIHRoaXMuaGV4U2lkZXMpICogMTAwMCkgLyAxMDAwO1xuXG5cdFx0XHRpZiAoKChoZXhTbGljZS0yKSAlIDQpID09PSAwKSB7XG5cdFx0XHRcdGxldCBjdXIgPSAoaGV4U2xpY2UtMikgLyA0O1xuXHRcdFx0XHR0aGlzLnBvaW50eVRvcENvcm5lclhbY3VyXSA9IHRoaXMuaGV4U2xpY2VYW2hleFNsaWNlXTtcblx0XHRcdFx0dGhpcy5wb2ludHlUb3BDb3JuZXJZW2N1cl0gPSB0aGlzLmhleFNsaWNlWVtoZXhTbGljZV07XG5cdFx0XHR9XG5cblx0XHRcdGlmICgoaGV4U2xpY2UgJSA0KSA9PT0gMCkge1xuXHRcdFx0XHRsZXQgY3VyID0gaGV4U2xpY2UgLyA0O1xuXHRcdFx0XHR0aGlzLmZsYXRUb3BDb3JuZXJYW2N1cl0gPSB0aGlzLmhleFNsaWNlWFtoZXhTbGljZV07XG5cdFx0XHRcdHRoaXMuZmxhdFRvcENvcm5lcllbY3VyXSA9IHRoaXMuaGV4U2xpY2VZW2hleFNsaWNlXTtcblx0XHRcdH1cblxuLy9cdFx0XHRpZiAoKChoZXhTbGljZS0yKSAlIDQpID09PSAwKSB7XG4vL1x0XHRcdFx0bGV0IGN1ciA9IChoZXhTbGljZS0yKSAvIDQ7XG4vL1x0XHRcdFx0dGhpcy5oZXhDb3JuZXJYW2N1cl0gPSB0aGlzLmhleFNsaWNlWFtoZXhTbGljZV07XG4vL1x0XHRcdFx0dGhpcy5oZXhDb3JuZXJZW2N1cl0gPSB0aGlzLmhleFNsaWNlWVtoZXhTbGljZV07XG4vL1x0XHRcdH1cbi8vXG4vL1x0XHRcdGlmICgoaGV4U2xpY2UgJSA0KSA9PT0gMCkge1xuLy9cdFx0XHRcdGxldCBjdXIgPSBoZXhTbGljZSAvIDQ7XG4vL1x0XHRcdFx0dGhpcy5oZXhEb3RYW2N1cisxXSA9IHRoaXMuaGV4U2xpY2VYW2hleFNsaWNlXTtcbi8vXHRcdFx0XHR0aGlzLmhleERvdFlbY3VyKzFdID0gdGhpcy5oZXhTbGljZVlbaGV4U2xpY2VdO1xuLy9cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0b3V0bGluZShzZXR0aW5ncykge1xuXHRcdGxldCB4ID0gc2V0dGluZ3MueDtcblx0XHRsZXQgeSA9IHNldHRpbmdzLnk7XG5cdFx0bGV0IHNjYWxlID0gc2V0dGluZ3Muc2NhbGU7XG5cdFx0bGV0IGhleENvcm5lclggPSBzZXR0aW5ncy5wb2ludHkgPyB0aGlzLnBvaW50eVRvcENvcm5lclggOiB0aGlzLmZsYXRUb3BDb3JuZXJYO1xuXHRcdGxldCBoZXhDb3JuZXJZID0gc2V0dGluZ3MucG9pbnR5ID8gdGhpcy5wb2ludHlUb3BDb3JuZXJZIDogdGhpcy5mbGF0VG9wQ29ybmVyWTtcblxuXHRcdHRoaXMuY29udGV4dC5iZWdpblBhdGgoKTtcblx0XHR0aGlzLmNvbnRleHQubW92ZVRvKHggKyBzY2FsZSAqIGhleENvcm5lclhbMF0sIHkgKyBzY2FsZSAqIGhleENvcm5lcllbMF0pO1xuXHRcdHRoaXMuY29udGV4dC5saW5lVG8oeCArIHNjYWxlICogaGV4Q29ybmVyWFsxXSwgeSArIHNjYWxlICogaGV4Q29ybmVyWVsxXSk7XG5cdFx0dGhpcy5jb250ZXh0LmxpbmVUbyh4ICsgc2NhbGUgKiBoZXhDb3JuZXJYWzJdLCB5ICsgc2NhbGUgKiBoZXhDb3JuZXJZWzJdKTtcblx0XHR0aGlzLmNvbnRleHQubGluZVRvKHggKyBzY2FsZSAqIGhleENvcm5lclhbM10sIHkgKyBzY2FsZSAqIGhleENvcm5lcllbM10pO1xuXHRcdHRoaXMuY29udGV4dC5saW5lVG8oeCArIHNjYWxlICogaGV4Q29ybmVyWFs0XSwgeSArIHNjYWxlICogaGV4Q29ybmVyWVs0XSk7XG5cdFx0dGhpcy5jb250ZXh0LmxpbmVUbyh4ICsgc2NhbGUgKiBoZXhDb3JuZXJYWzVdLCB5ICsgc2NhbGUgKiBoZXhDb3JuZXJZWzVdKTtcblx0XHR0aGlzLmNvbnRleHQuY2xvc2VQYXRoKCk7XG5cblx0XHR0aGlzLmNvbnRleHQubGluZVdpZHRoID0gc2V0dGluZ3Mud2lkdGggPyBzZXR0aW5ncy53aWR0aCA6IDE7XG5cdFx0dGhpcy5jb250ZXh0LnN0cm9rZVN0eWxlID0gc2V0dGluZ3MuY29sb3IgfHwgUkdCQV9ERUZBVUxUO1xuXHRcdHRoaXMuY29udGV4dC5zdHJva2UoKTtcblx0fVxuXG5cdGZpbGxlZChzZXR0aW5ncykge1xuXHRcdGxldCB4ID0gc2V0dGluZ3MueDtcblx0XHRsZXQgeSA9IHNldHRpbmdzLnk7XG5cdFx0bGV0IHNjYWxlID0gc2V0dGluZ3Muc2NhbGU7XG5cdFx0bGV0IGhleENvcm5lclggPSBzZXR0aW5ncy5wb2ludHkgPyB0aGlzLnBvaW50eVRvcENvcm5lclggOiB0aGlzLmZsYXRUb3BDb3JuZXJYO1xuXHRcdGxldCBoZXhDb3JuZXJZID0gc2V0dGluZ3MucG9pbnR5ID8gdGhpcy5wb2ludHlUb3BDb3JuZXJZIDogdGhpcy5mbGF0VG9wQ29ybmVyWTtcblxuXHRcdHRoaXMuY29udGV4dC5iZWdpblBhdGgoKTtcblx0XHR0aGlzLmNvbnRleHQubW92ZVRvKHggKyBzY2FsZSAqIGhleENvcm5lclhbMF0sIHkgKyBzY2FsZSAqIGhleENvcm5lcllbMF0pO1xuXHRcdHRoaXMuY29udGV4dC5saW5lVG8oeCArIHNjYWxlICogaGV4Q29ybmVyWFsxXSwgeSArIHNjYWxlICogaGV4Q29ybmVyWVsxXSk7XG5cdFx0dGhpcy5jb250ZXh0LmxpbmVUbyh4ICsgc2NhbGUgKiBoZXhDb3JuZXJYWzJdLCB5ICsgc2NhbGUgKiBoZXhDb3JuZXJZWzJdKTtcblx0XHR0aGlzLmNvbnRleHQubGluZVRvKHggKyBzY2FsZSAqIGhleENvcm5lclhbM10sIHkgKyBzY2FsZSAqIGhleENvcm5lcllbM10pO1xuXHRcdHRoaXMuY29udGV4dC5saW5lVG8oeCArIHNjYWxlICogaGV4Q29ybmVyWFs0XSwgeSArIHNjYWxlICogaGV4Q29ybmVyWVs0XSk7XG5cdFx0dGhpcy5jb250ZXh0LmxpbmVUbyh4ICsgc2NhbGUgKiBoZXhDb3JuZXJYWzVdLCB5ICsgc2NhbGUgKiBoZXhDb3JuZXJZWzVdKTtcblxuXHRcdHRoaXMuY29udGV4dC5maWxsU3R5bGUgPSBzZXR0aW5ncy5jb2xvciB8fCBSR0JBX0RFRkFVTFQ7XG5cdFx0dGhpcy5jb250ZXh0LmZpbGwoKTtcblx0fVxufVxuXG5cblxuXG4vKiogV0VCUEFDSyBGT09URVIgKipcbiAqKiAuL3NyYy90aWxlSGV4LmpzXG4gKiovIiwiXG5pbXBvcnQgVGlsZSBmcm9tICcuL3RpbGUuanMnO1xuXG5sZXQgUkdCQV9ERUZBVUxUID0gJ3JnYmEoMCwgMCwgMCwgMC41KSc7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRpbGVTcXVhcmUgZXh0ZW5kcyBUaWxlIHtcblx0Y29uc3RydWN0b3Ioc2V0dGluZ3MpIHtcblx0XHRzdXBlcihzZXR0aW5ncyk7XG5cdH1cblxuXHRmaWxsZWQoc2V0dGluZ3MpIHtcblx0XHRsZXQgeCA9IHNldHRpbmdzLng7XG5cdFx0bGV0IHkgPSBzZXR0aW5ncy55O1xuXHRcdGxldCBzY2FsZSA9IHNldHRpbmdzLnNjYWxlO1xuXG5cdFx0dGhpcy5jb250ZXh0LmJlZ2luUGF0aCgpO1xuXHRcdHRoaXMuY29udGV4dC5tb3ZlVG8oeCtzY2FsZSwgeStzY2FsZSk7XG5cdFx0dGhpcy5jb250ZXh0LmxpbmVUbyh4K3NjYWxlLCB5LXNjYWxlKTtcblx0XHR0aGlzLmNvbnRleHQubGluZVRvKHgtc2NhbGUsIHktc2NhbGUpO1xuXHRcdHRoaXMuY29udGV4dC5saW5lVG8oeC1zY2FsZSwgeStzY2FsZSk7XG5cblx0XHR0aGlzLmNvbnRleHQuZmlsbFN0eWxlID0gc2V0dGluZ3MuY29sb3IgfHwgUkdCQV9ERUZBVUxUO1xuXHRcdHRoaXMuY29udGV4dC5maWxsKCk7XG5cdH1cblxuXHRvdXRsaW5lKHNldHRpbmdzKSB7XG5cdFx0bGV0IHggPSBzZXR0aW5ncy54O1xuXHRcdGxldCB5ID0gc2V0dGluZ3MueTtcblx0XHRsZXQgc2NhbGUgPSBzZXR0aW5ncy5zY2FsZTtcblxuXHRcdHRoaXMuY29udGV4dC5iZWdpblBhdGgoKTtcblx0XHR0aGlzLmNvbnRleHQubW92ZVRvKHgrc2NhbGUsIHkrc2NhbGUpO1xuXHRcdHRoaXMuY29udGV4dC5saW5lVG8oeCtzY2FsZSwgeS1zY2FsZSk7XG5cdFx0dGhpcy5jb250ZXh0LmxpbmVUbyh4LXNjYWxlLCB5LXNjYWxlKTtcblx0XHR0aGlzLmNvbnRleHQubGluZVRvKHgtc2NhbGUsIHkrc2NhbGUpO1xuXHRcdHRoaXMuY29udGV4dC5jbG9zZVBhdGgoKTtcblxuXHRcdHRoaXMuY29udGV4dC5saW5lV2lkdGggPSBzZXR0aW5ncy53aWR0aCA/IHNldHRpbmdzLndpZHRoIDogMTtcblx0XHR0aGlzLmNvbnRleHQuc3Ryb2tlU3R5bGUgPSBzZXR0aW5ncy5jb2xvciB8fCBSR0JBX0RFRkFVTFQ7XG5cdFx0dGhpcy5jb250ZXh0LnN0cm9rZSgpO1xuXHR9XG59XG5cblxuXG5cbi8qKiBXRUJQQUNLIEZPT1RFUiAqKlxuICoqIC4vc3JjL3RpbGVTcXVhcmUuanNcbiAqKi8iXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file diff --git a/src/hex.js b/src/hex.js new file mode 100644 index 0000000..5e9dbac --- /dev/null +++ b/src/hex.js @@ -0,0 +1,123 @@ + +import Point from './point.js'; + +function computeY(x, z) { + return -x - z; +} + +// convert real numbers to integers: +// round off coords +// throw out whichever one changed the most +// re-establish "x + y + z = 0" +function roundOff(x, y, z) { + let rX = Math.round(x); + let rY = Math.round(y); + let rZ = Math.round(z); + + let xDiff = Math.abs(rX - x); + let yDiff = Math.abs(rY - y); + let zDiff = Math.abs(rZ - z); + + if ((xDiff > yDiff) && (xDiff > zDiff)) { + rX = -rY-rZ; + } + else if (yDiff > zDiff) { + rY = -rX-rZ; + } + else { + rZ = -rX-rY; + } + + x = rX === -0 ? 0 : rX; + y = rY === -0 ? 0 : rY; + z = rZ === -0 ? 0 : rZ; + + return {x, y, z}; +} + +export default class Hex extends Point { + + constructor() { + super(); + + if (arguments.length === 2) { // hex = Hex(q, r); + this.x = arguments[0]; + this.z = arguments[1]; + this.y = computeY(this.x, this.z); + } + else if (arguments.length === 3) { // hex = Hex(x, y, z); + this.x = arguments[0]; + this.y = arguments[1]; + this.z = arguments[2]; + } + + roundOff(this.x, this.y, this.z); + } + + return { + getX: function() {return this.x;}, + getY: function() {return this.y;}, + getZ: function() {return this.z;}, + + setX: function(newX) {this.x = newX; return this;}, + setY: function(newY) {this.y = newY; return this;}, + setZ: function(newZ) {this.z = newZ; return this;}, + + moveX: function(byX) {this.x += byX; return this;}, + moveY: function(byY) {this.y += byY; return this;}, + moveZ: function(byZ) {this.z += byZ; return this;}, + + getQ: function() {return this.x;}, + getR: function() {return this.z;}, + + setQ: function(newQ) { + this.x = newQ; + this.y = computeY(this.x, this.z); + return this; + }, + setR: function(newR) { + this.z = newR; + this.y = computeY(this.x, this.z); + return this; + }, + + moveQ: function(byQ) { + this.x += byQ; + this.y = computeY(this.x, this.z); + return this; + }, + moveR: function(byR) { + this.z += byR; + this.y = computeY(this.x, this.z); + return this; + }, + + getHex: function() { return {x: this.x, y: this.y, z: this.z}; }, + setHex: function(newHex) { + this.x = newHex.x; + this.y = newHex.y; + this.z = newHex.z; + return this; + }, + moveHex: function(byHex) { + this.x += byHex.x; + this.y += byHex.y; + this.z += byHex.z; + return this; + }, + + getAxial: function() {return {x: this.x, z: this.z};}, + setAxial: function(newAxial) { + this.x = newAxial.q; + this.z = newAxial.r; + this.y = computeY(this.x, this.y); + return this; + }, + moveAxial: function(byAxial) { + this.x += byAxial.q; + this.z += byAxial.r; + this.y = computeY(this.x, this.z); + return this; + } + }; +}; diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..a047e0c --- /dev/null +++ b/src/main.js @@ -0,0 +1,7 @@ + +import Tessellate from './tessellate.js'; + +var tessellate = new Tessellate({ + element: '#container' +}); + diff --git a/src/onTap.js b/src/onTap.js new file mode 100644 index 0000000..f09fd0f --- /dev/null +++ b/src/onTap.js @@ -0,0 +1,24 @@ + +export default class OnTap { + constructor(settings) { + this.element = settings.element || document.body; + + this.callbacks = {}; + + this.events = ['click']; + + this.events.map(method => { + this[method] = this[method].bind(this); + + if (typeof settings[method] === 'function') { + this.element.addEventListener(method, this[method]); + this.callbacks[method] = settings[method]; + } + }); + } + + click(event) { + this.callbacks.click(event); + } +} + diff --git a/src/point.js b/src/point.js new file mode 100644 index 0000000..c5f9e33 --- /dev/null +++ b/src/point.js @@ -0,0 +1,15 @@ + +export default class Point { + constructor(newX, newY) { + this.x = newX; + this.y = newY; + } + + getX() { return this.x; } + getY() { return this.y; } + + setX(newX) { this.x = newX; } + setY(newY) { this.y = newY; } + + getPoint() { return {x: this.x, y: this.y}; } +}; diff --git a/src/sketch.js b/src/sketch.js new file mode 100644 index 0000000..6894595 --- /dev/null +++ b/src/sketch.js @@ -0,0 +1,37 @@ + +export default class Sketch { + constructor(settings) { + this.lastTime = null; + + ['getContext', 'onResize', 'render'].map(method => this[method] = this[method].bind(this)); + + this.draw = settings.draw || function() {}; // () => {}; + this.container = settings.element || document.body; + + window.addEventListener('optimizedResize', this.onResize); + + this.canvas = document.createElement('canvas'); + this.canvas.width = this.container.offsetWidth; + this.canvas.height = this.container.offsetHeight; + + this.context = this.canvas.getContext('2d'); + + this.container.appendChild(this.canvas); + + requestAnimationFrame(this.render); + } + + getContext() { return this.context; } + + onResize(event) { + console.log('sketch - onResize', arguments); + } + + render(now) { + this.draw(this.context, now, this.lastTime); + + this.lastTime = now; + requestAnimationFrame(this.render); + } +} + diff --git a/src/tessellate.js b/src/tessellate.js new file mode 100644 index 0000000..04f1d0e --- /dev/null +++ b/src/tessellate.js @@ -0,0 +1,62 @@ + +import {random} from './utils.js'; + +import OnTap from './onTap.js'; +import Point from './point.js'; +import Sketch from './sketch.js'; + +import TileCircle from './tileCircle.js'; +import TileDiamond from './tileDiamond.js'; +import TileHex from './tileHex.js'; +import TileSquare from './tileSquare.js'; + +export default class Tessellate { + constructor(settings) { + ['draw'].map(method => {this[method] = this[method].bind(this)}); + + this.element = document.querySelector(settings.element); + + this.sketch = new Sketch({ + element: this.element, + draw: this.draw + }); + + this.map = []; + + this.onTap = new OnTap({ + element: this.element, + click: (event) => { + let x = event.pageX; + let y = event.pageY; + let tiles = ['circle', 'diamond', 'hex', 'square']; + let styles = ['filled', 'outline']; + let scale = random(10, 50); + + this.map.push({ + tile: tiles[random(tiles.length-1)], + style: styles[random(styles.length-1)], + x, + y, + scale, + pointy: random(1) ? true : false, + color: `rgba(${ random(255) }, ${ random(255) }, ${ random(255) }, 0.5)` + }); + } + }); + + this.circle = new TileCircle({context: this.sketch.getContext()}); + this.diamond = new TileDiamond({context: this.sketch.getContext()}); + this.hex = new TileHex({context: this.sketch.getContext()}); + this.square = new TileSquare({context: this.sketch.getContext()}); + } + + draw(context, now, lastTime) { + let canvas = context.canvas; + let width = canvas.width; + let height = canvas.height; + + context.clearRect(0, 0, width, height); + + this.map.forEach(cell => this[cell.tile][cell.style](cell)); + } +} diff --git a/src/tile.js b/src/tile.js new file mode 100644 index 0000000..d123178 --- /dev/null +++ b/src/tile.js @@ -0,0 +1,11 @@ + +export default class Tile { + constructor(settings) { + this.context = settings.context || null; + } + + setContext(newContext) { + this.context = newContext || this.context; + } +} + diff --git a/src/tileCircle.js b/src/tileCircle.js new file mode 100644 index 0000000..240806e --- /dev/null +++ b/src/tileCircle.js @@ -0,0 +1,26 @@ + +import Tile from './tile.js'; + +let RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)'; + +export default class TileCircle extends Tile { + constructor(settings) { + super(settings); + } + + filled(settings) { + this.context.beginPath(); + this.context.arc(settings.x, settings.y, settings.scale, 0, 2*Math.PI, false); + this.context.fillStyle = settings.color || RGBA_DEFAULT; + this.context.fill(); + } + + outline(settings) { + this.context.beginPath(); + this.context.arc(settings.x, settings.y, settings.scale, 0, 2*Math.PI, false); + this.context.lineWidth = settings.width ? settings.width : 1; + this.context.strokeStyle = settings.color || RGBA_DEFAULT; + this.context.stroke(); + } +} + diff --git a/src/tileDiamond.js b/src/tileDiamond.js new file mode 100644 index 0000000..1f9f92f --- /dev/null +++ b/src/tileDiamond.js @@ -0,0 +1,45 @@ + +import Tile from './tile.js'; + +import {hypotenuse} from './utils.js'; + +let RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)'; + +export default class TileDiamond extends Tile { + constructor(settings) { + super(settings); + } + + filled(settings) { + let x = settings.x; + let y = settings.y; + let scale = settings.scale; + + this.context.beginPath(); + this.context.moveTo(x+hypotenuse(scale), y); + this.context.lineTo(x, y-hypotenuse(scale)); + this.context.lineTo(x-hypotenuse(scale), y); + this.context.lineTo(x, y+hypotenuse(scale)); + + this.context.fillStyle = settings.color || RGBA_DEFAULT; + this.context.fill(); + } + + outline(settings) { + let x = settings.x; + let y = settings.y; + let scale = settings.scale; + + this.context.beginPath(); + this.context.moveTo(x+hypotenuse(scale), y); + this.context.lineTo(x, y-hypotenuse(scale)); + this.context.lineTo(x-hypotenuse(scale), y); + this.context.lineTo(x, y+hypotenuse(scale)); + this.context.closePath(); + + this.context.lineWidth = settings.width ? settings.width : 1; + this.context.strokeStyle = settings.color || RGBA_DEFAULT; + this.context.stroke(); + } +} + diff --git a/src/tileHex.js b/src/tileHex.js new file mode 100644 index 0000000..4b8a071 --- /dev/null +++ b/src/tileHex.js @@ -0,0 +1,97 @@ + +import Tile from './tile.js'; + +let RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)'; + +export default class TileHex extends Tile { + constructor(settings) { + super(settings); + + this.sqrt3 = Math.sqrt(3); + + this.hexSides = 6; + this.pointyTopCornerX = []; + this.pointyTopCornerY = []; + this.flatTopCornerX = []; + this.flatTopCornerY = []; + + // dice dots, 0,0 is center + // TODO: if pointy: [0].concat(flatTop), if flat: [0].concat(pointy) +// this.hexDotX = [0]; +// this.hexDotY = [0]; + + this.hexSlices = 24; + this.hexSliceX = [0]; + this.hexSliceY = [0]; + + for (let hexSlice = 0; hexSlice < this.hexSlices; hexSlice++) { + this.hexSliceX[hexSlice] = parseInt(Math.cos(((hexSlice/this.hexSlices)*this.hexSides) * (2 * Math.PI) / this.hexSides) * 1000) / 1000; + this.hexSliceY[hexSlice] = parseInt(Math.sin(((hexSlice/this.hexSlices)*this.hexSides) * (2 * Math.PI) / this.hexSides) * 1000) / 1000; + + if (((hexSlice-2) % 4) === 0) { + let cur = (hexSlice-2) / 4; + this.pointyTopCornerX[cur] = this.hexSliceX[hexSlice]; + this.pointyTopCornerY[cur] = this.hexSliceY[hexSlice]; + } + + if ((hexSlice % 4) === 0) { + let cur = hexSlice / 4; + this.flatTopCornerX[cur] = this.hexSliceX[hexSlice]; + this.flatTopCornerY[cur] = this.hexSliceY[hexSlice]; + } + +// if (((hexSlice-2) % 4) === 0) { +// let cur = (hexSlice-2) / 4; +// this.hexCornerX[cur] = this.hexSliceX[hexSlice]; +// this.hexCornerY[cur] = this.hexSliceY[hexSlice]; +// } +// +// if ((hexSlice % 4) === 0) { +// let cur = hexSlice / 4; +// this.hexDotX[cur+1] = this.hexSliceX[hexSlice]; +// this.hexDotY[cur+1] = this.hexSliceY[hexSlice]; +// } + } + } + + outline(settings) { + let x = settings.x; + let y = settings.y; + let scale = settings.scale; + let hexCornerX = settings.pointy ? this.pointyTopCornerX : this.flatTopCornerX; + let hexCornerY = settings.pointy ? this.pointyTopCornerY : this.flatTopCornerY; + + this.context.beginPath(); + this.context.moveTo(x + scale * hexCornerX[0], y + scale * hexCornerY[0]); + this.context.lineTo(x + scale * hexCornerX[1], y + scale * hexCornerY[1]); + this.context.lineTo(x + scale * hexCornerX[2], y + scale * hexCornerY[2]); + this.context.lineTo(x + scale * hexCornerX[3], y + scale * hexCornerY[3]); + this.context.lineTo(x + scale * hexCornerX[4], y + scale * hexCornerY[4]); + this.context.lineTo(x + scale * hexCornerX[5], y + scale * hexCornerY[5]); + this.context.closePath(); + + this.context.lineWidth = settings.width ? settings.width : 1; + this.context.strokeStyle = settings.color || RGBA_DEFAULT; + this.context.stroke(); + } + + filled(settings) { + let x = settings.x; + let y = settings.y; + let scale = settings.scale; + let hexCornerX = settings.pointy ? this.pointyTopCornerX : this.flatTopCornerX; + let hexCornerY = settings.pointy ? this.pointyTopCornerY : this.flatTopCornerY; + + this.context.beginPath(); + this.context.moveTo(x + scale * hexCornerX[0], y + scale * hexCornerY[0]); + this.context.lineTo(x + scale * hexCornerX[1], y + scale * hexCornerY[1]); + this.context.lineTo(x + scale * hexCornerX[2], y + scale * hexCornerY[2]); + this.context.lineTo(x + scale * hexCornerX[3], y + scale * hexCornerY[3]); + this.context.lineTo(x + scale * hexCornerX[4], y + scale * hexCornerY[4]); + this.context.lineTo(x + scale * hexCornerX[5], y + scale * hexCornerY[5]); + + this.context.fillStyle = settings.color || RGBA_DEFAULT; + this.context.fill(); + } +} + diff --git a/src/tileSquare.js b/src/tileSquare.js new file mode 100644 index 0000000..09fa3c4 --- /dev/null +++ b/src/tileSquare.js @@ -0,0 +1,43 @@ + +import Tile from './tile.js'; + +let RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)'; + +export default class TileSquare extends Tile { + constructor(settings) { + super(settings); + } + + filled(settings) { + let x = settings.x; + let y = settings.y; + let scale = settings.scale; + + this.context.beginPath(); + this.context.moveTo(x+scale, y+scale); + this.context.lineTo(x+scale, y-scale); + this.context.lineTo(x-scale, y-scale); + this.context.lineTo(x-scale, y+scale); + + this.context.fillStyle = settings.color || RGBA_DEFAULT; + this.context.fill(); + } + + outline(settings) { + let x = settings.x; + let y = settings.y; + let scale = settings.scale; + + this.context.beginPath(); + this.context.moveTo(x+scale, y+scale); + this.context.lineTo(x+scale, y-scale); + this.context.lineTo(x-scale, y-scale); + this.context.lineTo(x-scale, y+scale); + this.context.closePath(); + + this.context.lineWidth = settings.width ? settings.width : 1; + this.context.strokeStyle = settings.color || RGBA_DEFAULT; + this.context.stroke(); + } +} + diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..ad815f2 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,48 @@ + +export function throttleEvent(type, name, obj) { + obj = obj || window; + let running = false; + + let throttle = () => { + if (!running) { + running = true; + + requestAnimationFrame(() => { + obj.dispatchEvent(new CustomEvent(name)); + running = false; + }); + } + } + + obj.addEventListener(type, throttle); +} + +throttleEvent('resize', 'optimizedResize'); + +export function clone(obj) { + return JSON.parse(JSON.stringify(obj)); +} + +export function extend(obj, src) { + for (let key in src) { + if (src.hasOwnProperty(key)) obj[key] = src[key]; + } + + return obj; +} + +export function hypotenuse(a, b) { + if (b == null) b = a; + + return Math.sqrt(a*a + b*b); +} + +export function random(min, max) { + if (max == null) { + max = min; + min = 0; + } + + return min + Math.floor(Math.random() * (max - min + 1)); +} + diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..7bb8c12 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,21 @@ +var path = require('path'); + +module.exports = { + entry: path.join(__dirname, 'src', 'main.js'), + output: { + libraryTarget: 'this', + path: path.join(__dirname, 'public', 'js'), + filename: 'main.js' + }, + devtool: 'inline-source-map', + module: { + loaders: [{ + test: path.join(__dirname, 'src'), + loader: 'babel-loader', + query: { + presets: ['es2015'] + } + }] + } +}; +