From 7126e83f5f5bcca624dd28dde1265e95da8b4904 Mon Sep 17 00:00:00 2001 From: Sam Keddy Date: Tue, 26 Mar 2019 23:20:54 +0000 Subject: [PATCH] first commit --- .gitignore | 4 + README.md | 36 + _ext/sass/_colors.scss | 160 + _ext/sass/_zindex.scss | 67 + _ext/scripts/libraries/cookies.js | 172 + _ext/scripts/utilities/getSetText.js | 10 + _ext/scripts/utilities/getSetValue.js | 11 + _ext/scripts/utilities/hexToRgb.js | 21 + _ext/scripts/utilities/hslToRgb.js | 32 + _ext/scripts/utilities/on.js | 22 + _ext/scripts/utilities/onChildren.js | 14 + _ext/scripts/utilities/onClick.js | 9 + _ext/scripts/utilities/onClickChildren.js | 13 + _ext/scripts/utilities/rgbToHex.js | 23 + _ext/scripts/utilities/rgbToHsl.js | 36 + _ext/scripts/utilities/select.js | 20 + build.js | 88 + css/pixel-editor-splash-page.scss | 10 + css/pixel-editor.scss | 719 ++++ images/dropdown-arrow-hover.png | Bin 0 -> 2915 bytes images/dropdown-arrow.png | Bin 0 -> 2916 bytes images/eyedropper.png | Bin 0 -> 3053 bytes images/fill.png | Bin 0 -> 3187 bytes images/pan-held.png | Bin 0 -> 3073 bytes images/pan.png | Bin 0 -> 3147 bytes images/pencil-tool-cursor.png | Bin 0 -> 974 bytes images/pencil.png | Bin 0 -> 3047 bytes images/pixel-editor-screenshot.png | Bin 0 -> 35193 bytes images/zoom-in.png | Bin 0 -> 3062 bytes js/_addColor.js | 49 + js/_addColorButton.js | 58 + js/_changeTool.js | 18 + js/_changeZoom.js | 34 + js/_checkCompatibility.js | 20 + js/_clickedColor.js | 29 + js/_colorChanged.js | 102 + js/_createButton.js | 22 + js/_createColorPalette.js | 45 + js/_deleteColor.js | 84 + js/_dialogue.js | 36 + js/_drawLine.js | 21 + js/_fileMenu.js | 140 + js/_fill.js | 107 + js/_getCursorPosition.js | 18 + js/_history.js | 198 + js/_hotkeyListener.js | 66 + js/_initColor.js | 23 + js/_jscolor.js | 1964 ++++++++++ js/_loadImage.js | 61 + js/_loadPalette.js | 50 + js/_mouseEvents.js | 210 ++ js/_newPixel.js | 79 + js/_onLoad.js | 12 + js/_onbeforeunload.js | 7 + js/_palettes.js | 62 + js/_presets.js | 64 + js/_replaceAllOfColor.js | 25 + js/_setCanvasOffset.js | 24 + js/_settings.js | 45 + js/_toolButtons.js | 47 + js/_updateCursor.js | 32 + js/_variables.js | 25 + js/pixel-editor.js | 67 + package-lock.json | 4122 +++++++++++++++++++++ package.json | 24 + views/pixel-editor-splash-page.hbs | 31 + views/pixel-editor.hbs | 234 ++ 67 files changed, 9722 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 _ext/sass/_colors.scss create mode 100644 _ext/sass/_zindex.scss create mode 100644 _ext/scripts/libraries/cookies.js create mode 100644 _ext/scripts/utilities/getSetText.js create mode 100644 _ext/scripts/utilities/getSetValue.js create mode 100644 _ext/scripts/utilities/hexToRgb.js create mode 100644 _ext/scripts/utilities/hslToRgb.js create mode 100644 _ext/scripts/utilities/on.js create mode 100644 _ext/scripts/utilities/onChildren.js create mode 100644 _ext/scripts/utilities/onClick.js create mode 100644 _ext/scripts/utilities/onClickChildren.js create mode 100644 _ext/scripts/utilities/rgbToHex.js create mode 100644 _ext/scripts/utilities/rgbToHsl.js create mode 100644 _ext/scripts/utilities/select.js create mode 100644 build.js create mode 100644 css/pixel-editor-splash-page.scss create mode 100644 css/pixel-editor.scss create mode 100644 images/dropdown-arrow-hover.png create mode 100644 images/dropdown-arrow.png create mode 100644 images/eyedropper.png create mode 100644 images/fill.png create mode 100644 images/pan-held.png create mode 100644 images/pan.png create mode 100644 images/pencil-tool-cursor.png create mode 100644 images/pencil.png create mode 100644 images/pixel-editor-screenshot.png create mode 100644 images/zoom-in.png create mode 100644 js/_addColor.js create mode 100644 js/_addColorButton.js create mode 100644 js/_changeTool.js create mode 100644 js/_changeZoom.js create mode 100644 js/_checkCompatibility.js create mode 100644 js/_clickedColor.js create mode 100644 js/_colorChanged.js create mode 100644 js/_createButton.js create mode 100644 js/_createColorPalette.js create mode 100644 js/_deleteColor.js create mode 100644 js/_dialogue.js create mode 100644 js/_drawLine.js create mode 100644 js/_fileMenu.js create mode 100644 js/_fill.js create mode 100644 js/_getCursorPosition.js create mode 100644 js/_history.js create mode 100644 js/_hotkeyListener.js create mode 100644 js/_initColor.js create mode 100644 js/_jscolor.js create mode 100644 js/_loadImage.js create mode 100644 js/_loadPalette.js create mode 100644 js/_mouseEvents.js create mode 100644 js/_newPixel.js create mode 100644 js/_onLoad.js create mode 100644 js/_onbeforeunload.js create mode 100644 js/_palettes.js create mode 100644 js/_presets.js create mode 100644 js/_replaceAllOfColor.js create mode 100644 js/_setCanvasOffset.js create mode 100644 js/_settings.js create mode 100644 js/_toolButtons.js create mode 100644 js/_updateCursor.js create mode 100644 js/_variables.js create mode 100644 js/pixel-editor.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 views/pixel-editor-splash-page.hbs create mode 100644 views/pixel-editor.hbs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..70fbaa0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +_ext +routes +build +node_modules \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3f2b57b --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# Lospec Procedural Pixel Art Generator + +This is a browser based tool used to create a template which can generate randomized pixel art. + +The tool can be viewed online here: https://lospec.com/procedural-pixel-art-generator/ + +## What to Contribute + +Any changes that fix bugs or add features are welcome. + +Suggestions / Planned features: + +- Mobile support +- Variable canvas size +- More Layers +- More Colors +- Color variations (choose a color randomly) + +## How to Contribute + +1. Click **Fork** above. It will automatically create a copy of this repository and add it to your account. +2. Clone the repository to your computer. +3. Open the folder in command prompt and run **npm install** +4. Make any changes you would like to suggest. +5. In command prompt run **node build.js** which will compile it to the */build* folder, where you can make sure it works +6. Add, Commit and Push your changes to your fork. +7. On this page, click **New Pull Request** above the file list. +8. Change the **head repository** dropdown to your fork. +9. Add a title and description explaining your changes. +10. Click create pull request. + +If you have any trouble, see this page: https://help.github.com/en/articles/creating-a-pull-request-from-a-fork + +## License + +This software may not be resold, redistributed, rehosted or otherwise conveyed to a third party. diff --git a/_ext/sass/_colors.scss b/_ext/sass/_colors.scss new file mode 100644 index 0000000..c4ff3a4 --- /dev/null +++ b/_ext/sass/_colors.scss @@ -0,0 +1,160 @@ +$base-color: #332f35; +$shop: #b63831; +$red: #e3474a; + +$palettes: ( + base: ( + background: ( + default: $base-color, + hover: lighten($base-color, 5%), + lighthover: lighten($base-color, 4%), + ), + foreground: ( + default: lighten($base-color, 20%), + text: lighten($base-color, 50%), + bold: lighten($base-color, 60%), + weak: lighten($base-color, 30%), + link: lighten($base-color, 100%), + h1: lighten($base-color, 100%), + h2: lighten($base-color, 70%), + h3: lighten($base-color, 60%), + surveyQuestion: lighten($base-color, 60%), + hover: lighten($base-color, 40%), + separator: lighten($base-color, 5%), + disabled: lighten($base-color, 10%), + ) + ), + user-menu: ( + background: ( + default: darken($base-color, 3%), + hover: darken($base-color, 1.5%), + ), + foreground: ( + default: lighten($base-color, 50%), + hover: lighten($base-color, 100%), + ) + ), + menu: ( + background: ( + default: lighten($base-color, 5%), + hover: lighten($base-color, 15%), + ), + foreground: ( + default: lighten($base-color, 50%), + hover: lighten($base-color, 100%), + ) + ), + button: ( + background: ( + default: lighten($base-color, 10%), + hover: lighten($base-color, 15%), + ), + foreground: ( + default: lighten($base-color, 80%), + text: lighten($base-color, 100%), + ) + ), + selectedTool: ( + background: ( + default: lighten($base-color, 10%), + ), + foreground: ( + default: lighten($base-color, 50%), + ) + ), + subbutton: ( + background: ( + hover: lighten($base-color, 15%), + ), + foreground: ( + default: lighten($base-color, 30%), + hover: lighten($base-color, 50%), + ) + ), + indent: ( + background: ( + default: darken($base-color, 4%), + separator: darken($base-color, 8%), + hover: lighten($base-color, 5%), + ), + foreground: ( + default: #fff, + symbol: lighten($base-color, 5%), + symbol-hover: lighten($base-color, 20%), + weak: lighten($base-color, 20%), + form: lighten($base-color, 50%), + ) + ), + indent-dark: ( + background: ( + default: darken($base-color, 6%), + separator: darken($base-color, 11%), + button: lighten($base-color, 2.5%), + button-hover: lighten($base-color, 5%), + ), + foreground: ( + default: lighten($base-color, 30%), + link: lighten($base-color, 40%), + hover: lighten($base-color, 50%), + button: lighten($base-color, 50%), + button-hover: lighten($base-color, 70%), + ) + ), + footer: ( + background: ( + default: darken($base-color, 9%), + ), + foreground: ( + default: lighten($base-color, 20%), + hover: lighten($base-color, 35%), + symbol: lighten($base-color, 7.5%), + ), + ), + warning-banner: ( + background: ( + default: lighten($base-color, 10%), + button: lighten($base-color, 20%), + button-hover: lighten($base-color, 25%), + ), + foreground: ( + default: lighten($base-color, 50%), + button: lighten($base-color, 40%), + button-hover: lighten($base-color, 45%), + ), + ), + image-label: ( + background: ( + default: lighten($base-color, 15%), + ), + foreground: ( + default: lighten($base-color, 80%), + ), + triangle: ( + default: lighten($base-color, 6%), + ), + ), + shop: ( + background: ( + default: $shop, + hover: lighten($shop, 5%), + ), + foreground: ( + default: lighten($shop, 65%), + ), + ), +); + +@function color($element: 'base', $location: 'background', $hover: 'default') { + @return map-get(map-get(map-get($palettes, $element), $location), $hover); +} + +$twitter: #00b6f1; +$patreon: #F96854; +$facebook: #3b5998; +$reddit: #ff5700; +$youtube: #b31217; +$pintrest: #cb2027; +$tumblr: #2c4762; +$deviantart: #4a5d4e; +$instagram: #c2368a; +$pixeljoint: #73d731; diff --git a/_ext/sass/_zindex.scss b/_ext/sass/_zindex.scss new file mode 100644 index 0000000..9e254cf --- /dev/null +++ b/_ext/sass/_zindex.scss @@ -0,0 +1,67 @@ +// this function is used whenever you need to define a z-index +@function -z($key, $mod: 0) { + + $index: -index($layers, $key) * 10; + + @if $mod { + @return $index + $mod; + } + + @return $index; +} + +// dependency for the other function +@function -index($list, $value) { + @for $i from 1 through length($list) { + @if nth($list, $i) == $value { + @return $i; + } + } + + @return null; +} + + +/* + +define your layers from bottom to top: + +$layers: ( + background, + content, + dropdown, + subnav, + mobile-nav, + header, + overlay, + modal, + modal-background, + modal-content +); + + + +//////////use: + +.test { + z-index: -z(content); +} + + + + +//////////go one layer above content + +.test-2 { + z-index: -z(content, 1); +} + + + +///////////go one layer below content + +.test-2 { + z-index: -z(content, -1) +} + +*/ \ No newline at end of file diff --git a/_ext/scripts/libraries/cookies.js b/_ext/scripts/libraries/cookies.js new file mode 100644 index 0000000..df88e2b --- /dev/null +++ b/_ext/scripts/libraries/cookies.js @@ -0,0 +1,172 @@ +/* + * Cookies.js - 1.2.3 + * https://github.com/ScottHamper/Cookies + * + * This is free and unencumbered software released into the public domain. + */ +(function (global, undefined) { + 'use strict'; + + var factory = function (window) { + if (typeof window.document !== 'object') { + throw new Error('Cookies.js requires a `window` with a `document` object'); + } + + var Cookies = function (key, value, options) { + return arguments.length === 1 ? + Cookies.get(key) : Cookies.set(key, value, options); + }; + + // Allows for setter injection in unit tests + Cookies._document = window.document; + + // Used to ensure cookie keys do not collide with + // built-in `Object` properties + Cookies._cacheKeyPrefix = 'cookey.'; // Hurr hurr, :) + + Cookies._maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC'); + + Cookies.defaults = { + path: '/', + secure: false + }; + + Cookies.get = function (key) { + if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) { + Cookies._renewCache(); + } + + var value = Cookies._cache[Cookies._cacheKeyPrefix + key]; + + return value === undefined ? undefined : decodeURIComponent(value); + }; + + Cookies.set = function (key, value, options) { + options = Cookies._getExtendedOptions(options); + options.expires = Cookies._getExpiresDate(value === undefined ? -1 : options.expires); + + Cookies._document.cookie = Cookies._generateCookieString(key, value, options); + + return Cookies; + }; + + Cookies.expire = function (key, options) { + return Cookies.set(key, undefined, options); + }; + + Cookies._getExtendedOptions = function (options) { + return { + path: options && options.path || Cookies.defaults.path, + domain: options && options.domain || Cookies.defaults.domain, + expires: options && options.expires || Cookies.defaults.expires, + secure: options && options.secure !== undefined ? options.secure : Cookies.defaults.secure + }; + }; + + Cookies._isValidDate = function (date) { + return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime()); + }; + + Cookies._getExpiresDate = function (expires, now) { + now = now || new Date(); + + if (typeof expires === 'number') { + expires = expires === Infinity ? + Cookies._maxExpireDate : new Date(now.getTime() + expires * 1000); + } else if (typeof expires === 'string') { + expires = new Date(expires); + } + + if (expires && !Cookies._isValidDate(expires)) { + throw new Error('`expires` parameter cannot be converted to a valid Date instance'); + } + + return expires; + }; + + Cookies._generateCookieString = function (key, value, options) { + key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent); + key = key.replace(/\(/g, '%28').replace(/\)/g, '%29'); + value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent); + options = options || {}; + + var cookieString = key + '=' + value; + cookieString += options.path ? ';path=' + options.path : ''; + cookieString += options.domain ? ';domain=' + options.domain : ''; + cookieString += options.expires ? ';expires=' + options.expires.toUTCString() : ''; + cookieString += options.secure ? ';secure' : ''; + + return cookieString; + }; + + Cookies._getCacheFromString = function (documentCookie) { + var cookieCache = {}; + var cookiesArray = documentCookie ? documentCookie.split('; ') : []; + + for (var i = 0; i < cookiesArray.length; i++) { + var cookieKvp = Cookies._getKeyValuePairFromCookieString(cookiesArray[i]); + + if (cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined) { + cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] = cookieKvp.value; + } + } + + return cookieCache; + }; + + Cookies._getKeyValuePairFromCookieString = function (cookieString) { + // "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')` + var separatorIndex = cookieString.indexOf('='); + + // IE omits the "=" when the cookie value is an empty string + separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex; + + var key = cookieString.substr(0, separatorIndex); + var decodedKey; + try { + decodedKey = decodeURIComponent(key); + } catch (e) { + if (console && typeof console.error === 'function') { + console.error('Could not decode cookie with key "' + key + '"', e); + } + } + + return { + key: decodedKey, + value: cookieString.substr(separatorIndex + 1) // Defer decoding value until accessed + }; + }; + + Cookies._renewCache = function () { + Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie); + Cookies._cachedDocumentCookie = Cookies._document.cookie; + }; + + Cookies._areEnabled = function () { + var testKey = 'cookies.js'; + var areEnabled = Cookies.set(testKey, 1).get(testKey) === '1'; + Cookies.expire(testKey); + return areEnabled; + }; + + Cookies.enabled = Cookies._areEnabled(); + + return Cookies; + }; + var cookiesExport = (global && typeof global.document === 'object') ? factory(global) : factory; + + // AMD support + if (typeof define === 'function' && define.amd) { + define(function () { return cookiesExport; }); + // CommonJS/Node.js support + } else if (typeof exports === 'object') { + // Support Node.js specific `module.exports` (which can be a function) + if (typeof module === 'object' && typeof module.exports === 'object') { + exports = module.exports = cookiesExport; + } + // But always support CommonJS module 1.1.1 spec (`exports` cannot be a function) + exports.Cookies = cookiesExport; + } else { + global.Cookies = cookiesExport; + } +})(typeof window === 'undefined' ? this : window); \ No newline at end of file diff --git a/_ext/scripts/utilities/getSetText.js b/_ext/scripts/utilities/getSetText.js new file mode 100644 index 0000000..4604c19 --- /dev/null +++ b/_ext/scripts/utilities/getSetText.js @@ -0,0 +1,10 @@ +//get text of specified element +function getText(elementId) { + var element = (typeof elementId == 'string' ? document.getElementById(elementId) : elementId); + return element.textContent; +} + +function setText(elementId, text) { + var element = (typeof elementId == 'string' ? document.getElementById(elementId) : elementId); + element.textContent = text; +} diff --git a/_ext/scripts/utilities/getSetValue.js b/_ext/scripts/utilities/getSetValue.js new file mode 100644 index 0000000..eed10fd --- /dev/null +++ b/_ext/scripts/utilities/getSetValue.js @@ -0,0 +1,11 @@ + + +function getValue(elementId) { + var element = (typeof elementId == 'string' ? document.getElementById(elementId) : elementId); + return element.value; +} + +function setValue(elementId, value) { + var element = (typeof elementId == 'string' ? document.getElementById(elementId) : elementId); + element.value = value; +} \ No newline at end of file diff --git a/_ext/scripts/utilities/hexToRgb.js b/_ext/scripts/utilities/hexToRgb.js new file mode 100644 index 0000000..83da090 --- /dev/null +++ b/_ext/scripts/utilities/hexToRgb.js @@ -0,0 +1,21 @@ +//put in a hex color code (f464b2 or #f464b2) string +//and get an rgb color object {r:0,g:0,b:0} +//divisor is an optional argument, which makes it so you can get values other than 0-255 + +function hexToRgb(hex, divisor) { + //if divisor isn't set, set it to one (so it has no effect) + divisor = divisor || 1; + + //split given hex code into array of 3 values + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex.trim()); + + //console.log('hex: '+hex) + //console.log([parseInt(result[1], 16)/divisor, parseInt(result[2], 16)/divisor, parseInt(result[3], 16)/divisor]) + //console.log(result) + + return result ? { + r: parseInt(result[1], 16)/divisor, + g: parseInt(result[2], 16)/divisor, + b: parseInt(result[3], 16)/divisor + } : null; +} \ No newline at end of file diff --git a/_ext/scripts/utilities/hslToRgb.js b/_ext/scripts/utilities/hslToRgb.js new file mode 100644 index 0000000..bdd0f39 --- /dev/null +++ b/_ext/scripts/utilities/hslToRgb.js @@ -0,0 +1,32 @@ +function hslToRgb(h, s, l){ + h /= 255; + s /= 255; + l /= 255; + + var r, g, b; + + if(s == 0){ + r = g = b = l; // achromatic + }else{ + var hue2rgb = function hue2rgb(p, q, t){ + if(t < 0) t += 1; + if(t > 1) t -= 1; + if(t < 1/6) return p + (q - p) * 6 * t; + if(t < 1/2) return q; + if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + } + + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } + + return { + r:Math.round(r * 255), + g:Math.round(g * 255), + b:Math.round(b * 255) + }; +} \ No newline at end of file diff --git a/_ext/scripts/utilities/on.js b/_ext/scripts/utilities/on.js new file mode 100644 index 0000000..8eba088 --- /dev/null +++ b/_ext/scripts/utilities/on.js @@ -0,0 +1,22 @@ +//add event listener for any element which calls a function +//element can be provided as a direct reference or with just a string of the name + +function on(event, elementId, functionCallback) { + + + + //if element provided is string, get the actual element + var element = (typeof elementId == 'string' ? document.getElementById(elementId) : elementId); + + //console.log('added '+event+' event listener on '+element) + + element.addEventListener(event, + function (e) { + // e = event + //this = element clicked + functionCallback(e, this); + //if you need to access the event or this variable, you need to add them + //when you define the callback, but you cant use the word this, eg: + //on('click', menuButton, function (e, button) {}); + }); +} \ No newline at end of file diff --git a/_ext/scripts/utilities/onChildren.js b/_ext/scripts/utilities/onChildren.js new file mode 100644 index 0000000..4ad6f16 --- /dev/null +++ b/_ext/scripts/utilities/onChildren.js @@ -0,0 +1,14 @@ +//add event listener to each of specified element's children + +function onChildren(event, parentElement, functionCallback) { + console.log('onChildren()'); + + var parentElement = (typeof parentElement == 'string' ? document.getElementById(parentElement) : parentElement); + + var children = parentElement.children; + + //loop through children and add onClick listener + for (var i = 0; i < children.length; i++) { + on(event, children[i],functionCallback); + } +} \ No newline at end of file diff --git a/_ext/scripts/utilities/onClick.js b/_ext/scripts/utilities/onClick.js new file mode 100644 index 0000000..f70c671 --- /dev/null +++ b/_ext/scripts/utilities/onClick.js @@ -0,0 +1,9 @@ +//DEPRECATED - USE on('click') + + +//add click event listener for any element which calls a function +//element can be provided as a direct reference or with just a string of the name +function onClick(elementId, functionCallback) { + var element = (typeof elementId == 'string' ? document.getElementById(elementId) : elementId); + element.addEventListener('click',functionCallback); +} \ No newline at end of file diff --git a/_ext/scripts/utilities/onClickChildren.js b/_ext/scripts/utilities/onClickChildren.js new file mode 100644 index 0000000..a4b073a --- /dev/null +++ b/_ext/scripts/utilities/onClickChildren.js @@ -0,0 +1,13 @@ +//add click listener to each of specified element's children + +function onClickChildren(parentElement, functionCallback) { + + var parentElement = (typeof parentElement == 'string' ? document.getElementById(parentElement) : parentElement); + + var children = parentElement.children; + + //loop through children and add onClick listener + for (var i = 0; i < children.length; i++) { + onClick(children[i],functionCallback); + } +} \ No newline at end of file diff --git a/_ext/scripts/utilities/rgbToHex.js b/_ext/scripts/utilities/rgbToHex.js new file mode 100644 index 0000000..2ef8f73 --- /dev/null +++ b/_ext/scripts/utilities/rgbToHex.js @@ -0,0 +1,23 @@ +//convert rgb values to a hex string for html +function rgbToHex (argument0,g,b) { + var r; + + //if the first argument is an object + if (typeof argument0 === 'object'){ + r = argument0.r; + g = argument0.g; + b = argument0.b; + } + else + r = argument0; + + //console.log('converting rgb('+r+','+g+','+b+') to hex'); + + //convert a decimal number to 2-digit hex + function componentToHex (c) { + var hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; + } + + return componentToHex(r) + componentToHex(g) + componentToHex(b); +} diff --git a/_ext/scripts/utilities/rgbToHsl.js b/_ext/scripts/utilities/rgbToHsl.js new file mode 100644 index 0000000..c915732 --- /dev/null +++ b/_ext/scripts/utilities/rgbToHsl.js @@ -0,0 +1,36 @@ +//put in red green blue values and get out hue saturation luminosity values + +function rgbToHsl(argument0, g, b){ + var r; + + //if the first argument is an object + if (typeof argument0 === 'object'){ + r = argument0.r; + g = argument0.g; + b = argument0.b; + } + else + r = argument0; + + + + r /= 255, g /= 255, b /= 255; + + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var hue, saturation, luminosity = (max + min) / 2; + + if(max == min){ + hue = saturation = 0; // achromatic + }else{ + var d = max - min; + saturation = luminosity > 0.5 ? d / (2 - max - min) : d / (max + min); + switch(max){ + case r: hue = (g - b) / d + (g < b ? 6 : 0); break; + case g: hue = (b - r) / d + 2; break; + case b: hue = (r - g) / d + 4; break; + } + hue /= 6; + } + + return {h:hue, s:saturation, l:luminosity}; +} diff --git a/_ext/scripts/utilities/select.js b/_ext/scripts/utilities/select.js new file mode 100644 index 0000000..e4b6664 --- /dev/null +++ b/_ext/scripts/utilities/select.js @@ -0,0 +1,20 @@ +//add class .selected to specified element +function select(elementId) { + //console.log(arguments.callee.caller.name, 'selected ', elementId); + var element = (typeof elementId == 'string' ? document.getElementById(elementId) : elementId); + element.classList.add('selected'); +} + +//remove .selected class from specified element +function deselect(elementId) { + //console.log('deselected ', elementId); + var element = (typeof elementId == 'string' ? document.getElementById(elementId) : elementId); + element.classList.remove('selected'); +} + +//toggle the status of the .selected class on the specified element +function toggle(elementId) { + //console.log('toggled ', elementId); + var element = (typeof elementId == 'string' ? document.getElementById(elementId) : elementId); + element.classList.toggle('selected'); +} \ No newline at end of file diff --git a/build.js b/build.js new file mode 100644 index 0000000..bad3149 --- /dev/null +++ b/build.js @@ -0,0 +1,88 @@ +var fs = require('fs-extra'); +var hbs = require('handlebars'); +var glob = require("glob"); +var sass = require("sass"); +var path = require("path"); +var gulp = require('gulp'); +var include = require('gulp-include'); + +const BUILDDIR = './build'; + +console.log(process.env.HOST?true:false) + +console.log('Building Procedural Pixel Art Generator'); + +hbs.registerHelper('svg', require('handlebars-helper-svg')); +require('hbs-register-helpers')(hbs,'./_ext/modules/hbs/helpers/**/*.js'); +require('hbs-register-partials')(hbs,'./_ext/modules/hbs/_*.hbs'); + +//empty the build folder, or create it +fs.emptyDirSync(BUILDDIR); + + +//copy images +fs.copySync('./images','./build/procedural-pixel-art-generator'); + +//render js +gulp.src('./js/*.js') + .pipe(include({includePaths: [ + '_ext/scripts', + 'js' + ]})) + .on('error', console.log) + .pipe(gulp.dest("build/procedural-pixel-art-generator")); + + +//render css +var sassFiles = glob.sync('css/*.scss'); + +sassFiles.forEach((s) => { + + var f = sass.renderSync({file: s, outFile: 'test.css', includePaths: ['css', '_ext/sass', '_ext/modules/css']}); + + console.log('compiling:',path.basename(f.stats.entry)) + fs.writeFileSync('build/procedural-pixel-art-generator/'+path.basename(f.stats.entry,'scss')+'css', f.css); +}); + + + +//compile content +var contentTemplate = hbs.compile(fs.readFileSync('./views/procedural-pixel-art-generator.hbs',{encoding: 'utf8'})); +var content = contentTemplate(); + +//compile page +var pageTemplate = hbs.compile(fs.readFileSync('./_ext/modules/hbs/layout-contribute.hbs',{encoding: 'utf8'})); +var page = pageTemplate({ + projectSlug: 'procedural-pixel-art-generator', + title: 'Lospec Procedural Pixel Art Generator', + body: content, + css: ['https://lospec.com/stylesheets/style.css','procedural-pixel-art-generator/procedural-pixel-art-generator.css'], + js: ['procedural-pixel-art-generator/procedural-pixel-art-generator.js'], +}); + +//save output +fs.writeFileSync('./build/index.htm',page); + + +//server +const express = require('express'); +const app = express(); + + +//ROUTE - index.htm +app.get('/', (req, res) => res.sendFile(path.join(__dirname+'/build/index.htm'))); + +//ROUTE - other files +app.use(express.static(path.join(__dirname, 'build'))); + +//start server +app.listen(3000, () => { + console.log('\nTemp server started at http://localhost:3000!'); + console.log('press ctrl+c to stop '); + + var opn = require('opn'); + + // opens the url in the default browser + opn('http://localhost:3000'); + +}); diff --git a/css/pixel-editor-splash-page.scss b/css/pixel-editor-splash-page.scss new file mode 100644 index 0000000..36d5f30 --- /dev/null +++ b/css/pixel-editor-splash-page.scss @@ -0,0 +1,10 @@ + +.pixel-editor-screenshot { + box-shadow: 0px 10px 40px 0px rgba(0,0,0,0.45); + margin: auto; + display: block; + max-width: 100%; + border: solid 5px white; +} + + diff --git a/css/pixel-editor.scss b/css/pixel-editor.scss new file mode 100644 index 0000000..185b214 --- /dev/null +++ b/css/pixel-editor.scss @@ -0,0 +1,719 @@ +@import 'colors'; +@import 'zindex'; +body { + background: color(indent-dark); + font-family: 'Roboto', sans-serif; + margin: 0; + padding: 0; + color: #fff; + font-size: 14px; + width: 100%; + height: 100%; + overflow: hidden; + -moz-user-select: none; + /* Firefox */ + -ms-user-select: none; + /* Internet Explorer */ + -khtml-user-select: none; + /* KHTML browsers (e.g. Konqueror) */ + -webkit-user-select: none; + /* Chrome, Safari, and Opera */ + -webkit-touch-callout: none; + /* Disable Android and iOS callouts*/ +} + +//don't let svg handle click events, just send to parents +svg { + pointer-events: none; + path { + pointer-events: none; + } +} + +//remove blue outline in chrome +*:focus { + outline: 0 !important; +} + +#pixel-canvas { + cursor: url('/pixel-art-where-to-start/pencil-tool-cursor.png'); + + border: solid 1px #fff; + image-rendering:optimizeSpeed; /* Legal fallback */ + image-rendering:-moz-crisp-edges; /* Firefox */ + image-rendering:-o-crisp-edges; /* Opera */ + image-rendering:-webkit-optimize-contrast; /* Safari */ + image-rendering:optimize-contrast; /* CSS3 Proposed */ + image-rendering:crisp-edges; /* CSS4 Proposed */ + image-rendering:pixelated; /* CSS4 Proposed */ + -ms-interpolation-mode:nearest-neighbor; /* IE8+ */ + width: 400px; + height: 400px; + display: none; + position: fixed; + box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.64); +} + +#eyedropper-preview { + position: absolute; + width: 45px; + height: 45px; + border-radius: 30px; + border: solid 10px red; + z-index: 1200; + display: none; + box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.25), inset 0 0 6px 0 rgba(0, 0, 0, 0.2); + pointer-events: none; + &.dark { + box-shadow: 0 0 8px 0 rgba(255, 255, 255, 0.5), inset 0 0 6px 0 rgba(255, 255, 255, 0.5); + } +} + +#brush-preview { + position: absolute; + border: solid 1px #fff; + z-index: 1200; + box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.5), inset 0 0 2px 0 rgba(0, 0, 0, 0.5); + pointer-events: none; + left: -500px; + &.dark { + border: solid 1px #000; + box-shadow: 0 0 3px 0 rgba(255, 255, 255, 0.8), inset 0 0 3px 0 rgba(255, 255, 255, 0.8); + } +} + +#canvas-view { + background: color(indent-dark); + bottom: 0px; + left: 64px; + right: 48px; + top: 48px; + cursor: none; + position: fixed; + display: block; +} + +#canvas-view-shadow { + box-shadow: inset 0px 0px 4px 0px rgba(0, 0, 0, 0.4); + position: fixed; + bottom: 0px; + left: 64px; + right: 48px; + top: 48px; + display: block; + pointer-events: none; +} + +#main-menu { + height: 48px; + left: 0; + right: 0; + list-style-type: none; + margin: 0; + padding: 0; + background-color: color(base); + position: fixed; + z-index: 1110; + overflow: visible; + &>li { + float: left; + height: 100%; + } + li button { + color: color(menu, foreground); + height: 100%; + padding: 17px; + background: none; + border: none; + cursor: pointer; + } + li.selected { + background-color: color(menu); + &>button { + color: color(menu, foreground, hover); + } + ul { + display: block; + } + } + li ul { + display: none; + position: absolute; + top: 48px; + list-style-type: none; + padding: 0; + margin: 0; + background-color: color(menu); + box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.5); + padding-bottom: 2px; + li { + width: 100%; + button { + width: 100%; + text-align: left; + padding: 8px 32px 8px 16px; + &:hover { + background-color: color(menu, background, hover); + color: color(menu, foreground, hover); + } + } + } + } + .disabled { + color: #6f6e70 !important; + } +} + + +/*app title*/ + +.logo { + color: #6f6873; + text-transform: uppercase; + font-weight: bold; + padding: 17px 10px 0; + cursor: default; + box-sizing: border-box; +} + +#data-holders { + display: none; +} + +#tools-menu, +#colors-menu { + list-style-type: none; + top: 48px; + bottom: 0; + padding: 0; + margin: 0; + background-color: color(base); + box-sizing: border-box; + position: fixed; + z-index: 1120; +} + +#tools-menu { + left: 0; + width: 64px; +} + +#colors-menu { + right: 0; + width: 48px; + display: flex; + justify-content: flex-start; + flex-direction: column; + li { + width: 48px; + flex-basis: 48px; + + &:not(.noshrink) { + flex-grow: 1; + } + &.noshrink { + flex-grow: 0; + flex-shrink: 0; + } + } +} + +//added when the color is a duplicate of another +#duplicate-color-warning { + display: inline-block; + visibility: hidden; + margin-left: 5px; + opacity: 0.75; + cursor: help; + &:hover { + opacity: 0.9; + } +} + +.shake { + animation: shake 0.82s cubic-bezier(.36, .07, .19, .97) both; + position: relative; +} + +@keyframes shake { + 10%, + 90% { + transform: translate3d(-1px, 0, 0); + } + 20%, + 80% { + transform: translate3d(1px, 0, 0); + } + 30%, + 50%, + 70% { + transform: translate3d(-2px, 0, 0); + } + 40%, + 60% { + transform: translate3d(2px, 0, 0); + } +} + +//floating button to open jscolor picker +.color-edit-button { + position: absolute; + top: 3px; + left: 0px; + background: color(base); + padding: 6px 10px 3px 6px; + border-radius: 4px 0 0 4px; + cursor: pointer; + transition: left 0.25s; + z-index: -1; + box-shadow: 0px 15px 15px 0px rgba(0, 0, 0, 0.2); + path { + fill: color(base, foreground); + } + &:hover { + background: color(base, background, hover); + path { + fill: color(base, foreground, hover); + } + } + //class added when jscolor is opened + &.hidden { + left: 0px !important; + } +} + +#colors-menu li:hover .color-edit-button { + display: block; + left: -32px; +} + +#colors-menu li.selected:hover .color-edit-button { + display: block; + left: -35px; +} + +#tools-menu li, +#colors-menu li { + position: relative; +} + +#colors-menu li button { + height: 100%; + display: block; +} + +.color-value { + display: none; +} + +#add-color-button { + background: color(base); + + path { + fill: #6f6873; + } +} + +#tools-menu li button:first-child { + text-align: center; + border: none; + background: none; + width: 100%; + padding: 0; + cursor: pointer; + height: 64px; +} + +#tools-menu li button path { + fill: color(base, foreground); +} + +#tools-menu li:hover button:first-child path { + fill: color(base, foreground, hover); +} + +#colors-menu li { + button { + border: none; + width: 100%; + cursor: url('/pixel-editor/eyedropper.png'), auto; + } + //white outline + &.selected button::before { + content: ""; + display: block; + position: absolute; + top: -3px; + left: -3px; + border: solid 3px #fff; + width: 100%; + height: 100%; + border-radius: 4px; + box-shadow: 0px 0px 0px 3px rgba(0, 0, 0, 0.15); + z-index: 10; + } + //inner outline + &.selected button::after { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + border: solid 1px rgba(0, 0, 0, 0.15); + width: 100%; + height: 100%; + box-sizing: border-box; + } +} + +#colors-menu li.noshrink button { + cursor: pointer; +} + +#tools-menu li.selected { + background: color(selectedTool, background) !important; +} + +#tools-menu li.selected button:first-child path { + fill: color(selectedTool, foreground); +} + +#tools-menu li.selected.expanded { + padding-bottom: 10px; +} + +#tools-menu li:hover, +#main-menu li button:hover, +#add-color-button:hover, +#main-menu li.open { + background: color(base, background, hover); +} + +.tools-menu-sub-button { + text-align: center; + border: none; + background: none; + cursor: pointer; + width: 50%; + height: 22px; + display: none; + line-height: 0; + overflow: hidden; + position: absolute; + bottom: 0; + + path { + fill: color(subbutton, foreground) !important; + } + &:hover { + background: color(subbutton, background, hover) !important; + path { + fill: color(subbutton, foreground, hover) !important; + } + } +} + +#tools-menu li button#pencil-bigger-button, +#tools-menu li button#zoom-in-button { + left: 0; +} + +#tools-menu li button#pencil-smaller-button, +#tools-menu li button#zoom-out-button { + right: 0; +} + +#tools-menu li.selected button#pencil-bigger-button, +#tools-menu li.selected button#pencil-smaller-button, +#tools-menu li.selected button#zoom-in-button, +#tools-menu li.selected button#zoom-out-button { + display: block; +} + +#pop-up-container { + position: fixed; + z-index: 2000; + width: 100%; + height: 100%; + background-color: rgba(35, 32, 36, 0.75); + display: none; + color: color(base, foreground, text); + cursor: default; + &>div { + background: color(base); + border-radius: 3px; + box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.5); + width: 400px; + padding: 20px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: none; + } + h1 { + margin: 0 0 10px; + color: color(base, foreground, h1); + text-transform: uppercase; + font-size: 1.5em; + } + h2 { + margin: 25px 0 10px; + color: color(base, foreground, h2); + text-transform: uppercase; + font-size: 1em; + } + a { + color: color(base, foreground, link); + border-bottom: dotted 1px transparent; + text-decoration: none; + &:hover { + border-bottom: dotted 1px color(base, foreground, text); + } + } + + .close-button { + width: 32px; + height: 32px; + position: absolute; + right: 0; + top: 0; + background: transparent; + border: none; + color: color(base, foreground); + font-weight: bold; + font-size: 1em; + cursor: pointer; + border-radius: 0 3px 0 0; + path { + fill: color(base, foreground); + } + &:hover { + background: color(base, background, hover); + path { + fill: color(base, foreground, hover); + } + } + } + + button.default { + float: right; + background: color(button); + border: none; + border-radius: 4px; + color: color(button, foreground); + padding: 10px 20px; + cursor: pointer; + margin: 20px 0 0 10px; + &:hover { + background: color(button, background, hover); + } + } + + input { + background: color(indent); + border: none; + border-radius: 4px; + color: color(indent, foreground); + padding: 10px 20px; + margin: 0; + width: 60px; + text-align: center; + } + + .dropdown-button { + background: color(button) url('/pixel-editor/dropdown-arrow.png') right center no-repeat; + border: none; + border-radius: 4px; + color: color(button, foreground); + padding: 5px 20px 5px 5px; + cursor: pointer; + margin: 0; + width: 200px; + text-align: left; + &:hover { + background: color(button, background, hover) url('/pixel-editor/dropdown-arrow-hover.png') right center no-repeat; + } + &.selected { + border-radius: 4px 4px 0 0; + } + } + + .dropdown-menu { + background: color(button); + border: none; + color: color(button, foreground); + padding: 0; + margin: -1px 0 0 0; + width: 200px; + text-align: left; + position: absolute; + border-radius: 0 0 4px 4px; + overflow: hidden; + display: none; + &.selected { + display: block; + } + + button { + background: color(button); + border: none; + color: color(button, foreground); + padding: 5px 20px 5px 5px; + cursor: pointer; + margin: 0; + width: 100%; + text-align: left; + &:hover { + background: color(button, background, hover); + } + } + } +} + +.keyboard-key { + background: lighten($base-color, 20%); + box-shadow: 0 3px 0 2px lighten($base-color, 12%); + padding: 0 4px; + border-radius: 2px; + margin: 6px; + display: inline-block; + color: #c0bfc1; +} + +#settings-container { + display: flex; + align-items: baseline; + label { + flex: 1; + } + input { + width: 90px !important; + display: block; + box-sizing: border-box; + } +} + +.preload { + display: none; +} + +#new-pixel-warning { + display: none; + text-align: center; + margin: 20px 0 0; + font-style: italic; +} + +.dimentions-x { + margin: -2px 7px; + path { + fill: color(base, foreground) + } +} + +.jscolor-picker-bottom { + display: none; + position: absolute; + left: -4px; + right: -4px; + bottom: -7px; + color: color(base, foreground, text); + span { + margin-left: 5px; + } + input { + width: 64px; + background: color(indent); + color: color(indent, foreground); + border-radius: 4px; + border: none; + margin: 0; + padding: 3px 12px; + margin-left: 5px; + } +} + +.delete-color-button { + background: none; + padding: 0px; + border: none; + text-align: center; + cursor: pointer; + float: right; + path { + fill: color(base, foreground); + } + &:hover path { + fill: color(base, foreground, hover); + } + &.disabled { + cursor: not-allowed; + & path { + fill: color(base, foreground, disabled) !important; + } + } +} + +#no-palette-button { + display: none; +} + +#cookies-disabled-warning { + display: none; + color: color(base, foreground, weak); + font-style: italic; +} + +#compatibility-warning { + display: flex; + justify-content: center; + align-items: center; + visibility: hidden; + z-index: 3000; + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(35, 32, 36, 0.92); + color: color(base, foreground, text); + div { + position: relative; + width: 100%; + height: 100%; + + div { + width: 400px; + background-color: color(base); + padding: 20px; + width: 400px; + height: 200px; + + position: absolute; + top: 50%; + left: 50%; + + margin: -120px 0 0 -220px; + } + } + a { + color: color(base, foreground, link); + border-bottom: dotted 1px transparent; + text-decoration: none; + &:hover { + border-bottom: dotted 1px color(base, foreground, text); + } + } + button { + background: color(button); + border: none; + border-radius: 4px; + color: color(button, foreground); + padding: 10px 20px; + cursor: pointer; + margin: 0 auto; + display: block; + &:hover { + background: color(button, background, hover); + } + } +} \ No newline at end of file diff --git a/images/dropdown-arrow-hover.png b/images/dropdown-arrow-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..cae9998844325a4f93fbac89bbd3065b9d424c15 GIT binary patch literal 2915 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 N002ovPDHLkV1iZ)ZGr#* literal 0 HcmV?d00001 diff --git a/images/dropdown-arrow.png b/images/dropdown-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..69fdd40896655655d5191325e6053a9287b67c39 GIT binary patch literal 2916 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 literal 0 HcmV?d00001 diff --git a/images/fill.png b/images/fill.png new file mode 100644 index 0000000000000000000000000000000000000000..febd707b9dbaf62e56ec04acb99b166612640fa2 GIT binary patch literal 3187 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 P00000NkvXXu0mjfQFyY% literal 0 HcmV?d00001 diff --git a/images/pan.png b/images/pan.png new file mode 100644 index 0000000000000000000000000000000000000000..df135ab0a90e4de1fd88717151826ed00e5005f6 GIT binary patch literal 3147 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 literal 0 HcmV?d00001 diff --git a/images/pencil.png b/images/pencil.png new file mode 100644 index 0000000000000000000000000000000000000000..c8dc7a40a342b46a5af28ef3ee6d65e9e8eaaf20 GIT binary patch literal 3047 zcmVKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0003JNklmqt462F-bvzyqKsJAio|Dqwmy@HaYOQ@(g8Vvjm$EFW>ly$N zK~?wGT14btRVNne4{-p1F=i8##U87wLRAqFilW$CYY!rlC0iMW;mPGMh03x_cjN_?gdE`R|Da$BlTe5`2*_jO9EBVZ z1`S-L_F^?757{kkX%zC8bR5UWWG6AG>$(^Jcly4625x{{V$yfYWT4!8z#;0t&M pK7h|v#DmUfAOofu`Ua+-0RZxZ8;=|p6Mg^y002ovPDHLkV1l{lqMQH# literal 0 HcmV?d00001 diff --git a/images/pixel-editor-screenshot.png b/images/pixel-editor-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..2723e651a02215bd257d10fff669f83595100695 GIT binary patch literal 35193 zcmeFZ2UJwe)-Bp*1rtxCqLOnK5Xo6WlUhJQvShlE ztVGF@GrVg24##uOckc6l_l`H-8$-qri@jG>?LDh%)m(E`x38Rx*d-hi90&w*NkUvi z9s)TDg+NYxKYtcnab{Vg0{>mG5?8Z@Kya^O|DAxuy}k~C2s|}WRJBu;mV)UaEEsk4 z5xNG9P8L?c8Uo=JaI(_TGc&NG)HN_NvE-v#Le)@Fn&|UUsc=X$OIryW7@LT@*cd3d z$SCT$nCbE8Qwi|n@H)Xj0~Q8$I+RWp=9acFCqAm9c4455z05>Kd1PW|#z*x4+aaZ@ zv>c@{!p4A-gOQU#kA;n!l8c9tg@cEim7SiFm6?T;iJ6Cqg`I(!1;)$+V`icJ>xYUT z2b6ei^bKM1BBFnF2ma!tGPbj`f-x~UIyy2svN0lTjF?z>czBqYS(#W_8Gr?Yt+S<_ zjuV5W?d_k9{G%Na16w^C6DvCtge4`mT^(J7y&WGF6}F?}pTFMA!s@sqOWVJ&11K^% z=~ywbFfud!T_t_J<2qLMHs(iaqN7Ju72`9m3Wa@n5<;zWrZ2 z24f;EecbooKCgwv-*#EeUH}?Q*v3G|4q>B+K$!FY zoUU^J7z;{aVeIteqP(kOVyTaCw58=``u8gToH+k%%|JxQ&VV0VlYxbqftg*Al@-Ru z3gh4eWf(K_FOB@P{I@ow5&9;E&i~p5haw9Xj1AQ0__d8+EdRX?un_fi>~wysm0xXt zX-8iVW{9w{(6Qq;vCuIxV6w6_;$`~h#a}FsrzcDpVUDl?k7dBm#>@0~mw%~sG-Y8D zmbP{}mU;#fBK+V2qlt+=jFXE~kIj&qoq?0hP=|q8hn<5#hlib$fs>t$n~R-=O_!DD zCy3J0zgGW`rbQ5X_Sj_yn*Qmb^bvZX`F}o^zP>&OmoBp|gN_bxKP)T;TnswQ?CcEs zz?tZ?aEMe{aMVVQA;5V`K2Z2+WV)S@%C%{nu*$ zy?H%j9ZMqveSW5YAoq_8|6YZc3A+oKoBV%qn*mSvd0I-}a91N_6VCr(~v9U7f zaT^%280s_YGjp>4rVfz`oFBjzgh=8<{!l7f5tO@KHM+W|E_h!|1O*P*~{NF^UvP?f&4E3|FQ-0 zVp%>6&>^rnA2~687G9?Rxb{nHNB2~*w8q^2h_-OCGJ_u`7VPi8s{VTW->a(qudDug z``@env!;@zi5)-dU%ma?cke_K=SpEcRJ|E1*7@CUBnLFw4$4_wDk{7&HyT)%_T zvCSX2j-mLS!XLPP2c=`1KX4sG@jHb-aQzNS$2Nc9I)>tR3V-1G9h8o3{=jt%#qSjU z!1X&Q9ozhY>lljPDg1%!cThUE`2*K66u(pW1K01MbZqknu45>Er|<`^-$Civ<_}!Q zQ2b8e4_v>4(y`4SxQ?Otox&fueg~yvn?GKXCmHO2;;T;5vrlcM5;t`W=*x zZT`S@48`vh{=oG+C>`7Uf$JEG-zofo>vvE(w)q3sF%-X3_ygDPpmc2W2d-l%ey8vU zuHQlF*yayh$58xE;SXHDgVM3hAGnU8_?^Q45-yy7eyrZW5`3245qxM~-M143K5$Q| zCoV4yfw)jZAWyv^knKb8{|p3T&kTXgK7l}BF%SqbB22qp6ata3l@NKL=rk}t>>QH1 zsX4vf!Bkw7U9`A6Kbr#|HqD~;GPqbof2!g3_=nGnuY6TwUa=IPIPKK$bN1XJ*#YW!9tTUD54#;?vm20Xst zNeJXTbRPT`q&xwEoF;GpzbQk2(a8(gSH>5|+CSF>zt4j&EO;td%Lt0639$A$#GrWc z0@nWB-!vPi<-bI|%QU+m9Gs;|TDia8XJ1xJEs|^)m*M?6VX4|xwqiRUL4BE zcy);{#owp_-Pe^}9{PGdagk{h-8P6j5NS8-i#KOpuxXz#pU63lm>ouKM&uVswSTL0 z?yJ8SKY*DXCapx;Y4>LjPU#j)S`-*hRmH=Hnk8X*8Hx9EM)q8X;@w0$UN0)~qLK$k z-8CKYpjoohNgk3{6GXGnj%x$!oP5heYDyZQznxhj*EV;T3E0#EQ&kosA1{h`>27gZ zTBOgW9jd%-#6k|eAER9#Tkt)QhjxS~d39RGEHg*RT0uu!8P0Vlr)u|6oa@pHqRm>D z9f7>VsXl>R^x>?Swpe zQzOm$+k54%l(f!pSzc%5DrTsLzO*~3$@$H_I8eIEpSSsLpVgSPs3jb}(}bi5F!2=^AHue6h=_@}h@z z>^JK`6}2fjRJ&AA*0A_)Yv0;Jd`?THR*Iv({3+Wdr}Oh$R`BGdbfGqP+_@c324_Z=6L9I??;^Jbf{ zN^P_E^V?e=sNI*+9vzb-(Rwc(RbiW_*e&hp1 z|LmaB@Z6>9Hn2=R>@Q{Cp>4J_$=hO@$}OG7lyG$Ajp!GgMWwa{gyWtgI~7NQ${6&I zmt?9*L(o%H_h#WRC20SbB#lW3I*W=^V$U5tjWdivRVsx#W7nw{Q9PSXF<8oaJQeFz4Rdft?c{$cS zWUZ|u<<4g<;X0toHxxZHNu4Kq*`%aO#a+1DExS8@i7qmV5b>Dm%7&+}iltZiMulgx z0BWF#;VzvEy132rR#k-O!L&dMv7L0%ZlC~q?oy?DWU#D}=X-B|I^3AEh;)Lx(MXl)qQ}Fe zgFu1E{Jm8E{cZtF@Oz0eyw00mpUTMEYF4F(=aM>mw_?ts!e?*trad}UN8+-Csw^$> z+`6Vkz^UIep!yo^mvb%6?qc)e$GUv&=IAP@5#q5U@kY*}B>hxeHla%H*!#3@7U*^% zBVMZhMF|(#nG~vrxrtY^BsY?z&E^(e4KyFFv*Z$c9I&~3-0wslzL7n2j;t3e_`dwj z%w@fJ+f#x`6Fo?m)ICYpJ(p}sO*@)zmb6Q}Qydw#ju5{Vq?Q~xt=eexu)u?wLLt98 z&5UXP`HL6SSHz#voQKYs=kwI$X_8sbXqA5X{AQzch}edcfjW%OH26{MZgFKbf6m;p zL*Q$7$>2aJ2e&o+)T1SG4 z`e)?05nnb@jb86A@DydLsF@eu49zRjRC&QZ^useqvaAi=vihOXNG*r18{c!zi{=7! z&}?8JUeTyX#XEc$9@8v+ZXxEnVsL0lSr*d5uLgQOMTnK{!|6;AnOL?Im=S)MYv((=1KRybwdJkmFY zzClgyDW9@%8^(uBMGle%?|p4J*KVAt9yZ)PWx8=fOXo4Ex)Dd`X#T-BYibN}L=+(f z`D0I1>U=t!&hPVVJT%%MVD5rjZ0XrE%taQ(S;rkxQC3hg_)6^ zz^@nVPX5*^>uhQ^pZ`92kj;O#hC6++*=WsO*cE04_Q3`NbGRAql8Hk1)JhS}ZX=q1 zEoLO?W{(3bU0*hO0BW#^Fx70_jh1@rC@HIz_~W$Sq#2jJqlZKtabqu!$3ub6vJ|om zX-%>`Kf0VYx3Z49xt`eNTS&G2LV4BEkmHN3a+Npn+QHKOw0-aL zf&1`l(J2dQsJMa%jw0L2D(1Hb3d1BZF_;hCs7m4u*;@3WNNk-jC=C1!_ zxyQs66n;`%V0lD_$t$9K3!BqNPB@m1DA5}d`ZwwagtE}E&vCNz<2TLaG7jOQVSTpl zwGQ;N&hKX{+I6m$+UCXvpOIN~E5)?H>F&%mS?BJ&a3Q%L!oKSjR39+Luv{ldfGbNW zQ9^UlIl+mFJlj5hdgu9qYVWEO+BTt%#(Y|pjRN=e(6)Jk1|>^_c&vFH;*J-TT!Efk z;XO5@UVv^g4)o*A#Hc8zOiV@M+|^RO#~tHJ9rl-pDN3W9dJ+YU6Ic72&k;z}C!G#E ztNcBk_W2dP0KzaS0{=5?pE45j_&qQ@Qh9Rg{6y=#TI(9qo$mR<4nZ8Ng&!^?bSV4P zLmMA|e?f{bPEHf@>2jU;w-537sbL?gW2DNfrRFO51Gml)_a2yh`bu2tejWYvc>{k^ zp7|n0y9|HC^Y0Yhr^7xRnBII5@$PvVWf>j&+|W~s*OiG^(@*sAq$W+K^LpZMul6@Z z*muR8hY}22Noyigga|(G4|_upTEKt}ATE*1m1$Q5@UDQM zMUbraRiOOfdF=fL+}n-LL4siTpLI~@r(ZGiFE)S6ybukp2$m~|t9`$VOZmA71U~B* zZyWfFq+XY}i<=S{5b(Vvl5SvlxC_mdZ|Z7vhkXw7%BOTMSC+4g&V0ACV~73in;_TQ zIHsssXYzyQoWr6i*24)OPc7RUeitXWb+mLes?XlP_- z)C;Q82g?w)Bj9V^7|fj1#pb_p6PRhXb{g0g>4=2AID2lr+9(O#Uu?v~a{_W#n*Pd_ zIHIsSjM|uUNv`>0w!+@tJDs>p#(H|1q@?k$K`^=^_U)FLL7v4HMn1Ik&EZ^u1E0ibOoE5%~kUssvH&?-req^ z*ncboM#uJCMBPbf=(6~!=E#i1ZqTdDbVpL382`!@?c~&q43+Tk@Yk;)Pp8`NZZL<6 z=*%wN#)bNv$UTF#dMU7{V^9>NwcQ=fUc;=tyjfbxi7YN|jXe*cj37J+R^!AY7vZust+9 zEVbrvr*9n-kKpenBPylR(;3Wn@iTo~AWG=DF9R)Wm7V@EHlRF=nI=AL6UeFfytFq< zfzfy|X7X(twev)x-o-RT5r-)-GoJy9u5J4ACB~>iQcv>WU{1Ra%{B7%;yDO=^zDq^ zFHLs+y?H*L5ZA3=643U^uPhzzC(+u6jHRB2P*R9cg$eu3b_>}IF^^rQ&X7r<3V=Xf zQA8$DhP<}B9%`c}YG`Q44ZiuWErLIYj6Y_>wN~dj%WVjxRw5F`3F8o#VGqFv+5)#% zKv+$8LEsmMgr+yjwdWxayeqJv0Me1^n~jfQkzEIiUb*A!6B}`=^jm^smv8Ux?moW) zBK1f2@{5dvlwgs{vy^$5(vt2hyHf~N&M#P-un0-A?{ySAAunFMjK3Gat{YnTPUrBZ zg^fE?3^5!p{Z0Sjftdh#)0`CVvPd17Kf-N>jw5;->ACw*2G0AgOQEiUwpzyWZNa#z z0h`)_`NF;U=6eb4cf(uQ_Z-d=i6=U(H0xeh)6nSs{F%)C>#5PDIY*NlCm@s=x{iUa zU$c>t3mJaiYGNngI#a@` z*4U#fbo~V6YZJS?0!_FQO?V_U@M7@y&4XY8d4wZE7_sr>P|nILRqp-#$Uq$HjD~>n zE#vox?j704JmkEimg@os7CN!#S2yd|awCgk&6htOE*g1ceXA3H@Wd+>pWDU7#cp}* zp#kfS;Y$0Q>)ev+lE&bX-@FdZVr^{@FKRI)Q&UqjTd&SYWZ>Twa0gu7(Q`0nJd%GU zMSy6JKEAM!B``41WcR=wH1H^FZDSBI*Agz9nUN9Ad=m2ER5PAwo1uU3S~*2yr8zw87aQa8{iR0;uoujG|ILVnw5<)#?V`j36{C!UX4(CS zX_f{si+(`V>aPg}_!{2zMb8Cd->qt6!0fdW1adQ+WyDHXpQf>U_55Gb6$RJz3GjZLHOdK+aYnw6Wfo#wpXlXNJtKh|*&=J8`2kCw>HtZCu1p<^(y_>Su? zdKj&+;Urd6XM31>@&R&_$^P{w1qp@RTaIKAGykY*6=D0QcVUF&9)5}LEA_1$(G?E% z=~JxH1+Oj3_t_3H>dEk=Xzc@K%7Rrsf!#v!#ExasN5~pV#()(^diE?Rv1CA~%_SPq zyoEeTvQ*oontGo7;F^P+oi@+?o=a7+jW|%0E2{p7jxGx|oFFXKj)q~5J1lh}A)NHl zn8%^1%*y@=h|}{}C>N?>qS9jE{(#-PB>5dm22w8}YEXIA-!@MBbvV@C(SEMmXQ<}< z(&rIO!_-dmA>HS700}#p%HpfWaj@k?hPUy3>FUC~JJTER4bBOHJQsLbp1$8gyG@1g zZ8fpz`m!Y`jL@6^=-94vvDJ|6H3LvQ$wd4jJN@u(5ySU`{avsXx4Vbz?Dqs2fF*N_Ylny(X zh^e`_4P=}FEtc`GG`_sRl9B7Nb&`4YDcM?xd@i!Sai4?gDjVzLs#3Ol zIN9IT=b_sz; zY3eU><+K?T2dXfo$dH~8PMNe%nqNPZDsP9OO>5iE>5YFe1q5HKRQ~$UwQ79uZe%cB ze(zoV+<*x6>whe*%v^N#WXXTz)QUM29zo@!f}Ck29NOyNX>_6xMRD zjMCi=RDIGW`=D5M|FanOam5oT^b~GiavY*i(<#wCcvt^M$HEelQ|O~1U*VJLSuQ*9 z#q4`caB!**l-!>#RQ$&Ra3Jx^(C}H@FYwkY;%+{njp8^aKKph{_dePgEglZj@W$7{Khkf$2naNIKKnMu=JKQvtWz~gkj(N)#x zz|gLW;vN#x8_}VUVF}$kJ8_GQpNYI*M56Nsxsc^0Z|q=sk?3ohHQV_Fd3lxW`{iSW z+(uN8H$ZWVT>8@OO0BZR5h&mXeJ9Au%9~xC$S=`7JK(3~xr(afb5(cebC-k?)H4@V zTX(Fw4&LANPo5~8@y7y`{en|W^KB=oOe`?B`X-rhmw((SZl zXzV;?1Oy_-5gwt@nC^rgZIGq*3cO`vW)Tjxu(Tk`*udc278TXh9QLaZC@n3s+n++Y znD4wLkFU<0;~G`MG-K?IG-9gXsPaaU9?GY!`@c0hieiq&ySJ@B5bUwC>tK)FCsf7F z#_vbwuzAhby}Rav3)VJYU!&W!a+|>(As=qb9qsoTwz1LLCl&OIBAnucCG(3nuO9AEpY(SKc%p#CX;azfZWdHkHJ6Vu?Nb zL!~(|rj|J6z7N+@>)vQ@4;^L?9}ll*rjl65TYzy`51`$9^1mhPP=}M5npr5Ro8?r( zP#ioSlCs(!q8531uirzSinFD8tF#io`iwL`i3_DOdlZ(sm`@I-&TPUQLg~_N`Fv%G ztRT105}~_U7=v)}lG)k{e^!>dejLavmwP5SUZ=rqW4xVGpLRx~d*V~X&Wg9jBYVg7 z@4#$!c9zlA($Nv+gu<81mY})c+jswiFAl``t(1JS1eC-;oZRE0Z)lfZ4>3A+zFWO- zRME&NqY~x9?!NI1yc@E*f>A=Uq$CrS)7ZlrW?xi)gJZ@{(s8=Vd%DzdaqeIv3K*}n zrts~Ha1Fi{e80z>-|nZGWGBauNi)ckV5%3NyWds49(l_XS|OZ zc@ipmL-id3PKzJf_@!so&?@x zZ3G+J+apt-K6(dY)D4AmUW-rJGCDHzs-!Iz@+z%YUr2p!x^B4iA?J#J#x`JRYJBcv z)reRU0+;YNp*N=f*Qs)t-#<*wedAWf&(W>3y5tizS~~>%!@D!n#f4kwV-Q zrSeyZWahZM!mppUMCuL`+`sb&SNQNq$OG2fo|KU95;;m)`zm5?2RvxDd8Aj)OPy5t zkZ-0nl{HmL^Kmmvq)wS~@^k^>^7kTbhDWA9pMi*k^W^rLO7=)da5&eYJ#3?ti6~$Z zTNgm&(`Fda+|j^eP^usAvz23?q6w2C?jp{jS9EsERgSUK7#ud`r=(Y0FGg37)d7 z>7<(`L+2AFLUrpZ8_ah0^Qo7ilIJc$p1w*A^=lar3Gf|a4vPwRTTY97C{ck4IjBCd zzP{dS{d(-oIyrj)ucU|Hw=L`a277)dG1cuAQjTcngB7Qq#UvLZc9h$s9cAm5+BC7Y zn>N<#oZFqJcrR}oTjj6dHJ>zrD)xHcez<}-*cH^8Ce_}NY%@rwd4ct0PH_|-IA^Q( zDwxn3Q+tmnE=?@i@*Qqe|ETK#Re9@!7>ZL`B-OK~yqywmI8RCg9uO{R6 z5Ch%M#$Zv{%B@a$;+zx-yOVOLh_O;T_ z364apcg#w)5|b11F5Xz)ang|D2hS17PzBL^nm4?y%l#)iS;LH@3-dl7y2$_zEP9Q*G#xDVZTS%6qlfVNk8_@ zNULlYn*d26Ad`cW*s=T>>cu9`jt6fGyL@|@#Po=WbM&HaUL;akdmmLJpaHEk5I4RB z)*F59=*H#o{UhCudnQ8uez*uk6HB6fcWXJ)U1(?-guB0MMjpjn8&BoQg{f5hpJNHv z`SQl#h#+c~Yy!!aBq(_Lr{OqsV$}FJDJLLLVa#GacxJb|+=a1(bBH#z@_gWJlN~iq zVwog`q7-2=vIUpT_O=)5hb}~_xGgQwqnQ<-zdVs`Ga%|KHDz3FWa`tJ3TM*FuaNrU zm$ALP(mI+w=&(&IKm9UE$N>Z?8_SYNfA5D#r?{l?gB3Gdj%eU@E}|XyoWCsttenRe zjLD?t#xLBKy|FDSiVaLJg-hqYRq;A{c425}N@ECi1_=rV+;MPXnlwv^;>U)z?APZ4 z8Low01cM8CEeD~E2;o}%`~X{@TW8TSzNJ5-c73M-HDRea~SV zX|BOQO+(@>4(DF!B1S(GdT>qF%&BsqT_bFCuXgT)#Xb;S zwg!+sFYZsflQ~i}wsur_!3rY@9<9N}g-FH2nlE1hd2W5-?SMi_nDy2~A#4315-7yqP6PGt-q4xVk#n73zF(A+O|! zntgC@kKYg*v$#rY*C&z_3ANJ`uS8YaJItEbGmmldGQagYc+jo|5lpS~ZGnqTkSW&E z$G_xa;mq}ePg+F=^`z;z{qS# z$tND`6Ohm+d@8wxBb_?I=c%te-eipTEtHVGPRnfxtP_UG+MhrK?a;GPEPhgbSYnLb znb}s}IXHr`14%_{={NbBoV({-;TrJHFWYcQCNbY+Zk+yU>p!Ww;MUxmlGOWgv6 zfKTck;en|k7$97~q?2Yp1CG_&&xe4Vb?hxBohxeB-t|e!uK1irY(`Ayy1pg=%8nj~ z{;x}}v_Uc9GLk3@k{aJsAR%H3D^bijk?5DW1T%JUHb*17kb!CXGZ6MG>XVk^Tq(Wo za%O<1z=K)B(}C8IKNUSyQd86Dd;wxybX^x(ksU$W=XpA8+$YqxOJR0l#{JFcD3GaZ z$6j-madO`W0%E903)u7nbOXVrUwH=X9m%!|)98^zdj8UqqPr5;jD{m5GkPjHZ2hCI z0I`=PtZ+pNC+KY@$f8pvKk5mN_PMf&G&c1KYuk+n+M(cc+n2O8QGr>656TCYPSM)+ zM7#*yuHPQp-}d2AGvnZtci(91{15_!xv8kd7FZM;F$7fTYgoMjG=AOzngEVcIEtOa507Q8+=1N(!uR40-kkq~wN2hFeF-7?sqN74%gvFT5C? z`hYUuyZFSOuBuZDmVk+0(qd@;a0_U1aei4ggR4Ju>Xr50K33=NBnqRfZMx4jia9t) zG@Lf{^Hy16{A}~=BqT1JkLsO>_!F;BpQN7Xq&}rQ379xUKKU|y@mp=gliU5q0kjwB zLoVV4ZIOJqClNkA!g71?{(ZP=<)Z{JHsMC0VK8VU2_#j6OZrnoDNtL1oa0~SxZx0l zX0>sR!pVg|6mjk(-dir+b8&gxOzm|h%zWdAu7C`udgAwhuC4E<8Gy=slX~=_!ic1< zzSR2G@;1|Za3X~)r;ju+SjJe_yof%)+|t5kF{rJoNbn20} zK&l`HtobrEfkpjpv;Ip)O{e!`MRq;U zd$bSQw9l--=(IgjrdT_)$qOn1l7!Sfq=M}*78GZdpxIZp`#ZR_^W809doE|iwYu%b z_niV<#f^T|MrA=rzav2nPv6~R&Om@}O&uBR6HMj7WyEz-jV9Cf;HQ|mH+8r%i0nv; z<{z+1O1Psr8i_%7f8*vPev}(&pL?@0bey}Q)>uuf-&}bHh-BUo8xEUmZHK|N#$l-k z)en$mNVGjJ1B=)!01u?}DtHtcw+kpFK>q{U71NxVi80M>VG+#qfb{eO)pqd2;c-Ouo;j zPr4)X-Sb|lw>^DE_Is;|h^LVbh&)pkaK7#bW&bB4PZGTe?uw`@db*)m(8ErziH5wn z*_nl&h8@0V=Yj4NBCNkcJ*KHAX44@y9lx1;4r!Q>i|I4D2DZ|>KC$oXZ&c+Ig$dz@ z2DV3x3SwCWHxDL3e(1!fn4y3p_H&;1>CKIv2^SL_9q8yi0wsoiOrK!JbQ~qI1k;3crc{os# z_S^(X3$1#3KgnS^uCH;z z;S9&Jp*np6g+w{?h4SO1XMZw9SBjdu%eU zb?Ei*d42aiLPwnJEv4JpM$08;Dn_#)f4!~sU}0KxtyN%Oqs_o$HMp9*$#t6tkF+aC z`(R)>x-g94Q}AAJ(mvuaHg~)UPS*$rxgCRIwX~iaX_w^(hWpV;hxOW=dk1fUTK|-p zwZcjls-obVllRGeneQwTC_t3LO*&tH@Ehc}bAofYEu2g;`)O51zh>S^!7M6Zd)iu?R5f#=cq4}EOG@wUJo&y|9t+B@zt+BGKhac-h zGW0h71EI^1Lq-cey4JSIw&XI0%??NDL&C0mcBN@3lG`{+` z+2+zMK_(yZ_z`cq!qp6a9cXx4CUBTQKr7DZXtpjI7##(HVZHAKh@b?1sLd%jStD)3 zwUfTGz*V-thGpcp%XEx8E>>rt(Mrft8oO_M)YOCFF7tcT5*0+@X_F5b=-Z<=0fp`~ zmtu4x%q}4k68~0s*G(z$JAJ%-yk_;okXNwEXRJ78@(8WQIbVc3jWFffr2!O-s~$Zf-fw_^#US z_oa>w?7Zx6KOQ`|bm_90g}d}-Ss6DA;2mk~CWQHmW~*p;pqbqTm0donFZBVi`zWZy z>LXn61YP?J3_vF-9$TR^ql0T`^=`Dg4_Dhh0yHSBw`aIc0#KY4D|xLfrLX|shf{FQ zI~bwvfv~$WDgy%p^!$l38FZK7!*&dM21w6$8Gx%C9Gu_@N?Ay-fJ*}8BZd7KC>yRy z4V_X;coh*;&_ho;?d4fqh13im&%ovn=_=V=yZ6e)^YHW?&JHu6&aA?7%L9vh`x|( z^*UJZiXw=m2G<+;4}#o(Ol#qwr`L!*7L6{~y+k#L>$tr_76tKLyvY2<$R+BWq zS)2jOdDfN>NZ`?@Wv(@t6_a-4D?-}+{laYZ4 ziWI-Je|SkHTDp4U)h?WKgvun4p@ae2pPQ0~P8({EMz5G!n!fYBfkc+tGyqCL`8q;C z`KF9K(UbGgJ5=FqH#g!XS|(5yAT$o+mS;3D6>D!65j66tX7XtbtF z0m*o0cL$BZ-IGHq%Vu zupW>-dhHkAW@v|S*IZbzsHfXeE-G~HuoZ|LD5$sy#wPT>xrZsj8S`$1^OcCn#bzKX z6^2?%RJhziRPT#gQ_jXJ7=cLzrw%c9T2#p%f>(Z)s9;%592gkv#f)S-@s3!X>y&-K zCXiUe+pg{PQn5owe|;k3RaBCYz6Xx_naP#No@AGVH-6sRw1ULF4rh?<9)7K7l$sdL zhaPs2H!SU=1R~6nRR?ss*c+`U^vMjmW5wqSUXu61%fpstXN!!KKYt#+VX-tct&RiI zbdS@-8br+5C@OuR;r4{VkCvStH6IjLI`5_@y6xfI0a_9NjlQqw?zr36Y$d;m_8AK! zHigSVhH8>fcSnA7z9R5_K3!o4OWVxW7zP{pmc-p_davF(sFeW`4)+mm3rr39N!{+? zvz>Li^v5ZTVLb~~qgL0KZ(Bi9m~W>6Ay%GwqP)a47b?q%pyUx>Fta?!PaO(~UoP~$ zs@ByWG^852?|q6#CaU-vq?7l=WYg(dq`6;|N_5HlFcju<(~r!vJLgVmcWrFw^M}ko zOt%z(Ks;hnYF{ElzgJ(#wc8tO3FGAC>IEwC_s&e%{mPS?UHa;-j3M(&)`nmP-Fu;U zE!i{a9Fe$u)+vSpMw6u{+6f7oBsa*F9=)f2Bdpu2Yl$_IIKs3#xL>=b>>Ok^`VXj38Y-njnj-u_+z)HGE0OgY3&`Ym0ev? zroN5#rn=d={LS8H!xU-q>}c9<0MV^P=t0R!t#MaEuQip|CzvwV2r4-@T!|{QbFCj2 z@1`VtjDh3^Nhs6;fv^QqU!4CPZ$;xurYR|zl zKh|@vf$dmTP4AWOgkh;wYVe)RXNHHfD*Qy*3o8mk&9dKU1ONq6d(zh7;FH%GMe*QT zDUPIEBfI(1HO}?D`P1taKUQzJZ6kR;MhXCm#yNU@Y@xQW;?VCJJOv0jSPkTs!T_#b z%VKQPrQ1iePN+UAtOyet_FO1U8D!ezhl=I#*k024ip+;`2HXN{K>tCiT~y#bk?=A7 z`1kvhs7qDDM@kZF{YYF`=VO)#k<&*q8Y{BW0Ef+j3Sa7oRKOzH8;>ZFag3C1%T+&y z18reFxt!L%!=;)CmZ>J@on~UDKDPu>`jTrAZ-7Ab7zlaAnjTv`Tp-o53ltSWjFI`f zjEw`ek*tc({v9%yg_~xdI{E-793C-q@IYEFO#(st%fZ3v^?ql&Y|V-E<@m=~LF|o= zu6Xm)D=B_~G*}jjuYq5fEqA?5@ev*NPb(oQlhcKvLsD2)qti;+u-g+wb42sFB{0gQ z<#6f%xtCb!=%zw$FWf+)~?uvGEO+os1=oJD?PA28`m0UHYf zhhgwB7UZXZgBgMHT&K_CVuWI5pWaI8&}k{kg4MOEz`Jmj!n4w{u@>YS!44))t{|}c zF4q4lMnSIr!kn6Fhpct0z(G|9&~Cj2HsvYjR#7#Z=dN|X(ziWSQRJ8UL`altC-L=# z2zCs0lrh(9>A@LGtFi4m7eu@y89PQPnSUCGt!4}nwK}P-3cD>xqvB+LfPWdGyX%3YkG4hCZdCjz=6`VQ z2CCw{!mjFRMfQTkddM6t5M>Lru3ut*R(|J{t7iBJ-!I~@i;ky=xr z?OwG%yD%H+>zpzK!aCBqIrwO0MnVEvJ3kOovO_p<9(J4ec8l?ylA!9!{&;W#xFy=y z`OHcUS2I+DXd?$FEZ=og8ChAFo|h*rBL#u9u!`6B3Q=aIqjJQ+agN!!1p&;`rHdD{ zP#Ln1T36w4wJ>DV)hm!r{p~uCCPh}9$Za6fJU+!s#XRQ6DQ<_92L?+k-efAODozlMkL`-nypjO#jrgzlq znF)f+jNf_6B{a8>vHG|AyUZnU(VdM_7*}|m01hD8c-=e z$TBu8p|GfOO)yfGIGVU5X<+kgShZIX2JHQDOsQX1o)d(saRN(y*KI+}e(%RqH6t7Q zyNSCD%S1>d(pnzuDZEEHXYPmP%L)>d#pVM8aK25pektpxvum*?NRZT?CSa%-%wxEq zS^1-UdK0A0O^uAiT(6xiD=mShdb_tv0xec4&Om`SE<(#_No>mVrJ%g2=ixj1)Edus zE0=?BfN;f${81@2IE|U&t?2xN`2`~>_9(6-UOx_cY$3F!r`HQQ#|_eh=1e2iwwpl& z3@;Hqq4SE`h@_(afGFkn-dbgjwhuk;*`G*bGm*iFK9+=jcr(=c19hcpRW*9i%le6UljxrcrD3lKb-Zk!_PwVVM zNlI_{5&{v)hZ#n%3l>}pu171apw8_EkOA+!@?3$`_YHwKW&h9s{UBKejjTZDnGLX1 z?(J5!?M4KFEtB2(=4B7+5b>6_?0(gnY3E!JpRp+zwMMl~?bg3GPoJ*<3F}LE&P?q3 zWI6SdXJo)x2&tK2kg`4x4f|l&KUCo=Hq2L*hzSI_w*)?StzuiYmZ?)s;AF$dmx=-@ znz`-$gK96o6NVOg(lhL*;2XR>3BEW5*_egcvjC5b2crjO=_WFE+iFAX`V*9q6bIQS>& z?8*&_VvF+Qh3+P&O-%F_gH+P9dx;xInHA^d{^oQ(E|7KdodDE9j&xdfZHqQ>qrhWQ zuYPcG%t(H*bbP|^4G*W|{Wa~~M#Hw1^0Cj^pDH~&B!!vwJ%p2FS@`9_2Q6IEG}+D=5VzZ26W1GHf{9UYoF&+Maknc9b7<>dn?iMIy!;OFzl z{q^gYDcaA3*EPOn4mwJ>eJPgkp*xh6r#JNbmfFR#Knzl+=R$SAW_D8PGGNn`R>&V3)^a)K63oe$0qZ%>8#|%*V)vO#!~IMo zzHlzG7jQku$9p*~a2-iRvsIYJoNtT)NHxm{BEL~Bw%mS9ONvz{Da`|RD>>8Gvcfur zuo7UVkqpbo^6m63Zf~&e1rsJ3{3Z~t2q%toJv)RU}H-Tj;cntayTH6nk*5J!xkP?Cy9(;L6|D&&W{?AxXyNk=H|51s(amYG>>)_W%w3GX3WID$klQz&yAT+an!kL+Zw zc;NpbLJ6``w>&4#y_;$XB2<^8dWL14=gU!=lBg%VY_6?BuY5oKMV#>HF!3@&47UkI z!K>RpK2KI}Qk|cK84$M{KFY6KpQpv8ZV9F?3>#;Df#tPtxN=ElUD1>vXUgWJC zFZPMfUbvICE_NAZN#aQwUwyfG8yDn(jFpUBb1bVBP{!Bl(>w1@i}LwzfzQXxsR>^E zwygoEI{%>$c36Fb1%{0ur(Wl_QG=>Drs9j(e1wt=a~{PBCl}|sZjSKhKCrnic)2m; z=Gh}Tk^2D#EjdO`7iPvmTQx+E&DEX`7xeQIE@EY^XN))PaZO=K9QC-K0N*q2d{jI1UnCb<`FSUi;4;~V>Vk9a#b`1AEUpMNpCJPImU4` z2q9o-hbQu^`$#8{gd&PC!Leg*G`nSH*7q7rg$Xxp6l`aGb*31vx%u}#@YmV>e-eH{Bz*$rb!M8bLAx9hvE@89>D>wVwndG5LI-+ljnzgI$- z9ez69gJ8L@6|Qq7oEweRKWE9PDSekKAB`vq)w9^-&?MG+sy@-@R-*F?4P3Z$@&hj|Jk@3@1yVptHC{A z5SWrPOjmjfL=SPqJDc91AtJ3iOS zYYeGo16#VBSXcXR@ffPf0>L>D*Ur`?sN9@Ee#z$#57LIvJ?Ed8hNZ@i?82l=beMR@ zX$BIl(<~Lp#5(7TCGJ}AX!?oq{o=9w%Kx@HNym+7Lwg5SP*^eEY#%~ycLMnzj%Q)p zTLl_7C2Wy)QZ77Z$Q}leHnH0eA5*=I(G<5+D1RyYEmCZg+qtYQ(*KIhZe7WV*IM!B zEWtDIy9+NhkUY8yHl5U;Em-WJp-FO)2BG$}WkhN6l;7+t@27)UcnD7U|16$7{2y2Z zuDqk5xU!ON96=6g+fJU()*t)u*b+MTTq~8>+_tO91Chvp9~k(yhqD=y;dR@zbyTn@ z?m=IrE3^>_0z~c}`MI@GojQLR*d`bFg|9uaquiBJgrD*GYzSL{{Vn9Qdj|$P7TnkU+jY}$q#E2YM%yl zPQP_lp2U?4j)g)yocm6BHo0}~U>>g_h===sV|u7V!c3MW$tBV3Pz7kjIBf+kZ({kE zBHzvtF0CiOm>dsSVCBPviK=9+QcK3C~gU_^89^~rRNbQIDd(_br-Khko6H(@|09w zqE4(E${hX)F_%4$sM;fT$DHv7B~Fww%sK%~meGMI{?)4MMLjb*v}Lz53A%sh3z6KJ zi%bV#SHySeQLC$~JxXE5tBYEq>W(5cc+aB7u$t$8$NTyDWw-Y&rZO5rFl(bKp#Yf* z7|(SyyQynR=TWKDa-c4aGcDGm$^OwKhe06}+pWvWA6G~xk8eeatryShnQaouirAp* z_j}Cv)Z05Jf=hbD&AP(?*+C^NmhkmC`mG|p$nHityT@<*!DtFAs)^1tJtGKih0s1S z&87Kfu~9I?4}Q6ubxLx2-253+dp~A<`I)A~XiNEKrI4O}U;C#)BwS>DT$A$g zEUyy8b0IPVIn=mhuyy`Y-}Y2_mE0bJDxZ*ac|5E?e(0rIWs-~`t+J}h+r!JWR5X9f zoS-lE;(I-qns@1q#P`RNm6HZ^MH;Fok9nX>*i{kC?c{@RE>F0H5I!+j18 z4mmH`lunNXzoeCSle&YxeE?|~5nJO5zoysJ&t0(hmEVkO&9O!aF;|$3 z?eRUYB@C4Ptz6h1a{ zi~M5SnNwM1z$kcQ@RzXAP}OU}EOBa>6^a7Y2Cwuy8raeDK@s)J?5|auR>z`KpF(T0 znNOlU7_QAxP&f2k7puy^kWB&EBatP5RQuqL(tZ`&I_=y^k>a8k5zhg<65mtTy&&B{_N{_lOZ^ zliJP=(MKeoxTlHBu88V2*LwzcEah-A?Z-e<=8<)?B5gWj`j;owC9UGNSGPh2xdyaK z=f5{U7A*zRiDM~YsbRr2T&T;Os1X$cCMPa}fp~8ZzzO@~=INKRKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0003YNkl6quR`%rz55a_0%(50nA_T~u!#+f{1 z%2Fs^1S;h^L~<3!P80IOA+U9y?oPkpPlB)>F{G-hr}!!dz+Il_?}wb& zgv5SG0wh$@)MBytXoCQK33di9eQzTV>;d0CE|$yX<76^<5Rou^MC5AcO?Zb5J!I_H z0KH$WRi_@% literal 0 HcmV?d00001 diff --git a/js/_addColor.js b/js/_addColor.js new file mode 100644 index 0000000..a3f5fa6 --- /dev/null +++ b/js/_addColor.js @@ -0,0 +1,49 @@ +//adds the given color to the palette +//input hex color string +//returns list item element +function addColor (newColor) { + + //add # at beginning if not present + if (newColor.charAt(0) != '#') + newColor = '#' + newColor; + + //create list item + var listItem = document.createElement("li"); + + //create button + var button = document.createElement("button"); + button.classList.add("color-button"); + button.style.backgroundColor = newColor; + button.addEventListener("mouseup", clickedColor); + listItem.appendChild(button); + + /* + //create input to hold color value + var colorValue = document.createElement("input"); + colorValue.classList.add("color-value"); + listItem.appendChild(colorValue); + */ + + //insert new listItem element at the end of the colors menu (right before add button) + colorsMenu.insertBefore(listItem, colorsMenu.children[colorsMenu.children.length-1]); + + //add jscolor functionality + initColor(button); + + //add edit button + var editButtonTemplate = document.getElementsByClassName('color-edit-button')[0]; + newEditButton = editButtonTemplate.cloneNode(true); + listItem.appendChild(newEditButton); + + //when you click the edit button + on('click', newEditButton, function (event, button) { + + //hide edit button + button.parentElement.lastChild.classList.add('hidden'); + + //show jscolor picker + button.parentElement.firstChild.jscolor.show(); + }); + + return listItem; +} \ No newline at end of file diff --git a/js/_addColorButton.js b/js/_addColorButton.js new file mode 100644 index 0000000..64597aa --- /dev/null +++ b/js/_addColorButton.js @@ -0,0 +1,58 @@ +//add color button +on('click', 'add-color-button', function(){ + if (!documentCreated) return; + + var colorCheckingStyle = ` + color: white; + background: #3c4cc2; + `; + + var colorIsUnique = true + do { + //console.log('%cchecking for unique colors', colorCheckingStyle) + //generate random color + var hue = Math.floor(Math.random()*255); + var sat = 130+Math.floor(Math.random()*100); + var lit = 70+Math.floor(Math.random()*100); + var newColorRgb = hslToRgb(hue,sat,lit) + var newColor = rgbToHex(newColorRgb.r,newColorRgb.g,newColorRgb.b) + + var newColorHex = newColor; + + //check if color has been used before + colors = document.getElementsByClassName('color-button'); + colorCheckingLoop: for (var i = 0; i < colors.length; i++) { + //console.log('%c'+newColorHex +' '+ colors[i].jscolor.toString(), colorCheckingStyle) + + //if generated color matches this color + if (newColorHex == colors[i].jscolor.toString()) { + //console.log('%ccolor already exists', colorCheckingStyle) + + //start loop again + colorIsUnique = false; + + //exit + break colorCheckingLoop; + } + } + } + while (colorIsUnique == false); + + //remove current color selection + document.querySelector("#colors-menu li.selected").classList.remove("selected"); + + //add new color and make it selected + var addedColor = addColor(newColor); + addedColor.classList.add('selected'); + context.fillStyle = '#' + newColor; + + //add history state + //saveHistoryState({type: 'addcolor', colorValue: addedColor.firstElementChild.jscolor.toString()}); + new HistoryStateAddColor(addedColor.firstElementChild.jscolor.toString()); + + //show color picker + addedColor.firstElementChild.jscolor.show(); + + //hide edit button + addedColor.lastChild.classList.add('hidden'); +}, false); \ No newline at end of file diff --git a/js/_changeTool.js b/js/_changeTool.js new file mode 100644 index 0000000..e2af449 --- /dev/null +++ b/js/_changeTool.js @@ -0,0 +1,18 @@ +function changeTool (selectedTool) { + + //set tool and temp tje tje tpp; + currentTool = selectedTool; + currentToolTemp = selectedTool; + + var tools = document.getElementById("tools-menu").children; + + for (var i = 0; i < tools.length; i++) { + tools[i].classList.remove("selected"); + } + + //give the button of the selected tool the .selected class + document.getElementById(selectedTool+"-button").parentNode.classList.add("selected"); + + //change cursor + updateCursor(); +} \ No newline at end of file diff --git a/js/_changeZoom.js b/js/_changeZoom.js new file mode 100644 index 0000000..832cd07 --- /dev/null +++ b/js/_changeZoom.js @@ -0,0 +1,34 @@ + +function changeZoom (direction, cursorLocation) { + + var oldWidth = canvasSize[0] * zoom; + var oldHeight = canvasSize[1] * zoom; + var newWidth, newHeight; + + //change zoom level + //if you want to zoom out, and the zoom isnt already at the smallest level + if (direction == 'out' && zoom > 1) { + zoom -= Math.ceil(zoom / 10); + newWidth = canvasSize[0] * zoom; + newHeight = canvasSize[1] * zoom; + + //adjust canvas position + setCanvasOffset(canvas.offsetLeft + (oldWidth - newWidth) *cursorLocation[0]/oldWidth, canvas.offsetTop + (oldHeight - newHeight) *cursorLocation[1]/oldWidth) + } + //if you want to zoom in + else if (direction == 'in' && zoom + Math.ceil(zoom/10) < window.innerHeight/4){ + zoom += Math.ceil(zoom/10); + newWidth = canvasSize[0] * zoom; + newHeight = canvasSize[1] * zoom; + + //adjust canvas position + setCanvasOffset(canvas.offsetLeft - Math.round((newWidth - oldWidth)*cursorLocation[0]/oldWidth), canvas.offsetTop - Math.round((newHeight - oldHeight)*cursorLocation[1]/oldHeight)) + } + + //resize canvas + canvas.style.width = (canvas.width*zoom)+'px'; + canvas.style.height = (canvas.height*zoom)+'px'; + + // adjust brush size + updateCursor(); +} \ No newline at end of file diff --git a/js/_checkCompatibility.js b/js/_checkCompatibility.js new file mode 100644 index 0000000..39201c8 --- /dev/null +++ b/js/_checkCompatibility.js @@ -0,0 +1,20 @@ + +/////=include libraries/bowser.js + +function closeCompatibilityWarning () { + document.getElementById('compatibility-warning').style.visibility = 'hidden'; +} + +console.log('checking compatibility') + +//check browser/version +if ( (bowser.msie && bowser.version < 11) || + (bowser.firefox && bowser.version < 28) || + (bowser.chrome && bowser.version < 29) || + (bowser.msedge && bowser.version < 12) || + (bowser.safari && bowser.version < 9) || + (bowser.opera && bowser.version < 17) ) + //show warning + document.getElementById('compatibility-warning').style.visibility = 'visible'; + +else alert(bowser.name+' '+bowser.version+' is fine!') \ No newline at end of file diff --git a/js/_clickedColor.js b/js/_clickedColor.js new file mode 100644 index 0000000..ad5b054 --- /dev/null +++ b/js/_clickedColor.js @@ -0,0 +1,29 @@ +//color in palette has been clicked +function clickedColor (e){ + + //left clicked color + if (e.which == 1) { + + //remove current color selection + var selectedColor = document.querySelector("#colors-menu li.selected") + if (selectedColor) selectedColor.classList.remove("selected"); + + //set current color + context.fillStyle = this.style.backgroundColor; + + //make color selected + e.target.parentElement.classList.add('selected'); + + //right clicked color + } else if (e.which == 3) { + console.log('right clicked color button') + + //hide edit color button (to prevent it from showing) + e.target.parentElement.lastChild.classList.add('hidden'); + + //show color picker + e.target.jscolor.show(); + + } +} + diff --git a/js/_colorChanged.js b/js/_colorChanged.js new file mode 100644 index 0000000..e782722 --- /dev/null +++ b/js/_colorChanged.js @@ -0,0 +1,102 @@ + + +document.getElementById('jscolor-hex-input').addEventListener('change', colorChanged, false); + +on('input', 'jscolor-hex-input', function (e) { + + //get hex value + var newColorHex = e.target.value.toLowerCase(); + + //if the color is not (yet) a valid hex color, exit this function and do nothing + if (/^[0-9a-f]{6}$/i.test(newColorHex) == false) + return; + + //get currently editing color + var currentlyEditedColor = document.getElementsByClassName('jscolor-active')[0]; + + //update the actual color picker to the inputted color + currentlyEditedColor.firstChild.jscolor.fromString(newColorHex); + + colorChanged(e); +}) + + +//changes all of one color to another after being changed from color picker +function colorChanged(e) { + console.log('colorChanged()'); + + + + //get colors + var newColor = hexToRgb(e.target.value); + var oldColor = e.target.oldColor; + + console.log('newColor',newColor) + console.log('oldColor',oldColor) + + //save undo state + //saveHistoryState({type: 'colorchange', newColor: e.target.value, oldColor: rgbToHex(oldColor), canvas: context.getImageData(0, 0, canvasSize[0], canvasSize[1])}); + new HistoryStateEditColor(e.target.value.toLowerCase(), rgbToHex(oldColor)); + + //get the currently selected color + var currentlyEditedColor = document.getElementsByClassName('jscolor-active')[0]; + var duplicateColorWarning = document.getElementById("duplicate-color-warning"); + + //check if selected color already matches another color + colors = document.getElementsByClassName('color-button'); + var colorCheckingStyle = "background: #bc60c4; color: white" + var newColorHex = e.target.value.toLowerCase(); + + //if the color is not a valid hex color, exit this function and do nothing + if (/^[0-9a-f]{6}$/i.test(newColorHex) == false) + return; + + //loop through all colors in palette + for (var i = 0; i < colors.length; i++) { + console.log('%c'+newColorHex +' '+ colors[i].jscolor.toString(), colorCheckingStyle) + + //if generated color matches this color + if (newColorHex == colors[i].jscolor.toString()) { + console.log('%ccolor already exists'+(colors[i].parentElement.classList.contains('jscolor-active')?' (but is the current color)':''), colorCheckingStyle); + + //if the color isnt the one that has the picker currently open + if (!colors[i].parentElement.classList.contains('jscolor-active')) { + console.log('%cColor is duplicate', colorCheckingStyle) + + //show the duplicate color warning + duplicateColorWarning.style.visibility = 'visible'; + + //shake warning icon + duplicateColorWarning.classList.remove('shake'); + void duplicateColorWarning.offsetWidth; + duplicateColorWarning.classList.add('shake'); + + //exit function without updating color + return; + } + } + } + + //if the color being edited has a duplicate color warning, remove it + duplicateColorWarning.style.visibility = 'hidden'; + + currentlyEditedColor.firstChild.jscolor.fromString(newColorHex) + + replaceAllOfColor(oldColor, newColor); + + //set new old color to changed color + e.target.oldColor = newColor; + + console.log(e.target.colorElement); + + //if this is the current color, update the drawing color + if (e.target.colorElement.parentElement.classList.contains('selected')) { + console.log('this color is the current color'); + context.fillStyle = '#'+ rgbToHex(newColor.r,newColor.g,newColor.b); + } + /* this is wrong and bad + if (settings.switchToChangedColor) { + + }*/ +} + diff --git a/js/_createButton.js b/js/_createButton.js new file mode 100644 index 0000000..7664561 --- /dev/null +++ b/js/_createButton.js @@ -0,0 +1,22 @@ + +on('click', 'create-button', function (){ + var width = getValue('size-width'); + var height = getValue('size-height'); + newPixel(width,height,'asdfg'); + document.getElementById('new-pixel-warning').style.display = 'block'; + + //get selected palette name + var selectedPalette = getText('palette-button'); + if (selectedPalette == 'Choose a palette...') + selectedPalette = 'none'; + + //track google event + ga('send', 'event', 'Pixel Editor New', selectedPalette, width+'/'+height); /*global ga*/ + + + //reset new form + setValue('size-width', 64); + setValue('size-height', 64); + setText('palette-button', 'Choose a palette...'); + setText('preset-button', 'Choose a preset...'); +}); \ No newline at end of file diff --git a/js/_createColorPalette.js b/js/_createColorPalette.js new file mode 100644 index 0000000..8f93bc8 --- /dev/null +++ b/js/_createColorPalette.js @@ -0,0 +1,45 @@ + +function createColorPalette(selectedPalette, fillBackground) { + //remove current palette + colors = document.getElementsByClassName('color-button'); + while (colors.length > 0) { + colors[0].parentElement.remove(); + } + + var lightestColor = '#000000'; + var darkestColor = '#ffffff'; + + for (var i = 0; i < selectedPalette.length; i++) { + var newColor = selectedPalette[i]; + var newColorElement = addColor(newColor); + + var newColorHex = hexToRgb(newColor); + + var lightestColorHex = hexToRgb(lightestColor); + if (newColorHex.r + newColorHex.g + newColorHex.b > lightestColorHex.r + lightestColorHex.g + lightestColorHex.b) + lightestColor = newColor; + + var darkestColorHex = hexToRgb(darkestColor); + if (newColorHex.r + newColorHex.g + newColorHex.b < darkestColorHex.r + darkestColorHex.g + darkestColorHex.b) { + + //remove current color selection + var selectedColor = document.querySelector("#colors-menu li.selected") + if (selectedColor) selectedColor.classList.remove("selected"); + + //set as current color + newColorElement.classList.add('selected'); + + darkestColor = newColor; + } + + } + + //fill bg with lightest color + if (fillBackground) { + context.fillStyle = lightestColor; + context.fillRect(0, 0, canvasSize[0], canvasSize[1]); + } + + //set as current color + context.fillStyle = darkestColor; +} \ No newline at end of file diff --git a/js/_deleteColor.js b/js/_deleteColor.js new file mode 100644 index 0000000..57283a7 --- /dev/null +++ b/js/_deleteColor.js @@ -0,0 +1,84 @@ + + +//called when the delete button is pressed on color picker +//input color button or hex string +function deleteColor (color) { + const logStyle = 'background: #913939; color: white; padding: 5px;'; + + //console.log('%c'+'deleting color', logStyle); + + //if color is a string, then find the corresponding button + if (typeof color === 'string') { + console.log('trying to find ',color) + //get all colors in palette + colors = document.getElementsByClassName('color-button'); + + //loop through colors + for (var i = 0; i < colors.length; i++) { + console.log(color,'=',colors[i].jscolor.toString()) + + if (color == colors[i].jscolor.toString()) { + console.log('match') + //set color to the color button + color = colors[i]; + console.log('found color', color); + + //exit loop + break; + } + } + + //if the color wasn't found + if (typeof color === 'string') { + console.log('color not found'); + //exit function + return; + } + + } + + //hide color picker + color.jscolor.hide(); + + + //find lightest color in palette + var colors = document.getElementsByClassName('color-button'); + var lightestColor = [0,null]; + for (var i = 0; i < colors.length; i++) { + + //get colors lightness + var lightness = rgbToHsl(colors[i].jscolor.toRgb()).l; + //console.log('%c'+lightness, logStyle) + + //if not the color we're deleting + if (colors[i] != color) { + + //if lighter than the current lightest, set as the new lightest + if (lightness > lightestColor[0]) { + lightestColor[0] = lightness; + lightestColor[1] = colors[i]; + } + } + } + + //console.log('%c'+'replacing with lightest color: '+lightestColor[1].jscolor.toString(), logStyle) + + //replace deleted color with lightest color + replaceAllOfColor(color.jscolor.toString(),lightestColor[1].jscolor.toString()); + + + //if the color you are deleting is the currently selected color + if (color.parentElement.classList.contains('selected')) { + //console.log('%c'+'deleted color is currently selected', logStyle); + + //set current color TO LIGHTEST COLOR + lightestColor[1].parentElement.classList.add('selected'); + context.fillStyle = '#'+lightestColor[1].jscolor.toString(); + } + + //delete the element + colorsMenu.removeChild(color.parentElement); + + + +} \ No newline at end of file diff --git a/js/_dialogue.js b/js/_dialogue.js new file mode 100644 index 0000000..bc2f4f9 --- /dev/null +++ b/js/_dialogue.js @@ -0,0 +1,36 @@ +function showDialogue (dialogueName, trackEvent) { + if (typeof trackEvent === 'undefined') trackEvent = true; + + dialogueOpen = true; + popUpContainer.style.display = 'block'; + + document.getElementById(dialogueName).style.display = 'block'; + + //track google event + if (trackEvent) + ga('send', 'event', 'Palette Editor Dialogue', dialogueName); /*global ga*/ +} + +function closeDialogue () { + popUpContainer.style.display = 'none'; + + var popups = popUpContainer.children; + for (var i = 0; i < popups.length; i++) { + popups[i].style.display = 'none'; + } + + dialogueOpen = false; +} + +popUpContainer.addEventListener("click", function (e) { + if (e.target == popUpContainer) + closeDialogue(); +}); + +//add click handlers for all cancel buttons +var cancelButtons = popUpContainer.getElementsByClassName('close-button'); +for (var i = 0; i < cancelButtons.length; i++) { + cancelButtons[i].addEventListener('click', function () { + closeDialogue(); + }); +} \ No newline at end of file diff --git a/js/_drawLine.js b/js/_drawLine.js new file mode 100644 index 0000000..034c43d --- /dev/null +++ b/js/_drawLine.js @@ -0,0 +1,21 @@ +//draw a line between two points on canvas +function line(x0,y0,x1,y1) { + + var dx = Math.abs(x1-x0); + var dy = Math.abs(y1-y0); + var sx = (x0 < x1 ? 1 : -1); + var sy = (y0 < y1 ? 1 : -1); + var err = dx-dy; + var breaker = 0; + + while (true) { + //set pixel + context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); + + //if we've reached the end goal, exit the loop + if ((x0==x1) && (y0==y1)) break; + var e2 = 2*err; + if (e2 >-dy) {err -=dy; x0+=sx;} + if (e2 < dx) {err +=dx; y0+=sy;} + } +} \ No newline at end of file diff --git a/js/_fileMenu.js b/js/_fileMenu.js new file mode 100644 index 0000000..928c408 --- /dev/null +++ b/js/_fileMenu.js @@ -0,0 +1,140 @@ +var mainMenuItems = document.getElementById("main-menu").children; + +//for each button in main menu (starting at 1 to avoid logo) +for (var i = 1; i < mainMenuItems.length; i++) { + + //get the button that's in the list item + var menuItem = mainMenuItems[i]; + var menuButton = menuItem.children[0]; + + console.log(mainMenuItems) + + //when you click a main menu items button + on('click', menuButton, function (e, button) { + console.log('parent ', button.parentElement) + select(button.parentElement); + }); + + var subMenu = menuItem.children[1]; + var subMenuItems = subMenu.children; + + + + //when you click an item within a menu button + for (var j = 0; j < subMenuItems.length; j++) { + + var subMenuItem = subMenuItems[j]; + var subMenuButton = subMenuItem.children[0]; + + subMenuButton.addEventListener("click", function () { + + switch(this.textContent) { + + //File Menu + case 'New': + showDialogue('new-pixel'); + break; + case 'Open': + + + //if a document exists + if (documentCreated) { + //check if the user wants to overwrite + if (confirm('Opening a pixel will discard your current one. Are you sure you want to do that?')) + //open file selection dialog + document.getElementById("open-image-browse-holder").click(); + } + else + //open file selection dialog + document.getElementById("open-image-browse-holder").click(); + + break; + + case 'Save as...': + if (documentCreated) { + + //create name + var selectedPalette = getText('palette-button'); + if (selectedPalette != 'Choose a palette...'){ + var paletteAbbreviation = palettes[selectedPalette].abbreviation; + var fileName = 'pixel-'+paletteAbbreviation+'-'+canvasSize[0]+'x'+canvasSize[1]+'.png'; + } else { + var fileName = 'pixel-'+canvasSize[0]+'x'+canvasSize[1]+'.png'; + selectedPalette = 'none'; + } + + //set download link + var linkHolder = document.getElementById("save-image-link-holder"); + linkHolder.href = canvas.toDataURL(); + linkHolder.download = fileName; + + linkHolder.click(); + + //track google event + ga('send', 'event', 'Pixel Editor Save', selectedPalette, canvasSize[0]+'/'+canvasSize[1]); /*global ga*/ + } + + break; + + case 'Exit': + //if a document exists, make sure they want to delete it + if (documentCreated && confirm('Exiting will discard your current pixel. Are you sure you want to do that?')) { + + //skip onbeforeunload prompt + window.onbeforeunload = null; + + //go back to main site + window.location.href = "https://lospec.com/pixel-editor"; + } + else { + + //skip onbeforeunload prompt + window.onbeforeunload = null; + + //go back to main site + window.location.href = "https://lospec.com/pixel-editor"; + } + break; + //Edit Menu + case 'Undo': + undo(); + break; + case 'Redo': + redo(); + break; + + //Palette Menu + case 'Add color': + addColor('#eeeeee'); + break; + //Help Menu + case 'Settings': + + //fill form with current settings values + setValue('setting-numberOfHistoryStates', settings.numberOfHistoryStates); + + showDialogue('settings'); + break; + //Help Menu + case 'Help': + showDialogue('help'); + break; + case 'About': + showDialogue('about'); + break; + case 'Changelog': + showDialogue('changelog'); + break; + } + + closeMenu(); + }); + } +} + +function closeMenu () { + //remove .selected class from all menu buttons + for (var i = 0; i < mainMenuItems.length; i++) { + deselect(mainMenuItems[i]); + } +} \ No newline at end of file diff --git a/js/_fill.js b/js/_fill.js new file mode 100644 index 0000000..4b66e1c --- /dev/null +++ b/js/_fill.js @@ -0,0 +1,107 @@ +function fill(cursorLocation) { + + //changes a pixels color + function colorPixel(tempImage, pixelPos, fillColor) { + //console.log('colorPixel:',pixelPos); + tempImage.data[pixelPos] = fillColor.r; + tempImage.data[pixelPos + 1] = fillColor.g; + tempImage.data[pixelPos + 2] = fillColor.b; + } + + //change x y to color value passed from the function and use that as the original color + function matchStartColor(tempImage, pixelPos, color) { + //console.log('matchPixel:',x,y) + + var r = tempImage.data[pixelPos]; + var g = tempImage.data[pixelPos + 1]; + var b = tempImage.data[pixelPos + 2]; + //console.log(r == color[0] && g == color[1] && b == color[2]); + return (r == color[0] && g == color[1] && b == color[2]); + } + + //save history state + new HistoryStateEditCanvas(); + //saveHistoryState({type: 'canvas', canvas: context.getImageData(0, 0, canvasSize[0], canvasSize[1])}); + //console.log('filling at '+ Math.floor(cursorLocation[0]/zoom) + ','+ Math.floor(cursorLocation[1]/zoom)); + + //temporary image holds the data while we change it + var tempImage = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + + //this is an array that holds all of the pixels at the top of the cluster + var topmostPixelsArray = [[Math.floor(cursorLocation[0]/zoom), Math.floor(cursorLocation[1]/zoom)]]; + //console.log('topmostPixelsArray:',topmostPixelsArray) + + //the offset of the pixel in the temp image data to start with + var startingPosition = (topmostPixelsArray[0][1] * canvasSize[0] + topmostPixelsArray[0][0]) * 4; + + //the color of the cluster that is being filled + var clusterColor = [tempImage.data[startingPosition],tempImage.data[startingPosition+1],tempImage.data[startingPosition+2]]; + + //the new color to fill with + var fillColor = hexToRgb(context.fillStyle); + + //if you try to fill with the same color that's already there, exit the function + if (clusterColor[0] == fillColor.r && + clusterColor[1] == fillColor.g && + clusterColor[2] == fillColor.b ) + return; + + //loop until there are no more values left in this array + while (topmostPixelsArray.length) { + + var reachLeft, reachRight; + + //move the most recent pixel from the array and set it as our current working pixels + var currentPixel = topmostPixelsArray.pop(); + + //set the values of this pixel to x/y variables just for readability + var x = currentPixel[0]; + var y = currentPixel[1]; + + //this variable holds the index of where the starting values for the current pixel are in the data array + //we multiply the number of rows down (y) times the width of each row, then add x. at the end we multiply by 4 because + //each pixel has 4 values, rgba + var pixelPos = (y * canvasSize[0] + x) * 4; + + + //move up in the image until you reach the top or the pixel you hit was not the right color + while (y-- >= 0 && matchStartColor(tempImage, pixelPos, clusterColor)) { + pixelPos -= canvasSize[0] * 4; + } + pixelPos += canvasSize[0] * 4; + ++y; + reachLeft = false; + reachRight = false; + while (y++ < canvasSize[1] - 1 && matchStartColor(tempImage, pixelPos, clusterColor)) { + colorPixel(tempImage, pixelPos, fillColor); + + if (x > 0) { + if (matchStartColor(tempImage, pixelPos - 4, clusterColor)) { + if (!reachLeft) { + topmostPixelsArray.push([x - 1, y]); + reachLeft = true; + } + } + else if (reachLeft) { + reachLeft = false; + } + } + + if (x < canvasSize[0] - 1) { + if (matchStartColor(tempImage, pixelPos + 4, clusterColor)) { + if (!reachRight) { + topmostPixelsArray.push([x + 1, y]); + reachRight = true; + } + } + else if (reachRight) { + reachRight = false; + } + } + + pixelPos += canvasSize[0] * 4; + } + } + context.putImageData(tempImage, 0, 0); + //console.log('done filling') +} \ No newline at end of file diff --git a/js/_getCursorPosition.js b/js/_getCursorPosition.js new file mode 100644 index 0000000..b8d0715 --- /dev/null +++ b/js/_getCursorPosition.js @@ -0,0 +1,18 @@ +//get cursor position relative to canvas +function getCursorPosition(e) { + var x; + var y; + if (e.pageX != undefined && e.pageY != undefined) { + x = e.pageX; + y = e.pageY; + } + else { + x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; + y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; + } + + x -= canvas.offsetLeft; + y -= canvas.offsetTop; + + return [x,y]; +} \ No newline at end of file diff --git a/js/_history.js b/js/_history.js new file mode 100644 index 0000000..667bb5a --- /dev/null +++ b/js/_history.js @@ -0,0 +1,198 @@ +var undoStates = []; +var redoStates = []; + +const undoLogStyle = 'background: #87ff1c; color: black; padding: 5px;'; + +//prototype for undoing canvas changes +function HistoryStateEditCanvas () { + this.canvas = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + + this.undo = function () { + var currentCanvas = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + context.putImageData(this.canvas, 0, 0); + + this.canvas = currentCanvas; + redoStates.push(this); + } + + this.redo = function () { + var currentCanvas = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + context.putImageData(this.canvas, 0, 0); + + this.canvas = currentCanvas; + undoStates.push(this); + } + + //add self to undo array + saveHistoryState(this); +} + +//prototype for undoing added colors +function HistoryStateAddColor (colorValue) { + this.colorValue = colorValue; + + this.undo = function () { + redoStates.push(this); + deleteColor(this.colorValue); + } + + this.redo = function () { + addColor(this.colorValue); + undoStates.push(this); + } + + //add self to undo array + saveHistoryState(this); +} + +//prototype for undoing deleted colors +function HistoryStateDeleteColor (colorValue) { + this.colorValue = colorValue; + this.canvas = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + + this.undo = function () { + var currentCanvas = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + context.putImageData(this.canvas, 0, 0); + + addColor(this.colorValue); + + this.canvas = currentCanvas; + redoStates.push(this); + } + + this.redo = function () { + var currentCanvas = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + context.putImageData(this.canvas, 0, 0); + + deleteColor(this.colorValue); + + this.canvas = currentCanvas; + undoStates.push(this); + } + + //add self to undo array + saveHistoryState(this); +} + +//prototype for undoing colors edits +function HistoryStateEditColor (newColorValue, oldColorValue) { + this.newColorValue = newColorValue; + this.oldColorValue = oldColorValue; + this.canvas = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + + this.undo = function () { + var currentCanvas = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + context.putImageData(this.canvas, 0, 0); + + //find new color in palette and change it back to old color + var colors = document.getElementsByClassName('color-button'); + for (var i = 0; i < colors.length; i++) { + console.log(newColorValue, '==', colors[i].jscolor.toString()) + if (newColorValue == colors[i].jscolor.toString()) { + colors[i].jscolor.fromString(oldColorValue); + break; + } + } + + this.canvas = currentCanvas; + redoStates.push(this); + } + + this.redo = function () { + var currentCanvas = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + context.putImageData(this.canvas, 0, 0); + + //find old color in palette and change it back to new color + var colors = document.getElementsByClassName('color-button'); + for (var i = 0; i < colors.length; i++) { + console.log(oldColorValue, '==', colors[i].jscolor.toString()) + if (oldColorValue == colors[i].jscolor.toString()) { + colors[i].jscolor.fromString(newColorValue); + break; + } + } + + this.canvas = currentCanvas; + undoStates.push(this); + } + + //add self to undo array + saveHistoryState(this); +} + + +//rename to add undo state +function saveHistoryState (state) { + console.log('%csaving history state', undoLogStyle) + console.log(state) + + //get current canvas data and save to undoStates array + undoStates.push(state); + + //limit the number of states to settings.numberOfHistoryStates + if (undoStates.length > settings.numberOfHistoryStates) { + undoStates = undoStates.splice(-settings.numberOfHistoryStates, settings.numberOfHistoryStates); + } + + //there is now definitely at least 1 undo state, so the button shouldnt be disabled + document.getElementById('undo-button').classList.remove('disabled'); + + //there should be no redoStates after an undoState is saved + redoStates = []; + + console.log(undoStates) + console.log(redoStates) +} + +function undo () { + console.log('%cundo', undoLogStyle); + + //if there are any states saved to undo + if (undoStates.length > 0) { + + document.getElementById('redo-button').classList.remove('disabled'); + + //get state + var undoState = undoStates[undoStates.length-1]; + console.log(undoState); + + //restore the state + undoState.undo(); + + //remove from the undo list + undoStates.splice(undoStates.length-1,1); + + //if theres none left to undo, disable the option + if (undoStates.length == 0) + document.getElementById('undo-button').classList.add('disabled'); + } + + console.log(undoStates) + console.log(redoStates) +} + +function redo () { + console.log('%credo', undoLogStyle); + + if (redoStates.length > 0) { + + //enable undo button + document.getElementById('undo-button').classList.remove('disabled'); + + //get state + var redoState = redoStates[redoStates.length-1]; + console.log(redoState); + + //restore the state + redoState.redo(); + + //remove from redo array + redoStates.splice(redoStates.length-1,1); + + //if theres none left to redo, disable the option + if (redoStates.length == 0) + document.getElementById('redo-button').classList.add('disabled'); + } + console.log(undoStates) + console.log(redoStates) +} \ No newline at end of file diff --git a/js/_hotkeyListener.js b/js/_hotkeyListener.js new file mode 100644 index 0000000..104e5ab --- /dev/null +++ b/js/_hotkeyListener.js @@ -0,0 +1,66 @@ +var spacePressed = false; + +function KeyPress(e) { + var keyboardEvent = window.event? event : e; + + //if the user is typing in an input field, ignore these hotkeys + if (document.activeElement.tagName == 'INPUT') return; + + //if no document has been created yet, + //orthere is a dialog box open + //ignore hotkeys + if (!documentCreated || dialogueOpen) return; + + // + switch (keyboardEvent.keyCode) { + //pencil tool - 1, b + case 49: case 66: + changeTool('pencil'); + break; + //fill tool - 2, f + case 50: case 70: + changeTool('fill'); + break; + //eyedropper - 3, e + case 51: case 69: + changeTool('eyedropper'); + break; + //pan - 4, p, m + case 52: case 80: case 77: + changeTool('pan'); + break; + //zoom - 5 + case 53: + changeTool('zoom'); + break; + //Z + case 90: + console.log('PRESSED Z ', keyboardEvent.ctrlKey) + //CTRL+ALT+Z redo + if (keyboardEvent.altKey && keyboardEvent.ctrlKey) + redo(); + //CTRL+Z undo + else if (keyboardEvent.ctrlKey) + undo(); + //Z switch to zoom tool + else + changeTool('zoom'); + break; + //redo - ctrl y + case 89: + if (keyboardEvent.ctrlKey) + redo(); + break; + case 32: + spacePressed=true; + break; + } +} + +document.onkeydown = KeyPress; + +window.addEventListener("keyup", function (e) { + + if (e.keyCode == 32) spacePressed = false; + +}); diff --git a/js/_initColor.js b/js/_initColor.js new file mode 100644 index 0000000..b333751 --- /dev/null +++ b/js/_initColor.js @@ -0,0 +1,23 @@ + +//format a color button +function initColor (colorElement) { + //console.log('initColor()'); + //console.log(document.getElementById('jscolor-hex-input')) + + + //add jscolor picker for this color + colorElement.jscolor = new jscolor(colorElement.parentElement, { + valueElement: null, //if you dont set this to null, it turns the button (colorElement) into text, we set it when you open the picker + styleElement: colorElement, + width:151, + position: 'left', + padding:0, + borderWidth:14, + borderColor: '#332f35', + backgroundColor: '#332f35', + insetColor: 'transparent', + value: colorElement.style.backgroundColor, + deleteButton: true, + }); + +} diff --git a/js/_jscolor.js b/js/_jscolor.js new file mode 100644 index 0000000..2fad864 --- /dev/null +++ b/js/_jscolor.js @@ -0,0 +1,1964 @@ +/** + * jscolor - JavaScript Color Picker + * + * @link http://jscolor.com + * @license For open source use: GPLv3 + * For commercial use: JSColor Commercial License + * @author Jan Odvarko + * @version 2.0.4 + * + * See usage examples at http://jscolor.com/examples/ + */ + + //Ive made changes to this script. ctrl+f [lospec] + //skeddles variables [lospec] +var colorPickerBottomAdded; +var newjsColorPickerBottom; +"use strict"; + + +if (!window.jscolor) { window.jscolor = (function () { + + +var jsc = { + + + register : function () { + jsc.attachDOMReadyEvent(jsc.init); + jsc.attachEvent(document, 'mousedown', jsc.onDocumentMouseDown); + jsc.attachEvent(document, 'touchstart', jsc.onDocumentTouchStart); + jsc.attachEvent(window, 'resize', jsc.onWindowResize); + }, + + + init : function () { + //console.log('init()') + if (jsc.jscolor.lookupClass) { + jsc.jscolor.installByClassName(jsc.jscolor.lookupClass); + } + }, + + + tryInstallOnElements : function (elms, className) { + var matchClass = new RegExp('(^|\\s)(' + className + ')(\\s*(\\{[^}]*\\})|\\s|$)', 'i'); + + for (var i = 0; i < elms.length; i += 1) { + if (elms[i].type !== undefined && elms[i].type.toLowerCase() == 'color') { + if (jsc.isColorAttrSupported) { + // skip inputs of type 'color' if supported by the browser + continue; + } + } + var m; + if (!elms[i].jscolor && elms[i].className && (m = elms[i].className.match(matchClass))) { + var targetElm = elms[i]; + var optsStr = null; + + var dataOptions = jsc.getDataAttr(targetElm, 'jscolor'); + if (dataOptions !== null) { + optsStr = dataOptions; + } else if (m[4]) { + optsStr = m[4]; + } + + var opts = {}; + if (optsStr) { + try { + opts = (new Function ('return (' + optsStr + ')'))(); + } catch(eParseError) { + jsc.warn('Error parsing jscolor options: ' + eParseError + ':\n' + optsStr); + } + } + targetElm.jscolor = new jsc.jscolor(targetElm, opts); + } + } + }, + + + isColorAttrSupported : (function () { + var elm = document.createElement('input'); + if (elm.setAttribute) { + elm.setAttribute('type', 'color'); + if (elm.type.toLowerCase() == 'color') { + return true; + } + } + return false; + })(), + + + isCanvasSupported : (function () { + var elm = document.createElement('canvas'); + return !!(elm.getContext && elm.getContext('2d')); + })(), + + + fetchElement : function (mixed) { + return typeof mixed === 'string' ? document.getElementById(mixed) : mixed; + }, + + + isElementType : function (elm, type) { + return elm.nodeName.toLowerCase() === type.toLowerCase(); + }, + + + getDataAttr : function (el, name) { + var attrName = 'data-' + name; + var attrValue = el.getAttribute(attrName); + if (attrValue !== null) { + return attrValue; + } + return null; + }, + + + attachEvent : function (el, evnt, func) { + if (el.addEventListener) { + el.addEventListener(evnt, func, false); + } else if (el.attachEvent) { + el.attachEvent('on' + evnt, func); + } + }, + + + detachEvent : function (el, evnt, func) { + if (el.removeEventListener) { + el.removeEventListener(evnt, func, false); + } else if (el.detachEvent) { + el.detachEvent('on' + evnt, func); + } + }, + + + _attachedGroupEvents : {}, + + + attachGroupEvent : function (groupName, el, evnt, func) { + if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) { + jsc._attachedGroupEvents[groupName] = []; + } + jsc._attachedGroupEvents[groupName].push([el, evnt, func]); + jsc.attachEvent(el, evnt, func); + }, + + + detachGroupEvents : function (groupName) { + if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) { + for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) { + var evt = jsc._attachedGroupEvents[groupName][i]; + jsc.detachEvent(evt[0], evt[1], evt[2]); + } + delete jsc._attachedGroupEvents[groupName]; + } + }, + + + attachDOMReadyEvent : function (func) { + var fired = false; + var fireOnce = function () { + if (!fired) { + fired = true; + func(); + } + }; + + if (document.readyState === 'complete') { + setTimeout(fireOnce, 1); // async + return; + } + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fireOnce, false); + + // Fallback + window.addEventListener('load', fireOnce, false); + + } else if (document.attachEvent) { + // IE + document.attachEvent('onreadystatechange', function () { + if (document.readyState === 'complete') { + document.detachEvent('onreadystatechange', arguments.callee); + fireOnce(); + } + }) + + // Fallback + window.attachEvent('onload', fireOnce); + + // IE7/8 + if (document.documentElement.doScroll && window == window.top) { + var tryScroll = function () { + if (!document.body) { return; } + try { + document.documentElement.doScroll('left'); + fireOnce(); + } catch (e) { + setTimeout(tryScroll, 1); + } + }; + tryScroll(); + } + } + }, + + + warn : function (msg) { + if (window.console && window.console.warn) { + window.console.warn(msg); + } + }, + + + preventDefault : function (e) { + if (e.preventDefault) { e.preventDefault(); } + e.returnValue = false; + }, + + + captureTarget : function (target) { + // IE + if (target.setCapture) { + jsc._capturedTarget = target; + jsc._capturedTarget.setCapture(); + } + }, + + + releaseTarget : function () { + // IE + if (jsc._capturedTarget) { + jsc._capturedTarget.releaseCapture(); + jsc._capturedTarget = null; + } + }, + + + fireEvent : function (el, evnt) { + if (!el) { + return; + } + if (document.createEvent) { + var ev = document.createEvent('HTMLEvents'); + ev.initEvent(evnt, true, true); + el.dispatchEvent(ev); + } else if (document.createEventObject) { + var ev = document.createEventObject(); + el.fireEvent('on' + evnt, ev); + } else if (el['on' + evnt]) { // alternatively use the traditional event model + el['on' + evnt](); + } + }, + + + classNameToList : function (className) { + return className.replace(/^\s+|\s+$/g, '').split(/\s+/); + }, + + + // The className parameter (str) can only contain a single class name + hasClass : function (elm, className) { + if (!className) { + return false; + } + return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' '); + }, + + + // The className parameter (str) can contain multiple class names separated by whitespace + setClass : function (elm, className) { + var classList = jsc.classNameToList(className); + for (var i = 0; i < classList.length; i += 1) { + if (!jsc.hasClass(elm, classList[i])) { + elm.className += (elm.className ? ' ' : '') + classList[i]; + } + } + }, + + + // The className parameter (str) can contain multiple class names separated by whitespace + unsetClass : function (elm, className) { + var classList = jsc.classNameToList(className); + for (var i = 0; i < classList.length; i += 1) { + var repl = new RegExp( + '^\\s*' + classList[i] + '\\s*|' + + '\\s*' + classList[i] + '\\s*$|' + + '\\s+' + classList[i] + '(\\s+)', + 'g' + ); + elm.className = elm.className.replace(repl, '$1'); + } + }, + + + getStyle : function (elm) { + return window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle; + }, + + + setStyle : (function () { + var helper = document.createElement('div'); + var getSupportedProp = function (names) { + for (var i = 0; i < names.length; i += 1) { + if (names[i] in helper.style) { + return names[i]; + } + } + }; + var props = { + borderRadius: getSupportedProp(['borderRadius', 'MozBorderRadius', 'webkitBorderRadius']), + boxShadow: getSupportedProp(['boxShadow', 'MozBoxShadow', 'webkitBoxShadow']) + }; + return function (elm, prop, value) { + switch (prop.toLowerCase()) { + case 'opacity': + var alphaOpacity = Math.round(parseFloat(value) * 100); + elm.style.opacity = value; + elm.style.filter = 'alpha(opacity=' + alphaOpacity + ')'; + break; + default: + elm.style[props[prop]] = value; + break; + } + }; + })(), + + + setBorderRadius : function (elm, value) { + jsc.setStyle(elm, 'borderRadius', value || '0'); + }, + + + setBoxShadow : function (elm, value) { + jsc.setStyle(elm, 'boxShadow', value || 'none'); + }, + + + getElementPos : function (e, relativeToViewport) { + var x=0, y=0; + var rect = e.getBoundingClientRect(); + x = rect.left; + y = rect.top; + if (!relativeToViewport) { + var viewPos = jsc.getViewPos(); + x += viewPos[0]; + y += viewPos[1]; + } + return [x, y]; + }, + + + getElementSize : function (e) { + return [e.offsetWidth, e.offsetHeight]; + }, + + + // get pointer's X/Y coordinates relative to viewport + getAbsPointerPos : function (e) { + if (!e) { e = window.event; } + var x = 0, y = 0; + if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { + // touch devices + x = e.changedTouches[0].clientX; + y = e.changedTouches[0].clientY; + } else if (typeof e.clientX === 'number') { + x = e.clientX; + y = e.clientY; + } + return { x: x, y: y }; + }, + + + // get pointer's X/Y coordinates relative to target element + getRelPointerPos : function (e) { + if (!e) { e = window.event; } + var target = e.target || e.srcElement; + var targetRect = target.getBoundingClientRect(); + + var x = 0, y = 0; + + var clientX = 0, clientY = 0; + if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { + // touch devices + clientX = e.changedTouches[0].clientX; + clientY = e.changedTouches[0].clientY; + } else if (typeof e.clientX === 'number') { + clientX = e.clientX; + clientY = e.clientY; + } + + x = clientX - targetRect.left; + y = clientY - targetRect.top; + return { x: x, y: y }; + }, + + + getViewPos : function () { + var doc = document.documentElement; + return [ + (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), + (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) + ]; + }, + + + getViewSize : function () { + var doc = document.documentElement; + return [ + (window.innerWidth || doc.clientWidth), + (window.innerHeight || doc.clientHeight), + ]; + }, + + + redrawPosition : function () { + + if (jsc.picker && jsc.picker.owner) { + var thisObj = jsc.picker.owner; + + var tp, vp; + + if (thisObj.fixed) { + // Fixed elements are positioned relative to viewport, + // therefore we can ignore the scroll offset + tp = jsc.getElementPos(thisObj.targetElement, true); // target pos + vp = [0, 0]; // view pos + } else { + tp = jsc.getElementPos(thisObj.targetElement); // target pos + vp = jsc.getViewPos(); // view pos + } + + var ts = jsc.getElementSize(thisObj.targetElement); // target size + var vs = jsc.getViewSize(); // view size + var ps = jsc.getPickerOuterDims(thisObj); // picker size + var a, b, c; + switch (thisObj.position.toLowerCase()) { + case 'left': a=1; b=0; c=-1; break; + case 'right':a=1; b=0; c=1; break; + case 'top': a=0; b=1; c=-1; break; + default: a=0; b=1; c=1; break; + } + var l = (ts[b]+ps[b])/2; + + // compute picker position + if (!thisObj.smartPosition) { + var pp = [ + tp[a], + tp[b]+ts[b]-l+l*c + ]; + } else { + var pp = [ + -vp[a]+tp[a]+ps[a] > vs[a] ? + (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : + tp[a], + -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? + (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) : + (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) + ]; + } + + var x = pp[a]; + var y = pp[b]; + var positionValue = thisObj.fixed ? 'fixed' : 'absolute'; + var contractShadow = + (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) && + (pp[1] + ps[1] < tp[1] + ts[1]); + + jsc._drawPosition(thisObj, x, y, positionValue, contractShadow); + } + }, + + + _drawPosition : function (thisObj, x, y, positionValue, contractShadow) { + var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px + + jsc.picker.wrap.style.position = positionValue; + jsc.picker.wrap.style.left = x + 'px'; + jsc.picker.wrap.style.top = y + 'px'; + + jsc.setBoxShadow( + jsc.picker.boxS, + thisObj.shadow ? + new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) : + null); + }, + + + getPickerDims : function (thisObj) { + var displaySlider = !!jsc.getSliderComponent(thisObj); + var dims = [ + 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.width + + (displaySlider ? 2 * thisObj.insetWidth + jsc.getPadToSliderPadding(thisObj) + thisObj.sliderSize : 0), + 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.height + + (thisObj.closable ? 2 * thisObj.insetWidth + thisObj.padding + thisObj.buttonHeight : 0) + ]; + return dims; + }, + + + getPickerOuterDims : function (thisObj) { + var dims = jsc.getPickerDims(thisObj); + return [ + dims[0] + 2 * thisObj.borderWidth, + dims[1] + 2 * thisObj.borderWidth + ]; + }, + + + getPadToSliderPadding : function (thisObj) { + return Math.max(thisObj.padding, 1.5 * (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness)); + }, + + + getPadYComponent : function (thisObj) { + switch (thisObj.mode.charAt(1).toLowerCase()) { + case 'v': return 'v'; break; + } + return 's'; + }, + + + getSliderComponent : function (thisObj) { + if (thisObj.mode.length > 2) { + switch (thisObj.mode.charAt(2).toLowerCase()) { + case 's': return 's'; break; + case 'v': return 'v'; break; + } + } + return null; + }, + + + onDocumentMouseDown : function (e) { + //console.log(e) + + if (!e) { e = window.event; } + var target = e.target || e.srcElement; + + if (target._jscLinkedInstance) { + if (target._jscLinkedInstance.showOnClick) { + target._jscLinkedInstance.show(); + } + } else if (target._jscControlName) { + jsc.onControlPointerStart(e, target, target._jscControlName, 'mouse'); + } else { + // Mouse is outside the picker controls -> hide the color picker! + if (jsc.picker && jsc.picker.owner) { + //console.log(e.target,'=====================================') + //if they clicked on the delete button [lospec] + if (e.target.className == 'delete-color-button') { + //saveHistoryState({type: 'deletecolor', colorValue: jsc.picker.owner.toString(), canvas: context.getImageData(0, 0, canvasSize[0], canvasSize[1])}); + new HistoryStateDeleteColor(jsc.picker.owner.toString()); + + deleteColor(jsc.picker.owner.styleElement); + } + else if (e.target.className == 'jscolor-picker-bottom') { + //console.log('clicked color picker bottom') + } + else if (e.target.parentElement.classList.contains('jscolor-picker-bottom')) { + //console.log('clicked element in color picker bottom') + } + else { + //console.log('clicked outside of color picker') + //unhide hidden edit button [lospec] + var hiddenButton = document.querySelector(".color-edit-button.hidden") + if (hiddenButton) hiddenButton.classList.remove("hidden"); + + //close color picker + jsc.picker.owner.hide(); + + } + } + } + }, + + + onDocumentTouchStart : function (e) { + if (!e) { e = window.event; } + var target = e.target || e.srcElement; + + if (target._jscLinkedInstance) { + if (target._jscLinkedInstance.showOnClick) { + target._jscLinkedInstance.show(); + } + } else if (target._jscControlName) { + jsc.onControlPointerStart(e, target, target._jscControlName, 'touch'); + } else { + if (jsc.picker && jsc.picker.owner) { + jsc.picker.owner.hide(); + } + } + }, + + + onWindowResize : function (e) { + jsc.redrawPosition(); + }, + + + onParentScroll : function (e) { + // hide the picker when one of the parent elements is scrolled + if (jsc.picker && jsc.picker.owner) { + jsc.picker.owner.hide(); + } + }, + + + _pointerMoveEvent : { + mouse: 'mousemove', + touch: 'touchmove' + }, + _pointerEndEvent : { + mouse: 'mouseup', + touch: 'touchend' + }, + + + _pointerOrigin : null, + _capturedTarget : null, + + + onControlPointerStart : function (e, target, controlName, pointerType) { + var thisObj = target._jscInstance; + + jsc.preventDefault(e); + jsc.captureTarget(target); + + var registerDragEvents = function (doc, offset) { + jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType], + jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset)); + jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType], + jsc.onDocumentPointerEnd(e, target, controlName, pointerType)); + }; + + registerDragEvents(document, [0, 0]); + + if (window.parent && window.frameElement) { + var rect = window.frameElement.getBoundingClientRect(); + var ofs = [-rect.left, -rect.top]; + registerDragEvents(window.parent.window.document, ofs); + } + + var abs = jsc.getAbsPointerPos(e); + var rel = jsc.getRelPointerPos(e); + jsc._pointerOrigin = { + x: abs.x - rel.x, + y: abs.y - rel.y + }; + + switch (controlName) { + case 'pad': + // if the slider is at the bottom, move it up + switch (jsc.getSliderComponent(thisObj)) { + case 's': if (thisObj.hsv[1] === 0) { thisObj.fromHSV(null, 100, null); }; break; + case 'v': if (thisObj.hsv[2] === 0) { thisObj.fromHSV(null, null, 100); }; break; + } + jsc.setPad(thisObj, e, 0, 0); + break; + + case 'sld': + jsc.setSld(thisObj, e, 0); + break; + } + + jsc.dispatchFineChange(thisObj); + }, + + + onDocumentPointerMove : function (e, target, controlName, pointerType, offset) { + return function (e) { + var thisObj = target._jscInstance; + switch (controlName) { + case 'pad': + if (!e) { e = window.event; } + jsc.setPad(thisObj, e, offset[0], offset[1]); + jsc.dispatchFineChange(thisObj); + break; + + case 'sld': + if (!e) { e = window.event; } + jsc.setSld(thisObj, e, offset[1]); + jsc.dispatchFineChange(thisObj); + break; + } + } + }, + + + onDocumentPointerEnd : function (e, target, controlName, pointerType) { + return function (e) { + var thisObj = target._jscInstance; + jsc.detachGroupEvents('drag'); + jsc.releaseTarget(); + // Always dispatch changes after detaching outstanding mouse handlers, + // in case some user interaction will occur in user's onchange callback + // that would intrude with current mouse events + jsc.dispatchChange(thisObj); + }; + }, + + + dispatchChange : function (thisObj) { + if (thisObj.valueElement) { + if (jsc.isElementType(thisObj.valueElement, 'input')) { + jsc.fireEvent(thisObj.valueElement, 'change'); + } + } + }, + + + dispatchFineChange : function (thisObj) { + if (thisObj.onFineChange) { + var callback; + if (typeof thisObj.onFineChange === 'string') { + callback = new Function (thisObj.onFineChange); + } else { + callback = thisObj.onFineChange; + } + callback.call(thisObj); + } + }, + + + setPad : function (thisObj, e, ofsX, ofsY) { + var pointerAbs = jsc.getAbsPointerPos(e); + var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.insetWidth; + var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; + + var xVal = x * (360 / (thisObj.width - 1)); + var yVal = 100 - (y * (100 / (thisObj.height - 1))); + + switch (jsc.getPadYComponent(thisObj)) { + case 's': thisObj.fromHSV(xVal, yVal, null, jsc.leaveSld); break; + case 'v': thisObj.fromHSV(xVal, null, yVal, jsc.leaveSld); break; + } + }, + + + setSld : function (thisObj, e, ofsY) { + var pointerAbs = jsc.getAbsPointerPos(e); + var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; + + var yVal = 100 - (y * (100 / (thisObj.height - 1))); + + switch (jsc.getSliderComponent(thisObj)) { + case 's': thisObj.fromHSV(null, yVal, null, jsc.leavePad); break; + case 'v': thisObj.fromHSV(null, null, yVal, jsc.leavePad); break; + } + }, + + + _vmlNS : 'jsc_vml_', + _vmlCSS : 'jsc_vml_css_', + _vmlReady : false, + + + initVML : function () { + if (!jsc._vmlReady) { + // init VML namespace + var doc = document; + if (!doc.namespaces[jsc._vmlNS]) { + doc.namespaces.add(jsc._vmlNS, 'urn:schemas-microsoft-com:vml'); + } + if (!doc.styleSheets[jsc._vmlCSS]) { + var tags = ['shape', 'shapetype', 'group', 'background', 'path', 'formulas', 'handles', 'fill', 'stroke', 'shadow', 'textbox', 'textpath', 'imagedata', 'line', 'polyline', 'curve', 'rect', 'roundrect', 'oval', 'arc', 'image']; + var ss = doc.createStyleSheet(); + ss.owningElement.id = jsc._vmlCSS; + for (var i = 0; i < tags.length; i += 1) { + ss.addRule(jsc._vmlNS + '\\:' + tags[i], 'behavior:url(#default#VML);'); + } + } + jsc._vmlReady = true; + } + }, + + + createPalette : function () { + + var paletteObj = { + elm: null, + draw: null + }; + + if (jsc.isCanvasSupported) { + // Canvas implementation for modern browsers + + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + + var drawFunc = function (width, height, type) { + canvas.width = width; + canvas.height = height; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0); + hGrad.addColorStop(0 / 6, '#F00'); + hGrad.addColorStop(1 / 6, '#FF0'); + hGrad.addColorStop(2 / 6, '#0F0'); + hGrad.addColorStop(3 / 6, '#0FF'); + hGrad.addColorStop(4 / 6, '#00F'); + hGrad.addColorStop(5 / 6, '#F0F'); + hGrad.addColorStop(6 / 6, '#F00'); + + ctx.fillStyle = hGrad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height); + switch (type.toLowerCase()) { + case 's': + vGrad.addColorStop(0, 'rgba(255,255,255,0)'); + vGrad.addColorStop(1, 'rgba(255,255,255,1)'); + break; + case 'v': + vGrad.addColorStop(0, 'rgba(0,0,0,0)'); + vGrad.addColorStop(1, 'rgba(0,0,0,1)'); + break; + } + ctx.fillStyle = vGrad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }; + + paletteObj.elm = canvas; + paletteObj.draw = drawFunc; + + } else { + // VML fallback for IE 7 and 8 + + jsc.initVML(); + + var vmlContainer = document.createElement('div'); + vmlContainer.style.position = 'relative'; + vmlContainer.style.overflow = 'hidden'; + + var hGrad = document.createElement(jsc._vmlNS + ':fill'); + hGrad.type = 'gradient'; + hGrad.method = 'linear'; + hGrad.angle = '90'; + hGrad.colors = '16.67% #F0F, 33.33% #00F, 50% #0FF, 66.67% #0F0, 83.33% #FF0' + + var hRect = document.createElement(jsc._vmlNS + ':rect'); + hRect.style.position = 'absolute'; + hRect.style.left = -1 + 'px'; + hRect.style.top = -1 + 'px'; + hRect.stroked = false; + hRect.appendChild(hGrad); + vmlContainer.appendChild(hRect); + + var vGrad = document.createElement(jsc._vmlNS + ':fill'); + vGrad.type = 'gradient'; + vGrad.method = 'linear'; + vGrad.angle = '180'; + vGrad.opacity = '0'; + + var vRect = document.createElement(jsc._vmlNS + ':rect'); + vRect.style.position = 'absolute'; + vRect.style.left = -1 + 'px'; + vRect.style.top = -1 + 'px'; + vRect.stroked = false; + vRect.appendChild(vGrad); + vmlContainer.appendChild(vRect); + + var drawFunc = function (width, height, type) { + vmlContainer.style.width = width + 'px'; + vmlContainer.style.height = height + 'px'; + + hRect.style.width = + vRect.style.width = + (width + 1) + 'px'; + hRect.style.height = + vRect.style.height = + (height + 1) + 'px'; + + // Colors must be specified during every redraw, otherwise IE won't display + // a full gradient during a subsequential redraw + hGrad.color = '#F00'; + hGrad.color2 = '#F00'; + + switch (type.toLowerCase()) { + case 's': + vGrad.color = vGrad.color2 = '#FFF'; + break; + case 'v': + vGrad.color = vGrad.color2 = '#000'; + break; + } + }; + + paletteObj.elm = vmlContainer; + paletteObj.draw = drawFunc; + } + + return paletteObj; + }, + + + createSliderGradient : function () { + + var sliderObj = { + elm: null, + draw: null + }; + + if (jsc.isCanvasSupported) { + // Canvas implementation for modern browsers + + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + + var drawFunc = function (width, height, color1, color2) { + canvas.width = width; + canvas.height = height; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); + grad.addColorStop(0, color1); + grad.addColorStop(1, color2); + + ctx.fillStyle = grad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }; + + sliderObj.elm = canvas; + sliderObj.draw = drawFunc; + + } else { + // VML fallback for IE 7 and 8 + + jsc.initVML(); + + var vmlContainer = document.createElement('div'); + vmlContainer.style.position = 'relative'; + vmlContainer.style.overflow = 'hidden'; + + var grad = document.createElement(jsc._vmlNS + ':fill'); + grad.type = 'gradient'; + grad.method = 'linear'; + grad.angle = '180'; + + var rect = document.createElement(jsc._vmlNS + ':rect'); + rect.style.position = 'absolute'; + rect.style.left = -1 + 'px'; + rect.style.top = -1 + 'px'; + rect.stroked = false; + rect.appendChild(grad); + vmlContainer.appendChild(rect); + + var drawFunc = function (width, height, color1, color2) { + vmlContainer.style.width = width + 'px'; + vmlContainer.style.height = height + 'px'; + + rect.style.width = (width + 1) + 'px'; + rect.style.height = (height + 1) + 'px'; + + grad.color = color1; + grad.color2 = color2; + }; + + sliderObj.elm = vmlContainer; + sliderObj.draw = drawFunc; + } + + return sliderObj; + }, + + + leaveValue : 1<<0, + leaveStyle : 1<<1, + leavePad : 1<<2, + leaveSld : 1<<3, + + + BoxShadow : (function () { + var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) { + this.hShadow = hShadow; + this.vShadow = vShadow; + this.blur = blur; + this.spread = spread; + this.color = color; + this.inset = !!inset; + }; + + BoxShadow.prototype.toString = function () { + var vals = [ + Math.round(this.hShadow) + 'px', + Math.round(this.vShadow) + 'px', + Math.round(this.blur) + 'px', + Math.round(this.spread) + 'px', + this.color + ]; + if (this.inset) { + vals.push('inset'); + } + return vals.join(' '); + }; + + return BoxShadow; + })(), + + + // + // Usage: + // var myColor = new jscolor( [, ]) + // + + jscolor : function (targetElement, options) { + + // General options + // + this.value = null; // initial HEX color. To change it later, use methods fromString(), fromHSV() and fromRGB() + this.valueElement = targetElement; // element that will be used to display and input the color code + this.styleElement = targetElement; // element that will preview the picked color using CSS backgroundColor + this.required = true; // whether the associated text can be left empty + this.refine = true; // whether to refine the entered color code (e.g. uppercase it and remove whitespace) + this.hash = false; // whether to prefix the HEX color code with # symbol + this.uppercase = true; // whether to uppercase the color code + this.onFineChange = null; // called instantly every time the color changes (value can be either a function or a string with javascript code) + this.activeClass = 'jscolor-active'; // class to be set to the target element when a picker window is open on it + this.minS = 0; // min allowed saturation (0 - 100) + this.maxS = 100; // max allowed saturation (0 - 100) + this.minV = 0; // min allowed value (brightness) (0 - 100) + this.maxV = 100; // max allowed value (brightness) (0 - 100) + + // Accessing the picked color + // + this.hsv = [0, 0, 100]; // read-only [0-360, 0-100, 0-100] + this.rgb = [255, 255, 255]; // read-only [0-255, 0-255, 0-255] + + // Color Picker options + // + this.width = 181; // width of color palette (in px) + this.height = 101; // height of color palette (in px) + this.showOnClick = true; // whether to display the color picker when user clicks on its target element + this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls + this.position = 'bottom'; // left | right | top | bottom - position relative to the target element + this.smartPosition = true; // automatically change picker position when there is not enough space for it + this.sliderSize = 16; // px + this.crossSize = 8; // px + this.closable = false; // whether to display the Close button + this.closeText = 'Close'; + this.buttonColor = '#000000'; // CSS color + this.buttonHeight = 18; // px + this.padding = 12; // px + this.backgroundColor = '#FFFFFF'; // CSS color + this.borderWidth = 1; // px + this.borderColor = '#BBBBBB'; // CSS color + this.borderRadius = 8; // px + this.insetWidth = 1; // px + this.insetColor = '#BBBBBB'; // CSS color + this.shadow = true; // whether to display shadow + this.shadowBlur = 15; // px + this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color + this.pointerColor = '#4C4C4C'; // px + this.pointerBorderColor = '#FFFFFF'; // px + this.pointerBorderWidth = 1; // px + this.pointerThickness = 2; // px + this.zIndex = 1000; + this.container = null; // where to append the color picker (BODY element by default) + + + for (var opt in options) { + if (options.hasOwnProperty(opt)) { + this[opt] = options[opt]; + } + } + + + this.hide = function () { + ///console.log(this.styleElement) + if (isPickerOwner()) { + + //console.log('color picker hidden') + + //set the color to old color, in case the color is a duplicate that hasn't been resolved yet [lospec] + var hexInput = document.getElementById('jscolor-hex-input'); + var oldColor = '#'+rgbToHex(hexInput.oldColor); + this.fromString(oldColor) + document.getElementById('duplicate-color-warning').style.visibility = 'hidden'; + + //dialog is closed + dialogueOpen = false; + + detachPicker(); + } + }; + + //show the color picker + this.show = function () { + drawPicker(); + + //a dialog is open + dialogueOpen = true; + + //[lospec] + //find the hex input element + var hexInput = document.getElementById('jscolor-hex-input'); + + //set the value element to the hex input + this.valueElement = hexInput + + //update hex code + this.exportColor(); + + //set old color for updating colors on canvas + hexInput.oldColor = hexToRgb(hexInput.value); + + //set the color element to the clicked button + hexInput.colorElement = this.styleElement; + + //disable delete button if last color + var colors = document.getElementsByClassName('color-button'); + var deleteButton = document.getElementsByClassName('delete-color-button')[0]; + if(colors.length == 1) + deleteButton.classList.add('disabled'); + else + deleteButton.classList.remove('disabled'); + + //hide duplicate color warning + var duplicateColorWarning = document.getElementById("duplicate-color-warning"); + duplicateColorWarning.style.visibility = 'hidden'; + }; + + + this.redraw = function () { + if (isPickerOwner()) { + drawPicker(); + } + }; + + + this.importColor = function () { + if (!this.valueElement) { + this.exportColor(); + } else { + if (jsc.isElementType(this.valueElement, 'input')) { + if (!this.refine) { + if (!this.fromString(this.valueElement.value, jsc.leaveValue)) { + if (this.styleElement) { + this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; + this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; + this.styleElement.style.color = this.styleElement._jscOrigStyle.color; + } + this.exportColor(jsc.leaveValue | jsc.leaveStyle); + } + } else if (!this.required && /^\s*$/.test(this.valueElement.value)) { + this.valueElement.value = ''; + if (this.styleElement) { + this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; + this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; + this.styleElement.style.color = this.styleElement._jscOrigStyle.color; + } + this.exportColor(jsc.leaveValue | jsc.leaveStyle); + + } else if (this.fromString(this.valueElement.value)) { + // managed to import color successfully from the value -> OK, don't do anything + } else { + this.exportColor(); + } + } else { + // not an input element -> doesn't have any value + this.exportColor(); + } + } + }; + + + this.exportColor = function (flags) { + if (!(flags & jsc.leaveValue) && this.valueElement) { + var value = this.toString(); + if (this.uppercase) { value = value.toUpperCase(); } + if (this.hash) { value = '#' + value; } + + if (jsc.isElementType(this.valueElement, 'input')) { + //console.log('SETTING VALUE') + //this sets the value element's value + this.valueElement.value = value; + } else { + this.valueElement.innerHTML = value; + } + } + if (!(flags & jsc.leaveStyle)) { + if (this.styleElement) { + this.styleElement.style.backgroundImage = 'none'; + this.styleElement.style.backgroundColor = '#' + this.toString(); + this.styleElement.style.color = this.isLight() ? '#000' : '#FFF'; + } + } + if (!(flags & jsc.leavePad) && isPickerOwner()) { + redrawPad(); + } + if (!(flags & jsc.leaveSld) && isPickerOwner()) { + redrawSld(); + } + }; + + + // h: 0-360 + // s: 0-100 + // v: 0-100 + // + this.fromHSV = function (h, s, v, flags) { // null = don't change + if (h !== null) { + if (isNaN(h)) { return false; } + h = Math.max(0, Math.min(360, h)); + } + if (s !== null) { + if (isNaN(s)) { return false; } + s = Math.max(0, Math.min(100, this.maxS, s), this.minS); + } + if (v !== null) { + if (isNaN(v)) { return false; } + v = Math.max(0, Math.min(100, this.maxV, v), this.minV); + } + + this.rgb = HSV_RGB( + h===null ? this.hsv[0] : (this.hsv[0]=h), + s===null ? this.hsv[1] : (this.hsv[1]=s), + v===null ? this.hsv[2] : (this.hsv[2]=v) + ); + + this.exportColor(flags); + }; + + + // r: 0-255 + // g: 0-255 + // b: 0-255 + // + this.fromRGB = function (r, g, b, flags) { // null = don't change + if (r !== null) { + if (isNaN(r)) { return false; } + r = Math.max(0, Math.min(255, r)); + } + if (g !== null) { + if (isNaN(g)) { return false; } + g = Math.max(0, Math.min(255, g)); + } + if (b !== null) { + if (isNaN(b)) { return false; } + b = Math.max(0, Math.min(255, b)); + } + + var hsv = RGB_HSV( + r===null ? this.rgb[0] : r, + g===null ? this.rgb[1] : g, + b===null ? this.rgb[2] : b + ); + if (hsv[0] !== null) { + this.hsv[0] = Math.max(0, Math.min(360, hsv[0])); + } + if (hsv[2] !== 0) { + this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])); + } + this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); + + // update RGB according to final HSV, as some values might be trimmed + var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); + this.rgb[0] = rgb[0]; + this.rgb[1] = rgb[1]; + this.rgb[2] = rgb[2]; + + this.exportColor(flags); + }; + + + this.fromString = function (str, flags) { + var m; + if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) { + // HEX notation + // + + if (m[1].length === 6) { + // 6-char notation + this.fromRGB( + parseInt(m[1].substr(0,2),16), + parseInt(m[1].substr(2,2),16), + parseInt(m[1].substr(4,2),16), + flags + ); + } else { + // 3-char notation + this.fromRGB( + parseInt(m[1].charAt(0) + m[1].charAt(0),16), + parseInt(m[1].charAt(1) + m[1].charAt(1),16), + parseInt(m[1].charAt(2) + m[1].charAt(2),16), + flags + ); + } + return true; + + } else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { + var params = m[1].split(','); + var re = /^\s*(\d*)(\.\d+)?\s*$/; + var mR, mG, mB; + if ( + params.length >= 3 && + (mR = params[0].match(re)) && + (mG = params[1].match(re)) && + (mB = params[2].match(re)) + ) { + var r = parseFloat((mR[1] || '0') + (mR[2] || '')); + var g = parseFloat((mG[1] || '0') + (mG[2] || '')); + var b = parseFloat((mB[1] || '0') + (mB[2] || '')); + this.fromRGB(r, g, b, flags); + return true; + } + } + return false; + }; + + + this.toString = function () { + return ( + (0x100 | Math.round(this.rgb[0])).toString(16).substr(1) + + (0x100 | Math.round(this.rgb[1])).toString(16).substr(1) + + (0x100 | Math.round(this.rgb[2])).toString(16).substr(1) + ); + }; + + + this.toHEXString = function () { + return '#' + this.toString().toUpperCase(); + }; + + + this.toRGBString = function () { + return ('rgb(' + + Math.round(this.rgb[0]) + ',' + + Math.round(this.rgb[1]) + ',' + + Math.round(this.rgb[2]) + ')' + ); + }; + + //returns rgb color object [lospec] + this.toRgb = function () { + return { + r: Math.round(this.rgb[0]), + g: Math.round(this.rgb[1]), + b: Math.round(this.rgb[2]) + }; + }; + + + this.isLight = function () { + return ( + 0.213 * this.rgb[0] + + 0.715 * this.rgb[1] + + 0.072 * this.rgb[2] > + 255 / 2 + ); + }; + + + this._processParentElementsInDOM = function () { + if (this._linkedElementsProcessed) { return; } + this._linkedElementsProcessed = true; + + var elm = this.targetElement; + do { + // If the target element or one of its parent nodes has fixed position, + // then use fixed positioning instead + // + // Note: In Firefox, getComputedStyle returns null in a hidden iframe, + // that's why we need to check if the returned style object is non-empty + var currStyle = jsc.getStyle(elm); + if (currStyle && currStyle.position.toLowerCase() === 'fixed') { + this.fixed = true; + } + + if (elm !== this.targetElement) { + // Ensure to attach onParentScroll only once to each parent element + // (multiple targetElements can share the same parent nodes) + // + // Note: It's not just offsetParents that can be scrollable, + // that's why we loop through all parent nodes + if (!elm._jscEventsAttached) { + jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); + elm._jscEventsAttached = true; + } + } + } while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body')); + }; + + + // r: 0-255 + // g: 0-255 + // b: 0-255 + // + // returns: [ 0-360, 0-100, 0-100 ] + // + function RGB_HSV (r, g, b) { + r /= 255; + g /= 255; + b /= 255; + var n = Math.min(Math.min(r,g),b); + var v = Math.max(Math.max(r,g),b); + var m = v - n; + if (m === 0) { return [ null, 0, 100 * v ]; } + var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); + return [ + 60 * (h===6?0:h), + 100 * (m/v), + 100 * v + ]; + } + + + // h: 0-360 + // s: 0-100 + // v: 0-100 + // + // returns: [ 0-255, 0-255, 0-255 ] + // + function HSV_RGB (h, s, v) { + var u = 255 * (v / 100); + + if (h === null) { + return [ u, u, u ]; + } + + h /= 60; + s /= 100; + + var i = Math.floor(h); + var f = i%2 ? h-i : 1-(h-i); + var m = u * (1 - s); + var n = u * (1 - s * f); + switch (i) { + case 6: + case 0: return [u,n,m]; + case 1: return [n,u,m]; + case 2: return [m,u,n]; + case 3: return [m,n,u]; + case 4: return [n,m,u]; + case 5: return [u,m,n]; + } + } + + //this is called when the picker is closed + function detachPicker () { + jsc.unsetClass(THIS.targetElement, THIS.activeClass); + jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); + delete jsc.picker.owner; + } + + + function drawPicker () { + + //console.log('drawPicker ()') + // At this point, when drawing the picker, we know what the parent elements are + // and we can do all related DOM operations, such as registering events on them + // or checking their positioning + THIS._processParentElementsInDOM(); + + if (!jsc.picker) { + jsc.picker = { + owner: null, + wrap : document.createElement('div'), + box : document.createElement('div'), + boxS : document.createElement('div'), // shadow area + boxB : document.createElement('div'), // border + pad : document.createElement('div'), + padB : document.createElement('div'), // border + padM : document.createElement('div'), // mouse/touch area + padPal : jsc.createPalette(), + cross : document.createElement('div'), + crossBY : document.createElement('div'), // border Y + crossBX : document.createElement('div'), // border X + crossLY : document.createElement('div'), // line Y + crossLX : document.createElement('div'), // line X + sld : document.createElement('div'), + sldB : document.createElement('div'), // border + sldM : document.createElement('div'), // mouse/touch area + sldGrad : jsc.createSliderGradient(), + sldPtrS : document.createElement('div'), // slider pointer spacer + sldPtrIB : document.createElement('div'), // slider pointer inner border + sldPtrMB : document.createElement('div'), // slider pointer middle border + sldPtrOB : document.createElement('div'), // slider pointer outer border + btn : document.createElement('div'), + btnT : document.createElement('span') // text + }; + + jsc.picker.pad.appendChild(jsc.picker.padPal.elm); + jsc.picker.padB.appendChild(jsc.picker.pad); + jsc.picker.cross.appendChild(jsc.picker.crossBY); + jsc.picker.cross.appendChild(jsc.picker.crossBX); + jsc.picker.cross.appendChild(jsc.picker.crossLY); + jsc.picker.cross.appendChild(jsc.picker.crossLX); + jsc.picker.padB.appendChild(jsc.picker.cross); + jsc.picker.box.appendChild(jsc.picker.padB); + jsc.picker.box.appendChild(jsc.picker.padM); + + jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); + jsc.picker.sldB.appendChild(jsc.picker.sld); + jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); + jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); + jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); + jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); + jsc.picker.box.appendChild(jsc.picker.sldB); + jsc.picker.box.appendChild(jsc.picker.sldM); + + jsc.picker.btn.appendChild(jsc.picker.btnT); + jsc.picker.box.appendChild(jsc.picker.btn); + + jsc.picker.boxB.appendChild(jsc.picker.box); + jsc.picker.wrap.appendChild(jsc.picker.boxS); + jsc.picker.wrap.appendChild(jsc.picker.boxB); + } + + var p = jsc.picker; + + var displaySlider = !!jsc.getSliderComponent(THIS); + var dims = jsc.getPickerDims(THIS); + var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); + var padToSliderPadding = jsc.getPadToSliderPadding(THIS); + var borderRadius = Math.min( + THIS.borderRadius, + Math.round(THIS.padding * Math.PI)); // px + var padCursor = 'crosshair'; + + // wrap + p.wrap.classList.add('jscolor-wrap'); + p.wrap.style.clear = 'both'; + p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px'; + p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px'; + p.wrap.style.zIndex = THIS.zIndex; + + + // picker + p.box.classList.add('jscolor-box'); + p.box.style.width = dims[0] + 'px'; + p.box.style.height = (dims[1]+25) + 'px'; //added 25 for trash icon + + p.boxS.style.position = 'absolute'; + p.boxS.style.left = '0'; + p.boxS.style.top = '0'; + p.boxS.style.width = '100%'; + p.boxS.style.height = '100%'; + jsc.setBorderRadius(p.boxS, borderRadius + 'px'); + + // picker border + p.wrap.classList.add('jscolor-boxB'); + p.boxB.style.position = 'relative'; + p.boxB.style.border = THIS.borderWidth + 'px solid'; + p.boxB.style.borderColor = THIS.borderColor; + p.boxB.style.background = THIS.backgroundColor; + jsc.setBorderRadius(p.boxB, borderRadius + 'px'); + + // IE hack: + // If the element is transparent, IE will trigger the event on the elements under it, + // e.g. on Canvas or on elements with border + p.padM.style.background = + p.sldM.style.background = + '#FFF'; + jsc.setStyle(p.padM, 'opacity', '0'); + jsc.setStyle(p.sldM, 'opacity', '0'); + + // pad + p.pad.classList.add('jscolor-pad'); + p.pad.style.position = 'relative'; + p.pad.style.width = THIS.width + 'px'; + p.pad.style.height = THIS.height + 'px'; + + // pad palettes (HSV and HVS) + p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS)); + + // pad border + p.padB.classList.add('jscolor-padB'); + p.padB.style.position = 'absolute'; + p.padB.style.left = THIS.padding + 'px'; + p.padB.style.top = THIS.padding + 'px'; + p.padB.style.border = THIS.insetWidth + 'px solid'; + p.padB.style.borderColor = THIS.insetColor; + + // pad mouse area + p.padM.classList.add('jscolor-padM'); + p.padM._jscInstance = THIS; + p.padM._jscControlName = 'pad'; + p.padM.style.position = 'absolute'; + p.padM.style.left = '0'; + p.padM.style.top = '0'; + p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px'; + p.padM.style.height = dims[1] + 'px'; + p.padM.style.cursor = padCursor; + + // pad cross + p.cross.classList.add('jscolor-cross'); + p.cross.style.position = 'absolute'; + p.cross.style.left = + p.cross.style.top = + '0'; + p.cross.style.width = + p.cross.style.height = + crossOuterSize + 'px'; + + // pad cross border Y and X + p.crossBY.style.position = + p.crossBX.style.position = + 'absolute'; + p.crossBY.style.background = + p.crossBX.style.background = + THIS.pointerBorderColor; + p.crossBY.style.width = + p.crossBX.style.height = + (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; + p.crossBY.style.height = + p.crossBX.style.width = + crossOuterSize + 'px'; + p.crossBY.style.left = + p.crossBX.style.top = + (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px'; + p.crossBY.style.top = + p.crossBX.style.left = + '0'; + + // pad cross line Y and X + p.crossLY.style.position = + p.crossLX.style.position = + 'absolute'; + p.crossLY.style.background = + p.crossLX.style.background = + THIS.pointerColor; + p.crossLY.style.height = + p.crossLX.style.width = + (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px'; + p.crossLY.style.width = + p.crossLX.style.height = + THIS.pointerThickness + 'px'; + p.crossLY.style.left = + p.crossLX.style.top = + (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px'; + p.crossLY.style.top = + p.crossLX.style.left = + THIS.pointerBorderWidth + 'px'; + + // slider + p.sld.classList.add('jscolor-sld'); + p.sld.style.overflow = 'hidden'; + p.sld.style.width = THIS.sliderSize + 'px'; + p.sld.style.height = THIS.height + 'px'; + + // slider gradient + p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000'); + + // slider border + p.sldB.style.display = displaySlider ? 'block' : 'none'; + p.sldB.style.position = 'absolute'; + p.sldB.style.right = THIS.padding + 'px'; + p.sldB.style.top = THIS.padding + 'px'; + p.sldB.style.border = THIS.insetWidth + 'px solid'; + p.sldB.style.borderColor = THIS.insetColor; + + // slider mouse area + p.sldM._jscInstance = THIS; + p.sldM._jscControlName = 'sld'; + p.sldM.style.display = displaySlider ? 'block' : 'none'; + p.sldM.style.position = 'absolute'; + p.sldM.style.right = '0'; + p.sldM.style.top = '0'; + p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px'; + p.sldM.style.height = dims[1] + 'px'; + p.sldM.style.cursor = 'default'; + + // slider pointer inner and outer border + p.sldPtrIB.style.border = + p.sldPtrOB.style.border = + THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; + + // slider pointer outer border + p.sldPtrOB.style.position = 'absolute'; + p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; + p.sldPtrOB.style.top = '0'; + + // slider pointer middle border + p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; + + // slider pointer spacer + p.sldPtrS.style.width = THIS.sliderSize + 'px'; + p.sldPtrS.style.height = sliderPtrSpace + 'px'; + +/* REMOVED CLOSE BUTTON [lospec] + // the Close button + p.btn.classList.add('jscolor-btn'); + function setBtnBorder () { + var insetColors = THIS.insetColor.split(/\s+/); + var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; + p.btn.style.borderColor = outsetColor; + } + p.btn.style.display = THIS.closable ? 'block' : 'none'; + p.btn.style.position = 'absolute'; + p.btn.style.left = THIS.padding + 'px'; + p.btn.style.bottom = THIS.padding + 'px'; + p.btn.style.padding = '0 15px'; + p.btn.style.height = THIS.buttonHeight + 'px'; + p.btn.style.border = THIS.insetWidth + 'px solid'; + setBtnBorder(); + p.btn.style.color = THIS.buttonColor; + p.btn.style.font = '12px sans-serif'; + p.btn.style.textAlign = 'center'; + try { + p.btn.style.cursor = 'pointer'; + } catch(eOldIE) { + p.btn.style.cursor = 'hand'; + } + p.btn.onmousedown = function () { + THIS.hide(); + }; + p.btnT.style.lineHeight = THIS.buttonHeight + 'px'; + p.btnT.innerHTML = ''; + p.btnT.appendChild(document.createTextNode(THIS.closeText)); +*/ + + //CUSTOM PICKER BOTTOM - [lospec] + //when first initialized, we will want to move the colorPickerBottom + //to the bottom of the color picker box + if (colorPickerBottomAdded != true) { + //get color picker bottom + var jsColorPickerBottom = document.getElementsByClassName('jscolor-picker-bottom')[0]; + + //move it to the picker box + p.box.appendChild(jsColorPickerBottom); + + //unhide element + jsColorPickerBottom.style.display = 'block'; + + //set flag to only do this once + colorPickerBottomAdded = true; + + } + + // place pointers + redrawPad(); + redrawSld(); + + // If we are changing the owner without first closing the picker, + // make sure to first deal with the old owner + if (jsc.picker.owner && jsc.picker.owner !== THIS) { + jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass); + } + + // Set the new picker owner + jsc.picker.owner = THIS; + + // The redrawPosition() method needs picker.owner to be set, that's why we call it here, + // after setting the owner + if (jsc.isElementType(container, 'body')) { + jsc.redrawPosition(); + } else { + jsc._drawPosition(THIS, 0, 0, 'relative', false); + } + + if (p.wrap.parentNode != container) { + container.appendChild(p.wrap); + } + + jsc.setClass(THIS.targetElement, THIS.activeClass); + } + + + function redrawPad () { + // redraw the pad pointer + switch (jsc.getPadYComponent(THIS)) { + case 's': var yComponent = 1; break; + case 'v': var yComponent = 2; break; + } + var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1)); + var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); + var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); + var ofs = -Math.floor(crossOuterSize / 2); + jsc.picker.cross.style.left = (x + ofs) + 'px'; + jsc.picker.cross.style.top = (y + ofs) + 'px'; + + // redraw the slider + switch (jsc.getSliderComponent(THIS)) { + case 's': + var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]); + var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]); + var color1 = 'rgb(' + + Math.round(rgb1[0]) + ',' + + Math.round(rgb1[1]) + ',' + + Math.round(rgb1[2]) + ')'; + var color2 = 'rgb(' + + Math.round(rgb2[0]) + ',' + + Math.round(rgb2[1]) + ',' + + Math.round(rgb2[2]) + ')'; + jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); + break; + case 'v': + var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100); + var color1 = 'rgb(' + + Math.round(rgb[0]) + ',' + + Math.round(rgb[1]) + ',' + + Math.round(rgb[2]) + ')'; + var color2 = '#000'; + jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); + break; + } + } + + + function redrawSld () { + var sldComponent = jsc.getSliderComponent(THIS); + if (sldComponent) { + // redraw the slider pointer + switch (sldComponent) { + case 's': var yComponent = 1; break; + case 'v': var yComponent = 2; break; + } + var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); + jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px'; + } + } + + + function isPickerOwner () { + return jsc.picker && jsc.picker.owner === THIS; + } + + + function blurValue () { + //console.log('blurValue()') + THIS.importColor(); + } + + + // Find the target element + if (typeof targetElement === 'string') { + var id = targetElement; + var elm = document.getElementById(id); + if (elm) { + this.targetElement = elm; + } else { + jsc.warn('Could not find target element with ID \'' + id + '\''); + } + } else if (targetElement) { + this.targetElement = targetElement; + } else { + jsc.warn('Invalid target element: \'' + targetElement + '\''); + } + + if (this.targetElement._jscLinkedInstance) { + jsc.warn('Cannot link jscolor twice to the same element. Skipping.'); + return; + } + this.targetElement._jscLinkedInstance = this; + + // Find the value element + this.valueElement = jsc.fetchElement(this.valueElement); + // Find the style element + this.styleElement = jsc.fetchElement(this.styleElement); + + //console.log('VALUE ELEMENT: ', this.valueElement) + + var THIS = this; + var container = + this.container ? + jsc.fetchElement(this.container) : + document.getElementsByTagName('body')[0]; + var sliderPtrSpace = 3; // px + + // For BUTTON elements it's important to stop them from sending the form when clicked + // (e.g. in Safari) + if (jsc.isElementType(this.targetElement, 'button')) { + if (this.targetElement.onclick) { + var origCallback = this.targetElement.onclick; + this.targetElement.onclick = function (evt) { + origCallback.call(this, evt); + return false; + }; + } else { + this.targetElement.onclick = function () { return false; }; + } + } + + /* + var elm = this.targetElement; + do { + // If the target element or one of its offsetParents has fixed position, + // then use fixed positioning instead + // + // Note: In Firefox, getComputedStyle returns null in a hidden iframe, + // that's why we need to check if the returned style object is non-empty + var currStyle = jsc.getStyle(elm); + if (currStyle && currStyle.position.toLowerCase() === 'fixed') { + this.fixed = true; + } + + if (elm !== this.targetElement) { + // attach onParentScroll so that we can recompute the picker position + // when one of the offsetParents is scrolled + if (!elm._jscEventsAttached) { + jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); + elm._jscEventsAttached = true; + } + } + } while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body')); + */ + + // valueElement + if (this.valueElement) { + + if (jsc.isElementType(this.valueElement, 'input')) { + var updateField = function () { + //console.log('updateField()') + THIS.fromString(THIS.valueElement.value, jsc.leaveValue); + jsc.dispatchFineChange(THIS); + }; + jsc.attachEvent(this.valueElement, 'keyup', updateField); + jsc.attachEvent(this.valueElement, 'input', updateField); + jsc.attachEvent(this.valueElement, 'blur', blurValue); + this.valueElement.setAttribute('autocomplete', 'off'); + } + } + + // styleElement + if (this.styleElement) { + this.styleElement._jscOrigStyle = { + backgroundImage : this.styleElement.style.backgroundImage, + backgroundColor : this.styleElement.style.backgroundColor, + color : this.styleElement.style.color + }; + } + + if (this.value) { + // Try to set the color from the .value option and if unsuccessful, + // export the current color + this.fromString(this.value) || this.exportColor(); + } else { + this.importColor(); + } + } + +}; + + +//================================ +// Public properties and methods +//================================ + + +// By default, search for all elements with class="jscolor" and install a color picker on them. +// +// You can change what class name will be looked for by setting the property jscolor.lookupClass +// anywhere in your HTML document. To completely disable the automatic lookup, set it to null. +// +jsc.jscolor.lookupClass = 'jscolor'; + + +jsc.jscolor.installByClassName = function (className) { + var inputElms = document.getElementsByTagName('input'); + var buttonElms = document.getElementsByTagName('button'); + + jsc.tryInstallOnElements(inputElms, className); + jsc.tryInstallOnElements(buttonElms, className); +}; + + +jsc.register(); + + +return jsc.jscolor; + + +})(); } diff --git a/js/_loadImage.js b/js/_loadImage.js new file mode 100644 index 0000000..2867307 --- /dev/null +++ b/js/_loadImage.js @@ -0,0 +1,61 @@ +document.getElementById('open-image-browse-holder').addEventListener('change', function () { + if (this.files && this.files[0]) { + + //make sure file is allowed filetype + var fileContentType = this.files[0].type; + if (fileContentType == 'image/png' || fileContentType == 'image/gif') { + + //load file + var fileReader = new FileReader(); + fileReader.onload = function(e) { + var img = new Image(); + img.onload = function() { + + //create a new pixel with the images dimentions + newPixel(this.width, this.height, []); + + //draw the image onto the canvas + context.drawImage(img, 0, 0); + + var colorPalette = {}; + var imagePixelData = context.getImageData(0,0,this.width, this.height).data; + + var imagePixelDataLength = imagePixelData.length; + + console.log(imagePixelData) + for (var i = 0; i < imagePixelDataLength; i += 4) { + var color = imagePixelData[i]+','+imagePixelData[i + 1]+','+imagePixelData[i + 2]; + if (!colorPalette[color]) { + colorPalette[color] = {r:imagePixelData[i],g:imagePixelData[i + 1],b:imagePixelData[i + 2]}; + + //don't allow more than 256 colors to be added + if (Object.keys(colorPalette).length >= settings.maxColorsOnImportedImage) { + alert('The image loaded seems to have more than '+settings.maxColorsOnImportedImage+' colors.') + break; + } + } + } + + //create array out of colors object + var colorPaletteArray = []; + for (var color in colorPalette) { + if( colorPalette.hasOwnProperty(color) ) { + colorPaletteArray.push('#'+rgbToHex(colorPalette[color])); + } + } + console.log('COLOR PALETTE ARRAY', colorPaletteArray) + + //create palette form colors array + createColorPalette(colorPaletteArray, false); + + //track google event + ga('send', 'event', 'Pixel Editor Load', colorPalette.length, this.width+'/'+this.height); /*global ga*/ + + }; + img.src = e.target.result; + }; + fileReader.readAsDataURL(this.files[0]); + } + else alert('Only PNG and GIF files are allowed at this time.'); + } +}); \ No newline at end of file diff --git a/js/_loadPalette.js b/js/_loadPalette.js new file mode 100644 index 0000000..cbe9418 --- /dev/null +++ b/js/_loadPalette.js @@ -0,0 +1,50 @@ +//this is called when a user picks a file after selecting "load palette" from the new pixel dialogue + +document.getElementById('load-palette-browse-holder').addEventListener('change', function () { + if (this.files && this.files[0]) { + + //make sure file is allowed filetype + var fileContentType = this.files[0].type; + if (fileContentType == 'image/png' || fileContentType == 'image/gif') { + + //load file + var fileReader = new FileReader(); + fileReader.onload = function(e) { + var img = new Image(); + img.onload = function() { + + //draw image onto the temporary canvas + var loadPaletteCanvas = document.getElementById("load-palette-canvas-holder"); + var loadPaletteContext = loadPaletteCanvas.getContext("2d"); + + loadPaletteCanvas.width = img.width; + loadPaletteCanvas.height = img.height; + + loadPaletteContext.drawImage(img, 0, 0); + + //create array to hold found colors + var colorPalette = []; + var imagePixelData = loadPaletteContext.getImageData(0,0,this.width, this.height).data; + + console.log(imagePixelData) + + //loop through pixels looking for colors to add to palette + for (var i = 0; i < imagePixelData.length; i += 4) { + var color = '#'+rgbToHex(imagePixelData[i],imagePixelData[i + 1],imagePixelData[i + 2]); + if (colorPalette.indexOf(color) == -1) { + colorPalette.push(color); + } + } + + //add to palettes so that it can be loaded when they click okay + palettes['Loaded palette'] = {}; + palettes['Loaded palette'].colors = colorPalette; + setText('palette-button', 'Loaded palette'); + }; + img.src = e.target.result; + }; + fileReader.readAsDataURL(this.files[0]); + } + else alert('Only PNG and GIF files are supported at this time.'); + } +}); \ No newline at end of file diff --git a/js/_mouseEvents.js b/js/_mouseEvents.js new file mode 100644 index 0000000..cf6a54c --- /dev/null +++ b/js/_mouseEvents.js @@ -0,0 +1,210 @@ + +//mousedown - start drawing +window.addEventListener("mousedown", function (mouseEvent) { + + //if no document has been created yet, or this is a dialog open + if (!documentCreated || dialogueOpen) return; + //prevent right mouse clicks and such, which will open unwanted menus + //mouseEvent.preventDefault(); + + lastPos = getCursorPosition(mouseEvent); + + dragging = true; + //left or right click + if (mouseEvent.which == 1) { + + if (spacePressed) + currentTool = 'pan'; + else if (mouseEvent.altKey) + currentTool = 'eyedropper'; + else if (mouseEvent.target == canvas && currentTool == 'pencil') + new HistoryStateEditCanvas(); + //saveHistoryState({type: 'canvas', canvas: context.getImageData(0, 0, canvasSize[0], canvasSize[1])}); + + updateCursor(); + + draw(mouseEvent); + } + else if (currentTool == 'pencil' && mouseEvent.which == 3) { + currentTool = 'resize-brush'; + prevBrushSize=brushSize; + } + + if (currentTool == 'eyedropper' && mouseEvent.target == canvas) + eyedropperPreview.style.display = 'block'; + + return false; +}, false); + + + +//mouseup - end drawing +window.addEventListener("mouseup", function (mouseEvent) { + + closeMenu(); + + if (!documentCreated || dialogueOpen) return; + + if (currentTool == 'eyedropper' && mouseEvent.target == canvas) { + var cursorLocation = getCursorPosition(mouseEvent); + var selectedColor = context.getImageData(Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom),1,1); + var newColor = rgbToHex(selectedColor.data[0],selectedColor.data[1],selectedColor.data[2]); + + console.log(newColor); + + var colors = document.getElementsByClassName('color-button'); + for (var i = 0; i < colors.length; i++) { + console.log(colors[i].jscolor.toString()); + + //if picked color matches this color + if (newColor == colors[i].jscolor.toString()) { + console.log('color found'); + + //remove current color selection + var selectedColor = document.querySelector("#colors-menu li.selected") + if (selectedColor) selectedColor.classList.remove("selected"); + + //set current color + context.fillStyle = '#'+newColor; + + //make color selected + colors[i].parentElement.classList.add('selected'); + + //hide eyedropper + eyedropperPreview.style.display = 'none'; + } + } + + + } + else if (currentTool == 'fill' && mouseEvent.target == canvas) { + console.log('filling') + //if you clicked on anything but the canvas, do nothing + if (!mouseEvent.target == canvas) return; + + //get cursor postion + var cursorLocation = getCursorPosition(mouseEvent); + + //offset to match cursor point + cursorLocation[0] += 2; + cursorLocation[1] += 12; + + //fill starting at the location + fill(cursorLocation); + } + else if (currentTool == 'zoom' && mouseEvent.target == canvas) { + if (mouseEvent.which == 1) changeZoom('in', getCursorPosition(mouseEvent)); + else if (mouseEvent.which == 3) changeZoom('out', getCursorPosition(mouseEvent)) + } + + dragging = false; + currentTool = currentToolTemp; + + updateCursor(); + + +}, false); + + +//mouse is moving on canvas +window.addEventListener("mousemove", draw, false); +function draw (mouseEvent) { + var cursorLocation = getCursorPosition(mouseEvent); + + //if a document hasnt yet been created, exit this function + if (!documentCreated || dialogueOpen) return; + + + eyedropperPreview.style.display = 'none'; + + if (currentTool == 'pencil') { + + //move the brush preview + brushPreview.style.left = cursorLocation[0] + canvas.offsetLeft - brushSize * zoom / 2 + 'px'; + brushPreview.style.top = cursorLocation[1] + canvas.offsetTop - brushSize * zoom / 2 + 'px'; + + //hide brush preview outside of canvas / canvas view + if (mouseEvent.target == canvas || mouseEvent.target == canvasView) + brushPreview.style.visibility = 'visible'; + else + brushPreview.style.visibility = 'hidden'; + + //draw line to current pixel + if (dragging) { + if (mouseEvent.target == canvas || mouseEvent.target == canvasView) { + line(Math.floor(lastPos[0]/zoom),Math.floor(lastPos[1]/zoom),Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom)); + lastPos = cursorLocation; + } + } + + //get lightness value of color + var selectedColor = context.getImageData(Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom),1,1).data; + var colorLightness = Math.max(selectedColor[0],selectedColor[1],selectedColor[2]) + + //for the darkest 50% of colors, change the brush preview to dark mode + if (colorLightness>127) brushPreview.classList.remove('dark'); + else brushPreview.classList.add('dark'); + } + else if (currentTool == 'pan' && dragging) { + + + setCanvasOffset(canvas.offsetLeft + (cursorLocation[0] - lastPos[0]), canvas.offsetTop + (cursorLocation[1] - lastPos[1])) + /* + if ( + //right + canvas.offsetLeft + (cursorLocation[0] - lastPos[0]) < window.innerWidth - canvasSize[0]*zoom*0.25 - 48 && + //left + canvas.offsetLeft + (cursorLocation[0] - lastPos[0]) > -canvasSize[0]*zoom*0.75 + 64) + canvas.style.left = canvas.offsetLeft + (cursorLocation[0] - lastPos[0]) +'px'; + + if ( + //bottom + canvas.offsetTop + (cursorLocation[1] - lastPos[1]) < window.innerHeight-canvasSize[1]*zoom*0.25 && + //top + canvas.offsetTop + (cursorLocation[1] - lastPos[1]) > -canvasSize[0]*zoom*0.75 + 48) + canvas.style.top = canvas.offsetTop + (cursorLocation[1] - lastPos[1]) +'px'; + */ + } + else if (currentTool == 'eyedropper' && dragging && mouseEvent.target == canvas) { + var selectedColor = context.getImageData(Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom),1,1).data; + eyedropperPreview.style.borderColor = '#'+rgbToHex(selectedColor[0],selectedColor[1],selectedColor[2]); + eyedropperPreview.style.display = 'block'; + + eyedropperPreview.style.left = cursorLocation[0] + canvas.offsetLeft - 30 + 'px'; + eyedropperPreview.style.top = cursorLocation[1] + canvas.offsetTop - 30 + 'px'; + + var colorLightness = Math.max(selectedColor[0],selectedColor[1],selectedColor[2]); + + //for the darkest 50% of colors, change the eyedropper preview to dark mode + if (colorLightness>127) eyedropperPreview.classList.remove('dark'); + else eyedropperPreview.classList.add('dark'); + } + else if (currentTool == 'resize-brush' && dragging) { + + //get new brush size based on x distance from original clicking location + var distanceFromClick = cursorLocation[0] - lastPos[0]; + //var roundingAmount = 20 - Math.round(distanceFromClick/10); + //this doesnt work in reverse... because... it's not basing it off of the brush size which it should be + var brushSizeChange = Math.round(distanceFromClick/10); + var newBrushSize = prevBrushSize + brushSizeChange; + + //set the brush to the new size as long as its bigger than 1 + brushSize = Math.max(1,newBrushSize); + + //fix offset so the cursor stays centered + brushPreview.style.left = lastPos[0] + canvas.offsetLeft - brushSize * zoom / 2 + 'px'; + brushPreview.style.top = lastPos[1] + canvas.offsetTop - brushSize * zoom / 2 + 'px'; + + updateCursor(); + } +} + +//mousewheel scrroll +canvasView.addEventListener("wheel", function(mouseEvent){ + + if (currentTool == 'zoom' || mouseEvent.altKey) { + if (mouseEvent.deltaY < 0) changeZoom('in', getCursorPosition(mouseEvent)); + else if (mouseEvent.deltaY > 0) changeZoom('out', getCursorPosition(mouseEvent)) + } + +}); \ No newline at end of file diff --git a/js/_newPixel.js b/js/_newPixel.js new file mode 100644 index 0000000..21dc3ea --- /dev/null +++ b/js/_newPixel.js @@ -0,0 +1,79 @@ + +function newPixel (width, height, palette) { + + canvasSize = [width,height]; + + var maxHorizontalZoom = Math.floor(window.innerWidth/canvasSize[0]*0.75); + var maxVerticalZoom = Math.floor(window.innerHeight/canvasSize[1]*0.75); + + zoom = Math.min(maxHorizontalZoom,maxVerticalZoom); + if (zoom < 1) zoom = 1; + + //resize canvas + canvas.width = canvasSize[0]; + canvas.height = canvasSize[1]; + canvas.style.width = (canvas.width*zoom)+'px'; + canvas.style.height = (canvas.height*zoom)+'px'; + + //unhide canvas + canvas.style.display = 'block'; + + //center canvas in window + canvas.style.left = 64+canvasView.clientWidth/2-(canvasSize[0]*zoom/2)+'px'; + canvas.style.top = 48+canvasView.clientHeight/2-(canvasSize[1]*zoom/2)+'px'; + + //remove current palette + colors = document.getElementsByClassName('color-button'); + while (colors.length > 0) { + colors[0].parentElement.remove(); + } + + //add colors from selected palette + var selectedPalette = getText('palette-button'); + if (selectedPalette != 'Choose a palette...') { + + //if this palette isnt the one specified in the url, then reset the url + if (!palettes[selectedPalette].specified) + history.pushState(null, null, '/pixel-editor/app'); + + //fill the palette with specified palette + createColorPalette(palettes[selectedPalette].colors,true); + } + else { + //this wasn't a specified palette, so reset the url + history.pushState(null, null, '/pixel-editor/app'); + + //generate default colors + var fg = hslToRgb(Math.floor(Math.random()*255), 230,70); + var bg = hslToRgb(Math.floor(Math.random()*255), 230,170); + + //convert colors to hex + var defaultForegroundColor = rgbToHex(fg.r,fg.g,fg.b); + var defaultBackgroundColor = rgbToHex(bg.r,bg.g,bg.b); + + //add colors to paletee + addColor(defaultForegroundColor).classList.add('selected'); + addColor(defaultBackgroundColor); + + //fill background of canvas with bg color + context.fillStyle = '#'+defaultBackgroundColor; + context.fillRect(0, 0, canvasSize[0], canvasSize[1]); + + console.log('#'+defaultBackgroundColor) + + //set current drawing color as foreground color + context.fillStyle = '#'+defaultForegroundColor; + selectedPalette = 'none'; + } + + //reset undo and redo states + undoStates = []; + redoStates = []; + + closeDialogue(); + updateCursor(); + + document.getElementById('save-as-button').classList.remove('disabled'); + documentCreated = true; + +} \ No newline at end of file diff --git a/js/_onLoad.js b/js/_onLoad.js new file mode 100644 index 0000000..e4c9e18 --- /dev/null +++ b/js/_onLoad.js @@ -0,0 +1,12 @@ +//when the page is donw loading, you can get ready to start +window.onload = function(){ + updateCursor(); + + //if the user specified dimentions + if (specifiedDimentions) + //create a new pixel + newPixel(getValue('size-width'),getValue('size-height'),''); + else + //otherwise show the new pixel dialog + showDialogue('new-pixel', false); +}; \ No newline at end of file diff --git a/js/_onbeforeunload.js b/js/_onbeforeunload.js new file mode 100644 index 0000000..bfeef40 --- /dev/null +++ b/js/_onbeforeunload.js @@ -0,0 +1,7 @@ +//prevent user from leaving page with unsaved data +window.onbeforeunload = function() { + if (documentCreated) + return 'You will lose your pixel if it\'s not saved!'; + + else return; +} \ No newline at end of file diff --git a/js/_palettes.js b/js/_palettes.js new file mode 100644 index 0000000..d0c0e9e --- /dev/null +++ b/js/_palettes.js @@ -0,0 +1,62 @@ +//populate palettes list in new pixel menu +Object.keys(palettes).forEach(function(paletteName,index) { + + var palettesMenu = document.getElementById("palette-menu"); + + //create button + var button = document.createElement("button"); + button.appendChild(document.createTextNode(paletteName)); + + //insert new element + palettesMenu.appendChild(button); + + //if the palette was specified by the user, change the dropdown to it + if (palettes[paletteName].specified == true) { + setText('palette-button', paletteName); + //Show empty palette option + document.getElementById('no-palette-button').style.display = 'block'; + } + + on('click', button, function() { + + //hide the dropdown menu + deselect('palette-menu'); + deselect('palette-button'); + + //show empty palette option + document.getElementById('no-palette-button').style.display = 'block'; + + //set the text of the dropdown to the newly selected preset + setText('palette-button', paletteName); + }); +}); + +//select no palette +on('click', 'no-palette-button', function () { + document.getElementById('no-palette-button').style.display = 'none'; + setText('palette-button', 'Choose a palette...'); +}); + +//select load palette +on('click', 'load-palette-button', function () { + document.getElementById("load-palette-browse-holder").click(); +}); + + + + +on('click', 'palette-button', function (e){ + toggle('palette-button'); + toggle('palette-menu'); + + deselect('preset-button'); + deselect('preset-menu'); + e.stopPropagation(); +}); + +on('click', 'new-pixel', function (){ + deselect('preset-button'); + deselect('preset-menu'); + deselect('palette-button'); + deselect('palette-menu'); +}); \ No newline at end of file diff --git a/js/_presets.js b/js/_presets.js new file mode 100644 index 0000000..e93364d --- /dev/null +++ b/js/_presets.js @@ -0,0 +1,64 @@ +//prests +var presets = { + 'Gameboy Color': { + width: 240, + height: 203, + palette: 'Gameboy Color' + }, + 'PICO-8': { + width: 128, + height: 128, + palette: 'PICO-8', + }, + 'Commodore 64': { + width: 40, + height: 80, + palette: 'Commodore 64' + } +}; + +//populate preset list in new pixel menu +Object.keys(presets).forEach(function(presetName,index) { + + var presetsMenu = document.getElementById("preset-menu"); + + //create button + var button = document.createElement("button"); + button.appendChild(document.createTextNode(presetName)); + + //insert new element + presetsMenu.appendChild(button); + + //add click event listener + on('click', button, function() { + + //change dimentions on new pixel form + setValue('size-width', presets[presetName].width); + setValue('size-height', presets[presetName].height); + + //set the text of the dropdown to the newly selected preset + setText('palette-button', presets[presetName].palette); + + //hide the dropdown menu + deselect('preset-menu'); + deselect('preset-button'); + + //set the text of the dropdown to the newly selected preset + setText('preset-button', presetName); + }); + +}); + + +on('click', 'preset-button', function (e){ + //open or close the preset menu + toggle('preset-button'); + toggle('preset-menu'); + + //close the palette menu + deselect('palette-button'); + deselect('palette-menu'); + + //stop the click from propogating to the parent element + e.stopPropagation(); +}); diff --git a/js/_replaceAllOfColor.js b/js/_replaceAllOfColor.js new file mode 100644 index 0000000..d7c9f3a --- /dev/null +++ b/js/_replaceAllOfColor.js @@ -0,0 +1,25 @@ +//replaces all of a single color on the canvas with a different color +//input two rgb color objects {r:0,g:0,b:0} +function replaceAllOfColor (oldColor, newColor) { + + //convert strings to objects if nessesary + if (typeof oldColor === 'string') oldColor = hexToRgb(oldColor); + if (typeof newColor === 'string') newColor = hexToRgb(newColor); + + //create temporary image from canvas to search through + var tempImage = context.getImageData(0, 0, canvasSize[0], canvasSize[1]); + + //loop through all pixels + for (var i=0;i maxXOffset) + canvas.style.left = maxXOffset +'px'; + else + canvas.style.left = offsetLeft +'px'; + + //vertical offset + var minYOffset = -canvasSize[1]*zoom + 164; + var maxYOffset = window.innerHeight-100; + + if (offsetTop < minYOffset) + canvas.style.top = minYOffset +'px'; + else if (offsetTop > maxYOffset) + canvas.style.top = maxYOffset +'px'; + else + canvas.style.top = offsetTop +'px'; +} \ No newline at end of file diff --git a/js/_settings.js b/js/_settings.js new file mode 100644 index 0000000..fabcd75 --- /dev/null +++ b/js/_settings.js @@ -0,0 +1,45 @@ +var settings; + +if (!Cookies.enabled) { + document.getElementById('cookies-disabled-warning').style.display = 'block'; +} + +//try to load settings from cookie +var settingsFromCookie = Cookies.get('pixelEditorSettings'); +if(!settingsFromCookie) { + console.log('settings cookie not found') + settings = { + switchToChangedColor: true, + enableDynamicCursorOutline: true, //unused - performance + enableBrushPreview: true, //unused - performance + enableEyedropperPreview: true, //unused - performance + numberOfHistoryStates: 20, + maxColorsOnImportedImage: 128 + }; +} +else{ + console.log('settings cookie found'); + console.log(settingsFromCookie); + var settings = JSON.parse(settingsFromCookie); +} +console.log(settings); + +//on clicking the save button in the settings dialog +on('click', 'save-settings', function (){ + + //check if values are valid + if (isNaN(getValue('setting-numberOfHistoryStates'))) { + alert('Invalid value for numberOfHistoryStates') + return; + } + + //save new settings to settings object + settings.numberOfHistoryStates = getValue('setting-numberOfHistoryStates'); + + //save settings object to cookie + var cookieValue = JSON.stringify(settings); + Cookies.set('pixelEditorSettings', cookieValue, { expires: Infinity }); + + //close window + closeDialogue(); +}); \ No newline at end of file diff --git a/js/_toolButtons.js b/js/_toolButtons.js new file mode 100644 index 0000000..1678c56 --- /dev/null +++ b/js/_toolButtons.js @@ -0,0 +1,47 @@ +//pencil +on('click',"pencil-button", function(){ + changeTool('pencil'); +}, false); + +//pencil bigger +on('click',"pencil-bigger-button", function(){ + brushSize++; + updateCursor(); +}, false); + +//pencil smaller +on('click',"pencil-smaller-button", function(e){ + if(brushSize > 1) brushSize--; + updateCursor(); +}, false); + +//fill +on('click',"fill-button", function(){ + changeTool('fill'); +}, false); + +//pan +on('click',"pan-button", function(){ + changeTool('pan'); +}, false); + +//eyedropper +on('click',"eyedropper-button", function(){ + changeTool('eyedropper'); +}, false); + +//zoom tool button +on('click',"zoom-button", function(){ + changeTool('zoom'); +}, false); + +//zoom in button +on('click',"zoom-in-button", function(){ + //changeZoom('in',[window.innerWidth/2-canvas.offsetLeft,window.innerHeight/2-canvas.offsetTop]); + changeZoom('in',[canvasSize[0]*zoom/2,canvasSize[1]*zoom/2]); +}, false); + +//zoom out button +on('click',"zoom-out-button", function(){ + changeZoom('out',[canvasSize[0]*zoom/2,canvasSize[1]*zoom/2]); +}, false); \ No newline at end of file diff --git a/js/_updateCursor.js b/js/_updateCursor.js new file mode 100644 index 0000000..81bfe9e --- /dev/null +++ b/js/_updateCursor.js @@ -0,0 +1,32 @@ + +//set the correct cursor for the current tool +function updateCursor () { + if (currentTool == 'pencil' || currentTool == 'resize-brush') { + canvasView.style.cursor = 'crosshair'; + brushPreview.style.display = 'block'; + brushPreview.style.width = brushSize * zoom + 'px'; + brushPreview.style.height = brushSize * zoom + 'px'; + } else + brushPreview.style.display = 'none'; + + if (currentTool == 'eyedropper') { + canvasView.style.cursor = "url('/pixel-editor/eyedropper.png'), auto"; + } else + eyedropperPreview.style.display = 'none'; + + if (currentTool == 'pan') + if (dragging) + canvasView.style.cursor = "url('/pixel-editor/pan-held.png'), auto"; + else + canvasView.style.cursor = "url('/pixel-editor/pan.png'), auto"; + + if (currentTool == 'fill') + canvasView.style.cursor = "url('/pixel-editor/fill.png'), auto"; + + if (currentTool == 'zoom') + canvasView.style.cursor = "url('/pixel-editor/zoom-in.png'), auto"; + + if (currentTool == 'resize-brush') + canvasView.style.cursor = 'default'; + +} \ No newline at end of file diff --git a/js/_variables.js b/js/_variables.js new file mode 100644 index 0000000..819de71 --- /dev/null +++ b/js/_variables.js @@ -0,0 +1,25 @@ +//init variables +var canvasSize,zoom; +var dragging = false; +var lastPos = [0,0]; +var canvasPosition; +var currentTool = 'pencil'; +var currentToolTemp = 'pencil'; +var brushSize = 1; +var prevBrushSize = 1; +var menuOpen = false; +var dialogueOpen = false; +var documentCreated = false; + +//common elements +var brushPreview = document.getElementById("brush-preview"); +var eyedropperPreview = document.getElementById("eyedropper-preview"); +var canvasView = document.getElementById("canvas-view"); +var colors = document.getElementsByClassName("color-button"); +var colorsMenu = document.getElementById("colors-menu"); +var popUpContainer = document.getElementById("pop-up-container"); + +//html canvas +var canvas = document.getElementById("pixel-canvas"); +var context = canvas.getContext("2d"); + diff --git a/js/pixel-editor.js b/js/pixel-editor.js new file mode 100644 index 0000000..b507cac --- /dev/null +++ b/js/pixel-editor.js @@ -0,0 +1,67 @@ + + +/**utilities**/ +//=include utilities/on.js +//=include utilities/onChildren.js +//=include utilities/onClick.js +//=include utilities/onClickChildren.js +//=include utilities/select.js +//=include utilities/getSetText.js +//=include utilities/getSetValue.js +//=include utilities/hexToRgb.js +//=include utilities/rgbToHex.js +//=include utilities/rgbToHsl.js +//=include utilities/hslToRgb.js +//=include libraries/cookies.js + + + +/**init**/ +//=include _variables.js +//=include _settings.js + +/**dropdown formatting**/ +//=include _presets.js +//=include _palettes.js + +/**functions**/ +//=include _newPixel.js +//=include _createColorPalette.js +//=include _setCanvasOffset.js +//=include _changeZoom.js +//=include _addColor.js +//=include _colorChanged.js +//=include _initColor.js +//=include _changeTool.js +//=include _dialogue.js +//=include _updateCursor.js +//=include _drawLine.js +//=include _getCursorPosition.js +//=include _fill.js +//=include _history.js +//=include _deleteColor.js +//=include _replaceAllOfColor.js + + +/**load file**/ +//=include _loadImage.js +//=include _loadPalette.js + +/**event listeners**/ +//=include _hotkeyListener.js +//=include _mouseEvents.js + +/**buttons**/ +//=include _toolButtons.js +//=include _addColorButton.js +//=include _clickedColor.js +//=include _fileMenu.js +//=include _createButton.js + + +/**onload**/ +//=include _onLoad.js +//=include _onbeforeunload.js + +/**libraries**/ +//=include _jscolor.js \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e62fec8 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4122 @@ +{ + "name": "pixel-editor", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=" + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "requires": { + "buffer-equal": "^1.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" + }, + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "requires": { + "lodash": "^4.17.11" + } + }, + "async-done": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.1.tgz", + "integrity": "sha512-R1BaUeJ4PMoLNJuk+0tLJgjmEqVsdN118+Z8O+alhnQDQgy0kmD5Mqi0DNEmMx2LM0Ed5yekKu+ZXYvIHceicg==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^1.0.7", + "stream-exhaust": "^1.0.1" + } + }, + "async-each": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.2.tgz", + "integrity": "sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg==" + }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "requires": { + "async-done": "^1.2.2" + } + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "binary-extensions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", + "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + } + } + }, + "chalk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "requires": { + "ansi-styles": "^1.1.0", + "escape-string-regexp": "^1.0.0", + "has-ansi": "^0.1.0", + "strip-ansi": "^0.3.0", + "supports-color": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=" + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "requires": { + "ansi-regex": "^0.2.1" + } + } + } + }, + "chokidar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", + "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=" + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=" + }, + "cloneable-readable": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", + "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "requires": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "optional": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "copy-props": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "requires": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "requires": { + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "requires": { + "readable-stream": "~1.1.9" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "requires": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es5-ext": { + "version": "0.10.49", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.49.tgz", + "integrity": "sha512-3NMEhi57E31qdzmYp2jwRArIUsj1HI/RxbQ4bgnSB+AIKIxsAmTiK83bYMifIcpWvEc3P1X30DhUKOqEtF/kvg==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "requires": { + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "event-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.1.7.tgz", + "integrity": "sha1-tMVAAS0P4UmEIPPYlGAI22OTw3o=", + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.2", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "fined": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz", + "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==", + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } + }, + "foreachasync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", + "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=" + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "bundled": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "optional": true + }, + "semver": { + "version": "5.6.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "requires": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + } + }, + "glob-watcher": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", + "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "requires": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "object.defaults": "^1.1.0" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "requires": { + "sparkles": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "gulp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.0.tgz", + "integrity": "sha1-lXZsYB2t5Kd+0+eyttwDiBtZY2Y=", + "requires": { + "glob-watcher": "^5.0.0", + "gulp-cli": "^2.0.0", + "undertaker": "^1.0.0", + "vinyl-fs": "^3.0.0" + }, + "dependencies": { + "gulp-cli": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.1.0.tgz", + "integrity": "sha512-txzgdFVlEPShBZus6JJyGyKJoBVDq6Do0ZQgIgx5RAsmhNVTDjymmOxpQvo3c20m66FldilS68ZXj2Q9w5dKbA==", + "requires": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.1.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.0.1", + "yargs": "^7.1.0" + } + } + } + }, + "gulp-include": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/gulp-include/-/gulp-include-2.3.1.tgz", + "integrity": "sha1-8eDtPw/QdMNHx+WfnPA409vbPjA=", + "requires": { + "event-stream": "~3.1.0", + "glob": "^5.0.12", + "gulp-util": "~2.2.10", + "source-map": "^0.5.1", + "strip-bom": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "gulp-util": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", + "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", + "requires": { + "chalk": "^0.5.0", + "dateformat": "^1.0.7-1.2.3", + "lodash._reinterpolate": "^2.4.1", + "lodash.template": "^2.4.1", + "minimist": "^0.2.0", + "multipipe": "^0.1.0", + "through2": "^0.5.0", + "vinyl": "^0.2.1" + }, + "dependencies": { + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~3.0.0" + } + }, + "vinyl": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", + "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", + "requires": { + "clone-stats": "~0.0.1" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=" + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "requires": { + "glogg": "^1.0.0" + } + }, + "handlebars": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.13.tgz", + "integrity": "sha512-uydY0jy4Z3wy/iGXsi64UtLD4t1fFJe16c/NFxsYE4WdQis8ZCzOXUZaPQNG0e5bgtLQV41QTfqBindhEjnpyQ==", + "requires": { + "async": "^2.5.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "handlebars-helper-svg": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handlebars-helper-svg/-/handlebars-helper-svg-2.0.1.tgz", + "integrity": "sha1-thZfCDtYeMywB+EXNciISxd9ags=", + "requires": { + "ltx": "^2.3.0", + "resolve": "^1.1.7" + } + }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "requires": { + "ansi-regex": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=" + } + } + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hbs": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/hbs/-/hbs-4.0.3.tgz", + "integrity": "sha512-u8AAbtYUWqNATGEruLdlyAbN+lOtKED+mn38t/13ZkeaKy5BObknFuL1Gtmj0QwXgcSizQ68FHOP1ZST/HP+dg==", + "requires": { + "handlebars": "4.0.13", + "walk": "2.3.9" + } + }, + "hbs-register-helpers": { + "version": "git+https://skeddles@bitbucket.org/skeddles/hbs-register-helpers.git#e1b4688039cdbe7a175902357ffda5679837664c", + "from": "git+https://skeddles@bitbucket.org/skeddles/hbs-register-helpers.git", + "requires": { + "glob": "^7.1.3", + "hbs": "^4.0.3", + "path": "^0.12.7" + } + }, + "hbs-register-partials": { + "version": "git+https://skeddles@bitbucket.org/skeddles/hbs-register-partials.git#7618436ae3ea2d9536bcef3e64027729689f9ad3", + "from": "git+https://skeddles@bitbucket.org/skeddles/hbs-register-partials.git", + "requires": { + "glob": "^7.1.3", + "hbs": "^4.0.3" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", + "requires": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + } + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "requires": { + "readable-stream": "^2.0.5" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "requires": { + "flush-write-stream": "^1.0.2" + } + }, + "liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "requires": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash._escapehtmlchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", + "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", + "requires": { + "lodash._htmlescapes": "~2.4.1" + } + }, + "lodash._escapestringchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", + "integrity": "sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I=" + }, + "lodash._htmlescapes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", + "integrity": "sha1-MtFL8IRLbeb4tioFG09nwii2JMs=" + }, + "lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=" + }, + "lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=" + }, + "lodash._reinterpolate": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", + "integrity": "sha1-TxInqlqHEfxjL1sHofRgequLMiI=" + }, + "lodash._reunescapedhtml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", + "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", + "requires": { + "lodash._htmlescapes": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.defaults": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", + "requires": { + "lodash._objecttypes": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "lodash.escape": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", + "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", + "requires": { + "lodash._escapehtmlchar": "~2.4.1", + "lodash._reunescapedhtml": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "lodash.template": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", + "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", + "requires": { + "lodash._escapestringchar": "~2.4.1", + "lodash._reinterpolate": "~2.4.1", + "lodash.defaults": "~2.4.1", + "lodash.escape": "~2.4.1", + "lodash.keys": "~2.4.1", + "lodash.templatesettings": "~2.4.1", + "lodash.values": "~2.4.1" + } + }, + "lodash.templatesettings": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", + "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", + "requires": { + "lodash._reinterpolate": "~2.4.1", + "lodash.escape": "~2.4.1" + } + }, + "lodash.values": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", + "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", + "requires": { + "lodash.keys": "~2.4.1" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "ltx": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/ltx/-/ltx-2.8.1.tgz", + "integrity": "sha512-l4H1FS9I6IVqwvIpUHsSgyxE6t2jP7qd/2MeVG1UhmVK6vlHsQpfm2KNUcbdImeE0ai04vl1qTCF4CPCJqhknQ==", + "requires": { + "inherits": "^2.0.1" + } + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "requires": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "dependencies": { + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", + "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" + }, + "mime-types": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", + "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "requires": { + "mime-db": "~1.38.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", + "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=" + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "requires": { + "duplexer2": "0.0.2" + } + }, + "mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==" + }, + "nan": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "now-and-later": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.0.tgz", + "integrity": "sha1-vGHLtFbXnLMiB85HygUTb/Ln1u4=", + "requires": { + "once": "^1.3.2" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "opn": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", + "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + } + } + }, + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==" + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "requires": { + "through": "~2.3" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + } + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "requires": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "requires": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" + }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "requires": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "requires": { + "value-or-function": "^3.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.17.3.tgz", + "integrity": "sha512-S4vJawbrNUxJUBiHLXPYUKZCoO6cvq3/3ZFBV66a+PafTxcDEFJB+FHLDFl0P+rUfha/703ajEXMuGTYhJESkQ==", + "requires": { + "chokidar": "^2.0.0" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "requires": { + "sver-compat": "^1.5.0" + } + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==" + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==" + }, + "split": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", + "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", + "requires": { + "through": "2" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "requires": { + "duplexer": "~0.1.1" + } + }, + "stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==" + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=" + }, + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "requires": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" + }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "requires": { + "through2": "^2.0.3" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uglify-js": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.2.tgz", + "integrity": "sha512-imog1WIsi9Yb56yRt5TfYVxGmnWs3WSGU73ieSOlMVFwhJCA9W8fqFFMMj4kgDqiS/80LGdsYnWL7O9UcjEBlg==", + "optional": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, + "undertaker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz", + "integrity": "sha1-M52kZGJS0ILcN45wgGcpl1DhG0k=", + "requires": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=" + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==" + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "v8flags": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz", + "integrity": "sha512-MtivA7GF24yMPte9Rp/BWGCYQNaUj86zeYxV/x2RRJMKagImbbv3u8iJC57lNhWLPcGLJmHcHmFWkNsplbbLWw==", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + }, + "vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "requires": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + } + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "requires": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + } + }, + "vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "requires": { + "source-map": "^0.5.1" + } + }, + "walk": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz", + "integrity": "sha1-MbTbZnjyrgHDnqn7hyWpAx5Vins=", + "requires": { + "foreachasync": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "requires": { + "camelcase": "^3.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fca1eef --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "pixel-editor", + "version": "1.0.0", + "description": "Online pixel art creation tool", + "main": "build.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Lospec", + "license": "ISC", + "dependencies": { + "express": "^4.16.4", + "fs-extra": "^7.0.1", + "glob": "^7.1.3", + "gulp": "^4.0.0", + "gulp-include": "^2.3.1", + "handlebars-helper-svg": "^2.0.1", + "hbs": "^4.0.3", + "opn": "^6.0.0", + "sass": "^1.17.3", + "hbs-register-helpers": "git+https://skeddles@bitbucket.org/skeddles/hbs-register-helpers.git", + "hbs-register-partials": "git+https://skeddles@bitbucket.org/skeddles/hbs-register-partials.git" + } +} diff --git a/views/pixel-editor-splash-page.hbs b/views/pixel-editor-splash-page.hbs new file mode 100644 index 0000000..a8fe6c1 --- /dev/null +++ b/views/pixel-editor-splash-page.hbs @@ -0,0 +1,31 @@ +

{{title}}

+ +

+The Lospec Pixel editor is a free pixel art program that you can use right here +in your web browser. Our goal was to create an easy to use, intuitive and +unobtrusive pixel art application that you can use anywhere. Whether you're +creating assets for a game or just want to make 8 bit art, this tool is an easy +way to pixel fast. +

+ +Lospec Pixel Editor Screenshot + +

+This application does not have all the features of more advanced desktop editor, +but we will add more over time. Desperate for a feature? Talk about it with us +in the Lospec subreddit. +It currently features a pencil, fill, pan, eyedropper +and zoom tool. You can also easily adjust any colors in your palette. You can +use any palette in our Palette List by clicking the pencil next to the palette +title. +

+ +

+This app currently only works on desktops, and requires a modern browser such as +the latest versions of Firefox or Chrome. +

+ +Enter app now  {{svg "angle-right.svg" width="32" height="32"}} + + + diff --git a/views/pixel-editor.hbs b/views/pixel-editor.hbs new file mode 100644 index 0000000..e87e48c --- /dev/null +++ b/views/pixel-editor.hbs @@ -0,0 +1,234 @@ + + + + + + {{title}} + + + + + {{{google-analytics}}} + {{{favicons}}} + + + + + +
+
+

Warning: a modern, desktop, web browser is required to use this tool.

+

We detected that you may have an out of date or unsupported web browser. This tool, like many others on this site and across the web uses features only available in new web browsers. We reccommend updating your current browser or downloading Firefox or Chrome.

+ +
+
+ + +
+ + + + + + + + +
+ + +
    +
  • + + + +
  • +
  • +
  • +
  • +
  • + + + +
  • +
+ +
    + + {{! +
  • + }} + +
  • +
+
+
+ +
+ +
+
+ +
+ dl + + + +
+ + + +
+ # +
{{svg "warning.svg" width="14" height="12" }}
+ +
+ +
+ {{svg "adjust.svg" width="20" height="20" }} +
+ +
+
+ +

New Pixel

+ +

Preset

+ + + +

Size

+ {{svg "x.svg" width="16" height="16" class="dimentions-x"}} +

Palette

+ + + +
Creating a new pixel will discard your current one.
+
+ +
+
+
+ +

Help

+

Palette

+
    +
  • Left Click - Choose Color
  • +
  • Right Click - Edit Color
  • +
+

Hotkeys

+
    +
  • Pencil: B or 1
  • +
  • Fill: F or 2
  • +
  • Eyedropper: E or 3
  • +
  • Pan: P or M or 4
  • +
  • Zoom: Z or 5
  • +
  • Undo: Ctrl + Z
  • +
  • Redo: Ctrl + Y or Ctrl + Alt + Z
  • +
+

Mouse Shortcuts

+
    +
  • Alt + Click - Eyedropper
  • +
  • Space + Click - Pan
  • +
  • Alt + Scroll Wheel - Zoom
  • +
+
+
+ +

About Lospec Pixel Editor

+
version 1.0.0
+

This is a web-based tool for creating and editing pixel art.

+

The goal of this tool is to be an accessible and intuitive tool that's simple enough for a first time pixel artist while still being usable enough for a veteran.

+

In the future I hope to add enough features to become a full fledged pixel art editor, with everything an artist could need.

+

About Lospec

+

Lospec is a website created to host tools for pixel artists. To see more of our tools, visit our homepage. To hear about any updates or new tools, follow us on Twitter.

+
+
+ +

Changelog

+

Version 1.0.0

+
    +
  • Initial Release
  • +
+
+
+ +

Credits

+

Icons

+ +
+
+ +

Settings

+ +
+ +
+ +

Your browsers cookies are disabled, settings will be lost upon closing this page.

+ +
+ +
+
+
+ + + + + + + \ No newline at end of file